feat(batch2): verify sublist helper remainder features

This commit is contained in:
Joseph Doherty
2026-02-28 07:11:42 -05:00
parent 38b6fc86db
commit 1a0ac59df8
5 changed files with 155 additions and 35 deletions

View File

@@ -58,7 +58,7 @@ public sealed class SubscriptionIndex
private long _cacheHits;
private long _inserts;
private long _removes;
private SublistLevel _root;
private Level _root;
private Dictionary<string, SubscriptionIndexResult>? _cache;
private int _ccSweep;
private NotifyMaps? _notify;
@@ -70,7 +70,7 @@ public sealed class SubscriptionIndex
private SubscriptionIndex(bool enableCache)
{
_root = new SublistLevel();
_root = new Level();
_cache = enableCache ? new Dictionary<string, SubscriptionIndexResult>() : null;
}
@@ -116,7 +116,7 @@ public sealed class SubscriptionIndex
try
{
bool sfwc = false, haswc = false, isnew = false;
SublistNode? n = null;
Node? n = null;
var l = _root;
var start = 0;
@@ -135,7 +135,7 @@ public sealed class SubscriptionIndex
{
if (!l.Nodes.TryGetValue(t, out n))
{
n = new SublistNode();
n = new Node();
l.Nodes[t] = n;
}
}
@@ -148,7 +148,7 @@ public sealed class SubscriptionIndex
haswc = true;
if (n == null)
{
n = new SublistNode();
n = new Node();
l.Pwc = n;
}
break;
@@ -158,21 +158,21 @@ public sealed class SubscriptionIndex
sfwc = true;
if (n == null)
{
n = new SublistNode();
n = new Node();
l.Fwc = n;
}
break;
default:
if (!l.Nodes.TryGetValue(t, out n))
{
n = new SublistNode();
n = new Node();
l.Nodes[t] = n;
}
break;
}
}
n.Next ??= new SublistLevel();
n.Next ??= new Level();
l = n.Next;
start = i + 1;
@@ -446,7 +446,7 @@ public sealed class SubscriptionIndex
try
{
bool sfwc = false, haswc = false;
SublistNode? n = null;
Node? n = null;
var l = _root;
var lnts = new LevelNodeToken[32];
@@ -535,7 +535,7 @@ public sealed class SubscriptionIndex
}
}
private static (bool found, bool last) RemoveFromNode(SublistNode? n, Subscription sub)
private static (bool found, bool last) RemoveFromNode(Node? n, Subscription sub)
{
if (n == null) return (false, true);
@@ -743,9 +743,9 @@ public sealed class SubscriptionIndex
// Private: Trie matching (matchLevel)
// -------------------------------------------------------------------------
private static void MatchLevel(SublistLevel? l, string[] toks, SubscriptionIndexResult results)
private static void MatchLevel(Level? l, string[] toks, SubscriptionIndexResult results)
{
SublistNode? pwc = null, n = null;
Node? pwc = null, n = null;
for (int i = 0; i < toks.Length; i++)
{
if (l == null) return;
@@ -760,9 +760,9 @@ public sealed class SubscriptionIndex
if (pwc != null) AddNodeToResults(pwc, results);
}
private static bool MatchLevelForAny(SublistLevel? l, ReadOnlySpan<string> toks, ref int np, ref int nq)
private static bool MatchLevelForAny(Level? l, ReadOnlySpan<string> toks, ref int np, ref int nq)
{
SublistNode? pwc = null, n = null;
Node? pwc = null, n = null;
for (int i = 0; i < toks.Length; i++)
{
if (l == null) return false;
@@ -804,7 +804,7 @@ public sealed class SubscriptionIndex
// Private: Reverse match
// -------------------------------------------------------------------------
private static void ReverseMatchLevel(SublistLevel? l, ReadOnlySpan<string> toks, SublistNode? n, SubscriptionIndexResult results)
private static void ReverseMatchLevel(Level? l, ReadOnlySpan<string> toks, Node? n, SubscriptionIndexResult results)
{
if (l == null) return;
for (int i = 0; i < toks.Length; i++)
@@ -848,7 +848,7 @@ public sealed class SubscriptionIndex
if (n != null) AddNodeToResults(n, results);
}
private static void GetAllNodes(SublistLevel? l, SubscriptionIndexResult results)
private static void GetAllNodes(Level? l, SubscriptionIndexResult results)
{
if (l == null) return;
if (l.Pwc != null) AddNodeToResults(l.Pwc, results);
@@ -864,7 +864,7 @@ public sealed class SubscriptionIndex
// Private: addNodeToResults
// -------------------------------------------------------------------------
private static void AddNodeToResults(SublistNode n, SubscriptionIndexResult results)
private static void AddNodeToResults(Node n, SubscriptionIndexResult results)
{
// Plain subscriptions.
if (n.PList != null)
@@ -940,7 +940,7 @@ public sealed class SubscriptionIndex
}
}
private static void AddNodeToSubsLocal(SublistNode n, List<Subscription> subs, bool includeLeafHubs)
private static void AddNodeToSubsLocal(Node n, List<Subscription> subs, bool includeLeafHubs)
{
if (n.PList != null)
{
@@ -960,7 +960,7 @@ public sealed class SubscriptionIndex
}
}
private static void CollectLocalSubs(SublistLevel? l, List<Subscription> subs, bool includeLeafHubs)
private static void CollectLocalSubs(Level? l, List<Subscription> subs, bool includeLeafHubs)
{
if (l == null) return;
foreach (var n in l.Nodes.Values)
@@ -972,7 +972,7 @@ public sealed class SubscriptionIndex
if (l.Fwc != null) { AddNodeToSubsLocal(l.Fwc, subs, includeLeafHubs); CollectLocalSubs(l.Fwc.Next, subs, includeLeafHubs); }
}
private static void AddAllNodeToSubs(SublistNode n, List<Subscription> subs)
private static void AddAllNodeToSubs(Node n, List<Subscription> subs)
{
if (n.PList != null)
subs.AddRange(n.PList);
@@ -986,7 +986,7 @@ public sealed class SubscriptionIndex
subs.Add(sub);
}
private static void CollectAllSubs(SublistLevel? l, List<Subscription> subs)
private static void CollectAllSubs(Level? l, List<Subscription> subs)
{
if (l == null) return;
foreach (var n in l.Nodes.Values)
@@ -1204,7 +1204,7 @@ public sealed class SubscriptionIndex
// Private: visitLevel (depth calculation for tests)
// -------------------------------------------------------------------------
private static int VisitLevel(SublistLevel? l, int depth)
private static int VisitLevel(Level? l, int depth)
{
if (l == null || l.NumNodes() == 0) return depth;
depth++;
@@ -1529,18 +1529,18 @@ public sealed class SubscriptionIndex
}
// -------------------------------------------------------------------------
// Nested types: SublistNode, SublistLevel, NotifyMaps
// Nested types: Node, Level, NotifyMaps
// -------------------------------------------------------------------------
internal sealed class SublistNode
internal sealed class Node
{
public readonly Dictionary<Subscription, byte> PSubs = new(ReferenceEqualityComparer.Instance);
public Dictionary<string, Dictionary<Subscription, byte>>? QSubs;
public List<Subscription>? PList;
public SublistLevel? Next;
public Level? Next;
/// <summary>Factory method matching Go's <c>newNode()</c>.</summary>
public static SublistNode NewNode() => new();
public static Node NewNode() => new();
public bool IsEmpty()
{
@@ -1549,14 +1549,14 @@ public sealed class SubscriptionIndex
}
}
internal sealed class SublistLevel
internal sealed class Level
{
public readonly Dictionary<string, SublistNode> Nodes = new();
public SublistNode? Pwc;
public SublistNode? Fwc;
public readonly Dictionary<string, Node> Nodes = new();
public Node? Pwc;
public Node? Fwc;
/// <summary>Factory method matching Go's <c>newLevel()</c>.</summary>
public static SublistLevel NewLevel() => new();
public static Level NewLevel() => new();
public int NumNodes()
{
@@ -1566,8 +1566,9 @@ public sealed class SubscriptionIndex
return num;
}
public void PruneNode(SublistNode n, string t)
public void PruneNode(Node? n, string t)
{
if (n == null) return;
if (ReferenceEquals(n, Fwc)) Fwc = null;
else if (ReferenceEquals(n, Pwc)) Pwc = null;
else Nodes.Remove(t);
@@ -1582,11 +1583,11 @@ public sealed class SubscriptionIndex
private readonly struct LevelNodeToken
{
public readonly SublistLevel Level;
public readonly SublistNode Node;
public readonly Level Level;
public readonly Node Node;
public readonly string Token;
public LevelNodeToken(SublistLevel level, SublistNode node, string token)
public LevelNodeToken(Level level, Node node, string token)
{
Level = level;
Node = node;

View File

@@ -219,6 +219,51 @@ public class SubscriptionIndexTests
s.NumLevels().ShouldBe(0);
}
[Fact]
public void PruneNode_NullNode_DoesNotMutateLiteralNodes()
{
var level = new SubscriptionIndex.Level();
var literal = new SubscriptionIndex.Node();
level.Nodes["foo"] = literal;
level.Fwc = new SubscriptionIndex.Node();
level.Pwc = new SubscriptionIndex.Node();
level.PruneNode(null!, "foo");
level.Nodes.Count.ShouldBe(1);
level.Nodes["foo"].ShouldBeSameAs(literal);
}
[Fact]
public void IsEmpty_WithAndWithoutChildren_TracksNodeEmptiness()
{
var node = new SubscriptionIndex.Node();
node.IsEmpty().ShouldBeTrue();
node.Next = new SubscriptionIndex.Level();
node.Next.Nodes["bar"] = new SubscriptionIndex.Node();
node.IsEmpty().ShouldBeFalse();
node.Next.Nodes.Clear();
node.IsEmpty().ShouldBeTrue();
}
[Fact]
public void NumNodes_WithLiteralAndWildcardEntries_CountsAllBranches()
{
var level = new SubscriptionIndex.Level();
level.Nodes["foo"] = new SubscriptionIndex.Node();
level.Pwc = new SubscriptionIndex.Node();
level.Fwc = new SubscriptionIndex.Node();
level.NumNodes().ShouldBe(3);
level.PruneNode(level.Pwc, SubscriptionIndex.Pwcs);
level.PruneNode(level.Fwc, SubscriptionIndex.Fwcs);
level.PruneNode(level.Nodes["foo"], "foo");
level.NumNodes().ShouldBe(0);
}
[Theory] // T:2983, T:2984
[InlineData(true)]
[InlineData(false)]

Binary file not shown.

37
reports/current.md Normal file
View File

@@ -0,0 +1,37 @@
# NATS .NET Porting Status Report
Generated: 2026-02-28 12:11:43 UTC
## Modules (12 total)
| Status | Count |
|--------|-------|
| verified | 12 |
## Features (3673 total)
| Status | Count |
|--------|-------|
| deferred | 2361 |
| n_a | 24 |
| stub | 1 |
| verified | 1287 |
## Unit Tests (3257 total)
| Status | Count |
|--------|-------|
| deferred | 2091 |
| n_a | 187 |
| verified | 979 |
## Library Mappings (36 total)
| Status | Count |
|--------|-------|
| mapped | 36 |
## Overall Progress
**2489/6942 items complete (35.9%)**

37
reports/report_38b6fc8.md Normal file
View File

@@ -0,0 +1,37 @@
# NATS .NET Porting Status Report
Generated: 2026-02-28 12:11:43 UTC
## Modules (12 total)
| Status | Count |
|--------|-------|
| verified | 12 |
## Features (3673 total)
| Status | Count |
|--------|-------|
| deferred | 2361 |
| n_a | 24 |
| stub | 1 |
| verified | 1287 |
## Unit Tests (3257 total)
| Status | Count |
|--------|-------|
| deferred | 2091 |
| n_a | 187 |
| verified | 979 |
## Library Mappings (36 total)
| Status | Count |
|--------|-------|
| mapped | 36 |
## Overall Progress
**2489/6942 items complete (35.9%)**