public IRadixTreeNode Fetch(DbItemReference reference) { if (DbItemReference.IsNull(reference)) { return(null); } var page = _pageManager.FetchPage(reference.PageIndex); if (page.BackingObject == null) { var obj1 = RadixTreePageBackingObject.FromPage(page); page = new Page(_pageManager, page.Index, () => Serialize(obj1), obj1); } var backingObject = (RadixTreePageBackingObject)page.BackingObject; var obj = backingObject.Items[reference.ItemIndex]; var result = obj as IRadixTreeNode; if (result == null) { result = NodeFromBytes((byte[])obj); result.Reference = (DbItemReference)reference.Clone(); backingObject.Items[reference.ItemIndex] = result; } return(result); }
private IRadixTreeNode NodeFromBytes(byte[] nodeBytes) { var result = new RadixTreeNode(0); using (var ms = new MemoryStream(nodeBytes, false)) { var buffer = new byte[PageFormatter.OnPagePointerSize]; ms.Read(buffer, 0, PageFormatter.OnPagePointerSize); // overall size ms.Read(buffer, 0, PageFormatter.OnPagePointerSize); // prefix length short prefixLength = BitConverter.ToInt16(buffer, 0); var prefix = new byte[prefixLength]; ms.Read(prefix, 0, prefixLength); // prefix itself result.Prefix = prefix; result.ValueReference = DbItemReference.Read(ms); result.ParentNodeReference = DbItemReference.Read(ms); if (DbItemReference.IsNull(result.ValueReference)) { result.ValueReference = null; } if (DbItemReference.IsNull(result.ParentNodeReference)) { result.ParentNodeReference = null; } ms.Read(buffer, 0, sizeof(short)); var nodeCount = BitConverter.ToInt16(buffer, 0); for (int i = 0; i < nodeCount; i++) { var key = (byte)ms.ReadByte(); var value = DbItemReference.Read(ms); result.Entries.Add(new KeyValuePair <byte, DbItemReference>(key, value)); } return(result); } }
private TKey BuildKeyForNode(IRadixTreeNode node) { var prefixes = new List <byte[]>(); while (!DbItemReference.IsNull(node.ParentNodeReference)) { prefixes.Add(node.Prefix); node = _nodeStorage.Fetch(node.ParentNodeReference); } prefixes.Reverse(); using (var ms = new MemoryStream()) { foreach (var prefix in prefixes.Where(p => p != null)) { ms.Write(prefix, 0, prefix.Length); } return(_keySerializer.Deserialize(ms.ToArray())); } }
/// <summary> /// Inserts or updates key value pair. /// </summary> /// <param name="key">The key</param> /// <param name="value">The value</param> public void Set(TKey key, TValue value) { var binaryKey = _keySerializer.Serialize(key); int keyOffset; int nodePrefixOffset; bool isFullMatch; IRadixTreeNode targetNode = FindMostSuitableNode(binaryKey, out keyOffset, out nodePrefixOffset, out isFullMatch); if (isFullMatch) { // here we needn't any manipulation with key prefixes // simply update the existing or allocate a new value targetNode.ValueReference = targetNode.ValueReference != null ? _valueStorage.Reallocate(targetNode.ValueReference, value) : _valueStorage.AllocateNew(value); UpdateOrFail(targetNode); return; } // we have a partial match var remainingKeyPart = new byte[binaryKey.Length - keyOffset]; var nodeChain = new List <IRadixTreeNode>(); if (remainingKeyPart.Length > 0) { Buffer.BlockCopy(binaryKey, keyOffset, remainingKeyPart, 0, remainingKeyPart.Length); // the remaining prefix may exceed the _maxPrefixLength // we must create a chain of nodes, where each node // contain part of the prefix remaining nodeChain = CreateNodeChain(remainingKeyPart); } if (targetNode.Prefix != null && nodePrefixOffset < targetNode.Prefix.Length) { // we got into splitting update: // the targetNode is split into newParentNode and targetNode itself // nodeChain (if has any items) is attached to newParentNode as a child // split prefix byte[] remainingPrefix; byte[] prefixForNewParent; SplitPrefix(targetNode.Prefix, nodePrefixOffset, out prefixForNewParent, out remainingPrefix); targetNode.Prefix = remainingPrefix; var newParentNode = _nodeStorage.Create(prefixForNewParent.Length, 2); newParentNode.Prefix = prefixForNewParent; newParentNode.ParentNodeReference = targetNode.ParentNodeReference; targetNode.ParentNodeReference = newParentNode.Reference; var valueReference = _valueStorage.AllocateNew(value); if (nodeChain.Any()) { nodeChain.First().ParentNodeReference = newParentNode.Reference; nodeChain.Last().ValueReference = valueReference; } else { newParentNode.ValueReference = valueReference; } // add links to child nodes foreach (var item in GetChildLinks(nodeChain.FirstOrDefault(), targetNode)) { newParentNode.Entries.Add(item); } if (nodeChain.Any()) { foreach (var node in nodeChain) { UpdateOrFail(node); } } UpdateOrFail(newParentNode); UpdateOrFail(targetNode); // change link in parent node if needed if (!DbItemReference.IsNull(newParentNode.ParentNodeReference)) { AdjustParentNodeLink(newParentNode, true); } } else { // here is keeping update // all nodes above remain intact byte b = remainingKeyPart[0]; int index; FindSuitableEntry(targetNode.Entries, b, out index); targetNode.Entries.Insert(index, new KeyValuePair <byte, DbItemReference>(b, (DbItemReference)(nodeChain.First().Reference.Clone()))); bool reallocated; targetNode = UpdateOrReallocateNode(targetNode, out reallocated); if (reallocated) { AdjustParentNodeLink(targetNode, true); AdjustLinksInChildNodes(targetNode, true); } nodeChain.First().ParentNodeReference = targetNode.Reference; nodeChain.Last().ValueReference = _valueStorage.AllocateNew(value); foreach (var node in nodeChain) { UpdateOrFail(node); } } }
/// <summary> /// Removes key-value pair by key. /// </summary> /// <param name="key">The key</param> public void Remove(TKey key) { var binaryKey = _keySerializer.Serialize(key); int keyOffset; int nodePrefixOffset; bool isFullMatch; IRadixTreeNode node = FindMostSuitableNode(binaryKey, out keyOffset, out nodePrefixOffset, out isFullMatch); if (isFullMatch) { _valueStorage.Free(node.ValueReference); node.ValueReference = null; if (node.Entries.Any()) { if (node.Prefix != null && node.Entries.Count == 1) { var child = _nodeStorage.Fetch(node.Entries[0].Value); var newPrefixLength = node.Prefix.Length + child.Prefix.Length; if (newPrefixLength <= MaxPrefixLength) { var newPrefix = new byte[newPrefixLength]; Buffer.BlockCopy(node.Prefix, 0, newPrefix, 0, node.Prefix.Length); Buffer.BlockCopy(child.Prefix, 0, newPrefix, node.Prefix.Length, child.Prefix.Length); child.Prefix = newPrefix; child.ParentNodeReference = node.ParentNodeReference; _nodeStorage.Remove(node.Reference); bool reallocated; child = UpdateOrReallocateNode(child, out reallocated); var parent = _nodeStorage.Fetch(child.ParentNodeReference); int index; FindSuitableEntry(parent.Entries, child.Prefix[0], out index); parent.Entries[index] = new KeyValuePair <byte, DbItemReference>(child.Prefix[0], child.Reference); UpdateOrFail(parent); } else { UpdateOrFail(node); } } else { UpdateOrFail(node); } } else { while (!DbItemReference.IsNull(node.ParentNodeReference)) { var parentNode = _nodeStorage.Fetch(node.ParentNodeReference); int index; FindSuitableEntry(parentNode.Entries, node.Prefix[0], out index); parentNode.Entries.RemoveAt(index); _nodeStorage.Remove(node.Reference); if (parentNode.Entries.Any() || parentNode.ValueReference != null || DbItemReference.IsNull(parentNode.ParentNodeReference)) { UpdateOrFail(parentNode); break; } node = parentNode; } } } }
/// <summary> /// Gets the key next to the specified key. /// The existence of the specified key is not required. /// </summary> /// <returns>The key next to specified key</returns> public TKey NextTo(TKey key) { var binaryKey = _keySerializer.Serialize(key); int keyOffset; int nodePrefixOffset; bool isFullMatch; IRadixTreeNode targetNode = FindMostSuitableNode(binaryKey, out keyOffset, out nodePrefixOffset, out isFullMatch); if (isFullMatch || keyOffset == binaryKey.Length) { if (targetNode.Entries.Any()) { targetNode = _nodeStorage.Fetch(targetNode.Entries.First().Value); } else { while (!DbItemReference.IsNull(targetNode.ParentNodeReference)) { var parent = _nodeStorage.Fetch(targetNode.ParentNodeReference); targetNode = GetChildNextTo(parent, targetNode.Prefix[0]); if (targetNode != null) { break; } targetNode = parent; } } } else { byte b = binaryKey[keyOffset]; while (true) { var child = GetChildNextTo(targetNode, b); if (child != null) { targetNode = child; break; } if (DbItemReference.IsNull(targetNode.ParentNodeReference)) { break; } b = targetNode.Prefix[0]; targetNode = _nodeStorage.Fetch(targetNode.ParentNodeReference); } } if (DbItemReference.IsNull(targetNode.ParentNodeReference)) { return(default(TKey)); } while (DbItemReference.IsNull(targetNode.ValueReference) && targetNode.Entries.Any()) { targetNode = _nodeStorage.Fetch(targetNode.Entries.First().Value); } return(BuildKeyForNode(targetNode)); }