Пример #1
0
        private byte *SplitPageInHalf(Page rightPage)
        {
            bool toRight;

            var currentIndex = _page.LastSearchPosition;
            var splitIndex   = _page.NumberOfEntries / 2;

            if (currentIndex <= splitIndex)
            {
                toRight = false;
            }
            else
            {
                toRight = true;

                var leftPageEntryCount  = splitIndex;
                var rightPageEntryCount = _page.NumberOfEntries - leftPageEntryCount + 1;

                if (rightPageEntryCount > leftPageEntryCount)
                {
                    splitIndex++;

                    Debug.Assert(splitIndex < _page.NumberOfEntries);
                }
            }

            PrefixNode[] prefixes = null;

            if (_tree.KeysPrefixing && _page.HasPrefixes)
            {
                prefixes = _page.GetPrefixes();
            }

            if (_page.IsLeaf || prefixes != null)
            {
                splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref toRight);
            }

            var         currentKey = _page.GetNodeKey(splitIndex);
            MemorySlice seperatorKey;

            if (toRight && splitIndex == currentIndex)
            {
                seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey;
            }
            else
            {
                seperatorKey = currentKey;
            }

            Page parentOfRight;

            AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey, out parentOfRight);

            var parentOfPage = _cursor.CurrentPage;

            MemorySlice instance = _page.CreateNewEmptyKey();

            if (prefixes != null)
            {
                for (int i = 0; i < prefixes.Length; i++)
                {
                    var prefix = prefixes[i];

                    rightPage.WritePrefix(new Slice(prefix.ValuePtr, prefix.PrefixLength), i);
                }
            }

            bool addedAsImplicitRef = false;

            if (_page.IsBranch && toRight && seperatorKey == _newKey)
            {
                // _newKey needs to be inserted as first key (BeforeAllKeys) to the right page, so we need to add it before we move entries from the current page
                AddNodeToPage(rightPage, 0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys);
                addedAsImplicitRef = true;
            }

            // move the actual entries from page to right page
            ushort nKeys = _page.NumberOfEntries;

            for (int i = splitIndex; i < nKeys; i++)
            {
                NodeHeader *node = _page.GetNode(i);
                if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                {
                    rightPage.CopyNodeDataToEndOfPage(node, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys);
                }
                else
                {
                    _page.SetNodeKey(node, ref instance);
                    var key = rightPage.PrepareKeyToInsert(instance, rightPage.NumberOfEntries);

                    rightPage.CopyNodeDataToEndOfPage(node, key);
                }
            }

            _page.Truncate(_tx, splitIndex);

            byte *pos;

            if (addedAsImplicitRef == false)
            {
                try
                {
                    if (toRight && _cursor.CurrentPage.PageNumber != parentOfRight.PageNumber)
                    {
                        // modify the cursor if we are going to insert to the right page
                        _cursor.Pop();
                        _cursor.Push(parentOfRight);
                    }

                    // actually insert the new key
                    pos = toRight ? InsertNewKey(rightPage) : InsertNewKey(_page);
                }
                catch (InvalidOperationException e)
                {
                    if (e.Message.StartsWith("The page is full and cannot add an entry") == false)
                    {
                        throw;
                    }

                    throw new InvalidOperationException(GatherDetailedDebugInfo(rightPage, currentKey, seperatorKey, currentIndex, splitIndex, toRight), e);
                }
            }
            else
            {
                pos = null;
                _cursor.Push(rightPage);
            }

            if (_page.IsBranch) // remove a branch that has only one entry, the page ref needs to be added to the parent of the current page
            {
                Debug.Assert(_page.NumberOfEntries > 0);
                Debug.Assert(rightPage.NumberOfEntries > 0);

                if (_page.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(_page, parentOfPage);
                }

                if (rightPage.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(rightPage, parentOfRight);
                }
            }

            return(pos);
        }