コード例 #1
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <summary>
        /// Gets the minimal key.
        /// </summary>
        /// <returns>The minimal key</returns>
        public TKey Min()
        {
            var prefixes = new List <byte[]>();

            IRadixTreeNode node = _nodeStorage.FetchRoot();

            while (true)
            {
                prefixes.Add(node.Prefix);
                if (node.ValueReference != null)
                {
                    break;
                }

                if (node.Entries.Any())
                {
                    node = _nodeStorage.Fetch(node.Entries.First().Value);
                }
            }

            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()));
            }
        }
コード例 #2
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private List <IRadixTreeNode> CreateNodeChain(byte[] remainingKeyPart)
        {
            var result = new List <IRadixTreeNode>();

            var            offset       = 0;
            IRadixTreeNode previousNode = null;

            while (offset < remainingKeyPart.Length)
            {
                var prefixLength = Math.Min(MaxPrefixLength, remainingKeyPart.Length - offset);
                var newNode      = _nodeStorage.Create(prefixLength, 1);

                newNode.Prefix = new byte[prefixLength];
                Buffer.BlockCopy(remainingKeyPart, offset, newNode.Prefix, 0, newNode.Prefix.Length);

                if (previousNode != null)
                {
                    previousNode.Entries.Add(new KeyValuePair <byte, DbItemReference>(newNode.Prefix[0], newNode.Reference));
                    newNode.ParentNodeReference = previousNode.Reference;
                }

                result.Add(newNode);

                previousNode = newNode;

                offset += MaxPrefixLength;
                if (offset > remainingKeyPart.Length)
                {
                    offset = remainingKeyPart.Length;
                }
            }

            return(result);
        }
コード例 #3
0
        public bool Update(IRadixTreeNode node)
        {
            var page = _pageManager.FetchPage(node.Reference.PageIndex);

            if (page.BackingObject == null)
            {
                var obj = RadixTreePageBackingObject.FromPage(page);
                page = new Page(_pageManager, node.Reference.PageIndex, () => Serialize(obj), obj);
            }

            var backingObject = (RadixTreePageBackingObject)page.BackingObject;
            var oldNodeSize   = backingObject.GetNodeSize(node.Reference.ItemIndex);

            var newNodeSize = GetNodeSize((short)node.Prefix.Length, node.Entries.Count);

            var oldObjectSize = backingObject.Size;

            var newObjectSize = oldObjectSize + newNodeSize - oldNodeSize;

            if (newObjectSize > _pageManager.PageSize)
            {
                return(false);
            }

            backingObject.Items[node.Reference.ItemIndex] = node;

            _pageManager.UpdatePage(page);
            return(true);
        }
コード例 #4
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private bool CheckNode(IRadixTreeNode node, out string message)
        {
            message = string.Empty;

            if (node.ParentNodeReference != null)
            {
                if (!node.Entries.Any() && node.ValueReference == null)
                {
                    message = $"Node: {node.Reference} has no value and child nodes";
                    return(false);
                }

                if (_nodeStorage.Fetch(node.ParentNodeReference) == null)
                {
                    message = $"Node: {node.Reference} has invalid reference ({node.ParentNodeReference}) to parent node.";
                    return(false);
                }
            }

            foreach (var entry in node.Entries)
            {
                var childNode = _nodeStorage.Fetch(entry.Value);
                if (childNode == null)
                {
                    message = $"Invalid reference ({entry.Value}) to child in node: {node.Reference}";
                    return(false);
                }
            }

            return(true);
        }
コード例 #5
0
        public short AddNode(RadixTreePageBackingObject backingObject, IRadixTreeNode node, int prefixSize, int childCapacity)
        {
            var size     = backingObject.Size;
            var nodeSize = GetNodeSize((short)prefixSize, childCapacity);

            if (size + nodeSize > _pageManager.PageSize)
            {
                return(-1);
            }

            for (short i = 0; i < backingObject.Items.Count; i++)
            {
                if (backingObject.Items[i] == null)
                {
                    backingObject.Items[i] = node;
                    return(i);
                }
            }

            if (size + nodeSize + PageFormatter.OnPagePointerSize > _pageManager.PageSize)
            {
                return(-1);
            }

            backingObject.Items.Add(node);

            return((short)(backingObject.Items.Count - 1));
        }
コード例 #6
0
        private byte[] GetNodeBytes(IRadixTreeNode node, short minimalSize)
        {
            var prefix          = node.Prefix ?? new byte[0];
            var prefixLength    = (short)prefix.Length;
            var valueReference  = node.ValueReference ?? DbItemReference.Null;
            var parentReference = node.ParentNodeReference ?? DbItemReference.Null;

            var size = GetNodeSize(prefixLength, node.Entries.Count);

            var result = new byte[Math.Max(size, minimalSize)];

            using (var ms = new MemoryStream(result))
            {
                ms.Write(BitConverter.GetBytes(size), 0, PageFormatter.OnPagePointerSize);
                ms.Write(BitConverter.GetBytes(prefixLength), 0, PageFormatter.OnPagePointerSize);
                ms.Write(prefix, 0, prefixLength);
                valueReference.Write(ms);
                parentReference.Write(ms);
                ms.Write(BitConverter.GetBytes((short)node.Entries.Count), 0, sizeof(short));

                foreach (var entry in node.Entries)
                {
                    ms.WriteByte(entry.Key);
                    ms.Write(entry.Value.GetBytes(), 0, DbItemReference.BytesLength);
                }

                return(ms.ToArray());
            }
        }
コード例 #7
0
 private void CheckNode(IRadixTreeNode node)
 {
     if (!_nodes.ContainsKey(node.Reference.ToString()))
     {
         throw new ArgumentException("Radix tree node has an invalid index", nameof(node));
     }
 }
コード例 #8
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
 /// <summary>
 /// This method is used to guarantee the updating.
 /// Use this if the updating node is not increased in size.
 /// </summary>
 /// <param name="node"></param>
 private void UpdateOrFail(IRadixTreeNode node)
 {
     if (!_nodeStorage.Update(node))
     {
         throw new DataTankerException("Update failed. Node should be reallocated");
     }
 }
コード例 #9
0
        public bool Update(IRadixTreeNode node)
        {
            CheckNode(node);
            _nodes[node.Reference.ToString()] = node;

            return(true);
        }
コード例 #10
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Determines if the specified key has subkeys.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool HasSubkeys(TKey key)
        {
            var            binaryKey = _keySerializer.Serialize(key);
            bool           isFullMatch;
            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch);

            return(isFullMatch && node.Entries.Any());
        }
コード例 #11
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private IRadixTreeNode GetChildPreviousTo(IRadixTreeNode node, byte b)
        {
            int index;

            FindSuitableEntry(node.Entries, b, out index);
            if (index <= 0 || index > node.Entries.Count)
            {
                return(null);
            }
            return(_nodeStorage.Fetch(node.Entries[index - 1].Value));
        }
コード例 #12
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Retrieves a segment of binary representation
        /// of the value referenced by the specified key.
        /// </summary>
        /// <param name="key">The key</param>
        /// <param name="startIndex">The index in binary representation where the specified segment starts</param>
        /// <param name="endIndex">The index in binary representation where the specified segment ends</param>
        /// <returns></returns>
        public byte[] GetRawDataSegment(TKey key, long startIndex, long endIndex)
        {
            var            binaryKey = _keySerializer.Serialize(key);
            bool           isFullMatch;
            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch);

            if (!isFullMatch)
            {
                return(null);
            }

            return(node.ValueReference != null?_valueStorage.GetRawDataSegment(node.ValueReference, startIndex, endIndex) : null);
        }
コード例 #13
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private void Count(IRadixTreeNode node, ref long count)
        {
            if (node.ValueReference != null)
            {
                count++;
            }

            foreach (var entry in node.Entries)
            {
                var child = _nodeStorage.Fetch(entry.Value);
                Count(child, ref count);
            }
        }
コード例 #14
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Computes the number of child key-value pairs for a given key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long SubkeysCount(TKey key)
        {
            var            binaryKey = _keySerializer.Serialize(key);
            bool           isFullMatch;
            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch);

            long result = 0;

            if (isFullMatch)
            {
                Count(node, ref result);
            }

            return(result);
        }
コード例 #15
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
 private void AdjustLinksInChildNodes(IRadixTreeNode node, bool update)
 {
     if (node.Entries.Any())
     {
         foreach (var entry in node.Entries)
         {
             var child = _nodeStorage.Fetch(entry.Value);
             child.ParentNodeReference = node.Reference;
             if (update)
             {
                 UpdateOrFail(child);
             }
         }
     }
 }
コード例 #16
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Cheks if key-value pair exists in tree.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>True if key-value pair exists, false otherwise</returns>
        public bool Exists(TKey key)
        {
            var            binaryKey = _keySerializer.Serialize(key);
            bool           isFullMatch;
            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch);

            if (isFullMatch)
            {
                if (node.ValueReference != null)
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #17
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Retrieves the length (in bytes) of binary representation
        /// of the value referenced by the specified key.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The length of binary representation</returns>
        public long GetRawDataLength(TKey key)
        {
            var            binaryKey = _keySerializer.Serialize(key);
            bool           isFullMatch;
            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch);

            if (isFullMatch)
            {
                if (node.ValueReference != null)
                {
                    return(_valueStorage.GetRawDataLength(node.ValueReference));
                }
            }

            return(0);
        }
コード例 #18
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private IRadixTreeNode GetChildNextTo(IRadixTreeNode node, byte b)
        {
            int index;
            var entry = FindSuitableEntry(node.Entries, b, out index);

            if (entry != null)
            {
                index++;
            }

            if (index < 0 || index >= node.Entries.Count)
            {
                return(null);
            }
            return(_nodeStorage.Fetch(node.Entries[index].Value));
        }
コード例 #19
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private void AdjustParentNodeLink(IRadixTreeNode node, bool updateNode)
        {
            var parentNode = _nodeStorage.Fetch(node.ParentNodeReference);

            var b = node.Prefix[0];
            int index;

            if (FindSuitableEntry(parentNode.Entries, b, out index).HasValue)
            {
                parentNode.Entries[index] = new KeyValuePair <byte, DbItemReference>(b, (DbItemReference)(node.Reference.Clone()));
                if (updateNode)
                {
                    UpdateOrFail(parentNode);
                }
            }
        }
コード例 #20
0
ファイル: RadixTree.cs プロジェクト: mario206/DataTanker
        /// <summary>
        /// Gets the value by its key.
        /// </summary>
        /// <param name="key">The key</param>
        /// <returns>The value corresponding to the given key</returns>
        public TValue Get(TKey key)
        {
            var  binaryKey = _keySerializer.Serialize(key);
            bool isFullMatch;

            IRadixTreeNode node = FindMostSuitableNode(binaryKey, out _, out _, out isFullMatch /*, treeNode => references.Add(treeNode.Reference)*/);

            if (isFullMatch)
            {
                if (node.ValueReference != null)
                {
                    return(_valueStorage.Fetch(node.ValueReference));
                }
            }

            return(default(TValue));
        }
コード例 #21
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <summary>
        /// Gets the value corresponing to the maximal key.
        /// </summary>
        /// <returns>The value corresponding to the maximal key</returns>
        public TValue MaxValue()
        {
            IRadixTreeNode node = _nodeStorage.FetchRoot();

            while (true)
            {
                if (node.Entries.Any())
                {
                    node = _nodeStorage.Fetch(node.Entries.Last().Value);
                }
                else
                {
                    break;
                }
            }

            return(_valueStorage.Fetch(node.ValueReference));
        }
コード例 #22
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <summary>
        /// Gets a value corresponing to the minimal key.
        /// </summary>
        /// <returns>The value corresponding to the minimal key</returns>
        public TValue MinValue()
        {
            IRadixTreeNode node = _nodeStorage.FetchRoot();

            while (true)
            {
                if (node.ValueReference != null)
                {
                    break;
                }

                if (node.Entries.Any())
                {
                    node = _nodeStorage.Fetch(node.Entries.First().Value);
                }
            }

            return(_valueStorage.Fetch(node.ValueReference));
        }
コード例 #23
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        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()));
            }
        }
コード例 #24
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private IRadixTreeNode UpdateOrReallocateNode(IRadixTreeNode node, out bool reallocated)
        {
            if (!_nodeStorage.Update(node))
            {
                reallocated = true;
                _nodeStorage.Remove(node.Reference);

                var reallocatedNode = _nodeStorage.Create(node.Prefix.Length, node.Entries.Count);
                reallocatedNode.Prefix              = node.Prefix;
                reallocatedNode.ValueReference      = node.ValueReference;
                reallocatedNode.ParentNodeReference = node.ParentNodeReference;
                foreach (var entry in node.Entries)
                {
                    reallocatedNode.Entries.Add(entry);
                }

                _nodeStorage.Update(reallocatedNode);

                return(reallocatedNode);
            }

            reallocated = false;
            return(node);
        }
コード例 #25
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        private static IEnumerable <KeyValuePair <byte, DbItemReference> > GetChildLinks(IRadixTreeNode node1, IRadixTreeNode node2)
        {
            KeyValuePair <byte, DbItemReference>[] result;

            if (node1 != null)
            {
                result = new[]
                {
                    new KeyValuePair <byte, DbItemReference>(node2.Prefix[0], (DbItemReference)(node2.Reference.Clone())),
                    new KeyValuePair <byte, DbItemReference>(node1.Prefix[0], (DbItemReference)(node1.Reference.Clone()))
                };

                if (node2.Prefix[0] > node1.Prefix[0])
                {
                    Array.Reverse(result);
                }
            }
            else
            {
                result = new[]
                {
                    new KeyValuePair <byte, DbItemReference>(node2.Prefix[0], (DbItemReference)(node2.Reference.Clone()))
                }
            };

            return(result);
        }
コード例 #26
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <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));
        }
コード例 #27
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <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;
                    }
                }
            }
        }
コード例 #28
0
ファイル: RadixTree.cs プロジェクト: lulzzz/Sperone
        /// <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);
                }
            }
        }