private byte *AddSeparatorToParentPage(long pageNumber, MemorySlice seperatorKey, bool toRight, out Page parent) { var pos = _parentPage.NodePositionFor(seperatorKey); // select the appropriate place for this var separatorKeyToInsert = _parentPage.PrepareKeyToInsert(seperatorKey, pos); if (_parentPage.HasSpaceFor(_tx, SizeOf.BranchEntry(separatorKeyToInsert) + Constants.NodeOffsetSize + SizeOf.NewPrefix(separatorKeyToInsert)) == false) { var pageSplitter = new PageSplitter(_tx, _tree, seperatorKey, -1, pageNumber, NodeFlags.PageRef, 0, _cursor, _treeState); var posToInsert = pageSplitter.Execute(); if (toRight == false && _cursor.CurrentPage.PageNumber != _parentPage.PageNumber) { // _newKey being added to _page wasn't meant to be inserted to a newly created right page // however the above page split has modified the cursor that its first page is a parent page for the right page containing separator key // we need to ensure that the current _parentPage is first at the cursor parent = _cursor.Pop(); _cursor.Push(_parentPage); } else { parent = _parentPage; } return(posToInsert); } parent = _parentPage; return(_parentPage.AddPageRefNode(pos, separatorKeyToInsert, pageNumber)); }
private byte *SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries / 2; if (currentIndex < splitIndex) { newPosition = false; } if (_page.IsLeaf) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); MemorySlice instance = _page.CreateNewEmptyKey(); // 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); // actually insert the new key return((currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page)); }
private void ExpandMultiTreeNestedPageSize(Transaction tx, Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (tx.Environment.GetTemporaryPage(tx, out tmp)) { var tempPagePointer = tmp.TempPagePointer; MemoryUtils.Copy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page Page nestedPage = new Page(tempPagePointer, "multi tree", (ushort)currentSize); var ptr = DirectAdd(key, newSize); var newNestedPage = new Page(ptr, "multi tree", newSize) { Lower = (ushort)Constants.PageHeaderSize, Upper = KeysPrefixing ? (ushort)(newSize - Constants.PrefixInfoSectionSize) : newSize, Flags = KeysPrefixing ? PageFlags.Leaf | PageFlags.KeysPrefixed : PageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; newNestedPage.ClearPrefixInfo(); MemorySlice nodeKey = nestedPage.CreateNewEmptyKey(); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); nestedPage.SetNodeKey(nodeHeader, ref nodeKey); nodeKey = newNestedPage.PrepareKeyToInsert(nodeKey, i); 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 } newNestedPage.Search(value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, newNestedPage.PrepareKeyToInsert(value, newNestedPage.LastSearchPosition), 0, 0); } }
private void AddSeparatorToParentPage(Page parentPage, long pageNumber, MemorySlice seperatorKey, int separatorKeyPosition) { var separatorKeyToInsert = parentPage.PrepareKeyToInsert(seperatorKey, separatorKeyPosition); if (parentPage.HasSpaceFor(_tx, SizeOf.BranchEntry(separatorKeyToInsert) + Constants.NodeOffsetSize + SizeOf.NewPrefix(separatorKeyToInsert)) == false) { var pageSplitter = new PageSplitter(_tx, _tree, seperatorKey, -1, pageNumber, NodeFlags.PageRef, 0, _cursor, _tree.State); pageSplitter.Execute(); } else { parentPage.AddPageRefNode(separatorKeyPosition, separatorKeyToInsert, pageNumber); } }
private byte *AddSeparatorToParentPage(long pageNumber, MemorySlice seperatorKey) { var pos = _parentPage.NodePositionFor(seperatorKey); // select the appropriate place for this var separatorKeyToInsert = _parentPage.PrepareKeyToInsert(seperatorKey, pos); if (_parentPage.HasSpaceFor(_tx, SizeOf.BranchEntry(separatorKeyToInsert) + Constants.NodeOffsetSize + SizeOf.NewPrefix(separatorKeyToInsert)) == false) { var pageSplitter = new PageSplitter(_tx, _tree, seperatorKey, -1, pageNumber, NodeFlags.PageRef, 0, _cursor, _treeState); return(pageSplitter.Execute()); } return(_parentPage.AddPageRefNode(pos, separatorKeyToInsert, pageNumber)); }
private byte *AddNodeToPage(Page page, int index, MemorySlice alreadyPreparedNewKey = null) { var newKeyToInsert = alreadyPreparedNewKey ?? page.PrepareKeyToInsert(_newKey, index); switch (_nodeType) { case NodeFlags.PageRef: return(page.AddPageRefNode(index, newKeyToInsert, _pageNumber)); case NodeFlags.Data: return(page.AddDataNode(index, newKeyToInsert, _len, _nodeVersion)); case NodeFlags.MultiValuePageRef: return(page.AddMultiValueNode(index, newKeyToInsert, _len, _nodeVersion)); default: throw new NotSupportedException("Unknown node type"); } }
private byte *InsertNewKey(Page p) { int pos = p.NodePositionFor(_newKey); var newKeyToInsert = p.PrepareKeyToInsert(_newKey, pos); if (p.HasSpaceFor(_tx, p.GetRequiredSpace(newKeyToInsert, _len)) == false) { _cursor.Push(p); var pageSplitter = new PageSplitter(_tx, _tree, _newKey, _len, _pageNumber, _nodeType, _nodeVersion, _cursor, _treeState); return(pageSplitter.Execute()); } byte *dataPos = AddNodeToPage(p, pos, newKeyToInsert); _cursor.Push(p); return(dataPos); }
public void MultiAdd(Slice key, Slice value, ushort?version = null) { if (value == null) { throw new ArgumentNullException("value"); } int maxNodeSize = AbstractPager.NodeMaxSize; if (value.Size > maxNodeSize) { throw new ArgumentException( "Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value"); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } State.IsModified = true; Lazy <Cursor> lazy; NodeHeader * node; var page = FindPageFor(key, out node, out lazy); if ((page == null || page.LastMatch != 0)) { MultiAddOnNewValue(_tx, key, value, version, maxNodeSize); return; } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); // already was turned into a multi tree, not much to do here if (item->Flags == NodeFlags.MultiValuePageRef) { var existingTree = OpenMultiValueTree(_tx, key, item); existingTree.DirectAdd(value, 0, version: version); return; } byte *nestedPagePtr; if (item->Flags == NodeFlags.PageRef) { var overFlowPage = _tx.ModifyPage(item->PageNumber, null); nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize; } else { nestedPagePtr = NodeHeader.DirectAccess(_tx, item); } var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var existingItem = nestedPage.Search(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 = page.GetNodeKey(item); if (tmpKey.Compare(value) == 0) { return; // already there, turning into a no-op } nestedPage.RemoveNode(nestedPage.LastSearchPosition); } var valueToInsert = nestedPage.PrepareKeyToInsert(value, nestedPage.LastSearchPosition); if (nestedPage.HasSpaceFor(_tx, valueToInsert, 0)) { // we are now working on top of the modified root page, we can just modify the memory directly nestedPage.AddDataNode(nestedPage.LastSearchPosition, valueToInsert, 0, previousNodeRevision); return; } int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize; var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(valueToInsert, 0); if (newRequiredSize <= maxNodeSize) { // we can just expand the current value... no need to create a nested tree yet var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize); var currentDataSize = NodeHeader.GetDataSize(_tx, item); ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, currentDataSize); return; } // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_tx, KeysPrefixing, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var existingValue = nestedPage.GetNodeKey(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), NodeFlags.MultiValuePageRef); }
public byte *AddSeparator(MemorySlice separator, long pageRefNumber, int?nodePos = null) { var originalLastSearchPositionOfParent = _parentPage.LastSearchPosition; if (nodePos == null) { nodePos = _parentPage.NodePositionFor(separator); // select the appropriate place for this } var separatorKeyToInsert = _parentPage.PrepareKeyToInsert(separator, nodePos.Value); if (_parentPage.HasSpaceFor(_tx, SizeOf.BranchEntry(separatorKeyToInsert) + Constants.NodeOffsetSize + SizeOf.NewPrefix(separatorKeyToInsert)) == false) { var pageSplitter = new PageSplitter(_tx, _tree, separator, -1, pageRefNumber, NodeFlags.PageRef, 0, _cursor, _tree.State); var posToInsert = pageSplitter.Execute(); ParentOfAddedPageRef = _cursor.CurrentPage; var adjustParentPageOnCursor = true; for (int i = 0; i < _cursor.CurrentPage.NumberOfEntries; i++) { if (_cursor.CurrentPage.GetNode(i)->PageNumber == _currentPage.PageNumber) { adjustParentPageOnCursor = false; _cursor.CurrentPage.LastSearchPosition = i; break; } } if (adjustParentPageOnCursor) { // the above page split has modified the cursor that its first page points to the parent of the leaf where 'separatorKey' was inserted // and it doesn't have the reference to _page, we need to ensure that the actual parent is first at the cursor _cursor.Pop(); _cursor.Push(_parentPage); EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent); } #if VALIDATE Debug.Assert(_cursor.CurrentPage.GetNode(_cursor.CurrentPage.LastSearchPosition)->PageNumber == _currentPage.PageNumber, "The parent page is not referencing a page which is being split"); var parentToValidate = ParentOfAddedPageRef; Debug.Assert(Enumerable.Range(0, parentToValidate.NumberOfEntries).Any(i => parentToValidate.GetNode(i)->PageNumber == pageRefNumber), "The parent page of a page reference isn't referencing it"); #endif return(posToInsert); } ParentOfAddedPageRef = _parentPage; var pos = _parentPage.AddPageRefNode(nodePos.Value, separatorKeyToInsert, pageRefNumber); EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent); return(pos); }
private void MoveBranchNode(Page parentPage, Page from, Page to) { Debug.Assert(from.IsBranch); var originalFromKey = to.PrepareKeyToInsert(GetActualKey(from, from.LastSearchPositionOrLastEntry), to.LastSearchPosition); to.EnsureHasSpaceFor(_tx, originalFromKey, -1); var fromNode = from.GetNode(from.LastSearchPosition); long pageNum = fromNode->PageNumber; if (to.LastSearchPosition == 0) { // cannot add to left implicit side, adjust by moving the left node // to the right by one, then adding the new one as the left NodeHeader* actualKeyNode; var implicitLeftKey = GetActualKey(to, 0, out actualKeyNode); var implicitLeftNode = to.GetNode(0); var leftPageNumber = implicitLeftNode->PageNumber; MemorySlice implicitLeftKeyToInsert; if (implicitLeftNode == actualKeyNode) { // no need to create a prefix, just use the existing prefixed key from the node // this also prevents from creating a prefix which is the full key given in 'implicitLeftKey' if (_tree.KeysPrefixing) implicitLeftKeyToInsert = new PrefixedSlice(actualKeyNode); else implicitLeftKeyToInsert = new Slice(actualKeyNode); } else implicitLeftKeyToInsert = to.PrepareKeyToInsert(implicitLeftKey, 1); to.EnsureHasSpaceFor(_tx, implicitLeftKeyToInsert, -1); to.AddPageRefNode(1, implicitLeftKeyToInsert, leftPageNumber); to.ChangeImplicitRefPageNode(pageNum); // setup the new implicit node } else { to.AddPageRefNode(to.LastSearchPosition, originalFromKey, pageNum); } if (from.LastSearchPositionOrLastEntry == 0) { var rightPageNumber = from.GetNode(1)->PageNumber; from.RemoveNode(0); // remove the original implicit node from.ChangeImplicitRefPageNode(rightPageNumber); // setup the new implicit node Debug.Assert(from.NumberOfEntries >= 2); } else { 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(parentPage, pageNumber, newSeparatorKey, pos); }
private void MoveLeafNode(Page parentPage, Page from, Page 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; var prefixedOriginalFromKey = to.PrepareKeyToInsert(originalFromKeyStart, to.LastSearchPosition); byte* dataPos; var fromDataSize = fromNode->DataSize; switch (fromNode->Flags) { case NodeFlags.PageRef: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, -1); dataPos = to.AddPageRefNode(to.LastSearchPosition, prefixedOriginalFromKey, fromNode->PageNumber); break; case NodeFlags.Data: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, fromDataSize); dataPos = to.AddDataNode(to.LastSearchPosition, prefixedOriginalFromKey, fromDataSize, nodeVersion); break; case NodeFlags.MultiValuePageRef: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, fromDataSize); dataPos = to.AddMultiValueNode(to.LastSearchPosition, prefixedOriginalFromKey, 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(parentPage, pageNumber, newSeparatorKey, pos); }
private byte* AddNodeToPage(Page page, int index, MemorySlice alreadyPreparedNewKey = null) { var newKeyToInsert = alreadyPreparedNewKey ?? page.PrepareKeyToInsert(_newKey, index); switch (_nodeType) { case NodeFlags.PageRef: return page.AddPageRefNode(index, newKeyToInsert, _pageNumber); case NodeFlags.Data: return page.AddDataNode(index, newKeyToInsert, _len, _nodeVersion); case NodeFlags.MultiValuePageRef: return page.AddMultiValueNode(index, newKeyToInsert, _len, _nodeVersion); default: throw new NotSupportedException("Unknown node type"); } }
private byte* SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries/2; if (currentIndex < splitIndex) newPosition = false; if (_page.IsLeaf) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); MemorySlice instance = _page.CreateNewEmptyKey(); // 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); // actually insert the new key return (currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page); }
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; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); 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 { // 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, _cursor.ParentPage); if (rightPage.NumberOfEntries == 1) RemoveBranchWithOneEntry(rightPage, _cursor.ParentPage); } return pos; }
private void ExpandMultiTreeNestedPageSize(Transaction tx, Slice key, Slice value, byte* nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (tx.Environment.GetTemporaryPage(tx, out tmp)) { var tempPagePointer = tmp.TempPagePointer; NativeMethods.memcpy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page Page nestedPage = new Page(tempPagePointer, "multi tree", (ushort)currentSize); var ptr = DirectAdd(key, newSize); var newNestedPage = new Page(ptr, "multi tree", newSize) { Lower = (ushort)Constants.PageHeaderSize, Upper = KeysPrefixing ? (ushort) (newSize - Constants.PrefixInfoSectionSize) : newSize, Flags = KeysPrefixing ? PageFlags.Leaf | PageFlags.KeysPrefixed : PageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; newNestedPage.ClearPrefixInfo(); MemorySlice nodeKey = nestedPage.CreateNewEmptyKey(); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); nestedPage.SetNodeKey(nodeHeader, ref nodeKey); nodeKey = newNestedPage.PrepareKeyToInsert(nodeKey, i); 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 } newNestedPage.Search(value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, newNestedPage.PrepareKeyToInsert(value, newNestedPage.LastSearchPosition), 0, 0); } }
public void MultiAdd(Slice key, Slice value, ushort? version = null) { if (value == null) throw new ArgumentNullException("value"); int maxNodeSize = _tx.DataPager.MaxNodeSize; if (value.Size > maxNodeSize) throw new ArgumentException( "Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value"); if (value.Size == 0) throw new ArgumentException("Cannot add empty value to child tree"); State.IsModified = true; Lazy<Cursor> lazy; NodeHeader* node; var page = FindPageFor(key, out node, out lazy); if ((page == null || page.LastMatch != 0)) { MultiAddOnNewValue(_tx, key, value, version, maxNodeSize); return; } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); // already was turned into a multi tree, not much to do here if (item->Flags == NodeFlags.MultiValuePageRef) { var existingTree = OpenMultiValueTree(_tx, key, item); existingTree.DirectAdd(value, 0, version: version); return; } byte* nestedPagePtr; if (item->Flags == NodeFlags.PageRef) { var overFlowPage = _tx.ModifyPage(item->PageNumber, null); nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize; } else { nestedPagePtr = NodeHeader.DirectAccess(_tx, item); } var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var existingItem = nestedPage.Search(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 = page.GetNodeKey(item); if (tmpKey.Compare(value) == 0) return; // already there, turning into a no-op nestedPage.RemoveNode(nestedPage.LastSearchPosition); } var valueToInsert = nestedPage.PrepareKeyToInsert(value, nestedPage.LastSearchPosition); if (nestedPage.HasSpaceFor(_tx, valueToInsert, 0)) { // we are now working on top of the modified root page, we can just modify the memory directly nestedPage.AddDataNode(nestedPage.LastSearchPosition, valueToInsert, 0, previousNodeRevision); return; } int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize; var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(valueToInsert, 0); if (newRequiredSize <= maxNodeSize) { // we can just expand the current value... no need to create a nested tree yet var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize); ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, item->DataSize); return; } // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_tx, KeysPrefixing, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var existingValue = nestedPage.GetNodeKey(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), NodeFlags.MultiValuePageRef); }
private byte* SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries/2; if (currentIndex < splitIndex) newPosition = false; PrefixNode[] prefixes = null; if (_tree.KeysPrefixing && _page.HasPrefixes) { prefixes = _page.GetPrefixes(); } if (_page.IsLeaf || prefixes != null) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); 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); } } // 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); // actually insert the new key try { return (currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page); } catch (InvalidOperationException e) { if (e.Message.StartsWith("The page is full and cannot add an entry")) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("currentKey: {0}, seperatorKey: {1}\r\n", currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("newPosition: {0}\r\n", newPosition); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } throw new InvalidOperationException(debugInfo.ToString(), e); } throw; } }
private byte* InsertNewKey(Page p) { int pos = p.NodePositionFor(_newKey); var newKeyToInsert = p.PrepareKeyToInsert(_newKey, pos); if (p.HasSpaceFor(_tx, p.GetRequiredSpace(newKeyToInsert, _len)) == false) { _cursor.Push(p); var pageSplitter = new PageSplitter(_tx, _tree, _newKey, _len, _pageNumber, _nodeType, _nodeVersion, _cursor, _treeState); return pageSplitter.Execute(); } byte* dataPos = AddNodeToPage(p, pos, newKeyToInsert); _cursor.Push(p); return dataPos; }
private void MoveLeafNode(Page parentPage, Page from, Page 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; } var prefixedOriginalFromKey = to.PrepareKeyToInsert(originalFromKeyStart, to.LastSearchPosition); byte *dataPos; var fromDataSize = fromNode->DataSize; switch (fromNode->Flags) { case NodeFlags.PageRef: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, -1); dataPos = to.AddPageRefNode(to.LastSearchPosition, prefixedOriginalFromKey, fromNode->PageNumber); break; case NodeFlags.Data: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, fromDataSize); dataPos = to.AddDataNode(to.LastSearchPosition, prefixedOriginalFromKey, fromDataSize, nodeVersion); break; case NodeFlags.MultiValuePageRef: to.EnsureHasSpaceFor(_tx, prefixedOriginalFromKey, fromDataSize); dataPos = to.AddMultiValueNode(to.LastSearchPosition, prefixedOriginalFromKey, 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(parentPage, pageNumber, newSeparatorKey, pos); }
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); }
private void MoveBranchNode(Page parentPage, Page from, Page to) { Debug.Assert(from.IsBranch); var originalFromKey = to.PrepareKeyToInsert(GetActualKey(from, from.LastSearchPositionOrLastEntry), to.LastSearchPosition); to.EnsureHasSpaceFor(_tx, originalFromKey, -1); var fromNode = from.GetNode(from.LastSearchPosition); long pageNum = fromNode->PageNumber; if (to.LastSearchPosition == 0) { // cannot add to left implicit side, adjust by moving the left node // to the right by one, then adding the new one as the left NodeHeader *actualKeyNode; var implicitLeftKey = GetActualKey(to, 0, out actualKeyNode); var implicitLeftNode = to.GetNode(0); var leftPageNumber = implicitLeftNode->PageNumber; MemorySlice implicitLeftKeyToInsert; if (implicitLeftNode == actualKeyNode) { // no need to create a prefix, just use the existing prefixed key from the node // this also prevents from creating a prefix which is the full key given in 'implicitLeftKey' if (_tree.KeysPrefixing) { implicitLeftKeyToInsert = new PrefixedSlice(actualKeyNode); } else { implicitLeftKeyToInsert = new Slice(actualKeyNode); } } else { implicitLeftKeyToInsert = to.PrepareKeyToInsert(implicitLeftKey, 1); } to.EnsureHasSpaceFor(_tx, implicitLeftKeyToInsert, -1); to.AddPageRefNode(1, implicitLeftKeyToInsert, leftPageNumber); to.ChangeImplicitRefPageNode(pageNum); // setup the new implicit node } else { to.AddPageRefNode(to.LastSearchPosition, originalFromKey, pageNum); } if (from.LastSearchPositionOrLastEntry == 0) { var rightPageNumber = from.GetNode(1)->PageNumber; from.RemoveNode(0); // remove the original implicit node from.ChangeImplicitRefPageNode(rightPageNumber); // setup the new implicit node Debug.Assert(from.NumberOfEntries >= 2); } else { 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(parentPage, pageNumber, newSeparatorKey, pos); }
private byte *SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries / 2; if (currentIndex < splitIndex) { newPosition = false; } PrefixNode[] prefixes = null; if (_tree.KeysPrefixing && _page.HasPrefixes) { prefixes = _page.GetPrefixes(); } if (_page.IsLeaf || prefixes != null) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); 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); } } // 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); // actually insert the new key try { return((currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page)); } catch (InvalidOperationException e) { if (e.Message.StartsWith("The page is full and cannot add an entry")) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("currentKey: {0}, seperatorKey: {1}\r\n", currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("newPosition: {0}\r\n", newPosition); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } throw new InvalidOperationException(debugInfo.ToString(), e); } throw; } }