/// <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(TreeNodeHeader *other, Slice key) { var index = NumberOfEntries; Debug.Assert(HasSpaceFor(TreeSizeOf.NodeEntryWithAnotherKey(other, key) + Constants.Tree.NodeOffsetSize)); var nodeSize = TreeSizeOf.NodeEntryWithAnotherKey(other, key); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref var newNode = AllocateNewNode(index, nodeSize); Debug.Assert(key.Size <= ushort.MaxValue); newNode->KeySize = (ushort)key.Size; newNode->Flags = other->Flags; if (key.Options == SliceOptions.Key && key.Size > 0) { key.CopyTo((byte *)newNode + Constants.Tree.NodeHeaderSize); } if (IsBranch || other->Flags == (TreeNodeFlags.PageRef)) { newNode->PageNumber = other->PageNumber; newNode->Flags = TreeNodeFlags.PageRef; return; } newNode->DataSize = other->DataSize; Memory.Copy((byte *)newNode + Constants.Tree.NodeHeaderSize + key.Size, (byte *)other + Constants.Tree.NodeHeaderSize + other->KeySize, other->DataSize); }
private TreeNodeHeader *CreateNode(int index, Slice key, TreeNodeFlags flags, int len) { 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(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)); } // move higher pointers up one slot ushort *offsets = KeysOffsets; for (int i = NumberOfEntries; i > index; i--) { offsets[i] = offsets[i - 1]; } var nodeSize = TreeSizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, nodeSize); node->Flags = flags; Debug.Assert(key.Size <= ushort.MaxValue); node->KeySize = (ushort)key.Size; if (key.Options == SliceOptions.Key && node->KeySize > 0) { key.CopyTo((byte *)node + Constants.Tree.NodeHeaderSize); } return(node); }
private void MultiAddOnNewValue(Slice key, Slice value, ushort?version, int maxNodeSize) { var requiredPageSize = Constants.TreePageHeaderSize + // header of a nested page Constants.NodeOffsetSize + // one node in a nested page TreeSizeOf.LeafEntry(-1, value, 0); // node header and its value if (requiredPageSize + Constants.NodeHeaderSize > maxNodeSize) { // no choice, very big value, we might as well just put it in its own tree from the get go... // otherwise, we would have to put this in overflow page, and that won't save us any space anyway var tree = Create(_llt, _tx, TreeFlags.MultiValue); tree.DirectAdd(value, 0); _tx.AddMultiValueTree(this, key, tree); DirectAdd(key, sizeof(TreeRootHeader), TreeNodeFlags.MultiValuePageRef); return; } var actualPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredPageSize), maxNodeSize - Constants.NodeHeaderSize); var ptr = DirectAdd(key, actualPageSize); var nestedPage = new TreePage(ptr, "multi tree", actualPageSize) { PageNumber = -1L,// hint that this is an inner page Lower = (ushort)Constants.TreePageHeaderSize, Upper = actualPageSize, TreeFlags = TreePageFlags.Leaf, }; CheckConcurrency(key, value, version, 0, TreeActionType.Add); nestedPage.AddDataNode(0, value, 0, 0); }
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 byte *AddSeparator(Slice separator, long pageRefNumber, int?nodePos = null) { var originalLastSearchPositionOfParent = _parentPage.LastSearchPosition; if (nodePos == null) { nodePos = _parentPage.NodePositionFor(_tx, separator); // select the appropriate place for this } if (_parentPage.HasSpaceFor(_tx, TreeSizeOf.BranchEntry(separator) + Constants.NodeOffsetSize) == false) { var pageSplitter = new TreePageSplitter(_tx, _tree, separator, -1, pageRefNumber, TreeNodeFlags.PageRef, 0, _cursor); 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, separator, pageRefNumber); EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent); return(pos); }
private int AdjustSplitPosition(int currentIndex, int splitIndex, ref bool toRight) { Slice keyToInsert = _newKey; int pageSize = TreeSizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.Tree.NodeOffsetSize; if (toRight == false) { for (int i = 0; i < splitIndex; i++) { TreeNodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > _page.PageMaxSpace) { if (i <= currentIndex) { if (i < currentIndex) { toRight = true; } return(currentIndex); } return(i); } } } else { for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--) { TreeNodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > _page.PageMaxSpace) { if (i >= currentIndex) { toRight = false; return(currentIndex); } return(i + 1); } } } return(splitIndex); }
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); } } } }
public int GetRequiredSpace(Slice key, int len) { return(TreeSizeOf.NodeEntry(PageMaxSpace, key, len) + Constants.Tree.NodeOffsetSize); }