private bool AddHelper([CanBeNull] byte[] prefix, int start, int size, [NotNull] Pipe pipe) { // We are at the node corresponding to the prefix. We are done. if (size == 0) { bool result = this.m_pipes == null; if (this.m_pipes == null) { this.m_pipes = new HashSet <Pipe>(); } this.m_pipes.Add(pipe); return(result); } Debug.Assert(prefix != null); byte currentCharacter = prefix[start]; if (currentCharacter < this.m_minCharacter || currentCharacter >= this.m_minCharacter + this.m_count) { // The character is out of range of currently handled // characters. We have to extend the table. if (this.m_count == 0) { this.m_minCharacter = currentCharacter; this.m_count = 1; this.m_next = null; } else if (this.m_count == 1) { int oldc = this.m_minCharacter; MultiTrie oldp = this.m_next[0]; this.m_count = (this.m_minCharacter < currentCharacter ? currentCharacter - this.m_minCharacter : this.m_minCharacter - currentCharacter) + 1; this.m_next = new MultiTrie[this.m_count]; this.m_minCharacter = Math.Min(this.m_minCharacter, currentCharacter); this.m_next[oldc - this.m_minCharacter] = oldp; } else if (this.m_minCharacter < currentCharacter) { // The new character is above the current character range. this.m_count = currentCharacter - this.m_minCharacter + 1; this.m_next = this.m_next.Resize(this.m_count, true); } else { // The new character is below the current character range. this.m_count = this.m_minCharacter + this.m_count - currentCharacter; this.m_next = this.m_next.Resize(this.m_count, false); this.m_minCharacter = currentCharacter; } } // If next node does not exist, create one. if (this.m_count == 1) { if (this.m_next == null) { this.m_next = new MultiTrie[1]; this.m_next[0] = new MultiTrie(); ++this.m_liveNodes; } return(this.m_next[0].AddHelper(prefix, start + 1, size - 1, pipe)); } if (this.m_next[currentCharacter - this.m_minCharacter] == null) { this.m_next[currentCharacter - this.m_minCharacter] = new MultiTrie(); ++this.m_liveNodes; } return(this.m_next[currentCharacter - this.m_minCharacter].AddHelper(prefix, start + 1, size - 1, pipe)); }
private bool RemoveHelper([NotNull] byte[] prefix, int start, int size, [NotNull] Pipe pipe) { if (size == 0) { if (this.m_pipes != null) { bool erased = this.m_pipes.Remove(pipe); Debug.Assert(erased); if (this.m_pipes.Count == 0) { this.m_pipes = null; } } return(this.m_pipes == null); } byte currentCharacter = prefix[start]; if (this.m_count == 0 || currentCharacter < this.m_minCharacter || currentCharacter >= this.m_minCharacter + this.m_count) { return(false); } MultiTrie nextNode = this.m_count == 1 ? this.m_next[0] : this.m_next[currentCharacter - this.m_minCharacter]; if (nextNode == null) { return(false); } bool ret = nextNode.RemoveHelper(prefix, start + 1, size - 1, pipe); if (nextNode.IsRedundant) { Debug.Assert(this.m_count > 0); if (this.m_count == 1) { this.m_next = null; this.m_count = 0; --this.m_liveNodes; Debug.Assert(this.m_liveNodes == 0); } else { this.m_next[currentCharacter - this.m_minCharacter] = null; Debug.Assert(this.m_liveNodes > 1); --this.m_liveNodes; // Compact the table if possible if (this.m_liveNodes == 1) { // If there's only one live node in the table we can // switch to using the more compact single-node // representation int i; for (i = 0; i < this.m_count; ++i) { if (this.m_next[i] != null) { break; } } Debug.Assert(i < this.m_count); this.m_minCharacter += i; this.m_count = 1; MultiTrie old = this.m_next[i]; this.m_next = new[] { old }; } else if (currentCharacter == this.m_minCharacter) { // We can compact the table "from the left" int i; for (i = 1; i < this.m_count; ++i) { if (this.m_next[i] != null) { break; } } Debug.Assert(i < this.m_count); this.m_minCharacter += i; this.m_count -= i; this.m_next = this.m_next.Resize(this.m_count, false); } else if (currentCharacter == this.m_minCharacter + this.m_count - 1) { // We can compact the table "from the right" int i; for (i = 1; i < this.m_count; ++i) { if (this.m_next[this.m_count - 1 - i] != null) { break; } } Debug.Assert(i < this.m_count); this.m_count -= i; this.m_next = this.m_next.Resize(this.m_count, true); } } } return(ret); }
/// <summary> /// Signal all the matching pipes. /// </summary> public void Match([NotNull] byte[] data, int offset, int size, [NotNull] MultiTrieDelegate func, [CanBeNull] object arg) { MultiTrie current = this; int index = offset; while (true) { // Signal the pipes attached to this node. if (current.m_pipes != null) { foreach (Pipe it in current.m_pipes) { func(it, null, 0, arg); } } // If we are at the end of the message, there's nothing more to match. if (size == 0) { break; } // If there are no subnodes in the trie, return. if (current.m_count == 0) { break; } byte c = data[index]; // If there's one subnode (optimisation). if (current.m_count == 1) { if (c != current.m_minCharacter) { break; } current = current.m_next[0]; index++; size--; continue; } // If there are multiple subnodes. if (c < current.m_minCharacter || c >= current.m_minCharacter + current.m_count) { break; } if (current.m_next[c - current.m_minCharacter] == null) { break; } current = current.m_next[c - current.m_minCharacter]; index++; size--; } }
private bool RemoveHelper([NotNull] Pipe pipe, [NotNull] byte[] buffer, int bufferSize, int maxBufferSize, [NotNull] MultiTrieDelegate func, [CanBeNull] object arg) { // Remove the subscription from this node. if (this.m_pipes != null && this.m_pipes.Remove(pipe) && this.m_pipes.Count == 0) { func(pipe, buffer, bufferSize, arg); this.m_pipes = null; } // Adjust the buffer. if (bufferSize >= maxBufferSize) { maxBufferSize = bufferSize + 256; Array.Resize(ref buffer, maxBufferSize); } // If there are no subnodes in the trie, return. if (this.m_count == 0) { return(true); } // If there's one subnode (optimisation). if (this.m_count == 1) { buffer[bufferSize] = (byte)this.m_minCharacter; bufferSize++; this.m_next[0].RemoveHelper(pipe, buffer, bufferSize, maxBufferSize, func, arg); // Prune the node if it was made redundant by the removal if (this.m_next[0].IsRedundant) { this.m_next = null; this.m_count = 0; --this.m_liveNodes; Debug.Assert(this.m_liveNodes == 0); } return(true); } // If there are multiple subnodes. // New min non-null character in the node table after the removal int newMin = this.m_minCharacter + this.m_count - 1; // New max non-null character in the node table after the removal int newMax = this.m_minCharacter; for (int currentCharacter = 0; currentCharacter != this.m_count; currentCharacter++) { buffer[bufferSize] = (byte)(this.m_minCharacter + currentCharacter); if (this.m_next[currentCharacter] != null) { this.m_next[currentCharacter].RemoveHelper(pipe, buffer, bufferSize + 1, maxBufferSize, func, arg); // Prune redundant nodes from the mtrie if (this.m_next[currentCharacter].IsRedundant) { this.m_next[currentCharacter] = null; Debug.Assert(this.m_liveNodes > 0); --this.m_liveNodes; } else { // The node is not redundant, so it's a candidate for being // the new min/max node. // // We loop through the node array from left to right, so the // first non-null, non-redundant node encountered is the new // minimum index. Conversely, the last non-redundant, non-null // node encountered is the new maximum index. if (currentCharacter + this.m_minCharacter < newMin) { newMin = currentCharacter + this.m_minCharacter; } if (currentCharacter + this.m_minCharacter > newMax) { newMax = currentCharacter + this.m_minCharacter; } } } } Debug.Assert(this.m_count > 1); // Free the node table if it's no longer used. if (this.m_liveNodes == 0) { this.m_next = null; this.m_count = 0; } // Compact the node table if possible else if (this.m_liveNodes == 1) { // If there's only one live node in the table we can // switch to using the more compact single-node // representation Debug.Assert(newMin == newMax); Debug.Assert(newMin >= this.m_minCharacter && newMin < this.m_minCharacter + this.m_count); MultiTrie node = this.m_next[newMin - this.m_minCharacter]; Debug.Assert(node != null); this.m_next = null; this.m_next = new[] { node }; this.m_count = 1; this.m_minCharacter = newMin; } else if (this.m_liveNodes > 1 && (newMin > this.m_minCharacter || newMax < this.m_minCharacter + this.m_count - 1)) { Debug.Assert(newMax - newMin + 1 > 1); MultiTrie[] oldTable = this.m_next; Debug.Assert(newMin > this.m_minCharacter || newMax < this.m_minCharacter + this.m_count - 1); Debug.Assert(newMin >= this.m_minCharacter); Debug.Assert(newMax <= this.m_minCharacter + this.m_count - 1); Debug.Assert(newMax - newMin + 1 < this.m_count); this.m_count = newMax - newMin + 1; this.m_next = new MultiTrie[this.m_count]; Array.Copy(oldTable, newMin - this.m_minCharacter, this.m_next, 0, this.m_count); this.m_minCharacter = newMin; } return(true); }