feat(batch2): verify sublist helper remainder features
This commit is contained in:
@@ -58,7 +58,7 @@ public sealed class SubscriptionIndex
|
|||||||
private long _cacheHits;
|
private long _cacheHits;
|
||||||
private long _inserts;
|
private long _inserts;
|
||||||
private long _removes;
|
private long _removes;
|
||||||
private SublistLevel _root;
|
private Level _root;
|
||||||
private Dictionary<string, SubscriptionIndexResult>? _cache;
|
private Dictionary<string, SubscriptionIndexResult>? _cache;
|
||||||
private int _ccSweep;
|
private int _ccSweep;
|
||||||
private NotifyMaps? _notify;
|
private NotifyMaps? _notify;
|
||||||
@@ -70,7 +70,7 @@ public sealed class SubscriptionIndex
|
|||||||
|
|
||||||
private SubscriptionIndex(bool enableCache)
|
private SubscriptionIndex(bool enableCache)
|
||||||
{
|
{
|
||||||
_root = new SublistLevel();
|
_root = new Level();
|
||||||
_cache = enableCache ? new Dictionary<string, SubscriptionIndexResult>() : null;
|
_cache = enableCache ? new Dictionary<string, SubscriptionIndexResult>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ public sealed class SubscriptionIndex
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool sfwc = false, haswc = false, isnew = false;
|
bool sfwc = false, haswc = false, isnew = false;
|
||||||
SublistNode? n = null;
|
Node? n = null;
|
||||||
var l = _root;
|
var l = _root;
|
||||||
|
|
||||||
var start = 0;
|
var start = 0;
|
||||||
@@ -135,7 +135,7 @@ public sealed class SubscriptionIndex
|
|||||||
{
|
{
|
||||||
if (!l.Nodes.TryGetValue(t, out n))
|
if (!l.Nodes.TryGetValue(t, out n))
|
||||||
{
|
{
|
||||||
n = new SublistNode();
|
n = new Node();
|
||||||
l.Nodes[t] = n;
|
l.Nodes[t] = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ public sealed class SubscriptionIndex
|
|||||||
haswc = true;
|
haswc = true;
|
||||||
if (n == null)
|
if (n == null)
|
||||||
{
|
{
|
||||||
n = new SublistNode();
|
n = new Node();
|
||||||
l.Pwc = n;
|
l.Pwc = n;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -158,21 +158,21 @@ public sealed class SubscriptionIndex
|
|||||||
sfwc = true;
|
sfwc = true;
|
||||||
if (n == null)
|
if (n == null)
|
||||||
{
|
{
|
||||||
n = new SublistNode();
|
n = new Node();
|
||||||
l.Fwc = n;
|
l.Fwc = n;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!l.Nodes.TryGetValue(t, out n))
|
if (!l.Nodes.TryGetValue(t, out n))
|
||||||
{
|
{
|
||||||
n = new SublistNode();
|
n = new Node();
|
||||||
l.Nodes[t] = n;
|
l.Nodes[t] = n;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Next ??= new SublistLevel();
|
n.Next ??= new Level();
|
||||||
l = n.Next;
|
l = n.Next;
|
||||||
|
|
||||||
start = i + 1;
|
start = i + 1;
|
||||||
@@ -446,7 +446,7 @@ public sealed class SubscriptionIndex
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool sfwc = false, haswc = false;
|
bool sfwc = false, haswc = false;
|
||||||
SublistNode? n = null;
|
Node? n = null;
|
||||||
var l = _root;
|
var l = _root;
|
||||||
|
|
||||||
var lnts = new LevelNodeToken[32];
|
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);
|
if (n == null) return (false, true);
|
||||||
|
|
||||||
@@ -743,9 +743,9 @@ public sealed class SubscriptionIndex
|
|||||||
// Private: Trie matching (matchLevel)
|
// 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++)
|
for (int i = 0; i < toks.Length; i++)
|
||||||
{
|
{
|
||||||
if (l == null) return;
|
if (l == null) return;
|
||||||
@@ -760,9 +760,9 @@ public sealed class SubscriptionIndex
|
|||||||
if (pwc != null) AddNodeToResults(pwc, results);
|
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++)
|
for (int i = 0; i < toks.Length; i++)
|
||||||
{
|
{
|
||||||
if (l == null) return false;
|
if (l == null) return false;
|
||||||
@@ -804,7 +804,7 @@ public sealed class SubscriptionIndex
|
|||||||
// Private: Reverse match
|
// 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;
|
if (l == null) return;
|
||||||
for (int i = 0; i < toks.Length; i++)
|
for (int i = 0; i < toks.Length; i++)
|
||||||
@@ -848,7 +848,7 @@ public sealed class SubscriptionIndex
|
|||||||
if (n != null) AddNodeToResults(n, results);
|
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 == null) return;
|
||||||
if (l.Pwc != null) AddNodeToResults(l.Pwc, results);
|
if (l.Pwc != null) AddNodeToResults(l.Pwc, results);
|
||||||
@@ -864,7 +864,7 @@ public sealed class SubscriptionIndex
|
|||||||
// Private: addNodeToResults
|
// Private: addNodeToResults
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
private static void AddNodeToResults(SublistNode n, SubscriptionIndexResult results)
|
private static void AddNodeToResults(Node n, SubscriptionIndexResult results)
|
||||||
{
|
{
|
||||||
// Plain subscriptions.
|
// Plain subscriptions.
|
||||||
if (n.PList != null)
|
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)
|
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;
|
if (l == null) return;
|
||||||
foreach (var n in l.Nodes.Values)
|
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); }
|
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)
|
if (n.PList != null)
|
||||||
subs.AddRange(n.PList);
|
subs.AddRange(n.PList);
|
||||||
@@ -986,7 +986,7 @@ public sealed class SubscriptionIndex
|
|||||||
subs.Add(sub);
|
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;
|
if (l == null) return;
|
||||||
foreach (var n in l.Nodes.Values)
|
foreach (var n in l.Nodes.Values)
|
||||||
@@ -1204,7 +1204,7 @@ public sealed class SubscriptionIndex
|
|||||||
// Private: visitLevel (depth calculation for tests)
|
// 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;
|
if (l == null || l.NumNodes() == 0) return depth;
|
||||||
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 readonly Dictionary<Subscription, byte> PSubs = new(ReferenceEqualityComparer.Instance);
|
||||||
public Dictionary<string, Dictionary<Subscription, byte>>? QSubs;
|
public Dictionary<string, Dictionary<Subscription, byte>>? QSubs;
|
||||||
public List<Subscription>? PList;
|
public List<Subscription>? PList;
|
||||||
public SublistLevel? Next;
|
public Level? Next;
|
||||||
|
|
||||||
/// <summary>Factory method matching Go's <c>newNode()</c>.</summary>
|
/// <summary>Factory method matching Go's <c>newNode()</c>.</summary>
|
||||||
public static SublistNode NewNode() => new();
|
public static Node NewNode() => new();
|
||||||
|
|
||||||
public bool IsEmpty()
|
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 readonly Dictionary<string, Node> Nodes = new();
|
||||||
public SublistNode? Pwc;
|
public Node? Pwc;
|
||||||
public SublistNode? Fwc;
|
public Node? Fwc;
|
||||||
|
|
||||||
/// <summary>Factory method matching Go's <c>newLevel()</c>.</summary>
|
/// <summary>Factory method matching Go's <c>newLevel()</c>.</summary>
|
||||||
public static SublistLevel NewLevel() => new();
|
public static Level NewLevel() => new();
|
||||||
|
|
||||||
public int NumNodes()
|
public int NumNodes()
|
||||||
{
|
{
|
||||||
@@ -1566,8 +1566,9 @@ public sealed class SubscriptionIndex
|
|||||||
return num;
|
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;
|
if (ReferenceEquals(n, Fwc)) Fwc = null;
|
||||||
else if (ReferenceEquals(n, Pwc)) Pwc = null;
|
else if (ReferenceEquals(n, Pwc)) Pwc = null;
|
||||||
else Nodes.Remove(t);
|
else Nodes.Remove(t);
|
||||||
@@ -1582,11 +1583,11 @@ public sealed class SubscriptionIndex
|
|||||||
|
|
||||||
private readonly struct LevelNodeToken
|
private readonly struct LevelNodeToken
|
||||||
{
|
{
|
||||||
public readonly SublistLevel Level;
|
public readonly Level Level;
|
||||||
public readonly SublistNode Node;
|
public readonly Node Node;
|
||||||
public readonly string Token;
|
public readonly string Token;
|
||||||
|
|
||||||
public LevelNodeToken(SublistLevel level, SublistNode node, string token)
|
public LevelNodeToken(Level level, Node node, string token)
|
||||||
{
|
{
|
||||||
Level = level;
|
Level = level;
|
||||||
Node = node;
|
Node = node;
|
||||||
|
|||||||
@@ -219,6 +219,51 @@ public class SubscriptionIndexTests
|
|||||||
s.NumLevels().ShouldBe(0);
|
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
|
[Theory] // T:2983, T:2984
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
|
|||||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
37
reports/current.md
Normal file
37
reports/current.md
Normal 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
37
reports/report_38b6fc8.md
Normal 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%)**
|
||||||
Reference in New Issue
Block a user