private void MultiAddOnNewValue(Slice key, Slice value, ushort?version, int maxNodeSize)
        {
            var requiredPageSize = Constants.TreePageHeaderSize +      // header of a nested page
                                   Constants.NodeOffsetSize +          // one node in a nested page
                                   TreeSizeOf.LeafEntry(-1, value, 0); // node header and its value

            if (requiredPageSize + Constants.NodeHeaderSize > maxNodeSize)
            {
                // no choice, very big value, we might as well just put it in its own tree from the get go...
                // otherwise, we would have to put this in overflow page, and that won't save us any space anyway

                var tree = Create(_llt, _tx, TreeFlags.MultiValue);
                tree.DirectAdd(value, 0);
                _tx.AddMultiValueTree(this, key, tree);

                DirectAdd(key, sizeof(TreeRootHeader), TreeNodeFlags.MultiValuePageRef);
                return;
            }

            var actualPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredPageSize), maxNodeSize - Constants.NodeHeaderSize);

            var ptr = DirectAdd(key, actualPageSize);

            var nestedPage = new TreePage(ptr, "multi tree", actualPageSize)
            {
                PageNumber = -1L,// hint that this is an inner page
                Lower      = (ushort)Constants.TreePageHeaderSize,
                Upper      = actualPageSize,
                TreeFlags  = TreePageFlags.Leaf,
            };

            CheckConcurrency(key, value, version, 0, TreeActionType.Add);

            nestedPage.AddDataNode(0, value, 0, 0);
        }
Example #2
0
        private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize)
        {
            Debug.Assert(newSize > currentSize);

            TemporaryPage tmp;

            using (_llt.Environment.GetTemporaryPage(_llt, out tmp))
            {
                var tempPagePointer = tmp.TempPagePointer;
                Memory.Copy(tempPagePointer, nestedPagePtr, currentSize);
                Delete(key); // release our current page
                TreePage nestedPage = new TreePage(tempPagePointer, (ushort)currentSize);

                byte *ptr;
                using (DirectAdd(key, newSize, out ptr))
                {
                    var newNestedPage = new TreePage(ptr, newSize)
                    {
                        Lower      = (ushort)Constants.Tree.PageHeaderSize,
                        Upper      = newSize,
                        TreeFlags  = TreePageFlags.Leaf,
                        PageNumber = -1L, // mark as invalid page number
                        Flags      = 0
                    };

                    ByteStringContext allocator = _llt.Allocator;
                    for (int i = 0; i < nestedPage.NumberOfEntries; i++)
                    {
                        var nodeHeader = nestedPage.GetNode(i);

                        Slice nodeKey;
                        using (TreeNodeHeader.ToSlicePtr(allocator, nodeHeader, out nodeKey))
                            newNestedPage.AddDataNode(i, nodeKey, 0);
                    }

                    newNestedPage.Search(_llt, value);
                    newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0);
                }
            }
        }
        private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize)
        {
            Debug.Assert(newSize > currentSize);

            TemporaryPage tmp;

            using (_llt.Environment.GetTemporaryPage(_llt, out tmp))
            {
                var tempPagePointer = tmp.TempPagePointer;
                Memory.Copy(tempPagePointer, nestedPagePtr, currentSize);
                Delete(key); // release our current page
                TreePage nestedPage = new TreePage(tempPagePointer, "multi tree", (ushort)currentSize);

                var ptr = DirectAdd(key, newSize);

                var newNestedPage = new TreePage(ptr, "multi tree", newSize)
                {
                    Lower      = (ushort)Constants.TreePageHeaderSize,
                    Upper      = newSize,
                    TreeFlags  = TreePageFlags.Leaf,
                    PageNumber = -1L // mark as invalid page number
                };

                ByteStringContext allocator = _llt.Allocator;
                for (int i = 0; i < nestedPage.NumberOfEntries; i++)
                {
                    var nodeHeader = nestedPage.GetNode(i);

                    Slice nodeKey = TreeNodeHeader.ToSlicePtr(allocator, nodeHeader);

                    newNestedPage.AddDataNode(i, nodeKey, 0, (ushort)(nodeHeader->Version - 1)); // we dec by one because AdddataNode will inc by one, and we don't want to change those values

                    nodeKey.Release(allocator);
                }

                newNestedPage.Search(_llt, value);
                newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0);
            }
        }
Example #4
0
        private byte *AddNodeToPage(TreePage page, int index, Slice alreadyPreparedNewKey = default(Slice))
        {
            var newKeyToInsert = alreadyPreparedNewKey.HasValue ? alreadyPreparedNewKey : _newKey;

            switch (_nodeType)
            {
            case TreeNodeFlags.PageRef:
                return(page.AddPageRefNode(index, newKeyToInsert, _pageNumber));

            case TreeNodeFlags.Data:
                return(page.AddDataNode(index, newKeyToInsert, _len));

            case TreeNodeFlags.MultiValuePageRef:
                return(page.AddMultiValueNode(index, newKeyToInsert, _len));

            default:
                throw new NotSupportedException("Unknown node type");
            }
        }
Example #5
0
        public void MultiAdd(Slice key, Slice value)
        {
            if (!value.HasValue)
            {
                throw new ArgumentNullException(nameof(value));
            }

            int maxNodeSize = Llt.DataPager.NodeMaxSize;

            if (value.Size > maxNodeSize)
            {
                throw new ArgumentException("Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", nameof(value));
            }
            if (value.Size == 0)
            {
                throw new ArgumentException("Cannot add empty value to child tree");
            }

            State.IsModified = true;
            State.Flags     |= TreeFlags.MultiValueTrees;

            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                MultiAddOnNewValue(key, value, maxNodeSize);
                return;
            }

            page = ModifyPage(page);

            var   item = page.GetNode(page.LastSearchPosition);
            byte *_;

            // already was turned into a multi tree, not much to do here
            if (item->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var existingTree = OpenMultiValueTree(key, item);
                existingTree.DirectAdd(value, 0, out _).Dispose();
                return;
            }

            if (item->Flags == TreeNodeFlags.PageRef)
            {
                throw new InvalidOperationException("Multi trees don't use overflows");
            }

            var nestedPagePtr = DirectAccessFromHeader(item);

            var nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(item));

            var existingItem = nestedPage.Search(_llt, value);

            if (nestedPage.LastMatch != 0)
            {
                existingItem = null;// not an actual match, just greater than
            }
            if (existingItem != null)
            {
                // maybe same value added twice?
                Slice tmpKey;
                using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, item, out tmpKey))
                {
                    if (SliceComparer.Equals(tmpKey, value))
                    {
                        return; // already there, turning into a no-op
                    }
                }

                nestedPage.RemoveNode(nestedPage.LastSearchPosition);
            }

            if (nestedPage.HasSpaceFor(_llt, value, 0))
            {
                // we are now working on top of the modified root page, we can just modify the memory directly
                nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0);
                return;
            }

            if (page.HasSpaceFor(_llt, value, 0))
            {
                // page has space for an additional node in nested page ...

                var requiredSpace = nestedPage.PageSize +                  // existing page
                                    nestedPage.GetRequiredSpace(value, 0); // new node

                if (requiredSpace + Constants.Tree.NodeHeaderSize <= maxNodeSize)
                {
                    // ... and it won't require to create an overflow, so we can just expand the current value, no need to create a nested tree yet

                    EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr);

                    var newPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredSpace), maxNodeSize - Constants.Tree.NodeHeaderSize);

                    ExpandMultiTreeNestedPageSize(key, value, nestedPagePtr, newPageSize, nestedPage.PageSize);

                    return;
                }
            }

            EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr);

            // we now have to convert this into a tree instance, instead of just a nested page
            var tree = Create(_llt, _tx, key, TreeFlags.MultiValue);

            for (int i = 0; i < nestedPage.NumberOfEntries; i++)
            {
                Slice existingValue;
                using (nestedPage.GetNodeKey(_llt, i, out existingValue))
                {
                    tree.DirectAdd(existingValue, 0, out _).Dispose();
                }
            }
            tree.DirectAdd(value, 0, out _).Dispose();
            _tx.AddMultiValueTree(this, key, tree);
            // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again
            DirectAdd(key, sizeof(TreeRootHeader), TreeNodeFlags.MultiValuePageRef, out _).Dispose();
        }
Example #6
0
        private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage to)
        {
            Debug.Assert(from.IsBranch == false);
            Slice originalFromKeyStart;

            using (GetActualKey(from, from.LastSearchPositionOrLastEntry, out originalFromKeyStart))
            {
                var   fromNode = from.GetNode(from.LastSearchPosition);
                byte *val      = @from.Base + @from.KeysOffsets[@from.LastSearchPosition] + Constants.Tree.NodeHeaderSize +
                                 originalFromKeyStart.Size;

                byte *dataPos;
                var   fromDataSize = fromNode->DataSize;
                switch (fromNode->Flags)
                {
                case TreeNodeFlags.PageRef:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1);
                    dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber);
                    break;

                case TreeNodeFlags.Data:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                    dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize);
                    break;

                case TreeNodeFlags.MultiValuePageRef:
                    to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                    dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize);
                    break;

                default:
                    throw new NotSupportedException("Invalid node type to move: " + fromNode->Flags);
                }

                if (dataPos != null && fromDataSize > 0)
                {
                    Memory.Copy(dataPos, val, fromDataSize);
                }

                from.RemoveNode(from.LastSearchPositionOrLastEntry);

                var pos = parentPage.LastSearchPositionOrLastEntry;
                parentPage.RemoveNode(pos);

                Slice newSeparatorKey;
                var   scope = GetActualKey(to, 0, out newSeparatorKey); // get the next smallest key it has now
                try
                {
                    var pageNumber = to.PageNumber;
                    if (parentPage.GetNode(0)->PageNumber == to.PageNumber)
                    {
                        pageNumber = from.PageNumber;
                        scope.Dispose();
                        scope = GetActualKey(from, 0, out newSeparatorKey);
                    }

                    AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos);
                }
                finally
                {
                    scope.Dispose();
                }
            }
        }
Example #7
0
        private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage to)
        {
            Debug.Assert(from.IsBranch == false);
            var originalFromKeyStart = GetActualKey(from, from.LastSearchPositionOrLastEntry);

            var   fromNode = from.GetNode(from.LastSearchPosition);
            byte *val      = @from.Base + @from.KeysOffsets[@from.LastSearchPosition] + Constants.NodeHeaderSize + originalFromKeyStart.Size;

            var nodeVersion = fromNode->Version; // every time new node is allocated the version is increased, but in this case we do not want to increase it

            if (nodeVersion > 0)
            {
                nodeVersion -= 1;
            }

            byte *dataPos;
            var   fromDataSize = fromNode->DataSize;

            switch (fromNode->Flags)
            {
            case TreeNodeFlags.PageRef:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1);
                dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber);
                break;

            case TreeNodeFlags.Data:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize, nodeVersion);
                break;

            case TreeNodeFlags.MultiValuePageRef:
                to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize);
                dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize, nodeVersion);
                break;

            default:
                throw new NotSupportedException("Invalid node type to move: " + fromNode->Flags);
            }

            if (dataPos != null && fromDataSize > 0)
            {
                Memory.Copy(dataPos, val, fromDataSize);
            }

            from.RemoveNode(from.LastSearchPositionOrLastEntry);

            var pos = parentPage.LastSearchPositionOrLastEntry;

            parentPage.RemoveNode(pos);

            var newSeparatorKey = GetActualKey(to, 0); // get the next smallest key it has now
            var pageNumber      = to.PageNumber;

            if (parentPage.GetNode(0)->PageNumber == to.PageNumber)
            {
                pageNumber      = from.PageNumber;
                newSeparatorKey = GetActualKey(from, 0);
            }

            AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos);
        }