private bool HasEnoughSpaceToCopyNodes(Page left, Page right) { var actualSpaceNeeded = 0; 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); actualSpaceNeeded += (SizeOf.NodeEntryWithAnotherKey(node, key) + Constants.NodeOffsetSize); } right.LastSearchPosition = previousSearchPosition; //previous position --> prevent mutation of parameter return(left.HasSpaceFor(_tx, actualSpaceNeeded)); }
/// <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, MemorySlice key) { var index = NumberOfEntries; Debug.Assert(HasSpaceFor(SizeOf.NodeEntryWithAnotherKey(other, key) + Constants.NodeOffsetSize + SizeOf.NewPrefix(key))); var nodeSize = SizeOf.NodeEntryWithAnotherKey(other, key); Debug.Assert(IsBranch == false || index != 0 || key.KeyLength == 0); // branch page's first item must be the implicit ref var nodeVersion = other->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 prefixedKey = key as PrefixedSlice; if (prefixedKey != null && prefixedKey.NewPrefix != null) { WritePrefix(prefixedKey.NewPrefix, prefixedKey.Header.PrefixId); } var newNode = AllocateNewNode(index, nodeSize, nodeVersion); newNode->KeySize = key.Size; newNode->Flags = other->Flags; if (key.Options == SliceOptions.Key && key.Size > 0) { 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; MemoryUtils.Copy((byte *)newNode + Constants.NodeHeaderSize + key.Size, (byte *)other + Constants.NodeHeaderSize + other->KeySize, other->DataSize); }
/// <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) { var nodeKey = key ?? new Slice(other); Debug.Assert(HasSpaceFor(SizeOf.NodeEntryWithAnotherKey(other, nodeKey) + Constants.NodeOffsetSize)); var index = NumberOfEntries; var nodeSize = SizeOf.NodeEntryWithAnotherKey(other, nodeKey); if (other->KeySize == 0 && key == null) // when copy first item from branch which is implicit ref { nodeSize += nodeKey.Size; } Debug.Assert(IsBranch == false || index != 0 || nodeKey.Size == 0); // branch page's first item must be the implicit ref var nodeVersion = other->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 newNode = AllocateNewNode(index, nodeKey, nodeSize, nodeVersion); newNode->Flags = other->Flags; nodeKey.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); }
private bool TryMergePages(Page parentPage, Page left, Page right) { TemporaryPage tmp; using (_tx.Environment.GetTemporaryPage(_tx, out tmp)) { var mergedPage = tmp.GetTempPage(left.KeysPrefixed); 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); var prefixedKey = mergedPage.PrepareKeyToInsert(key, mergedPage.NumberOfEntries); if (mergedPage.HasSpaceFor(_tx, SizeOf.NodeEntryWithAnotherKey(node, prefixedKey) + Constants.NodeOffsetSize + SizeOf.NewPrefix(prefixedKey)) == false) { right.LastSearchPosition = previousSearchPosition; //previous position --> prevent mutation of parameter return(false); } mergedPage.CopyNodeDataToEndOfPage(node, prefixedKey); } Memory.Copy(left.Base, mergedPage.Base, left.PageSize); } parentPage.RemoveNode(parentPage.LastSearchPositionOrLastEntry); // unlink the right sibling _tree.FreePage(right); return(true); }