Exemplo n.º 1
0
        public IIterator MultiRead(Slice key)
        {
            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                return(new EmptyIterator());
            }

            Debug.Assert(node != null);

            var fetchedNodeKey = TreeNodeHeader.ToSlicePtr(_llt.Allocator, node);

            if (SliceComparer.Equals(fetchedNodeKey, key) == false)
            {
                throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
            }

            if (node->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var tree = OpenMultiValueTree(key, node);

                return(tree.Iterate(false));
            }

            var ptr        = TreeNodeHeader.DirectAccess(_llt, node);
            var dataSize   = (ushort)TreeNodeHeader.GetDataSize(_llt, node);
            var nestedPage = new TreePage(ptr, "multi tree", dataSize);

            return(new TreePageIterator(_llt, key, this, nestedPage));
        }
Exemplo n.º 2
0
 public int GetCurrentDataSize()
 {
     if (_disposed)
     {
         throw new ObjectDisposedException("TreeIterator " + _tree.Name);
     }
     return(TreeNodeHeader.GetDataSize(_tx, Current));
 }
Exemplo n.º 3
0
        private void EnsureNestedPagePointer(TreePage page, TreeNodeHeader *currentItem, ref TreePage nestedPage, ref byte *nestedPagePtr)
        {
            var movedItem = page.GetNode(page.LastSearchPosition);

            if (movedItem == currentItem)
            {
                return;
            }

            // HasSpaceFor could called Defrag internally and read item has moved
            // need to ensure the nested page has a valid pointer

            nestedPagePtr = TreeNodeHeader.DirectAccess(_llt, movedItem);
            nestedPage    = new TreePage(nestedPagePtr, "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, movedItem));
        }
Exemplo n.º 4
0
        public int GetDataSize(Slice key)
        {
            TreeNodeHeader *node;
            var             p = FindPageFor(key, out node);

            if (p == null || p.LastMatch != 0)
            {
                return(-1);
            }

            if (node == null || !SliceComparer.EqualsInline(TreeNodeHeader.ToSlicePtr(_llt.Allocator, node), key))
            {
                return(-1);
            }

            return(TreeNodeHeader.GetDataSize(_llt, node));
        }
Exemplo n.º 5
0
        public void MultiAdd(Slice key, Slice value, ushort?version = null)
        {
            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, version, maxNodeSize);
                return;
            }

            page = ModifyPage(page);

            var item = page.GetNode(page.LastSearchPosition);

            // 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, version: version);
                return;
            }

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

            var nestedPagePtr = TreeNodeHeader.DirectAccess(_llt, item);

            var nestedPage = new TreePage(nestedPagePtr, "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, item));

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

            if (nestedPage.LastMatch != 0)
            {
                existingItem = null;// not an actual match, just greater than
            }
            ushort previousNodeRevision = existingItem != null ?  existingItem->Version : (ushort)0;

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

            if (existingItem != null)
            {
                // maybe same value added twice?
                var tmpKey = TreeNodeHeader.ToSlicePtr(_llt.Allocator, item);
                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, previousNodeRevision);
                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.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.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, TreeFlags.MultiValue);

            for (int i = 0; i < nestedPage.NumberOfEntries; i++)
            {
                var existingValue = nestedPage.GetNodeKey(_llt, i);
                tree.DirectAdd(existingValue, 0);
            }
            tree.DirectAdd(value, 0, version: version);
            _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);
        }
Exemplo n.º 6
0
        //TODO: write a test for this
        public long MultiCount(Slice key)
        {
            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                return(0);
            }

            Debug.Assert(node != null);

            var fetchedNodeKey = TreeNodeHeader.ToSlicePtr(_llt.Allocator, node);

            if (SliceComparer.Equals(fetchedNodeKey, key) == false)
            {
                throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible");
            }

            if (node->Flags == TreeNodeFlags.MultiValuePageRef)
            {
                var tree = OpenMultiValueTree(key, node);

                return(tree.State.NumberOfEntries);
            }

            var nestedPage = new TreePage(TreeNodeHeader.DirectAccess(_llt, node), "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, node));

            return(nestedPage.NumberOfEntries);
        }
Exemplo n.º 7
0
        public void MultiDelete(Slice key, Slice value, ushort?version = null)
        {
            State.IsModified = true;
            TreeNodeHeader *node;
            var             page = FindPageFor(key, out node);

            if (page == null || page.LastMatch != 0)
            {
                return; //nothing to delete - key not found
            }

            page = ModifyPage(page);

            var item = page.GetNode(page.LastSearchPosition);

            if (item->Flags == TreeNodeFlags.MultiValuePageRef) //multi-value tree exists
            {
                var tree = OpenMultiValueTree(key, item);

                tree.Delete(value, version);

                // previously, we would convert back to a simple model if we dropped to a single entry
                // however, it doesn't really make sense, once you got enough values to go to an actual nested
                // tree, you are probably going to remain that way, or be removed completely.
                if (tree.State.NumberOfEntries != 0)
                {
                    return;
                }
                _tx.TryRemoveMultiValueTree(this, key);
                _llt.FreePage(tree.State.RootPageNumber);

                Delete(key);
            }
            else // we use a nested page here
            {
                var nestedPage = new TreePage(TreeNodeHeader.DirectAccess(_llt, item), "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, item));
                var nestedItem = nestedPage.Search(_llt, value);
                if (nestedPage.LastMatch != 0) // value not found
                {
                    return;
                }

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

                var nestedPagePtr = TreeNodeHeader.DirectAccess(_llt, item);

                nestedPage = new TreePage(nestedPagePtr, "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, item))
                {
                    LastSearchPosition = nestedPage.LastSearchPosition
                };

                CheckConcurrency(key, value, version, nestedItem->Version, TreeActionType.Delete);
                nestedPage.RemoveNode(nestedPage.LastSearchPosition);
                if (nestedPage.NumberOfEntries == 0)
                {
                    Delete(key);
                }
            }
        }