Example #1
0
        protected unsafe Tuple <Slice, Slice> ReadKey(Transaction txh, Tree tree, Slice key)
        {
            TreeNodeHeader *node;

            tree.FindPageFor(key, out node);

            if (node == null)
            {
                return(null);
            }

            Slice item1;

            TreeNodeHeader.ToSlicePtr(txh.Allocator, node, out item1);

            if (SliceComparer.CompareInline(item1, key) != 0)
            {
                return(null);
            }

            Slice item2;

            Slice.External(txh.Allocator, (byte *)node + node->KeySize + Constants.Tree.NodeHeaderSize, (ushort)node->DataSize, ByteStringType.Immutable, out item2);
            return(Tuple.Create(item1, item2));
        }
Example #2
0
        public void DebugValidate(Tree tree, long root)
        {
            if (NumberOfEntries == 0)
            {
                return;
            }
#if VALIDATE
            if (Freed)
            {
                return;
            }
#endif
            if (IsBranch && NumberOfEntries < 2)
            {
                throw new InvalidOperationException("The branch page " + PageNumber + " has " + NumberOfEntries + " entry");
            }

            Slice prev;
            var   prevScope = GetNodeKey(tree.Llt, 0, out prev);
            try
            {
                var pages = new HashSet <long>();
                for (int i = 1; i < NumberOfEntries; i++)
                {
                    var   node = GetNode(i);
                    Slice current;
                    var   currentScope = GetNodeKey(tree.Llt, i, out current);

                    if (SliceComparer.CompareInline(prev, current) >= 0)
                    {
                        DebugStuff.RenderAndShowTree(tree, root);
                        throw new InvalidOperationException("The page " + PageNumber + " is not sorted");
                    }

                    if (node->Flags == (TreeNodeFlags.PageRef))
                    {
                        if (pages.Add(node->PageNumber) == false)
                        {
                            DebugStuff.RenderAndShowTree(tree, root);
                            throw new InvalidOperationException("The page " + PageNumber + " references same page multiple times");
                        }
                    }
                    prevScope.Dispose();
                    prev      = current;
                    prevScope = currentScope;
                }
            }
            finally
            {
                prevScope.Dispose();
            }
        }
Example #3
0
 public unsafe static bool ValidateCurrentKey <T>(this T self, LowLevelTransaction tx, TreeNodeHeader *node) where T : IIterator
 {
     if (self.RequiredPrefix.HasValue)
     {
         var currentKey = TreeNodeHeader.ToSlicePtr(tx.Allocator, node);
         if (SliceComparer.StartWith(currentKey, self.RequiredPrefix) == false)
         {
             return(false);
         }
     }
     if (self.MaxKey.HasValue)
     {
         var currentKey = TreeNodeHeader.ToSlicePtr(tx.Allocator, node);
         if (SliceComparer.CompareInline(currentKey, self.MaxKey) >= 0)
         {
             return(false);
         }
     }
     return(true);
 }
Example #4
0
        public void DebugValidate(LowLevelTransaction tx, long root)
        {
            if (NumberOfEntries == 0)
            {
                return;
            }

            if (IsBranch && NumberOfEntries < 2)
            {
                throw new InvalidOperationException("The branch page " + PageNumber + " has " + NumberOfEntries + " entry");
            }

            var prev  = GetNodeKey(tx, 0);
            var pages = new HashSet <long>();

            for (int i = 1; i < NumberOfEntries; i++)
            {
                var node    = GetNode(i);
                var current = GetNodeKey(tx, i);

                if (SliceComparer.CompareInline(prev, current) >= 0)
                {
                    DebugStuff.RenderAndShowTree(tx, root);
                    throw new InvalidOperationException("The page " + PageNumber + " is not sorted");
                }

                if (node->Flags == (TreeNodeFlags.PageRef))
                {
                    if (pages.Add(node->PageNumber) == false)
                    {
                        DebugStuff.RenderAndShowTree(tx, root);
                        throw new InvalidOperationException("The page " + PageNumber + " references same page multiple times");
                    }
                }

                prev = current;
            }
        }
Example #5
0
            public int CompareTo(BatchOperation other)
            {
                var r = SliceComparer.CompareInline(Key, other.Key);

                if (r != 0)
                {
                    return(r);
                }

                if (ValueSlice != null)
                {
                    if (other.ValueSlice == null)
                    {
                        return(-1);
                    }

                    return(SliceComparer.CompareInline(ValueSlice, other.ValueSlice));
                }
                else if (other.ValueSlice != null)
                {
                    return(1);
                }
                return(0);
            }
Example #6
0
        private void HandleUncompressedNodes(DecompressedLeafPage decompressedPage, TreePage p, DecompressionUsage usage)
        {
            int numberOfEntries = p.NumberOfEntries;

            for (var i = 0; i < numberOfEntries; i++)
            {
                var uncompressedNode = p.GetNode(i);

                Slice nodeKey;
                using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, uncompressedNode, out nodeKey))
                {
                    if (uncompressedNode->Flags == TreeNodeFlags.CompressionTombstone)
                    {
                        HandleTombstone(decompressedPage, nodeKey, usage);
                        continue;
                    }

                    if (decompressedPage.HasSpaceFor(_llt, TreeSizeOf.NodeEntry(uncompressedNode)) == false)
                    {
                        throw new InvalidOperationException("Could not add uncompressed node to decompressed page");
                    }

                    int index;

                    if (decompressedPage.NumberOfEntries > 0)
                    {
                        Slice lastKey;
                        using (decompressedPage.GetNodeKey(_llt, decompressedPage.NumberOfEntries - 1, out lastKey))
                        {
                            // optimization: it's very likely that uncompressed nodes have greater keys than compressed ones
                            // when we insert sequential keys

                            var cmp = SliceComparer.CompareInline(nodeKey, lastKey);

                            if (cmp > 0)
                            {
                                index = decompressedPage.NumberOfEntries;
                            }
                            else
                            {
                                if (cmp == 0)
                                {
                                    // update of the last entry, just decrement NumberOfEntries in the page and
                                    // put it at the last position

                                    index = decompressedPage.NumberOfEntries - 1;
                                    decompressedPage.Lower -= Constants.Tree.NodeOffsetSize;
                                }
                                else
                                {
                                    index = decompressedPage.NodePositionFor(_llt, nodeKey);

                                    if (decompressedPage.LastMatch == 0) // update
                                    {
                                        decompressedPage.RemoveNode(index);

                                        if (usage == DecompressionUsage.Write)
                                        {
                                            State.NumberOfEntries--;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // all uncompressed nodes were compresion tombstones which deleted all entries from the decompressed page
                        index = 0;
                    }

                    switch (uncompressedNode->Flags)
                    {
                    case TreeNodeFlags.PageRef:
                        decompressedPage.AddPageRefNode(index, nodeKey, uncompressedNode->PageNumber);
                        break;

                    case TreeNodeFlags.Data:
                        var pos       = decompressedPage.AddDataNode(index, nodeKey, uncompressedNode->DataSize);
                        var nodeValue = TreeNodeHeader.Reader(_llt, uncompressedNode);
                        Memory.Copy(pos, nodeValue.Base, nodeValue.Length);
                        break;

                    case TreeNodeFlags.MultiValuePageRef:
                        throw new NotSupportedException("Multi trees do not support compression");

                    default:
                        throw new NotSupportedException("Invalid node type to copye: " + uncompressedNode->Flags);
                    }
                }
            }
        }
Example #7
0
        public TreeNodeHeader *Search(LowLevelTransaction tx, Slice key)
        {
            int numberOfEntries = NumberOfEntries;

            if (numberOfEntries == 0)
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(null);
            }

            switch (key.Options)
            {
            case SliceOptions.Key:
            {
                if (numberOfEntries == 1)
                {
                    var node = GetNode(0);

                    Slice pageKey;
                    using (TreeNodeHeader.ToSlicePtr(tx.Allocator, node, out pageKey))
                    {
                        LastMatch = SliceComparer.CompareInline(key, pageKey);
                    }

                    LastSearchPosition = LastMatch > 0 ? 1 : 0;
                    return(LastSearchPosition == 0 ? node : null);
                }

                int low      = IsLeaf ? 0 : 1;
                int high     = numberOfEntries - 1;
                int position = 0;

                ByteStringContext allocator = tx.Allocator;
                ushort *          offsets   = KeysOffsets;
                while (low <= high)
                {
                    position = (low + high) >> 1;

                    var node = (TreeNodeHeader *)(Base + offsets[position]);

                    Slice pageKey;
                    using (TreeNodeHeader.ToSlicePtr(allocator, node, out pageKey))
                    {
                        LastMatch = SliceComparer.CompareInline(key, pageKey);
                    }


                    if (LastMatch == 0)
                    {
                        break;
                    }

                    if (LastMatch > 0)
                    {
                        low = position + 1;
                    }
                    else
                    {
                        high = position - 1;
                    }
                }

                if (LastMatch > 0)      // found entry less than key
                {
                    position++;         // move to the smallest entry larger than the key
                }

                Debug.Assert(position < ushort.MaxValue);
                LastSearchPosition = position;

                if (position >= numberOfEntries)
                {
                    return(null);
                }

                return(GetNode(position));
            }

            case SliceOptions.BeforeAllKeys:
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(GetNode(0));
            }

            case SliceOptions.AfterAllKeys:
            {
                LastMatch          = -1;
                LastSearchPosition = numberOfEntries - 1;
                return(GetNode(LastSearchPosition));
            }

            default:
                throw new NotSupportedException("This SliceOptions is not supported. Make sure you have updated this code when adding a new one.");
            }
        }
Example #8
0
        private NodeHeader *Search(Slice key)
        {
            int numberOfEntries = NumberOfEntries;

            if (numberOfEntries == 0)
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(null);
            }

            switch (key.Options)
            {
            case SliceOptions.Key:
            {
                var pageKey = new Slice(SliceOptions.Key);

                if (numberOfEntries == 1)
                {
                    var node = GetNode(0);

                    SetNodeKey(node, ref pageKey);
                    LastMatch          = SliceComparer.CompareInline(key, pageKey);
                    LastSearchPosition = LastMatch > 0 ? 1 : 0;
                    return(LastSearchPosition == 0 ? node : null);
                }

                int low      = IsLeaf ? 0 : 1;
                int high     = numberOfEntries - 1;
                int position = 0;

                while (low <= high)
                {
                    position = (low + high) >> 1;

                    var node = (NodeHeader *)(_base + KeysOffsets[position]);

                    SetNodeKey(node, ref pageKey);

                    LastMatch = SliceComparer.CompareInline(key, pageKey);
                    if (LastMatch == 0)
                    {
                        break;
                    }

                    if (LastMatch > 0)
                    {
                        low = position + 1;
                    }
                    else
                    {
                        high = position - 1;
                    }
                }

                if (LastMatch > 0)      // found entry less than key
                {
                    position++;         // move to the smallest entry larger than the key
                }

                Debug.Assert(position < ushort.MaxValue);
                LastSearchPosition = position;

                if (position >= numberOfEntries)
                {
                    return(null);
                }

                return(GetNode(position));
            }

            case SliceOptions.BeforeAllKeys:
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(GetNode(0));
            }

            case SliceOptions.AfterAllKeys:
            {
                LastMatch          = -1;
                LastSearchPosition = numberOfEntries - 1;
                return(GetNode(LastSearchPosition));
            }

            default:
                throw new NotSupportedException("This SliceOptions is not supported. Make sure you have updated this code when adding a new one.");
            }
        }
Example #9
0
        public TreeNodeHeader *Search(LowLevelTransaction tx, Slice key)
        {
            int numberOfEntries = NumberOfEntries;

            if (numberOfEntries == 0)
            {
                goto NoEntries;
            }

            int lastMatch          = -1;
            int lastSearchPosition = 0;

            SliceOptions options = key.Options;

            if (options == SliceOptions.Key)
            {
                if (numberOfEntries == 1)
                {
                    goto SingleEntryKey;
                }

                int low      = IsLeaf ? 0 : 1;
                int high     = numberOfEntries - 1;
                int position = 0;

                ByteStringContext allocator = tx.Allocator;
                ushort *          offsets   = KeysOffsets;
                byte *            @base     = Base;
                while (low <= high)
                {
                    position = (low + high) >> 1;

                    var node = (TreeNodeHeader *)(@base + offsets[position]);

                    Slice pageKey;
                    using (TreeNodeHeader.ToSlicePtr(allocator, node, out pageKey))
                    {
                        lastMatch = SliceComparer.CompareInline(key, pageKey);
                    }

                    if (lastMatch == 0)
                    {
                        break;
                    }

                    if (lastMatch > 0)
                    {
                        low = position + 1;
                    }
                    else
                    {
                        high = position - 1;
                    }
                }

                if (lastMatch > 0) // found entry less than key
                {
                    position++;    // move to the smallest entry larger than the key
                }

                Debug.Assert(position < ushort.MaxValue);
                lastSearchPosition = position;
                goto MultipleEntryKey;
            }
            if (options == SliceOptions.BeforeAllKeys)
            {
                lastMatch = 1;
                goto MultipleEntryKey;
            }
            if (options == SliceOptions.AfterAllKeys)
            {
                lastSearchPosition = numberOfEntries - 1;
                goto MultipleEntryKey;
            }

            ThrowNotSupportedException();

NoEntries:
            {
                LastSearchPosition = 0;
                LastMatch          = 1;
                return(null);
            }

SingleEntryKey:
            {
                var node = GetNode(0);

                Slice pageKey;
                using (TreeNodeHeader.ToSlicePtr(tx.Allocator, node, out pageKey))
                {
                    LastMatch = SliceComparer.CompareInline(key, pageKey);
                }

                LastSearchPosition = LastMatch > 0 ? 1 : 0;
                return(LastSearchPosition == 0 ? node : null);
            }

MultipleEntryKey:
            {
                LastMatch          = lastMatch;
                LastSearchPosition = lastSearchPosition;

                if (lastSearchPosition >= numberOfEntries)
                {
                    return(null);
                }

                return(GetNode(lastSearchPosition));
            }
        }
Example #10
0
        public TreeNodeHeader *Original_WithPrefetch_Search(TreePage page, ByteStringContext allocator, Slice key)
        {
            int numberOfEntries = page.NumberOfEntries;

            if (numberOfEntries == 0)
            {
                goto NoEntries;
            }

            int lastMatch          = -1;
            int lastSearchPosition = 0;

            SliceOptions options = key.Options;

            if (options == SliceOptions.Key)
            {
                if (numberOfEntries == 1)
                {
                    goto SingleEntryKey;
                }

                int low      = page.IsLeaf ? 0 : 1;
                int high     = numberOfEntries - 1;
                int position = 0;

                ushort *offsets = page.KeysOffsets;
                byte *  @base   = page.Base;
                while (low <= high)
                {
                    position = (low + high) >> 1;

                    var node = (TreeNodeHeader *)(@base + offsets[position]);

                    Slice pageKey;
                    using (TreeNodeHeader.ToSlicePtr(allocator, node, out pageKey))
                    {
                        Sse.Prefetch0(pageKey.Content.Ptr);
                        lastMatch = SliceComparer.CompareInline(key, pageKey);
                    }

                    if (lastMatch == 0)
                    {
                        break;
                    }

                    if (lastMatch > 0)
                    {
                        low = position + 1;
                    }
                    else
                    {
                        high = position - 1;
                    }
                }

                if (lastMatch > 0) // found entry less than key
                {
                    position++;    // move to the smallest entry larger than the key
                }

                lastSearchPosition = position;
                goto MultipleEntryKey;
            }
            if (options == SliceOptions.BeforeAllKeys)
            {
                lastMatch = 1;
                goto MultipleEntryKey;
            }
            if (options == SliceOptions.AfterAllKeys)
            {
                lastSearchPosition = numberOfEntries - 1;
                goto MultipleEntryKey;
            }

            return(null);

NoEntries:
            {
                page.LastSearchPosition = 0;
                page.LastMatch          = 1;
                return(null);
            }

SingleEntryKey:
            {
                var node = page.GetNode(0);

                Slice pageKey;
                using (TreeNodeHeader.ToSlicePtr(allocator, node, out pageKey))
                {
                    page.LastMatch = SliceComparer.CompareInline(key, pageKey);
                }

                page.LastSearchPosition = page.LastMatch > 0 ? 1 : 0;
                return(page.LastSearchPosition == 0 ? node : null);
            }

MultipleEntryKey:
            {
                page.LastMatch          = lastMatch;
                page.LastSearchPosition = lastSearchPosition;

                if (lastSearchPosition >= numberOfEntries)
                {
                    return(null);
                }

                return(page.GetNode(lastSearchPosition));
            }
        }
Example #11
0
        private ActualKeyScope GetActualKey(TreePage page, int pos, out TreeNodeHeader *node, out Slice key)
        {
            DecompressedLeafPage decompressedLeafPage = null;

            node = page.GetNode(pos);
            var scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);

            while (key.Size == 0)
            {
                Debug.Assert(page.IsBranch);
                page = _tree.GetReadOnlyTreePage(node->PageNumber);
                if (page.IsCompressed == false)
                {
                    node = page.GetNode(0);
                }
                else
                {
                    decompressedLeafPage?.Dispose();
                    decompressedLeafPage = _tree.DecompressPage(page, DecompressionUsage.Read, skipCache: true);

                    if (decompressedLeafPage.NumberOfEntries > 0)
                    {
                        if (page.NumberOfEntries == 0)
                        {
                            node = decompressedLeafPage.GetNode(0);
                        }
                        else
                        {
                            // we want to find the smallest key in compressed page
                            // it can be inside compressed part or not compressed one
                            // in particular, it can be the key of compression tombstone node that we don't see after decompression
                            // so we need to take first keys from decompressed and compressed page and compare them

                            var decompressedNode = decompressedLeafPage.GetNode(0);
                            var compressedNode   = page.GetNode(0);

                            using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, decompressedNode, out var firstDecompressedKey))
                                using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, compressedNode, out var firstCompressedKey))
                                {
                                    node = SliceComparer.CompareInline(firstDecompressedKey, firstCompressedKey) > 0 ? compressedNode : decompressedNode;
                                }
                        }
                    }
                    else
                    {
                        // we have empty page after decompression (each compressed entry has a corresponding CompressionTombstone)
                        // we can safely use the node key of first tombstone (they have proper order)

                        node = page.GetNode(0);
                    }
                }

                scope.Dispose();
                scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key);
            }

            return(new ActualKeyScope
            {
                DecompressedLeafPage = decompressedLeafPage,
                ExternalScope = scope
            });
        }