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 = newSize, Flags = PageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; Slice nodeKey = new Slice(SliceOptions.Key); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); nodeKey.Set(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 } newNestedPage.Search(value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0); } }
private void MultiAddOnNewValue(Transaction tx, Slice key, Slice value, ushort?version, int maxNodeSize) { var requiredPageSize = Constants.PageHeaderSize + SizeOf.LeafEntry(-1, value, 0) + Constants.NodeOffsetSize; if (requiredPageSize > 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(tx, TreeFlags.MultiValue); tree.DirectAdd(value, 0); tx.AddMultiValueTree(this, key, tree); DirectAdd(key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef); return; } var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(requiredPageSize), maxNodeSize); var ptr = DirectAdd(key, actualPageSize); var nestedPage = new Page(ptr, "multi tree", actualPageSize) { PageNumber = -1L, // hint that this is an inner page Lower = (ushort)Constants.PageHeaderSize, Upper = actualPageSize, Flags = PageFlags.Leaf, }; CheckConcurrency(key, value, version, 0, TreeActionType.Add); nestedPage.AddDataNode(0, value, 0, 0); }
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; } byte *dataPos; switch (fromNode->Flags) { case NodeFlags.PageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, -1); dataPos = to.AddPageRefNode(to.LastSearchPosition, originalFromKeyStart, fromNode->PageNumber); break; case NodeFlags.Data: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromNode->DataSize); dataPos = to.AddDataNode(to.LastSearchPosition, originalFromKeyStart, fromNode->DataSize, nodeVersion); break; case NodeFlags.MultiValuePageRef: to.EnsureHasSpaceFor(_tx, originalFromKeyStart, fromNode->DataSize); dataPos = to.AddMultiValueNode(to.LastSearchPosition, originalFromKeyStart, fromNode->DataSize, nodeVersion); break; default: throw new NotSupportedException("Invalid node type to move: " + fromNode->Flags); } if (dataPos != null) { NativeMethods.memcpy(dataPos, val, fromNode->DataSize); } from.RemoveNode(from.LastSearchPositionOrLastEntry); var pos = parentPage.LastSearchPositionOrLastEntry; parentPage.RemoveNode(pos); var newKey = 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; newKey = GetActualKey(from, 0); } parentPage.EnsureHasSpaceFor(_tx, newKey, -1); parentPage.AddPageRefNode(pos, newKey, pageNumber); }
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 MultiAddOnNewValue(Transaction tx, Slice key, Slice value, ushort?version, int maxNodeSize) { MemorySlice valueToInsert; if (KeysPrefixing) { valueToInsert = new PrefixedSlice(value); // first item is never prefixed } else { valueToInsert = value; } var requiredPageSize = Constants.PageHeaderSize + // header of a nested page Constants.NodeOffsetSize + // one node in a nested page SizeOf.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(tx, KeysPrefixing, TreeFlags.MultiValue); tree.DirectAdd(value, 0); tx.AddMultiValueTree(this, key, tree); DirectAdd(key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef); return; } var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(requiredPageSize), maxNodeSize - Constants.NodeHeaderSize); var ptr = DirectAdd(key, actualPageSize); var nestedPage = new Page(ptr, "multi tree", actualPageSize) { PageNumber = -1L,// hint that this is an inner page Lower = (ushort)Constants.PageHeaderSize, Upper = KeysPrefixing ? (ushort)(actualPageSize - Constants.PrefixInfoSectionSize) : actualPageSize, Flags = KeysPrefixing ? PageFlags.Leaf | PageFlags.KeysPrefixed : PageFlags.Leaf, }; nestedPage.ClearPrefixInfo(); CheckConcurrency(key, value, version, 0, TreeActionType.Add); nestedPage.AddDataNode(0, valueToInsert, 0, 0); }
private byte *AddNodeToPage(Page page, int index) { switch (_nodeType) { case NodeFlags.PageRef: return(page.AddPageRefNode(index, _newKey, _pageNumber)); case NodeFlags.Data: return(page.AddDataNode(index, _newKey, _len, _nodeVersion)); case NodeFlags.MultiValuePageRef: return(page.AddMultiValueNode(index, _newKey, _len, _nodeVersion)); default: throw new NotSupportedException("Unknown node type"); } }
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"); } }
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; var page = FindPageFor(key, 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 = OpenOrCreateMultiValueTree(_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 = new Slice(item); if (tmpKey.Compare(value) == 0) return; // already there, turning into a no-op nestedPage.RemoveNode(nestedPage.LastSearchPosition); } if (nestedPage.HasSpaceFor(_tx, 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, previousNodeRevision); return; } int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize; var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(value, 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, 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 void MultiAddOnNewValue(Transaction tx, Slice key, Slice value, ushort? version, int maxNodeSize) { var requiredPageSize = Constants.PageHeaderSize + SizeOf.LeafEntry(-1, value, 0) + Constants.NodeOffsetSize; if (requiredPageSize > 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(tx, TreeFlags.MultiValue); tree.DirectAdd(value, 0); tx.AddMultiValueTree(this, key, tree); DirectAdd(key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef); return; } var actualPageSize = (ushort) Math.Min(Utils.NearestPowerOfTwo(requiredPageSize), maxNodeSize); var ptr = DirectAdd(key, actualPageSize); var nestedPage = new Page(ptr, "multi tree", actualPageSize) { PageNumber = -1L,// hint that this is an inner page Lower = (ushort) Constants.PageHeaderSize, Upper = actualPageSize, Flags = PageFlags.Leaf, }; CheckConcurrency(key, value, version, 0, TreeActionType.Add); nestedPage.AddDataNode(0, value, 0, 0); }
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 = newSize, Flags = PageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; Slice nodeKey = new Slice(SliceOptions.Key); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); nodeKey.Set(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 } newNestedPage.Search(value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0); } }
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); }
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); }
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* AddNodeToPage(Page page, int index) { switch (_nodeType) { case NodeFlags.PageRef: return page.AddPageRefNode(index, _newKey, _pageNumber); case NodeFlags.Data: return page.AddDataNode(index, _newKey, _len, _nodeVersion); case NodeFlags.MultiValuePageRef: return page.AddMultiValueNode(index, _newKey, _len, _nodeVersion); default: throw new NotSupportedException("Unknown node type"); } }
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; Memory.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); } }