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 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 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 byte *AddNodeToPage(TreePage page, int index, Slice alreadyPreparedNewKey = default(Slice)) { var newKeyToInsert = alreadyPreparedNewKey.HasValue ? alreadyPreparedNewKey : _newKey; switch (_nodeType) { case TreeNodeFlags.PageRef: return(page.AddPageRefNode(index, newKeyToInsert, _pageNumber)); case TreeNodeFlags.Data: return(page.AddDataNode(index, newKeyToInsert, _len)); case TreeNodeFlags.MultiValuePageRef: return(page.AddMultiValueNode(index, newKeyToInsert, _len)); default: throw new NotSupportedException("Unknown node type"); } }
public void MultiAdd(Slice key, Slice value) { if (!value.HasValue) { throw new ArgumentNullException(nameof(value)); } int maxNodeSize = Llt.DataPager.NodeMaxSize; if (value.Size > maxNodeSize) { throw new ArgumentException("Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", nameof(value)); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } State.IsModified = true; State.Flags |= TreeFlags.MultiValueTrees; TreeNodeHeader *node; var page = FindPageFor(key, out node); if (page == null || page.LastMatch != 0) { MultiAddOnNewValue(key, value, maxNodeSize); return; } page = ModifyPage(page); var item = page.GetNode(page.LastSearchPosition); byte *_; // already was turned into a multi tree, not much to do here if (item->Flags == TreeNodeFlags.MultiValuePageRef) { var existingTree = OpenMultiValueTree(key, item); existingTree.DirectAdd(value, 0, out _).Dispose(); return; } if (item->Flags == TreeNodeFlags.PageRef) { throw new InvalidOperationException("Multi trees don't use overflows"); } var nestedPagePtr = DirectAccessFromHeader(item); var nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(item)); var existingItem = nestedPage.Search(_llt, value); if (nestedPage.LastMatch != 0) { existingItem = null;// not an actual match, just greater than } if (existingItem != null) { // maybe same value added twice? Slice tmpKey; using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, item, out tmpKey)) { if (SliceComparer.Equals(tmpKey, value)) { return; // already there, turning into a no-op } } nestedPage.RemoveNode(nestedPage.LastSearchPosition); } if (nestedPage.HasSpaceFor(_llt, value, 0)) { // we are now working on top of the modified root page, we can just modify the memory directly nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0); return; } if (page.HasSpaceFor(_llt, value, 0)) { // page has space for an additional node in nested page ... var requiredSpace = nestedPage.PageSize + // existing page nestedPage.GetRequiredSpace(value, 0); // new node if (requiredSpace + Constants.Tree.NodeHeaderSize <= maxNodeSize) { // ... and it won't require to create an overflow, so we can just expand the current value, no need to create a nested tree yet EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr); var newPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredSpace), maxNodeSize - Constants.Tree.NodeHeaderSize); ExpandMultiTreeNestedPageSize(key, value, nestedPagePtr, newPageSize, nestedPage.PageSize); return; } } EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr); // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_llt, _tx, key, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { Slice existingValue; using (nestedPage.GetNodeKey(_llt, i, out existingValue)) { tree.DirectAdd(existingValue, 0, out _).Dispose(); } } tree.DirectAdd(value, 0, out _).Dispose(); _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), TreeNodeFlags.MultiValuePageRef, out _).Dispose(); }
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(); } } }
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); }