internal BPlusTreeNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent) { Tree = tree; Keywords = new List <TKey>(); Parent = parent; IsLeaf = false; }
public BPlusTreeIndexNode <TKey, TValue> PromoteToIndexNode() { BPlusTreeIndexNode <TKey, TValue> indexNode = null; try { BPlusTree <TKey, TValue> .LogInfo("Begin promoting leaf node to index node: " + this.ToString()); indexNode = new BPlusTreeIndexNode <TKey, TValue>(this.Tree, this.Parent, null); indexNode.Location = this.Location; indexNode.SlotSize = this.SlotSize; } finally { BPlusTree <TKey, TValue> .LogInfo("End promoting leaf node to index node: " + this.ToString()); } return(indexNode); }
protected internal BPlusTreeIndexNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr) : base(tree, parent) { IsLeaf = false; ChildLocations = new List <long>(); if (rdr != null) { // index nodes store only children locations (which are always longs) short childCount = rdr.ReadInt16(); for (int i = 0; i < childCount; i++) { ChildLocations.Add(rdr.ReadInt64()); } } }
private static BPlusTreeNode <TKey, TValue> processRead(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr, object[] args) { // stream has been positioned appropriately. // just read from it. BPlusTreeNode <TKey, TValue> node = null; tree.Lock(delegate() { long startLocation = rdr.BaseStream.Position; int slotSize = rdr.ReadInt32(); bool leaf = rdr.ReadBoolean(); short keywordCount = rdr.ReadInt16(); if (leaf) { node = new BPlusTreeLeafNode <TKey, TValue>(tree, parent, rdr); } else { node = new BPlusTreeIndexNode <TKey, TValue>(tree, parent, rdr); } node.Location = startLocation; // now we must read in all the keywords (they're always at the end of the stream) for (short i = 0; i < keywordCount; i++) { TKey keyManager = new TKey(); keyManager.Read(rdr); node.Keywords.Add(keyManager); } node.SlotSize = slotSize; // orient the stream pointer to the end of the node (which may be past the current data in it) rdr.BaseStream.Position = startLocation + node.SlotSize; }); return(node); }
protected internal BPlusTreeLeafNode(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, BinaryReader rdr) : base(tree, parent) { IsLeaf = true; // LeftSiblingLocation = -1; RightSiblingLocation = -1; Values = new List <TValue>(); // leaves store only sibling locations and values (keywords are handled by the base, since they're in every kind of node) if (rdr != null) { // LeftSiblingLocation = rdr.ReadInt64(); RightSiblingLocation = rdr.ReadInt64(); short valueCount = rdr.ReadInt16(); for (int i = 0; i < valueCount; i++) { TValue val = new TValue(); val.Read(rdr); Values.Add(val); } } }
internal BPlusTreeNode <TKey, TValue> SplitChild(BPlusTreeNode <TKey, TValue> originalChild, out int keywordIndex) { // Split the current node into two nodes: // a. Left node contains all pointers/keywords to the left of the median // b. Right node contains all pointers/keywords to the right of the median // c. Parent node gets median keyword pushed into it // // Disk-wise: the originalChild is always written back to its original position (since we're splitting it, it's guaranteed to get smaller) // the newChild is placed into either an available abandoned node or tacked on the end of the file. // the parent node is written back to its original position IF IT FITS. Since we're coyping a keyword // up into the parent node, it may cause it to outgrow its allocated size in the file. If this occurs, // the call to this.Write() implicitly calls relocate(). relocate() will then either write the parent to // an abandoned node or tack it onto the end of the file. // first, determine the offset of the current originalchild in this (the parent) keywordIndex = 0; try { BPlusTree <TKey, TValue> .LogInfo("Begin splitting node=" + this.ToString() + ", child=" + originalChild.ToString()); if (ChildLocations.Count > 0) { // TODO: troubleshoot this -- after several thousand splits, originalChild.Location is no longer found in the ChildLocations array!!! //if (LastChildLocation == originalChild.Location) { // // rightmost child, on a full node. //} else { while (keywordIndex < ChildLocations.Count && ChildLocations[keywordIndex] != originalChild.Location) { keywordIndex++; } //} } // assign the median keyword to the parent's Keywords list (inserts just before the current one) //if (keywordIndex > this.Keywords.Count) { // // rightmost keyword, add to the end of the list // this.Keywords.Add(originalChild.MedianKeyword); //} else { // not rightmost keyword, insert it at the proper position this.Keywords.Insert(keywordIndex, originalChild.MedianKeyword); //} if (originalChild.IsLeaf) { // since this is a leaf, we will copy up the median keyword to the parent (as opposed to push up for index nodes) // create a leaf node BPlusTreeLeafNode <TKey, TValue> origNode = (BPlusTreeLeafNode <TKey, TValue>)originalChild; BPlusTreeLeafNode <TKey, TValue> newNode = new BPlusTreeLeafNode <TKey, TValue>(this.Tree, (BPlusTreeIndexNode <TKey, TValue>) this, null); // copy right half of keywords + values from originalChild to newChild newNode.Keywords.AddRange(origNode.Keywords.Skip(this.Tree.Median - 1)); newNode.Values.AddRange(origNode.Values.Skip(this.Tree.Median - 1)); // remove right half of keywords + values from originalChild origNode.Keywords = origNode.Keywords.Take(this.Tree.Median - 1).ToList(); origNode.Values = origNode.Values.Take(this.Tree.Median - 1).ToList(); var nextKeywordIndex = keywordIndex + 1; Tree.Lock(delegate() { // write out nodes so sibling locations can be set properly // note: orig node should never relocate on write since we're making it smaller (moving 1/2 of its keywords to a new node) if (origNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("orig leaf node relocated during split child: " + origNode.ToString()); } if (newNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("new leaf node relocated during split child: " + newNode.ToString()); } // adjust sibling locations (we always add the new child to the right of the original one) newNode.RightSiblingLocation = origNode.RightSiblingLocation; // newNode.LeftSiblingLocation = origNode.Location; origNode.RightSiblingLocation = newNode.Location; ChildLocations.Insert(nextKeywordIndex, newNode.Location); // write them out again so we save sibling locations if (origNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("orig leaf node relocated on 2nd write during split child: " + origNode.ToString()); } if (newNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("new leaf node relocated on 2nd write during split child: " + newNode.ToString()); } // and write out the parent since it changed as well if (Write()) { BPlusTree <TKey, TValue> .LogInfo("parent of leaf node relocated during split child: " + this.ToString()); } }); return(newNode); } else { // since this is an index node, we push up the keyword to the parent (as opposed to copy up for leaves) // this means the median keyword will be removed from the original child but not moved to the new child // create an index node BPlusTreeIndexNode <TKey, TValue> origNode = (BPlusTreeIndexNode <TKey, TValue>)originalChild; BPlusTreeIndexNode <TKey, TValue> newNode = new BPlusTreeIndexNode <TKey, TValue>(this.Tree, (BPlusTreeIndexNode <TKey, TValue>) this, null); // copy right half of keywords + childlocations from originalChild to newChild // (since this is an index node, skip the median keyword newNode.Keywords.AddRange(origNode.Keywords.Skip(this.Tree.Median)); newNode.ChildLocations.AddRange(origNode.ChildLocations.Skip(this.Tree.Median)); // remove right half of keywords + childlocations from originalChild // (note we stop one shy of the median keyword -- this is because the median keyword needs to be "pushed up" // on an index node split. However, we don't drop the corresponding ChildLocation) origNode.Keywords = origNode.Keywords.Take(this.Tree.Median - 1).ToList(); origNode.ChildLocations = origNode.ChildLocations.Take(this.Tree.Median).ToList(); var ki = keywordIndex; Tree.Lock(delegate() { if (origNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("orig index node relocated during split child: " + origNode.ToString()); } if (newNode.Write()) { BPlusTree <TKey, TValue> .LogInfo("new index node relocated during split child: " + newNode.ToString()); } // original node may have relocated on write... ChildLocations[ki] = origNode.Location; ChildLocations.Insert(ki + 1, newNode.Location); // and write out the parent since it changed as well if (Write()) { BPlusTree <TKey, TValue> .LogInfo("parent of index node relocated during split child: " + this.ToString()); } }); return(newNode); } } finally { BPlusTree <TKey, TValue> .LogInfo("End splitting node=" + this.ToString() + ", child=" + originalChild.ToString()); } }
protected BPlusTreeNode <TKey, TValue> GetRightSibling(bool mustHaveSameParent) { if (this.IsLeaf) { // use the rightsibling pointer BPlusTreeLeafNode <TKey, TValue> leaf = this as BPlusTreeLeafNode <TKey, TValue>; return(BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, leaf.RightSiblingLocation)); } else { // look in parent for our smallest keyword, grab childlocation to the left of it int parentChildCount; int childLocationIndex = GetIndexInParent(out parentChildCount); if (childLocationIndex == -1) { // never found the index, or no parent (aka root). No sibling to return. return(null); } else { if (childLocationIndex < parentChildCount - 1) { // we know the right sibling is attached to the same parent. BPlusTreeNode <TKey, TValue> node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, Parent.ChildLocations[childLocationIndex + 1]); return(node); } else { // we are the rightmost child, as we're at the location that's 1 past the fanout size if (mustHaveSameParent) { // caller wants the sibling only if it's from the same parent, and it's not. return(null); } else { // we have to find the common ancestor between this and its right sibling. // spin up until we find the first node whose child index is <= Fanoutsize // then drill down the leftmost path until we're at the same depth as we started at int i = 0; int nodeChildLocationIndex = -1; BPlusTreeNode <TKey, TValue> node = this; while (node.Parent != null) { i++; // keep bubbling up until we're at a node whose parent has at least one child to our right. nodeChildLocationIndex = node.GetIndexInParent(out parentChildCount); if (nodeChildLocationIndex < parentChildCount - 1) { // this node is not the rightmost child of its parent. // we're done bubbling (node contains the ancestor node, nodeChildLocationIndex can tell us the subtree to search down) break; } else { node = node.Parent; } } if (nodeChildLocationIndex == parentChildCount - 1) { // the only common ancestor has our node being the rightmost node in the tree (at this depth). return(null); } // ok, node contains the ancestor somehwere up the tree that has the node we're looking for as its // leftmost descendent at the same depth. // So we just keep drilling down the leftmost path until we're at the same depth. node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, node as BPlusTreeIndexNode <TKey, TValue>, nodeChildLocationIndex + 1); while (i > 0) { BPlusTreeIndexNode <TKey, TValue> indexNode = node as BPlusTreeIndexNode <TKey, TValue>; node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, indexNode, indexNode.ChildLocations[0]); i--; } // node now contains the right sibling! return(node); } } } //// bounce up to the parent //if (Parent != null) { // TKey ourKey = FirstKeyword; // for (int i=0; i < Parent.Keywords.Count; i++) { // TKey parentKeyword = Parent.Keywords[i]; // if (parentKeyword.CompareTo(ourKey, KeywordMatchMode.ExactMatch, false) == 0) { // // follow right child // BPlusTreeNode<TKey, TValue> node = BPlusTreeNode<TKey, TValue>.Read(this.Tree, this.Parent, Parent.ChildLocations[i+1]); // return node; // } // } //} //return null; } }
protected BPlusTreeNode <TKey, TValue> GetLeftSibling(bool mustHaveSameParent) { // look in parent for our smallest keyword, grab childlocation to the left of it int parentChildCount; int childLocationIndex = GetIndexInParent(out parentChildCount); if (childLocationIndex == -1) { // never found the index, or no parent (aka root). No sibling to return. return(null); } else { if (childLocationIndex == 0) { if (mustHaveSameParent) { // caller wants the sibling only if it's from the same parent, and it's not. return(null); } else { // we have to find the parent of our left sibling. // spin up until we find the first node whose child index is > 0 // then drill down the rightmost path until we're at the same depth as we started at int i = 0; int nodeChildLocationIndex = -1; BPlusTreeNode <TKey, TValue> node = this; while (node.Parent != null) { i++; // keep bubbling up until we're at a node whose parent has at least one child to our left. nodeChildLocationIndex = node.GetIndexInParent(out parentChildCount); if (nodeChildLocationIndex > 0) { // this node is not the leftmost child of its parent. // we're done bubbling (node contains the ancestor node, nodeChildLocationIndex can tell us the subtree to search down) break; } else { node = node.Parent; } } if (nodeChildLocationIndex == 0) { // the only common ancestor has our node being the leftmost node in the tree (at this depth). return(null); } // ok, node contains the ancestor somehwere up the tree that has the node we're looking for as its // rightmost descendent at the same depth. // So we just keep drilling down until we're at the same depth. node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, node as BPlusTreeIndexNode <TKey, TValue>, nodeChildLocationIndex - 1); while (i > 0) { BPlusTreeIndexNode <TKey, TValue> indexNode = node as BPlusTreeIndexNode <TKey, TValue>; node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, indexNode, indexNode.LastChildLocation); i--; } // node now contains the left sibling! return(node); } } else { // we know the left sibling is attached to the same parent. BPlusTreeNode <TKey, TValue> node = BPlusTreeNode <TKey, TValue> .Read(this.Tree, this.Parent, Parent.ChildLocations[childLocationIndex - 1]); return(node); } } }
/// <summary> /// Factory method for reading data from the tree's stream and returning either a BPlusTreeIndexNode or BPlusTreeLeafNode object /// </summary> /// <param name="tree"></param> /// <param name="location"></param> /// <returns></returns> internal static BPlusTreeNode <TKey, TValue> Read(BPlusTree <TKey, TValue> tree, BPlusTreeIndexNode <TKey, TValue> parent, long location) { if (location < 1) { return(null); } BPlusTreeNode <TKey, TValue> node = null; if (!tree.NodeCache.TryGetValue(location, out node)) { node = tree.ReadNodeFromDisk(parent, processRead, location, null); } return(node); }