diff --git a/src/NATS.Server/Subscriptions/SubList.cs b/src/NATS.Server/Subscriptions/SubList.cs index b18e3fc..33059f7 100644 --- a/src/NATS.Server/Subscriptions/SubList.cs +++ b/src/NATS.Server/Subscriptions/SubList.cs @@ -362,9 +362,9 @@ public sealed class SubList : IDisposable } else { - var key = token.ToString(); - if (!level.Nodes.TryGetValue(key, out node)) + if (!TryGetLiteralNode(level, token, out _, out node)) { + var key = token.ToString(); node = new TrieNode(); level.Nodes[key] = node; } @@ -474,13 +474,20 @@ public sealed class SubList : IDisposable } else { - level.Nodes.TryGetValue(token.ToString(), out node); + if (!TryGetLiteralNode(level, token, out var existingToken, out node)) + return false; + + pathList.Add((level, node, existingToken, isPwc: false, isFwc: false)); + if (node.Next == null) + return false; // corrupted trie state + level = node.Next; + continue; } if (node == null) return false; // not found - var tokenStr = token.ToString(); + var tokenStr = isPwc ? "*" : ">"; pathList.Add((level, node, tokenStr, isPwc, isFwc)); if (node.Next == null) return false; // corrupted trie state @@ -652,6 +659,23 @@ public sealed class SubList : IDisposable return removed; } + private static bool TryGetLiteralNode(TrieLevel level, ReadOnlySpan token, out string existingToken, out TrieNode node) + { + foreach (var (candidate, existingNode) in level.Nodes) + { + if (!SubjectMatch.TokenEquals(token, candidate)) + continue; + + existingToken = candidate; + node = existingNode; + return true; + } + + existingToken = string.Empty; + node = null!; + return false; + } + private bool HasExactQueueInterestNoLock(string subject, string queue) { var subs = new List(); diff --git a/src/NATS.Server/Subscriptions/SubjectMatch.cs b/src/NATS.Server/Subscriptions/SubjectMatch.cs index a3df07f..69c28f3 100644 --- a/src/NATS.Server/Subscriptions/SubjectMatch.cs +++ b/src/NATS.Server/Subscriptions/SubjectMatch.cs @@ -249,6 +249,9 @@ public static class SubjectMatch return tokens.Count == test.Count; } + internal static bool TokenEquals(ReadOnlySpan token, string candidate) + => token.SequenceEqual(candidate); + private static bool TokensCanMatch(ReadOnlySpan t1, ReadOnlySpan t2) { if (t1.Length == 1 && (t1[0] == Pwc || t1[0] == Fwc))