public bool MovePrev() { if (_disposed) { throw new ObjectDisposedException("TreeIterator " + _tree.Name); } while (true) { _currentPage.LastSearchPosition--; if (_currentPage.LastSearchPosition >= 0) { // run out of entries, need to select the next page... while (_currentPage.IsBranch) { // In here we will also have the 'current' page (even if we are traversing a compressed node). if (_prefetch) { MaybePrefetchPagesReferencedBy(_currentPage); } _cursor.Push(_currentPage); var node = _currentPage.GetNode(_currentPage.LastSearchPosition); _currentPage = _tree.GetReadOnlyTreePage(node->PageNumber); if (_currentPage.IsCompressed) { DecompressedCurrentPage(); } _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1; } // We should be prefetching data pages down here. if (_prefetch) { MaybePrefetchPagesReferencedBy(_currentPage); } var current = _currentPage.GetNode(_currentPage.LastSearchPosition); if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false) { return(false); } _prevKeyScope.Dispose(); _prevKeyScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, out _currentInternalKey); _currentKey = _currentInternalKey; return(true);// there is another entry in this page } if (_cursor.PageCount == 0) { break; } _currentPage = _cursor.Pop(); } _currentPage = null; return(false); }
private TreePage SetupMoveOrMerge(TreePage page, TreePage parentPage) { TreePage sibling; if (parentPage.LastSearchPosition == 0) // we are the left most item { sibling = _tree.ModifyPage(parentPage.GetNode(1)->PageNumber); sibling.LastSearchPosition = 0; page.LastSearchPosition = page.NumberOfEntries; parentPage.LastSearchPosition = 1; } else // there is at least 1 page to our left { var beyondLast = parentPage.LastSearchPosition == parentPage.NumberOfEntries; if (beyondLast) { parentPage.LastSearchPosition--; } parentPage.LastSearchPosition--; sibling = _tree.ModifyPage(parentPage.GetNode(parentPage.LastSearchPosition)->PageNumber); parentPage.LastSearchPosition++; if (beyondLast) { parentPage.LastSearchPosition++; } sibling.LastSearchPosition = sibling.NumberOfEntries - 1; page.LastSearchPosition = 0; } return(sibling); }
private byte *OptimizedOnlyMoveNewValueToTheRightPage(TreePage rightPage) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density TreePage branchOfSeparator; byte *pos; if (_page.IsBranch) { if (_page.NumberOfEntries > 2) { // here we steal the last entry from the current page so we maintain the implicit null left entry TreeNodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == TreeNodeFlags.PageRef); rightPage.AddPageRefNode(0, Slices.BeforeAllKeys, node->PageNumber); pos = AddNodeToPage(rightPage, 1); Slice separatorKey; using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out separatorKey)) { AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator); } _page.RemoveNode(_page.NumberOfEntries - 1); } else { _tree.FreePage(rightPage); // return the unnecessary right page pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator); if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber) { _cursor.Push(branchOfSeparator); } return(pos); } } else { AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator); pos = AddNodeToPage(rightPage, 0); } _cursor.Push(rightPage); return(pos); }
private Slice GetActualKey(TreePage page, int pos, out TreeNodeHeader *node) { node = page.GetNode(pos); var key = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node); while (key.Size == 0) { Debug.Assert(page.IsBranch); page = _tx.GetReadOnlyTreePage(node->PageNumber); node = page.GetNode(0); key = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node); } return(key); }
private void RebalanceRoot(TreePage page) { if (page.NumberOfEntries == 0) { return; // nothing to do } if (!page.IsBranch || page.NumberOfEntries > 1) { return; // cannot do anything here } // in this case, we have a root pointer with just one pointer, we can just swap it out var node = page.GetNode(0); Debug.Assert(node->Flags == (TreeNodeFlags.PageRef)); var rootPage = _tree.ModifyPage(node->PageNumber); _tree.State.RootPageNumber = rootPage.PageNumber; _tree.State.Depth--; Debug.Assert(rootPage.Dirty); _cursor.Pop(); _cursor.Push(rootPage); _tree.FreePage(page); }
private string GatherDetailedDebugInfo(TreePage rightPage, Slice currentKey, Slice seperatorKey, int currentIndex, int splitIndex, bool toRight) { 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("key at LastSearchPosition: {0}, current key: {1}, seperatorKey: {2}\r\n", _page.GetNodeKey(_tx, _page.LastSearchPosition), currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("toRight: {0}\r\n", toRight); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.TreeFlags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == TreeNodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.TreeFlags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == TreeNodeFlags.Data ? "Size" : "Page"); } return(debugInfo.ToString()); }
private ByteStringContext.ExternalScope GetActualKey(TreePage page, int pos, out TreeNodeHeader *node, out Slice key) { 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); node = page.GetNode(0); scope.Dispose(); scope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out key); } return(scope); }
private void RemoveBranchWithOneEntry(TreePage page, TreePage parentPage) { Debug.Assert(page.NumberOfEntries == 1); var pageRefNumber = page.GetNode(0)->PageNumber; TreeNodeHeader *nodeHeader = null; for (int i = 0; i < parentPage.NumberOfEntries; i++) { nodeHeader = parentPage.GetNode(i); if (nodeHeader->PageNumber == page.PageNumber) { break; } } Debug.Assert(nodeHeader->PageNumber == page.PageNumber); nodeHeader->PageNumber = pageRefNumber; if (_cursor.CurrentPage.PageNumber == page.PageNumber) { _cursor.Pop(); _cursor.Push(_tree.GetReadOnlyTreePage(pageRefNumber)); } _tree.FreePage(page); }
private bool TryMergePages(TreePage parentPage, TreePage left, TreePage right) { TemporaryPage tmp; using (_tx.Environment.GetTemporaryPage(_tx, out tmp)) { var mergedPage = tmp.GetTempPage(); Memory.Copy(mergedPage.Base, left.Base, left.PageSize); var previousSearchPosition = right.LastSearchPosition; for (int i = 0; i < right.NumberOfEntries; i++) { right.LastSearchPosition = i; var key = GetActualKey(right, right.LastSearchPositionOrLastEntry); var node = right.GetNode(i); if (mergedPage.HasSpaceFor(_tx, TreeSizeOf.NodeEntryWithAnotherKey(node, key) + Constants.NodeOffsetSize) == false) { right.LastSearchPosition = previousSearchPosition; //previous position --> prevent mutation of parameter return(false); } mergedPage.CopyNodeDataToEndOfPage(node, key); } Memory.Copy(left.Base, mergedPage.Base, left.PageSize); } parentPage.RemoveNode(parentPage.LastSearchPositionOrLastEntry); // unlink the right sibling _tree.FreePage(right); return(true); }
public bool MovePrev() { if (_disposed) { throw new ObjectDisposedException("TreeIterator " + _tree.Name); } while (true) { _currentPage.LastSearchPosition--; if (_currentPage.LastSearchPosition >= 0) { // run out of entries, need to select the next page... while (_currentPage.IsBranch) { _cursor.Push(_currentPage); var node = _currentPage.GetNode(_currentPage.LastSearchPosition); _currentPage = _tree.GetReadOnlyTreePage(node->PageNumber); _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1; if (_prefetch && _currentPage.IsLeaf) { MaybePrefetchOverflowPages(_currentPage); } } var current = _currentPage.GetNode(_currentPage.LastSearchPosition); if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false) { return(false); } _prevKeyScope.Dispose(); _prevKeyScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, out _currentInternalKey); _currentKey = _currentInternalKey; return(true);// there is another entry in this page } if (_cursor.PageCount == 0) { break; } _currentPage = _cursor.Pop(); } _currentPage = null; return(false); }
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, skipCache: true); if (decompressedLeafPage.NumberOfEntries > 0) { node = decompressedLeafPage.GetNode(0); } 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 }); }
private static void EnsureValidLastSearchPosition(TreePage page, long referencedPageNumber, int originalLastSearchPosition) { if (page.NumberOfEntries <= originalLastSearchPosition || page.GetNode(originalLastSearchPosition)->PageNumber != referencedPageNumber) { page.LastSearchPosition = page.NodePositionReferencing(referencedPageNumber); } else { page.LastSearchPosition = originalLastSearchPosition; } }
private void RemoveLeafNode(TreePage page) { var node = page.GetNode(page.LastSearchPosition); if (node->Flags == (TreeNodeFlags.PageRef)) // this is an overflow pointer { var overflowPage = GetReadOnlyTreePage(node->PageNumber); FreePage(overflowPage); } page.RemoveNode(page.LastSearchPosition); }
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 = DirectAccessFromHeader(movedItem); nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(movedItem)); }
public bool MoveNext() { if (_disposed) { throw new ObjectDisposedException("TreeIterator " + _tree.Name); } while (_currentPage != null) { _currentPage.LastSearchPosition++; if (_currentPage.LastSearchPosition < _currentPage.NumberOfEntries) { // run out of entries, need to select the next page... while (_currentPage.IsBranch) { _cursor.Push(_currentPage); var node = _currentPage.GetNode(_currentPage.LastSearchPosition); _currentPage = _tx.GetReadOnlyTreePage(node->PageNumber); _currentPage.LastSearchPosition = 0; } var current = _currentPage.GetNode(_currentPage.LastSearchPosition); if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false) { return(false); } _currentInternalKey = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current, ByteStringType.Mutable); _currentKey = _currentInternalKey; return(true);// there is another entry in this page } if (_cursor.PageCount == 0) { break; } _currentPage = _cursor.Pop(); } _currentPage = null; return(false); }
private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (_llt.Environment.GetTemporaryPage(_llt, out tmp)) { var tempPagePointer = tmp.TempPagePointer; Memory.Copy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page TreePage nestedPage = new TreePage(tempPagePointer, (ushort)currentSize); byte *ptr; using (DirectAdd(key, newSize, out ptr)) { var newNestedPage = new TreePage(ptr, newSize) { Lower = (ushort)Constants.Tree.PageHeaderSize, Upper = newSize, TreeFlags = TreePageFlags.Leaf, PageNumber = -1L, // mark as invalid page number Flags = 0 }; ByteStringContext allocator = _llt.Allocator; for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); Slice nodeKey; using (TreeNodeHeader.ToSlicePtr(allocator, nodeHeader, out nodeKey)) newNestedPage.AddDataNode(i, nodeKey, 0); } newNestedPage.Search(_llt, value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0); } } }
private void RemoveBranchWithOneEntry(TreePage page, TreePage parentPage) { var pageRefNumber = page.GetNode(0)->PageNumber; TreeNodeHeader *nodeHeader = null; for (int i = 0; i < parentPage.NumberOfEntries; i++) { nodeHeader = parentPage.GetNode(i); if (nodeHeader->PageNumber == page.PageNumber) { break; } } Debug.Assert(nodeHeader->PageNumber == page.PageNumber); nodeHeader->PageNumber = pageRefNumber; _tree.FreePage(page); }
private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (_llt.Environment.GetTemporaryPage(_llt, out tmp)) { var tempPagePointer = tmp.TempPagePointer; Memory.Copy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page TreePage nestedPage = new TreePage(tempPagePointer, "multi tree", (ushort)currentSize); var ptr = DirectAdd(key, newSize); var newNestedPage = new TreePage(ptr, "multi tree", newSize) { Lower = (ushort)Constants.TreePageHeaderSize, Upper = newSize, TreeFlags = TreePageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; ByteStringContext allocator = _llt.Allocator; for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); Slice nodeKey = TreeNodeHeader.ToSlicePtr(allocator, nodeHeader); 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 nodeKey.Release(allocator); } newNestedPage.Search(_llt, value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0); } }
private bool TrySetPosition() { if (_disposed) { throw new ObjectDisposedException("PageIterator"); } if (_page.LastSearchPosition < 0 || _page.LastSearchPosition >= _page.NumberOfEntries) { return(false); } var current = _page.GetNode(_page.LastSearchPosition); if (DoRequireValidation && this.ValidateCurrentKey(_tx, current) == false) { return(false); } _currentInternalKey = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current); _currentKey = _currentInternalKey; return(true); }
private void MoveBranchNode(TreePage parentPage, TreePage from, TreePage to) { Debug.Assert(from.IsBranch); Slice originalFromKey; using (GetActualKey(from, from.LastSearchPositionOrLastEntry, out originalFromKey)) { 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 TreeNodeHeader *actualKeyNode; Slice implicitLeftKey; using (GetActualKey(to, 0, out actualKeyNode, out implicitLeftKey)) { var implicitLeftNode = to.GetNode(0); var leftPageNumber = implicitLeftNode->PageNumber; Slice implicitLeftKeyToInsert; ByteStringContext.ExternalScope?externalScope; if (implicitLeftNode == actualKeyNode) { externalScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, actualKeyNode, out implicitLeftKeyToInsert); } else { implicitLeftKeyToInsert = implicitLeftKey; externalScope = null; } to.EnsureHasSpaceFor(_tx, implicitLeftKeyToInsert, -1); to.AddPageRefNode(1, implicitLeftKeyToInsert, leftPageNumber); externalScope?.Dispose(); 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); Slice newSeparatorKey; var scope = GetActualKey(to, 0, out newSeparatorKey); // get the next smallest key it has now try { var pageNumber = to.PageNumber; if (parentPage.GetNode(0)->PageNumber == to.PageNumber) { pageNumber = from.PageNumber; scope.Dispose(); scope = GetActualKey(from, 0, out newSeparatorKey); } AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos); } finally { scope.Dispose(); } }
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 }); }
private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage 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; } byte *dataPos; var fromDataSize = fromNode->DataSize; switch (fromNode->Flags) { case TreeNodeFlags.PageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1); dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber); break; case TreeNodeFlags.Data: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize); dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize, nodeVersion); break; case TreeNodeFlags.MultiValuePageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize); dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, 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(to, parentPage, pageNumber, newSeparatorKey, pos); }
public byte *Execute() { using (DisableFreeSpaceUsageIfSplittingRootTree()) { TreePage rightPage = _tree.NewPage(_page.TreeFlags, 1); if (_cursor.PageCount == 0) // we need to do a root split { TreePage newRootPage = _tree.NewPage(TreePageFlags.Branch, 1); _cursor.Push(newRootPage); _tree.State.RootPageNumber = newRootPage.PageNumber; _tree.State.Depth++; // now add implicit left page newRootPage.AddPageRefNode(0, Slices.BeforeAllKeys, _page.PageNumber); _parentPage = newRootPage; _parentPage.LastSearchPosition++; } else { // we already popped the page, so the current one on the stack is the parent of the page _parentPage = _tree.ModifyPage(_cursor.CurrentPage); _cursor.Update(_cursor.Pages.First, _parentPage); } if (_page.IsLeaf) { _tree.ClearPagesCache(); } if (_page.IsCompressed) { _pageDecompressed = _tree.DecompressPage(_page); _pageDecompressed.Search(_tx, _newKey); _page = _pageDecompressed; } using (_pageDecompressed) { if (_page.LastSearchPosition >= _page.NumberOfEntries) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density TreePage branchOfSeparator; byte *pos; if (_page.IsBranch) { if (_page.NumberOfEntries > 2) { // here we steal the last entry from the current page so we maintain the implicit null left entry TreeNodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == TreeNodeFlags.PageRef); rightPage.AddPageRefNode(0, Slices.BeforeAllKeys, node->PageNumber); pos = AddNodeToPage(rightPage, 1); Slice separatorKey; using (TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out separatorKey)) { AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator); } _page.RemoveNode(_page.NumberOfEntries - 1); } else { _tree.FreePage(rightPage); // return the unnecessary right page pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator); if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber) { _cursor.Push(branchOfSeparator); } return(pos); } } else { AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator); pos = AddNodeToPage(rightPage, 0); } _cursor.Push(rightPage); return(pos); } return(SplitPageInHalf(rightPage)); } } }
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); } } } }
private void MoveLeafNode(TreePage parentPage, TreePage from, TreePage to) { Debug.Assert(from.IsBranch == false); Slice originalFromKeyStart; using (GetActualKey(from, from.LastSearchPositionOrLastEntry, out originalFromKeyStart)) { var fromNode = from.GetNode(from.LastSearchPosition); byte *val = @from.Base + @from.KeysOffsets[@from.LastSearchPosition] + Constants.Tree.NodeHeaderSize + originalFromKeyStart.Size; byte *dataPos; var fromDataSize = fromNode->DataSize; switch (fromNode->Flags) { case TreeNodeFlags.PageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1); dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber); break; case TreeNodeFlags.Data: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize); dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize); break; case TreeNodeFlags.MultiValuePageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromDataSize); dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromDataSize); 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); Slice newSeparatorKey; var scope = GetActualKey(to, 0, out newSeparatorKey); // get the next smallest key it has now try { var pageNumber = to.PageNumber; if (parentPage.GetNode(0)->PageNumber == to.PageNumber) { pageNumber = from.PageNumber; scope.Dispose(); scope = GetActualKey(from, 0, out newSeparatorKey); } AddSeparatorToParentPage(to, parentPage, pageNumber, newSeparatorKey, pos); } finally { scope.Dispose(); } } }