private NodeHeader *CreateNode(int index, Slice key, NodeFlags flags, int len, ushort previousNodeVersion) { 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("The page is full and cannot add an entry, this is probably a bug"); } // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, key, nodeSize, previousNodeVersion); if (key.Options == SliceOptions.Key) { key.CopyTo((byte *)node + Constants.NodeHeaderSize); } node->Flags = flags; return(node); }
/// <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) { Debug.Assert(SizeOf.NodeEntry(other) + Constants.NodeOffsetSize <= SizeLeft); var index = NumberOfEntries; var nodeSize = SizeOf.NodeEntry(other); key = key ?? new Slice(other); Debug.Assert(IsBranch == false || index != 0 || key.Size == 0);// branch page's first item must be the implicit ref var newNode = AllocateNewNode(index, key, nodeSize); newNode->Flags = other->Flags; 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; NativeMethods.memcpy((byte*)newNode + Constants.NodeHeaderSize + other->KeySize, (byte*)other + Constants.NodeHeaderSize + other->KeySize, other->DataSize); }
public byte* AddNode(int index, Slice key, int len, long pageNumber) { 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("The page is full and cannot add an entry, this is probably a bug"); // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(_pageMaxSpace, key, len); var node = AllocateNewNode(index, key, nodeSize); if (key.Options == SliceOptions.Key) key.CopyTo((byte*)node + Constants.NodeHeaderSize); if (len < 0) // branch or overflow { Debug.Assert(pageNumber != -1); node->PageNumber = pageNumber; node->Flags = NodeFlags.PageRef; return null; // write nothing here } Debug.Assert(key.Options == SliceOptions.Key); var dataPos = (byte*)node + Constants.NodeHeaderSize + key.Size; node->DataSize = len; node->Flags = NodeFlags.Data; return dataPos; }
public void RemoveNode(int index) { Debug.Assert(IsBranch == false || index > 0 || NumberOfEntries == 2); // cannot remove implicit left in branches, unless as part of removing this node entirely Debug.Assert(index < NumberOfEntries); var node = GetNode(index); var size = SizeOf.NodeEntry(node); var nodeOffset = KeysOffsets[index]; int modifiedEntries = 0; for (int i = 0; i < NumberOfEntries; i++) { if (i == index) continue; KeysOffsets[modifiedEntries] = KeysOffsets[i]; if (KeysOffsets[i] < nodeOffset) KeysOffsets[modifiedEntries] += (ushort)size; modifiedEntries++; } NativeMethods.memmove(_base + Upper + size, _base + Upper, nodeOffset - Upper); Lower -= (ushort)Constants.NodeOffsetSize; Upper += (ushort)size; }
/// <summary> /// For leaf pages, check the split point based on what /// fits where, since otherwise adding the node can fail. /// This check is only needed when the data items are /// relatively large, such that being off by one will /// make the difference between success or failure. /// It's also relevant if a page happens to be laid out /// such that one half of its nodes are all "small" and /// the other half of its nodes are "large." If the new /// item is also "large" and falls on the half with /// "large" nodes, it also may not fit. /// </summary> private int AdjustSplitPosition(Slice key, int len, Page page, int currentIndex, int splitIndex, ref bool newPosition) { int nodeSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, key, len) + Constants.NodeOffsetSize; if (page.NumberOfEntries >= 20 && nodeSize <= AbstractPager.PageMaxSpace / 16) { return(splitIndex); } int pageSize = nodeSize; if (currentIndex <= splitIndex) { newPosition = false; for (int i = 0; i < splitIndex; i++) { NodeHeader *node = page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i <= currentIndex) { if (i < currentIndex) { newPosition = true; } return(currentIndex); } return((ushort)i); } } } else { for (int i = page.NumberOfEntries - 1; i >= splitIndex; i--) { NodeHeader *node = page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i >= currentIndex) { newPosition = false; return(currentIndex); } return((ushort)(i + 1)); } } } return(splitIndex); }
private NodeHeader *CreateNode(int index, MemorySlice key, NodeFlags flags, int len, ushort previousNodeVersion) { Debug.Assert(index <= NumberOfEntries && index >= 0); Debug.Assert(IsBranch == false || index != 0 || key.KeyLength == 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)); } var prefixedKey = key as PrefixedSlice; if (prefixedKey != null && prefixedKey.NewPrefix != null) { WritePrefix(prefixedKey.NewPrefix, prefixedKey.Header.PrefixId); } // move higher pointers up one slot for (int i = NumberOfEntries; i > index; i--) { KeysOffsets[i] = KeysOffsets[i - 1]; } var nodeSize = SizeOf.NodeEntry(PageMaxSpace, key, len); var node = AllocateNewNode(index, nodeSize, previousNodeVersion); node->KeySize = key.Size; if (key.Options == SliceOptions.Key && key.Size > 0) { key.CopyTo((byte *)node + Constants.NodeHeaderSize); } node->Flags = flags; return(node); }
public static void DumpHumanReadable(Transaction tx, string path, Page start) { using (var writer = File.CreateText(path)) { var stack = new Stack <Page>(); stack.Push(start); writer.WriteLine("Root page #{0}", start.PageNumber); while (stack.Count > 0) { var currentPage = stack.Pop(); if (currentPage.IsLeaf) { writer.WriteLine(); writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Leaf), Used: {3} : {4}", currentPage.PageNumber, currentPage.NumberOfEntries, currentPage.Flags, currentPage.SizeUsed, currentPage.CalcSizeUsed()); if (currentPage.NumberOfEntries <= 0) { writer.WriteLine("Empty page (tree corrupted?)"); } for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); var key = currentPage.GetNodeKey(node); writer.WriteLine("Node #{0}, Flags = {1}, {4} = {2}, Key = {3}, Entry Size: {5}", nodeIndex, node->Flags, node->DataSize, MaxString(key.ToString(), 25), node->Flags == NodeFlags.Data ? "Size" : "Page", SizeOf.NodeEntry(node)); } writer.WriteLine(); } else if (currentPage.IsBranch) { writer.WriteLine(); writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Branch), Used: {3} : {4}", currentPage.PageNumber, currentPage.NumberOfEntries, currentPage.Flags, currentPage.SizeUsed, currentPage.SizeUsed); var key = new Slice(SliceOptions.Key); for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); writer.WriteLine("Node #{2}, {0} / to page #{1}, Entry Size: {3}", GetBranchNodeString(nodeIndex, key, currentPage, node), node->PageNumber, nodeIndex, SizeOf.NodeEntry(node)); } for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); if (node->PageNumber < 0 || node->PageNumber > tx.State.NextPageNumber) { writer.Write("Found invalid reference to page #{0}", currentPage.PageNumber); stack.Clear(); break; } var child = tx.GetReadOnlyPage(node->PageNumber); stack.Push(child); } writer.WriteLine(); } } } }
public int GetRequiredSpace(MemorySlice key, int len) { return(SizeOf.NodeEntry(PageMaxSpace, key, len) + Constants.NodeOffsetSize + SizeOf.NewPrefix(key)); }
private int AdjustSplitPosition(int currentIndex, int splitIndex, PrefixNode[] prefixes, ref bool toRight) { MemorySlice keyToInsert; int pageSize = 0; if (_tree.KeysPrefixing) { keyToInsert = new PrefixedSlice(_newKey); // let's assume that _newkey won't match any of the existing prefixes pageSize += Constants.PrefixInfoSectionSize; pageSize += Constants.PrefixNodeHeaderSize + 1; // possible new prefix, + 1 because of possible 2-byte alignment } else { keyToInsert = _newKey; } pageSize += SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize; if (prefixes != null) { // we are going to copy all existing prefixes so we need to take into account their sizes for (var i = 0; i < prefixes.Length; i++) { var prefixNodeSize = Constants.PrefixNodeHeaderSize + prefixes[i].Header.PrefixLength; pageSize += prefixNodeSize + (prefixNodeSize & 1); // & 1 because we need 2-byte alignment } } if (toRight == false) { for (int i = 0; i < splitIndex; i++) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i <= currentIndex) { if (i < currentIndex) { toRight = true; } return(currentIndex); } return(i); } } } else { for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i >= currentIndex) { toRight = false; return(currentIndex); } return(i + 1); } } } return(splitIndex); }
public int GetRequiredSpace(Slice key, int len) { return(SizeOf.NodeEntry(PageMaxSpace, key, len) + Constants.NodeOffsetSize); }
/// <summary> /// For leaf pages, check the split point based on what /// fits where, since otherwise adding the node can fail. /// This check is only needed when the data items are /// relatively large, such that being off by one will /// make the difference between success or failure. /// It's also relevant if a page happens to be laid out /// such that one half of its nodes are all "small" and /// the other half of its nodes are "large." If the new /// item is also "large" and falls on the half with /// "large" nodes, it also may not fit. /// </summary> private int AdjustSplitPosition(int currentIndex, int splitIndex, ref bool newPosition) { MemorySlice keyToInsert; if (_tree.KeysPrefixing) { keyToInsert = new PrefixedSlice(_newKey); // let's assume that _newkey won't be prefixed to ensure the destination page will have enough space } else { keyToInsert = _newKey; } int nodeSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize; if (_page.NumberOfEntries >= 20 && nodeSize <= AbstractPager.PageMaxSpace / 16) { return(splitIndex); } int pageSize = nodeSize; if (_tree.KeysPrefixing) { pageSize += (Constants.PrefixNodeHeaderSize + 1); // let's assume that prefix will be created to ensure the destination page will have enough space, + 1 because prefix node might require 2-byte alignment } if (currentIndex <= splitIndex) { newPosition = false; for (int i = 0; i < splitIndex; i++) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i <= currentIndex) { if (i < currentIndex) { newPosition = true; } return(currentIndex); } return((ushort)i); } } } else { for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i >= currentIndex) { newPosition = false; return(currentIndex); } return((ushort)(i + 1)); } } } return(splitIndex); }
private int AdjustSplitPosition(int currentIndex, int splitIndex, PrefixNode[] prefixes, ref bool newPosition) { MemorySlice keyToInsert; if (_tree.KeysPrefixing) { keyToInsert = new PrefixedSlice(_newKey); // let's assume that _newkey won't be prefixed to ensure the destination page will have enough space } else { keyToInsert = _newKey; } var pageSize = SizeOf.NodeEntry(AbstractPager.PageMaxSpace, keyToInsert, _len) + Constants.NodeOffsetSize; if (prefixes != null) { // we are going to copy all existing prefixes so we need to take into account their sizes for (var i = 0; i < prefixes.Length; i++) { pageSize += (Constants.PrefixNodeHeaderSize + prefixes[i].Header.PrefixLength) & 1; // & 1 because we need 2-byte alignment } } if (currentIndex <= splitIndex) { newPosition = false; for (int i = 0; i < splitIndex; i++) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i <= currentIndex) { if (i < currentIndex) { newPosition = true; } return(currentIndex); } return((ushort)i); } } } else { for (int i = _page.NumberOfEntries - 1; i >= splitIndex; i--) { NodeHeader *node = _page.GetNode(i); pageSize += node->GetNodeSize(); pageSize += pageSize & 1; if (pageSize > AbstractPager.PageMaxSpace) { if (i >= currentIndex) { newPosition = false; return(currentIndex); } return((ushort)(i + 1)); } } } return(splitIndex); }