/// <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 keyPath = GetPathForKey(key); var leafNode = keyPath.Last(); int index; var entry = FindSuitableEntry(leafNode.Entries, key, out index); if (entry.HasValue) { // update found value leafNode.Entries[index] = new KeyValuePair <TKey, DbItemReference>(key, _valueStorage.Reallocate(entry.Value.Value, value)); _nodeStorage.Update(leafNode); return; } var newEntry = new KeyValuePair <TKey, DbItemReference>(key, _valueStorage.AllocateNew(value)); var nodesToUpdate = new Dictionary <long, IBPlusTreeNode <TKey> >(); leafNode.Entries.Insert(index, newEntry); // node could be in the inconsistent state while inserting new value // pin it to prevent writing of the corresponding page _nodeStorage.PinNode(leafNode.Index); AlignMaximalValues(key, keyPath, nodesToUpdate); if (IsOverflow(leafNode)) { // the node is overflow, we should try to rotate it // or split if rotation is impossible if (!Rotate(leafNode, nodesToUpdate)) { Split(leafNode, nodesToUpdate); } } else { _nodeStorage.Update(leafNode); } foreach (var nodeToUpdate in nodesToUpdate) { _nodeStorage.Update(nodeToUpdate.Value); } }
/// <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); } } }