# Internal Data Structures — Gap Analysis > This file tracks what has and hasn't been ported from Go to .NET for the **Internal Data Structures** module. > See [stillmissing.md](stillmissing.md) for the full LOC comparison across all modules. ## LLM Instructions: How to Analyze This Category ### Step 1: Read the Go Reference Files Read each Go source file listed below. For every file: 1. Extract all **exported types** (structs, interfaces, type aliases) 2. Extract all **exported methods** on those types (receiver functions) 3. Extract all **exported standalone functions** 4. Note **key constants, enums, and protocol states** 5. Note **important unexported helpers** that implement core logic (functions >20 lines) 6. Pay attention to **concurrency patterns** (goroutines, mutexes, channels) — these map to different .NET patterns ### Step 2: Read the .NET Implementation Files Read all `.cs` files in the .NET directories listed below. For each Go symbol found in Step 1: 1. Search for a matching type, method, or function in .NET 2. If found, compare the behavior: does it handle the same edge cases? Same error paths? 3. If partially implemented, note what's missing 4. If not found, note it as MISSING ### Step 3: Cross-Reference Tests Compare Go test functions against .NET test methods: 1. For each Go `Test*` function, check if a corresponding .NET `[Fact]` or `[Theory]` exists 2. Note which test scenarios are covered and which are missing 3. Check the parity DB (`docs/test_parity.db`) for existing mappings: ```bash sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'" ``` ### Step 4: Classify Each Item Use these status values: | Status | Meaning | |--------|---------| | **PORTED** | Equivalent exists in .NET with matching behavior | | **PARTIAL** | .NET implementation exists but is incomplete (missing edge cases, error handling, or features) | | **MISSING** | No .NET equivalent found — needs to be ported | | **NOT_APPLICABLE** | Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.) | | **DEFERRED** | Intentionally skipped for now (document why) | ### Step 5: Fill In the Gap Inventory Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation. ### Key Porting Notes for Internal Data Structures - This category is at **105% LOC parity** — above Go. - The **subject tree** (stree) uses adaptive node sizing (4→10→16→48→256 children) for memory efficiency. - The **AVL tree** is used for tracking acknowledged sequences in JetStream consumers. - The **time hash wheel** provides O(1) TTL expiration checks. - **PSE** (platform-specific extensions) queries process RSS/CPU — may use `System.Diagnostics.Process` in .NET. - **sysmem** queries total system memory — may use `GC.GetGCMemoryInfo()` or P/Invoke in .NET. - Many Go files in pse/ and sysmem/ are platform-specific build-tagged files — classify platform-irrelevant ones as NOT_APPLICABLE. --- ## Go Reference Files (Source) - `golang/nats-server/server/avl/seqset.go` — AVL tree for sparse sequence sets (JetStream ack tracking) - `golang/nats-server/server/stree/` — Subject tree with adaptive nodes (node4, node10, node16, node48, node256) for per-subject state in streams. 11 files. - `golang/nats-server/server/thw/thw.go` — Time hash wheel for efficient TTL expiration - `golang/nats-server/server/gsl/gsl.go` — Generic subject list, optimized trie variant - `golang/nats-server/server/pse/` — Platform-specific extensions (proc info: RSS, CPU). 12 files across platforms. - `golang/nats-server/server/sysmem/` — System memory queries. 8 files across platforms. ## Go Reference Files (Tests) - `golang/nats-server/server/avl/seqset_test.go` - `golang/nats-server/server/stree/stree_test.go` - `golang/nats-server/server/thw/thw_test.go` - `golang/nats-server/server/gsl/gsl_test.go` ## .NET Implementation Files (Source) - `src/NATS.Server/Internal/` (all files including subdirectories) - `src/NATS.Server/Internal/Avl/` - `src/NATS.Server/Internal/Gsl/` - `src/NATS.Server/Internal/SubjectTree/` - `src/NATS.Server/Internal/TimeHashWheel/` ## .NET Implementation Files (Tests) - `tests/NATS.Server.Tests/Internal/` (all subdirectories) --- ## Gap Inventory ### avl/seqset.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `SequenceSet` (struct) | `golang/nats-server/server/avl/seqset.go:33` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:26` | Full class with all fields | | `SequenceSet.Insert` | `golang/nats-server/server/avl/seqset.go:44` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:52` | | | `SequenceSet.Exists` | `golang/nats-server/server/avl/seqset.go:52` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:63` | | | `SequenceSet.SetInitialMin` | `golang/nats-server/server/avl/seqset.go:69` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:89` | Go returns error; .NET throws | | `SequenceSet.Delete` | `golang/nats-server/server/avl/seqset.go:80` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:103` | | | `SequenceSet.Size` | `golang/nats-server/server/avl/seqset.go:97` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:43` | Property in .NET | | `SequenceSet.Nodes` | `golang/nats-server/server/avl/seqset.go:102` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:46` | Property in .NET | | `SequenceSet.Empty` | `golang/nats-server/server/avl/seqset.go:107` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:127` | | | `SequenceSet.IsEmpty` | `golang/nats-server/server/avl/seqset.go:114` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:49` | Property in .NET | | `SequenceSet.Range` | `golang/nats-server/server/avl/seqset.go:124` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:138` | | | `SequenceSet.Heights` | `golang/nats-server/server/avl/seqset.go:129` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:141` | Returns tuple in .NET | | `SequenceSet.State` | `golang/nats-server/server/avl/seqset.go:143` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:153` | Returns named tuple | | `SequenceSet.MinMax` | `golang/nats-server/server/avl/seqset.go:152` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:166` | | | `clone` (unexported) | `golang/nats-server/server/avl/seqset.go:169` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:424` | `CloneNode` private static method | | `SequenceSet.Clone` | `golang/nats-server/server/avl/seqset.go:180` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:195` | | | `SequenceSet.Union` (method) | `golang/nats-server/server/avl/seqset.go:191` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:203` | | | `Union` (function) | `golang/nats-server/server/avl/seqset.go:208` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:228` | `SequenceSet.CreateUnion` static method | | `SequenceSet.EncodeLen` | `golang/nats-server/server/avl/seqset.go:238` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:252` | `EncodeLength()` in .NET | | `SequenceSet.Encode` | `golang/nats-server/server/avl/seqset.go:242` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:266` | Added destination-buffer overload (`Encode(byte[] destination)`) enabling caller buffer reuse parity | | `ErrBadEncoding` | `golang/nats-server/server/avl/seqset.go:276` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:288` | Thrown as `InvalidOperationException` | | `ErrBadVersion` | `golang/nats-server/server/avl/seqset.go:277` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:295` | Thrown as `InvalidOperationException` | | `ErrSetNotEmpty` | `golang/nats-server/server/avl/seqset.go:278` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:93` | Thrown as `InvalidOperationException` | | `Decode` | `golang/nats-server/server/avl/seqset.go:282` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:285` | Static method, same v1/v2 dispatch | | `decodev2` (unexported) | `golang/nats-server/server/avl/seqset.go:298` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:300` | `DecodeV2` private static | | `decodev1` (unexported) | `golang/nats-server/server/avl/seqset.go:329` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:338` | `DecodeV1` private static | | `SequenceSet.insertNode` (unexported) | `golang/nats-server/server/avl/seqset.go:376` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:389` | `InsertNode` private method | | `node` (struct) | `golang/nats-server/server/avl/seqset.go:407` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:439` | `Node` nested class | | `node.set` | `golang/nats-server/server/avl/seqset.go:418` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:448` | `SetBit` | | `node.insert` | `golang/nats-server/server/avl/seqset.go:428` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:521` | `Node.Insert` static | | `node.rotateL` | `golang/nats-server/server/avl/seqset.go:464` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:672` | `RotateLeft` private static | | `node.rotateR` | `golang/nats-server/server/avl/seqset.go:478` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:692` | `RotateRight` private static | | `balanceF` | `golang/nats-server/server/avl/seqset.go:492` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:711` | `BalanceFactor` internal static | | `maxH` | `golang/nats-server/server/avl/seqset.go:506` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:724` | `MaxHeight` internal static | | `node.clear` | `golang/nats-server/server/avl/seqset.go:526` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:461` | `ClearBit` | | `node.delete` | `golang/nats-server/server/avl/seqset.go:542` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:571` | `Node.Delete` static | | `node.insertNodePrev` | `golang/nats-server/server/avl/seqset.go:588` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:635` | `InsertNodePrev` private static | | `node.exists` | `golang/nats-server/server/avl/seqset.go:613` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:484` | `ExistsBit` | | `node.min` | `golang/nats-server/server/avl/seqset.go:622` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:493` | `Min()` | | `node.max` | `golang/nats-server/server/avl/seqset.go:635` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:508` | `Max()` | | `node.nodeIter` | `golang/nats-server/server/avl/seqset.go:647` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:738` | `NodeIter` internal static | | `node.iter` | `golang/nats-server/server/avl/seqset.go:658` | PORTED | `src/NATS.Server/Internal/Avl/SequenceSet.cs:751` | `Iter` internal static | --- ### stree/stree.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `SubjectTree[T]` (struct) | `golang/nats-server/server/stree/stree.go:28` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:9` | | | `NewSubjectTree[T]` | `golang/nats-server/server/stree/stree.go:34` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:9` | In .NET, `new SubjectTree()` used directly | | `SubjectTree.Size` | `golang/nats-server/server/stree/stree.go:39` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:17` | | | `SubjectTree.Empty` | `golang/nats-server/server/stree/stree.go:47` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:22` | | | `SubjectTree.Insert` | `golang/nats-server/server/stree/stree.go:56` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:33` | Signature uses `ReadOnlySpan` | | `SubjectTree.Find` | `golang/nats-server/server/stree/stree.go:74` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:52` | | | `SubjectTree.Delete` | `golang/nats-server/server/stree/stree.go:106` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:97` | | | `SubjectTree.Match` | `golang/nats-server/server/stree/stree.go:119` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:115` | | | `SubjectTree.MatchUntil` | `golang/nats-server/server/stree/stree.go:137` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:135` | | | `SubjectTree.IterOrdered` | `golang/nats-server/server/stree/stree.go:149` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:149` | | | `SubjectTree.IterFast` | `golang/nats-server/server/stree/stree.go:158` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:158` | | | `SubjectTree.insert` (internal) | `golang/nats-server/server/stree/stree.go:169` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:170` | `InsertInternal` | | `SubjectTree.delete` (internal) | `golang/nats-server/server/stree/stree.go:253` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:286` | `DeleteInternal` | | `SubjectTree.match` (internal) | `golang/nats-server/server/stree/stree.go:318` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:383` | `MatchInternal` | | `SubjectTree.iter` (internal) | `golang/nats-server/server/stree/stree.go:418` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:512` | `IterInternal` | | `LazyIntersect[TL,TR]` | `golang/nats-server/server/stree/stree.go:463` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:584` | `SubjectTreeHelper.LazyIntersect` static | | `IntersectGSL[T,SL]` | `golang/nats-server/server/stree/stree.go:488` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:675` | Added GSL intersection traversal for subject tree entries | | `_intersectGSL` (internal) | `golang/nats-server/server/stree/stree.go:496` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:686` | Added recursive intersection helper | | `hasInterestForTokens` (internal) | `golang/nats-server/server/stree/stree.go:521` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:717` | Added token-boundary short-circuit using `HasInterestStartingIn` | | `bytesToString` (internal) | `golang/nats-server/server/stree/stree.go:534` | NOT_APPLICABLE | — | Go `unsafe` zero-copy string conversion. In .NET, `System.Text.Encoding.UTF8.GetString` or `MemoryMarshal.Cast` used instead | --- ### stree/node.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `node` (interface) | `golang/nats-server/server/stree/node.go:17` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:7` | `INode` interface | | `meta` (struct) | `golang/nats-server/server/stree/node.go:35` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:46` | `NodeMeta` class | | `meta.isLeaf` | `golang/nats-server/server/stree/node.go:40` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:9` | Via `INode.IsLeaf` | | `meta.base` | `golang/nats-server/server/stree/node.go:41` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:10` | Via `INode.Base` | | `meta.setPrefix` | `golang/nats-server/server/stree/node.go:43` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:11` | Via `INode.SetPrefix` | | `meta.numChildren` | `golang/nats-server/server/stree/node.go:47` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:26` | Via `INode.NumChildren` | | `meta.path` | `golang/nats-server/server/stree/node.go:48` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:27` | Via `INode.Path` | | `meta.matchParts` | `golang/nats-server/server/stree/node.go:51` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:22` | Via `INode.MatchParts` | --- ### stree/leaf.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `leaf[T]` (struct) | `golang/nats-server/server/stree/leaf.go:23` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:58` | `Leaf` class | | `newLeaf[T]` | `golang/nats-server/server/stree/leaf.go:30` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:63` | `new Leaf(suffix, value)` constructor | | `leaf.isLeaf` | `golang/nats-server/server/stree/leaf.go:34` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:69` | | | `leaf.base` | `golang/nats-server/server/stree/leaf.go:34` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:70` | Returns null | | `leaf.match` | `golang/nats-server/server/stree/leaf.go:36` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:75` | | | `leaf.setSuffix` | `golang/nats-server/server/stree/leaf.go:37` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:77` | | | `leaf.matchParts` | `golang/nats-server/server/stree/leaf.go:39` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:85` | | | `leaf.iter` | `golang/nats-server/server/stree/leaf.go:40` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:83` | No-op on leaf | | `leaf.children` | `golang/nats-server/server/stree/leaf.go:41` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:81` | Returns empty array | | `leaf.numChildren` | `golang/nats-server/server/stree/leaf.go:42` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:72` | Returns 0 | | `leaf.path` | `golang/nats-server/server/stree/leaf.go:43` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:79` | Returns suffix | --- ### stree/node4.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `node4` (struct) | `golang/nats-server/server/stree/node4.go:18` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:105` | `Node4` class | | `newNode4` | `golang/nats-server/server/stree/node4.go:24` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:111` | Constructor | | `node4.addChild` | `golang/nats-server/server/stree/node4.go:31` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:128` | | | `node4.findChild` | `golang/nats-server/server/stree/node4.go:40` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:136` | Returns `ChildRef?` wrapper | | `node4.isFull` | `golang/nats-server/server/stree/node4.go:49` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:119` | | | `node4.grow` | `golang/nats-server/server/stree/node4.go:51` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:174` | | | `node4.deleteChild` | `golang/nats-server/server/stree/node4.go:60` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:149` | | | `node4.shrink` | `golang/nats-server/server/stree/node4.go:80` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:184` | | | `node4.iter` | `golang/nats-server/server/stree/node4.go:88` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:190` | | | `node4.children` | `golang/nats-server/server/stree/node4.go:96` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:198` | | --- ### stree/node10.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `node10` (struct) | `golang/nats-server/server/stree/node10.go:20` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:217` | `Node10` class | | `newNode10` | `golang/nats-server/server/stree/node10.go:26` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:223` | Constructor | | `node10.addChild` | `golang/nats-server/server/stree/node10.go:34` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:240` | | | `node10.findChild` | `golang/nats-server/server/stree/node10.go:43` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:248` | | | `node10.isFull` | `golang/nats-server/server/stree/node10.go:52` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:231` | | | `node10.grow` | `golang/nats-server/server/stree/node10.go:54` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:286` | | | `node10.deleteChild` | `golang/nats-server/server/stree/node10.go:63` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:258` | | | `node10.shrink` | `golang/nats-server/server/stree/node10.go:83` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:295` | | | `node10.iter` | `golang/nats-server/server/stree/node10.go:95` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:307` | | | `node10.children` | `golang/nats-server/server/stree/node10.go:103` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:315` | | --- ### stree/node16.go, stree/node48.go, stree/node256.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `node16` (struct) | `golang/nats-server/server/stree/node16.go` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:334` | `Node16` class | | `node48` (struct) | `golang/nats-server/server/stree/node48.go` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:451` | `Node48` class | | `node256` (struct) | `golang/nats-server/server/stree/node256.go` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:569` | `Node256` class | --- ### stree/parts.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `genParts` | `golang/nats-server/server/stree/parts.go:23` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:57` | `Parts.GenParts` static method | | `matchParts` | `golang/nats-server/server/stree/parts.go:78` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:145` | `Parts.MatchPartsAgainstFragment` | --- ### stree/util.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `pwc`, `fwc`, `tsep` constants | `golang/nats-server/server/stree/util.go:17` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:10` | `Parts.Pwc`, `Parts.Fwc`, `Parts.Tsep` | | `commonPrefixLen` | `golang/nats-server/server/stree/util.go:24` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:33` | `Parts.CommonPrefixLen` | | `copyBytes` | `golang/nats-server/server/stree/util.go:36` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:47` | `Parts.CopyBytes` | | `position` (interface) | `golang/nats-server/server/stree/util.go:45` | NOT_APPLICABLE | — | Go generic constraint on numeric types; not needed in .NET | | `noPivot` constant | `golang/nats-server/server/stree/util.go:48` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:17` | `Parts.NoPivot` | | `pivot` | `golang/nats-server/server/stree/util.go:52` | PORTED | `src/NATS.Server/Internal/SubjectTree/Parts.cs:23` | `Parts.Pivot` | --- ### stree/dump.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `SubjectTree.Dump` | `golang/nats-server/server/stree/dump.go:23` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:172` | Added debug dump writer with root traversal and trailing newline | | `SubjectTree.dump` (internal) | `golang/nats-server/server/stree/dump.go:29` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:180` | Added recursive node/leaf dump helper | | `dumpPre` | `golang/nats-server/server/stree/dump.go:59` | PORTED | `src/NATS.Server/Internal/SubjectTree/SubjectTree.cs:205` | Added indentation helper matching Go depth formatting | | `leaf.kind`, `node4.kind`, etc. | `golang/nats-server/server/stree/dump.go:51` | PORTED | `src/NATS.Server/Internal/SubjectTree/Nodes.cs:21` | `INode.Kind` values are now actively consumed by `SubjectTree.Dump` output | --- ### thw/thw.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `ErrTaskNotFound` | `golang/nats-server/server/thw/thw.go:25` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:91` | Returned as `false`/exception instead of error | | `ErrInvalidVersion` | `golang/nats-server/server/thw/thw.go:28` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:289` | Thrown as `InvalidOperationException` | | `slot` (struct) | `golang/nats-server/server/thw/thw.go:39` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:406` | `Slot` internal class | | `HashWheel` (struct) | `golang/nats-server/server/thw/thw.go:45` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:17` | `HashWheel` class | | `HashWheelEntry` (struct) | `golang/nats-server/server/thw/thw.go:52` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:407` | Added `HashWheelEntry` record struct (`Sequence`, `Expires`) | | `NewHashWheel` | `golang/nats-server/server/thw/thw.go:58` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:34` | Constructor in .NET | | `HashWheel.getPosition` (internal) | `golang/nats-server/server/thw/thw.go:66` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:50` | `GetPosition` private static | | `newSlot` (internal) | `golang/nats-server/server/thw/thw.go:71` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:406` | Inline slot initialization in .NET | | `HashWheel.Add` | `golang/nats-server/server/thw/thw.go:79` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:60` | Return type `void` vs `error` (never errors in practice) | | `HashWheel.Remove` | `golang/nats-server/server/thw/thw.go:103` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:91` | Returns `bool` instead of `error` | | `HashWheel.Update` | `golang/nats-server/server/thw/thw.go:123` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:122` | | | `HashWheel.ExpireTasks` | `golang/nats-server/server/thw/thw.go:133` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:135` | | | `HashWheel.expireTasks` (internal) | `golang/nats-server/server/thw/thw.go:138` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:148` | `ExpireTasksInternal` — public in .NET for testability | | `HashWheel.GetNextExpiration` | `golang/nats-server/server/thw/thw.go:182` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:219` | | | `HashWheel.Count` | `golang/nats-server/server/thw/thw.go:190` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:44` | Property in .NET | | `HashWheel.Encode` | `golang/nats-server/server/thw/thw.go:197` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:235` | | | `HashWheel.Decode` | `golang/nats-server/server/thw/thw.go:216` | PORTED | `src/NATS.Server/Internal/TimeHashWheel/HashWheel.cs:282` | Returns `(highSeq, bytesRead)` tuple; Go returns `(uint64, error)` | --- ### gsl/gsl.go | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `ErrInvalidSubject` | `golang/nats-server/server/gsl/gsl.go:41` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:11` | `GslErrors.InvalidSubject` | | `ErrNotFound` | `golang/nats-server/server/gsl/gsl.go:42` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:12` | `GslErrors.NotFound` | | `ErrNilChan` | `golang/nats-server/server/gsl/gsl.go:43` | NOT_APPLICABLE | — | Go channel-specific error; channels don't apply to .NET pattern | | `ErrAlreadyRegistered` | `golang/nats-server/server/gsl/gsl.go:44` | NOT_APPLICABLE | — | Used by notification channels in Go; no notification channel pattern in .NET port | | `SimpleSublist` (type alias) | `golang/nats-server/server/gsl/gsl.go:49` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:656` | `SimpleSubjectList` now aliases `GenericSubjectList` where marker value mirrors Go's empty struct payload | | `NewSimpleSublist` | `golang/nats-server/server/gsl/gsl.go:52` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:650` | `new SimpleSubjectList()` | | `GenericSublist[T]` (struct) | `golang/nats-server/server/gsl/gsl.go:57` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:76` | `GenericSubjectList` | | `node[T]` (struct) | `golang/nats-server/server/gsl/gsl.go:64` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:52` | `Node` | | `level[T]` (struct) | `golang/nats-server/server/gsl/gsl.go:71` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:19` | `Level` | | `newNode[T]` | `golang/nats-server/server/gsl/gsl.go:77` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:52` | Inline `new Node()` | | `newLevel[T]` | `golang/nats-server/server/gsl/gsl.go:82` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:19` | Inline `new Level()` | | `NewSublist[T]` | `golang/nats-server/server/gsl/gsl.go:87` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:76` | `new GenericSubjectList()` constructor | | `GenericSublist.Insert` | `golang/nats-server/server/gsl/gsl.go:92` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:110` | Throws instead of returning error | | `GenericSublist.Match` | `golang/nats-server/server/gsl/gsl.go:150` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:205` | | | `GenericSublist.MatchBytes` | `golang/nats-server/server/gsl/gsl.go:156` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:214` | | | `GenericSublist.HasInterest` | `golang/nats-server/server/gsl/gsl.go:162` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:225` | | | `GenericSublist.NumInterest` | `golang/nats-server/server/gsl/gsl.go:168` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:234` | | | `GenericSublist.match` (internal) | `golang/nats-server/server/gsl/gsl.go:173` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:274` | `MatchInternal` | | `GenericSublist.hasInterest` (internal) | `golang/nats-server/server/gsl/gsl.go:198` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:295` | `HasInterestInternal` | | `matchLevelForAny[T]` | `golang/nats-server/server/gsl/gsl.go:223` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:400` | `MatchLevelForAny` private static | | `callbacksForResults[T]` | `golang/nats-server/server/gsl/gsl.go:266` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:448` | `CallbacksForResults` private static | | `matchLevel[T]` | `golang/nats-server/server/gsl/gsl.go:273` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:371` | `MatchLevel` private static | | `lnt[T]` (struct) | `golang/nats-server/server/gsl/gsl.go:301` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:68` | `Lnt` record struct | | `GenericSublist.remove` (internal) | `golang/nats-server/server/gsl/gsl.go:308` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:458` | `RemoveInternal` | | `GenericSublist.Remove` | `golang/nats-server/server/gsl/gsl.go:368` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:188` | | | `GenericSublist.HasInterestStartingIn` | `golang/nats-server/server/gsl/gsl.go:373` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:245` | | | `hasInterestStartingIn[T]` (internal) | `golang/nats-server/server/gsl/gsl.go:381` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:537` | `HasInterestStartingInLevel` private static | | `level.pruneNode` | `golang/nats-server/server/gsl/gsl.go:403` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:37` | `Level.PruneNode` | | `node.isEmpty` | `golang/nats-server/server/gsl/gsl.go:418` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:61` | `Node.IsEmpty()` | | `level.numNodes` | `golang/nats-server/server/gsl/gsl.go:423` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:25` | `Level.NumNodes()` | | `GenericSublist.removeFromNode` (internal) | `golang/nats-server/server/gsl/gsl.go:438` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:527` | `RemoveFromNode` private static | | `GenericSublist.Count` | `golang/nats-server/server/gsl/gsl.go:449` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:90` | | | `GenericSublist.numLevels` (internal) | `golang/nats-server/server/gsl/gsl.go:457` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:264` | `NumLevels()` internal | | `visitLevel[T]` | `golang/nats-server/server/gsl/gsl.go:463` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:557` | `VisitLevel` private static | | `tokenizeSubjectIntoSlice` | `golang/nats-server/server/gsl/gsl.go:496` | PORTED | `src/NATS.Server/Internal/Gsl/GenericSubjectList.cs:348` | `TokenizeSubjectIntoSpan` | --- ### pse/ (Platform-Specific Extensions) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `ProcUsage` (darwin) | `golang/nats-server/server/pse/pse_darwin.go:83` | PORTED | `src/NATS.Server/Monitoring/VarzHandler.cs:35` | CPU via `Process.TotalProcessorTime`; RSS via `proc.WorkingSet64`. Equivalent behavior in `/varz` reporting. | | `ProcUsage` (linux) | `golang/nats-server/server/pse/pse_linux.go` | PORTED | `src/NATS.Server/Monitoring/VarzHandler.cs:35` | Same cross-platform .NET equivalent | | `ProcUsage` (windows) | `golang/nats-server/server/pse/pse_windows.go` | PORTED | `src/NATS.Server/Monitoring/VarzHandler.cs:35` | Same cross-platform .NET equivalent | | `ProcUsage` (freebsd/netbsd/openbsd/dragonfly/solaris/zos) | `golang/nats-server/server/pse/pse_*.go` | NOT_APPLICABLE | — | Platform-specific Go build-tagged files. .NET runtime abstracts these OS differences. | | `ProcUsage` (wasm/rumprun) | `golang/nats-server/server/pse/pse_wasm.go` | NOT_APPLICABLE | — | Stub/no-op implementations for unsupported platforms; not needed in .NET | | `updateUsage` (darwin, internal) | `golang/nats-server/server/pse/pse_darwin.go:56` | PORTED | `src/NATS.Server/Monitoring/VarzHandler.cs:39` | CPU sampling logic in VarzHandler | | `periodic` (darwin, internal) | `golang/nats-server/server/pse/pse_darwin.go:76` | PORTED | `src/NATS.Server/Monitoring/VarzHandler.cs:30` | Added 1s background timer sampler (`SampleCpuUsage`) with synchronized cached CPU reads in `/varz`, matching periodic semantics instead of request-only sampling | --- ### sysmem/ (System Memory Queries) | Go Symbol | Go File:Line | Status | .NET Equivalent | Notes | |-----------|:-------------|--------|:----------------|-------| | `Memory` (darwin) | `golang/nats-server/server/sysmem/mem_darwin.go:18` | PORTED | `src/NATS.Server/Internal/SysMem/SystemMemory.cs:12` | Added cross-platform `SystemMemory.Memory()` backed by runtime memory info | | `Memory` (linux) | `golang/nats-server/server/sysmem/mem_linux.go:20` | PORTED | `src/NATS.Server/Internal/SysMem/SystemMemory.cs:12` | Added cross-platform `SystemMemory.Memory()` backed by runtime memory info | | `Memory` (windows) | `golang/nats-server/server/sysmem/mem_windows.go` | PORTED | `src/NATS.Server/Internal/SysMem/SystemMemory.cs:12` | Added cross-platform `SystemMemory.Memory()` backed by runtime memory info | | `Memory` (bsd/solaris/wasm/zos) | `golang/nats-server/server/sysmem/mem_bsd.go` etc. | NOT_APPLICABLE | — | Platform-specific stubs; .NET runtime abstracts these. `GCMemoryInfo` is the cross-platform equivalent. | | `sysctlInt64` | `golang/nats-server/server/sysmem/sysctl.go:23` | NOT_APPLICABLE | — | Darwin/BSD internal helper using unsafe sysctl; .NET abstracts this entirely | --- ## Keeping This File Updated After porting work is completed: 1. **Update status**: Change `MISSING → PORTED` or `PARTIAL → PORTED` for each item completed 2. **Add .NET path**: Fill in the ".NET Equivalent" column with the actual file:line 3. **Re-count LOC**: Update the LOC numbers in `stillmissing.md`: ```bash # Re-count .NET source LOC for this module find src/NATS.Server/Internal/ -name '*.cs' -type f -exec cat {} + | wc -l # Re-count .NET test LOC for this module find tests/NATS.Server.Tests/Internal/ -name '*.cs' -type f -exec cat {} + | wc -l ``` 4. **Add a changelog entry** below with date and summary of what was ported 5. **Update the parity DB** if new test mappings were created: ```bash sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')" ``` ## Change Log | Date | Change | By | |------|--------|----| | 2026-02-25 | File created with LLM analysis instructions | auto | | 2026-02-25 | Full gap inventory populated: 157 PORTED, 4 PARTIAL, 10 MISSING, 8 NOT_APPLICABLE, 0 DEFERRED | auto | | 2026-02-25 | Completed periodic PSE parity by moving CPU sampling to a 1s background timer in `VarzHandler` and adding targeted parity test coverage | codex |