Exemple #1
0
        private NodeHeader *CreateNode(int index, Slice key, NodeFlags flags, int len, ushort previousNodeVersion)
        {
            Debug.Assert(index <= NumberOfEntries && index >= 0);
            Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref
            if (HasSpaceFor(key, len) == false)
            {
                throw new InvalidOperationException("The page is full and cannot add an entry, this is probably a bug");
            }

            // move higher pointers up one slot
            for (int i = NumberOfEntries; i > index; i--)
            {
                KeysOffsets[i] = KeysOffsets[i - 1];
            }
            var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len);
            var node     = AllocateNewNode(index, key, nodeSize, previousNodeVersion);

            if (key.Options == SliceOptions.Key)
            {
                key.CopyTo((byte *)node + Constants.NodeHeaderSize);
            }

            node->Flags = flags;

            return(node);
        }
Exemple #2
0
        /// <summary>
        /// Internal method that is used when splitting pages
        /// No need to do any work here, we are always adding at the end
        /// </summary>
        internal void CopyNodeDataToEndOfPage(NodeHeader* other, Slice key = null)
        {
            Debug.Assert(SizeOf.NodeEntry(other) + Constants.NodeOffsetSize <= SizeLeft);
            
            var index = NumberOfEntries;

            var nodeSize = SizeOf.NodeEntry(other);

            key = key ?? new Slice(other);

            Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref


            var newNode = AllocateNewNode(index, key, nodeSize);
            newNode->Flags = other->Flags;
            key.CopyTo((byte*)newNode + Constants.NodeHeaderSize);

            if (IsBranch || other->Flags==(NodeFlags.PageRef))
            {
                newNode->PageNumber = other->PageNumber;
                newNode->Flags = NodeFlags.PageRef;
                return;
            }
            newNode->DataSize = other->DataSize;
            NativeMethods.memcpy((byte*)newNode + Constants.NodeHeaderSize + other->KeySize,
                                 (byte*)other + Constants.NodeHeaderSize + other->KeySize,
                                 other->DataSize);
        }
Exemple #3
0
        public byte* AddNode(int index, Slice key, int len, long pageNumber)
        {
            Debug.Assert(index <= NumberOfEntries && index >= 0);
            Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref
            if (HasSpaceFor(key, len) == false)
                throw new InvalidOperationException("The page is full and cannot add an entry, this is probably a bug");

            // move higher pointers up one slot
            for (int i = NumberOfEntries; i > index; i--)
            {
                KeysOffsets[i] = KeysOffsets[i - 1];
            }
            var nodeSize = SizeOf.NodeEntry(_pageMaxSpace, key, len);
            var node = AllocateNewNode(index, key, nodeSize);

            if (key.Options == SliceOptions.Key)
                key.CopyTo((byte*)node + Constants.NodeHeaderSize);

			if (len < 0) // branch or overflow
            {
                Debug.Assert(pageNumber != -1);
                node->PageNumber = pageNumber;
	            node->Flags = NodeFlags.PageRef;
                return null; // write nothing here
            }

            Debug.Assert(key.Options == SliceOptions.Key);
            var dataPos = (byte*)node + Constants.NodeHeaderSize + key.Size;
            node->DataSize = len;
	        node->Flags = NodeFlags.Data;
            return dataPos;
        }
Exemple #4
0
        public void RemoveNode(int index)
        {
            Debug.Assert(IsBranch == false || index > 0 || NumberOfEntries == 2); // cannot remove implicit left in branches, unless as part of removing this node entirely
            Debug.Assert(index < NumberOfEntries);

            var node = GetNode(index);

            var size = SizeOf.NodeEntry(node);

            var nodeOffset = KeysOffsets[index];

            int modifiedEntries = 0;
            for (int i = 0; i < NumberOfEntries; i++)
            {
                if (i == index)
                    continue;
                KeysOffsets[modifiedEntries] = KeysOffsets[i];
                if (KeysOffsets[i] < nodeOffset)
                    KeysOffsets[modifiedEntries] += (ushort)size;
                modifiedEntries++;
            }

            NativeMethods.memmove(_base + Upper + size, _base + Upper, nodeOffset - Upper);

            Lower -= (ushort)Constants.NodeOffsetSize;
            Upper += (ushort)size;

        }
Exemple #5
0
        /// <summary>
        ///     For leaf pages, check the split point based on what
        ///     fits where, since otherwise adding the node can fail.
        ///     This check is only needed when the data items are
        ///     relatively large, such that being off by one will
        ///     make the difference between success or failure.
        ///     It's also relevant if a page happens to be laid out
        ///     such that one half of its nodes are all "small" and
        ///     the other half of its nodes are "large." If the new
        ///     item is also "large" and falls on the half with
        ///     "large" nodes, it also may not fit.
        /// </summary>
        private int AdjustSplitPosition(Slice key, int len, Page page, int currentIndex, int splitIndex,
                                        ref bool newPosition)
        {
            int nodeSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, key, len) + Constants.NodeOffsetSize;

            if (page.NumberOfEntries >= 20 && nodeSize <= AbstractPager.PageMaxSpace / 16)
            {
                return(splitIndex);
            }

            int pageSize = nodeSize;

            if (currentIndex <= splitIndex)
            {
                newPosition = false;
                for (int i = 0; i < splitIndex; i++)
                {
                    NodeHeader *node = page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i <= currentIndex)
                        {
                            if (i < currentIndex)
                            {
                                newPosition = true;
                            }
                            return(currentIndex);
                        }
                        return((ushort)i);
                    }
                }
            }
            else
            {
                for (int i = page.NumberOfEntries - 1; i >= splitIndex; i--)
                {
                    NodeHeader *node = page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i >= currentIndex)
                        {
                            newPosition = false;
                            return(currentIndex);
                        }
                        return((ushort)(i + 1));
                    }
                }
            }
            return(splitIndex);
        }
Exemple #6
0
        private NodeHeader *CreateNode(int index, MemorySlice key, NodeFlags flags, int len, ushort previousNodeVersion)
        {
            Debug.Assert(index <= NumberOfEntries && index >= 0);
            Debug.Assert(IsBranch == false || index != 0 || key.KeyLength == 0);// branch page's first item must be the implicit ref
            if (HasSpaceFor(key, len) == false)
            {
                throw new InvalidOperationException(string.Format("The page is full and cannot add an entry, this is probably a bug. Key: {0}, data length: {1}, size left: {2}", key, len, SizeLeft));
            }

            var prefixedKey = key as PrefixedSlice;

            if (prefixedKey != null && prefixedKey.NewPrefix != null)
            {
                WritePrefix(prefixedKey.NewPrefix, prefixedKey.Header.PrefixId);
            }

            // move higher pointers up one slot
            for (int i = NumberOfEntries; i > index; i--)
            {
                KeysOffsets[i] = KeysOffsets[i - 1];
            }

            var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len);
            var node     = AllocateNewNode(index, nodeSize, previousNodeVersion);

            node->KeySize = key.Size;

            if (key.Options == SliceOptions.Key && key.Size > 0)
            {
                key.CopyTo((byte *)node + Constants.NodeHeaderSize);
            }

            node->Flags = flags;

            return(node);
        }
Exemple #7
0
        public static void DumpHumanReadable(Transaction tx, string path, Page start)
        {
            using (var writer = File.CreateText(path))
            {
                var stack = new Stack <Page>();
                stack.Push(start);
                writer.WriteLine("Root page #{0}", start.PageNumber);
                while (stack.Count > 0)
                {
                    var currentPage = stack.Pop();
                    if (currentPage.IsLeaf)
                    {
                        writer.WriteLine();
                        writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Leaf), Used: {3} : {4}", currentPage.PageNumber, currentPage.NumberOfEntries, currentPage.Flags, currentPage.SizeUsed, currentPage.CalcSizeUsed());
                        if (currentPage.NumberOfEntries <= 0)
                        {
                            writer.WriteLine("Empty page (tree corrupted?)");
                        }


                        for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++)
                        {
                            var node = currentPage.GetNode(nodeIndex);
                            var key  = currentPage.GetNodeKey(node);

                            writer.WriteLine("Node #{0}, Flags = {1}, {4} = {2}, Key = {3}, Entry Size: {5}", nodeIndex, node->Flags, node->DataSize, MaxString(key.ToString(), 25), node->Flags == NodeFlags.Data ? "Size" : "Page",
                                             SizeOf.NodeEntry(node));
                        }
                        writer.WriteLine();
                    }
                    else if (currentPage.IsBranch)
                    {
                        writer.WriteLine();
                        writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Branch), Used: {3} : {4}", currentPage.PageNumber, currentPage.NumberOfEntries, currentPage.Flags, currentPage.SizeUsed, currentPage.SizeUsed);

                        var key = new Slice(SliceOptions.Key);
                        for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++)
                        {
                            var node = currentPage.GetNode(nodeIndex);
                            writer.WriteLine("Node #{2}, {0}  / to page #{1}, Entry Size: {3}", GetBranchNodeString(nodeIndex, key, currentPage, node), node->PageNumber, nodeIndex,
                                             SizeOf.NodeEntry(node));
                        }

                        for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++)
                        {
                            var node = currentPage.GetNode(nodeIndex);
                            if (node->PageNumber < 0 || node->PageNumber > tx.State.NextPageNumber)
                            {
                                writer.Write("Found invalid reference to page #{0}", currentPage.PageNumber);
                                stack.Clear();
                                break;
                            }

                            var child = tx.GetReadOnlyPage(node->PageNumber);
                            stack.Push(child);
                        }

                        writer.WriteLine();
                    }
                }
            }
        }
Exemple #8
0
 public int GetRequiredSpace(MemorySlice key, int len)
 {
     return(SizeOf.NodeEntry(PageMaxSpace, key, len) + Constants.NodeOffsetSize + SizeOf.NewPrefix(key));
 }
Exemple #9
0
        private int AdjustSplitPosition(int currentIndex, int splitIndex, PrefixNode[] prefixes, ref bool toRight)
        {
            MemorySlice keyToInsert;

            int pageSize = 0;

            if (_tree.KeysPrefixing)
            {
                keyToInsert = new PrefixedSlice(_newKey); // let's assume that _newkey won't match any of the existing prefixes

                pageSize += Constants.PrefixInfoSectionSize;
                pageSize += Constants.PrefixNodeHeaderSize + 1; // possible new prefix,  + 1 because of possible 2-byte alignment
            }
            else
            {
                keyToInsert = _newKey;
            }

            pageSize += SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize;

            if (prefixes != null)
            {
                // we are going to copy all existing prefixes so we need to take into account their sizes
                for (var i = 0; i < prefixes.Length; i++)
                {
                    var prefixNodeSize = Constants.PrefixNodeHeaderSize + prefixes[i].Header.PrefixLength;
                    pageSize += prefixNodeSize + (prefixNodeSize & 1); // & 1 because we need 2-byte alignment
                }
            }

            if (toRight == false)
            {
                for (int i = 0; i < splitIndex; i++)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i <= currentIndex)
                        {
                            if (i < currentIndex)
                            {
                                toRight = true;
                            }
                            return(currentIndex);
                        }
                        return(i);
                    }
                }
            }
            else
            {
                for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i >= currentIndex)
                        {
                            toRight = false;
                            return(currentIndex);
                        }
                        return(i + 1);
                    }
                }
            }

            return(splitIndex);
        }
Exemple #10
0
 public int GetRequiredSpace(Slice key, int len)
 {
     return(SizeOf.NodeEntry(PageMaxSpace, key, len) + Constants.NodeOffsetSize);
 }
Exemple #11
0
        /// <summary>
        ///     For leaf pages, check the split point based on what
        ///     fits where, since otherwise adding the node can fail.
        ///     This check is only needed when the data items are
        ///     relatively large, such that being off by one will
        ///     make the difference between success or failure.
        ///     It's also relevant if a page happens to be laid out
        ///     such that one half of its nodes are all "small" and
        ///     the other half of its nodes are "large." If the new
        ///     item is also "large" and falls on the half with
        ///     "large" nodes, it also may not fit.
        /// </summary>
        private int AdjustSplitPosition(int currentIndex, int splitIndex,
                                        ref bool newPosition)
        {
            MemorySlice keyToInsert;

            if (_tree.KeysPrefixing)
            {
                keyToInsert = new PrefixedSlice(_newKey);         // let's assume that _newkey won't be prefixed to ensure the destination page will have enough space
            }
            else
            {
                keyToInsert = _newKey;
            }

            int nodeSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize;

            if (_page.NumberOfEntries >= 20 && nodeSize <= AbstractPager.PageMaxSpace / 16)
            {
                return(splitIndex);
            }

            int pageSize = nodeSize;

            if (_tree.KeysPrefixing)
            {
                pageSize += (Constants.PrefixNodeHeaderSize + 1);                 // let's assume that prefix will be created to ensure the destination page will have enough space, + 1 because prefix node might require 2-byte alignment
            }
            if (currentIndex <= splitIndex)
            {
                newPosition = false;
                for (int i = 0; i < splitIndex; i++)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i <= currentIndex)
                        {
                            if (i < currentIndex)
                            {
                                newPosition = true;
                            }
                            return(currentIndex);
                        }
                        return((ushort)i);
                    }
                }
            }
            else
            {
                for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i >= currentIndex)
                        {
                            newPosition = false;
                            return(currentIndex);
                        }
                        return((ushort)(i + 1));
                    }
                }
            }
            return(splitIndex);
        }
Exemple #12
0
        private int AdjustSplitPosition(int currentIndex, int splitIndex, PrefixNode[] prefixes, ref bool newPosition)
        {
            MemorySlice keyToInsert;

            if (_tree.KeysPrefixing)
            {
                keyToInsert = new PrefixedSlice(_newKey);         // let's assume that _newkey won't be prefixed to ensure the destination page will have enough space
            }
            else
            {
                keyToInsert = _newKey;
            }

            var pageSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize;

            if (prefixes != null)
            {
                // we are going to copy all existing prefixes so we need to take into account their sizes
                for (var i = 0; i < prefixes.Length; i++)
                {
                    pageSize += (Constants.PrefixNodeHeaderSize + prefixes[i].Header.PrefixLength) & 1;                     // & 1 because we need 2-byte alignment
                }
            }

            if (currentIndex <= splitIndex)
            {
                newPosition = false;
                for (int i = 0; i < splitIndex; i++)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i <= currentIndex)
                        {
                            if (i < currentIndex)
                            {
                                newPosition = true;
                            }
                            return(currentIndex);
                        }
                        return((ushort)i);
                    }
                }
            }
            else
            {
                for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--)
                {
                    NodeHeader *node = _page.GetNode(i);
                    pageSize += node->GetNodeSize();
                    pageSize += pageSize & 1;
                    if (pageSize > AbstractPager.PageMaxSpace)
                    {
                        if (i >= currentIndex)
                        {
                            newPosition = false;
                            return(currentIndex);
                        }
                        return((ushort)(i + 1));
                    }
                }
            }
            return(splitIndex);
        }