/// <summary> /// Add key to the trie. Returns true if this is a new item in the trie /// rather than a duplicate. /// </summary> /// <param name="prefix"></param> /// <param name="start"></param> /// <param name="size"></param> /// <returns></returns> public bool Add([NotNull] byte[] prefix, int start, int size) { // We are at the node corresponding to the prefix. We are done. if (size == 0) { ++this.m_referenceCount; return(this.m_referenceCount == 1); } 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) { byte oldc = this.m_minCharacter; Trie oldp = this.m_next[0]; this.m_count = (short)((this.m_minCharacter < currentCharacter ? currentCharacter - this.m_minCharacter : this.m_minCharacter - currentCharacter) + 1); this.m_next = new Trie[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 = (short)(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 = (short)(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 Trie[1]; this.m_next[0] = new Trie(); ++this.m_liveNodes; //alloc_Debug.Assert(next.node); } return(this.m_next[0].Add(prefix, start + 1, size - 1)); } if (this.m_next[currentCharacter - this.m_minCharacter] == null) { this.m_next[currentCharacter - this.m_minCharacter] = new Trie(); ++this.m_liveNodes; //alloc_Debug.Assert(next.table [c - min]); } return(this.m_next[currentCharacter - this.m_minCharacter].Add(prefix, start + 1, size - 1)); }
/// <summary> /// Remove key from the trie. Returns true if the item is actually /// removed from the trie. /// </summary> /// <param name="prefix"></param> /// <param name="start"></param> /// <param name="size"></param> /// <returns></returns> public bool Remove([NotNull] byte[] prefix, int start, int size) { if (size == 0) { if (this.m_referenceCount == 0) { return(false); } this.m_referenceCount--; return(this.m_referenceCount == 0); } byte currentCharacter = prefix[start]; if (this.m_count == 0 || currentCharacter < this.m_minCharacter || currentCharacter >= this.m_minCharacter + this.m_count) { return(false); } Trie nextNode = this.m_count == 1 ? this.m_next[0] : this.m_next[currentCharacter - this.m_minCharacter]; if (nextNode == null) { return(false); } bool wasRemoved = nextNode.Remove(prefix, start + 1, size - 1); if (nextNode.IsRedundant()) { //delete next_node; 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 Trie node = null; for (short i = 0; i < this.m_count; ++i) { if (this.m_next[i] != null) { node = this.m_next[i]; this.m_minCharacter = (byte)(i + this.m_minCharacter); break; } } Debug.Assert(node != null); this.m_next = null; this.m_next = new[] { node }; this.m_count = 1; } else if (currentCharacter == this.m_minCharacter) { // We can compact the table "from the left" byte newMin = this.m_minCharacter; for (short i = 1; i < this.m_count; ++i) { if (this.m_next[i] != null) { newMin = (byte)(i + this.m_minCharacter); break; } } Debug.Assert(newMin != this.m_minCharacter); Debug.Assert(newMin > this.m_minCharacter); Debug.Assert(this.m_count > newMin - this.m_minCharacter); this.m_count = (short)(this.m_count - (newMin - this.m_minCharacter)); this.m_next = this.m_next.Resize(this.m_count, false); this.m_minCharacter = newMin; } else if (currentCharacter == this.m_minCharacter + this.m_count - 1) { // We can compact the table "from the right" short newCount = this.m_count; for (short i = 1; i < this.m_count; ++i) { if (this.m_next[this.m_count - 1 - i] != null) { newCount = (short)(this.m_count - i); break; } } Debug.Assert(newCount != this.m_count); this.m_count = newCount; this.m_next = this.m_next.Resize(this.m_count, true); } } } return(wasRemoved); }