From 1a0ac59df8fafe080d654e0d058a94bbdbb44e62 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 07:11:42 -0500 Subject: [PATCH] feat(batch2): verify sublist helper remainder features --- .../DataStructures/SubscriptionIndex.cs | 71 +++++++++--------- .../DataStructures/SubscriptionIndexTests.cs | 45 +++++++++++ porting.db | Bin 6356992 -> 6356992 bytes reports/current.md | 37 +++++++++ reports/report_38b6fc8.md | 37 +++++++++ 5 files changed, 155 insertions(+), 35 deletions(-) create mode 100644 reports/current.md create mode 100644 reports/report_38b6fc8.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/DataStructures/SubscriptionIndex.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/DataStructures/SubscriptionIndex.cs index 8cb1bdc..0a5d6db 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Internal/DataStructures/SubscriptionIndex.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Internal/DataStructures/SubscriptionIndex.cs @@ -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? _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() : 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 toks, ref int np, ref int nq) + private static bool MatchLevelForAny(Level? l, ReadOnlySpan 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 toks, SublistNode? n, SubscriptionIndexResult results) + private static void ReverseMatchLevel(Level? l, ReadOnlySpan 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 subs, bool includeLeafHubs) + private static void AddNodeToSubsLocal(Node n, List subs, bool includeLeafHubs) { if (n.PList != null) { @@ -960,7 +960,7 @@ public sealed class SubscriptionIndex } } - private static void CollectLocalSubs(SublistLevel? l, List subs, bool includeLeafHubs) + private static void CollectLocalSubs(Level? l, List 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 subs) + private static void AddAllNodeToSubs(Node n, List 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 subs) + private static void CollectAllSubs(Level? l, List 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 PSubs = new(ReferenceEqualityComparer.Instance); public Dictionary>? QSubs; public List? PList; - public SublistLevel? Next; + public Level? Next; /// Factory method matching Go's newNode(). - 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 Nodes = new(); - public SublistNode? Pwc; - public SublistNode? Fwc; + public readonly Dictionary Nodes = new(); + public Node? Pwc; + public Node? Fwc; /// Factory method matching Go's newLevel(). - 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; diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/DataStructures/SubscriptionIndexTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/DataStructures/SubscriptionIndexTests.cs index fe11062..cdbc26e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/DataStructures/SubscriptionIndexTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/DataStructures/SubscriptionIndexTests.cs @@ -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)] diff --git a/porting.db b/porting.db index 424aa6511a5b2ea2cdf59f0233dadc8baab27c5c..6bbe23bec6f13c1c4c597054a54204115de6401d 100644 GIT binary patch delta 1701 zcmciC$xjne90%~uw532h!=fM-tAm1jnFXk{Zp8(~eP3~D>8nnpi!;*}4NFPRR?SiX) zB6I6_zi9T0%(NRU*I_<*%LHySuc^I{J!p3JvAY@bX+QhP$+XfZm)Y8Ku7hv2jMa@) zn9E>QIJ8RB@&UG4uvjdsA|y&QjYPN(R11cy3@WLobASaa*uV~X;DCH6fI@JB3plfL zfIn3=xo8Dn!8N;PJC_w)$$y^jb+qK2v|nVmFg=$0w#PQ7wT7;DGiT`87ra5Wydn=3 zU-C2P-734?E=z&BdK!MoXX(9e>qN>7@>5546eK-t z3+rG#Y=Dih2{ywP*b3WVJM4g+&(Cu`XmZA;(1@0N=as{1-3$GWv zFOc&MhiNiz*fWlbtiLzlw0Kxg*5S#P)kR6jNNGgp=(RCBE9qKsd+E`Qsj%LrsYydk z#9QJK(ox%{2jm8y+?I$X+lfJ9p?E6PE=1cC89_HlQV5f1LL4w zgitD?8YCiw^x-p#r1-p&;#C?2SqbE>WgVjV5v}xxY#G}0VTbqV&tug${M%SEVm0PE zR`Pwz(liv8-Sk{c{&mm%|1_N9&o1hngK#Ceo|R?Aznf?7U!@x%%~0d}1U(gQS9Rm- zfZDzcE|bs>42?OX8v6wt-G}i7vH8|+}h1__b67SIDC@d_m@nLYDr8_hLzV3ro#4A zSgCrW>c#flvQqz6S(dIz^|o6Ki<_duzB{RQDfXu+^_NFU$?Pk&#;=!(lq3Jnz+jJF z!qWe)V(Cw-jb)~0slD+V@&5jJuQ~i*4XxJLNP3k9CtPqN2f4_D4i5~N_DEVh4wM@! zo*8{PZ!Y(1PP6--Yu@?I8PuZQTG3-ZQ~Q(`j#=4Ks64eRKZwtUoP93_t2WZR;s zmOq|o9yz`!k}^AEmB?UL1pj^QS#e)Z)VVGBGb=*USD@*sB{8$)bw->E&WuAwrqu?! zsm#}{xW2o;xEJh~0pE%mdSqxt>R{qlqHic+j<1b}Yh%>V#xfN?tiyV2Kt49Yk4@N& z0u-VM#jsFvBa8!q88+HnO}aShkeflhRx+s;(P eqVvLKL>%5$@!b4q8b-tp#5!vXJMugBPWuBf5)2Ul diff --git a/reports/current.md b/reports/current.md new file mode 100644 index 0000000..506c986 --- /dev/null +++ b/reports/current.md @@ -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%)** diff --git a/reports/report_38b6fc8.md b/reports/report_38b6fc8.md new file mode 100644 index 0000000..506c986 --- /dev/null +++ b/reports/report_38b6fc8.md @@ -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%)**