public static BplusNode BinaryRoot(BplusNode LeftNode, string key, BplusNode RightNode, BplusTreeLong owner) { BplusNode newRoot = new BplusNode(owner, null, -1, false); //newRoot.Clear(); // redundant newRoot.ChildKeys[0] = key; LeftNode.Reparent(newRoot, 0); RightNode.Reparent(newRoot, 1); // new root is stored elsewhere return newRoot; }
public BplusTreeLong(System.IO.Stream fromfile, int NodeSize, int KeyLength, long StartSeek, int CultureId) { this.cultureContext = new System.Globalization.CultureInfo(CultureId); this.cmp = this.cultureContext.CompareInfo; this.fromfile = fromfile; this.NodeSize = NodeSize; this.seekStart = StartSeek; // add in key prefix overhead this.KeyLength = KeyLength + BufferFile.SHORTSTORAGE; this.rootSeek = NULLBUFFERNUMBER; this.root = null; this.freeHeadSeek = NULLBUFFERNUMBER; this.SanityCheck(); }
/// <summary> /// Create a new BplusNode and install in parent if parent is not null. /// </summary> /// <param name="owner">tree containing the node</param> /// <param name="parent">parent node (if provided)</param> /// <param name="indexInParent">location in parent if provided</param> public BplusNode(BplusTreeLong owner, BplusNode parent, int indexInParent, bool isLeaf) { this.isLeaf = isLeaf; this.owner = owner; this.parent = parent; this.Size = owner.NodeSize; //this.isValid = true; this.Dirty = true; // this.ChildBufferNumbers = new long[this.Size+1]; // this.ChildKeys = new string[this.Size]; // this.MaterializedChildNodes = new BplusNode[this.Size+1]; this.Clear(); if (parent!=null && indexInParent>=0) { if (indexInParent>this.Size) { throw new BplusTreeException("parent index too large"); } // key info, etc, set elsewhere this.parent.MaterializedChildNodes[indexInParent] = this; this.myBufferNumber = this.parent.ChildBufferNumbers[indexInParent]; this.indexInParent = indexInParent; } }
/// <summary> /// Find the first key below atIndex, or if no such node traverse to the next key to the right. /// If no such key exists, return nulls. /// </summary> /// <param name="atIndex">where to look in this node</param> /// <param name="foundInLeaf">leaf where found</param> /// <param name="keyFound">key value found</param> void TraverseToFollowingKey(int atIndex, out BplusNode foundInLeaf, out string keyFound) { foundInLeaf = null; keyFound = null; bool lookInParent; if (IsLeaf) { lookInParent = (atIndex>=m_size) || (m_childKeys[atIndex]==null); } else { lookInParent = (atIndex>m_size) || (atIndex>0 && m_childKeys[atIndex-1]==null); } if (lookInParent) { // if it's anywhere it's in the next child of parent if (m_parent!=null && m_indexInParent>=0) { m_parent.TraverseToFollowingKey(m_indexInParent+1, out foundInLeaf, out keyFound); return; } else { return; // no such following key } } if (IsLeaf) { // leaf, we found it. foundInLeaf = this; keyFound = m_childKeys[atIndex]; } else { // nonleaf, look in child (if there is one) if (atIndex==0 || m_childKeys[atIndex-1]!=null) { var thechild = MaterializeNodeAtIndex(atIndex); thechild.TraverseToFollowingKey(0, out foundInLeaf, out keyFound); } } }
public void RemoveKey(string key) { if (this.root==null) { throw new BplusTreeKeyMissing("tree is empty: cannot delete"); } bool MergeMe; BplusNode theroot = this.root; theroot.Delete(key, out MergeMe); // if the root is not a leaf and contains only one child (no key), reroot if (MergeMe && !this.root.isLeaf && this.root.SizeInUse()==0) { this.root = this.root.FirstChild(); this.rootSeek = this.root.makeRoot(); theroot.Free(); } }
public void ForgetTerminalNode(BplusNode nonterminalNode) { if (!this.TerminalNodeToId.ContainsKey(nonterminalNode)) { // silently ignore (?) return; } int id = (int) this.TerminalNodeToId[nonterminalNode]; if (id == this.LowerTerminalNodeCount) { this.LowerTerminalNodeCount++; } this.IdToTerminalNode.Remove(id); this.TerminalNodeToId.Remove(nonterminalNode); }
public long this[string key] { get { return MyGetKey(key, true); } set { if (!BplusNode.KeyOK(key, this)) { throw new BplusTreeBadKeyValue("null or too large key cannot be inserted into tree: "+key); } bool rootinit = false; if (this.root==null) { // allocate root this.root = new BplusNode(this, null, -1, true); rootinit = true; //this.rootSeek = root.DumpToFreshBuffer(); } // insert into root... string splitString; BplusNode splitNode; root.Insert(key, value, out splitString, out splitNode); if (splitNode!=null) { // split of root: make a new root. rootinit = true; BplusNode oldRoot = this.root; this.root = BplusNode.BinaryRoot(oldRoot, splitString, splitNode, this); } if (rootinit) { this.rootSeek = root.DumpToFreshBuffer(); } // check size in memory this.ShrinkFootprint(); } }
void Reparent(BplusNode newParent, int ParentIndex) { // keys and existing parent structure must be updated elsewhere. this.parent = newParent; this.indexInParent = ParentIndex; newParent.ChildBufferNumbers[ParentIndex] = this.myBufferNumber; newParent.MaterializedChildNodes[ParentIndex] = this; // parent is no longer terminal this.owner.ForgetTerminalNode(parent); }
/// <summary> /// insert key/position entry in self /// </summary> /// <param name="key">Key to associate with the leaf</param> /// <param name="position">position associated with key in external structur</param> /// <param name="splitString">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns>null unless the smallest key under this node has changed, in which case it returns the smallest key.</returns> public string Insert(string key, long position, out string splitString, out BplusNode splitNode) { if (IsLeaf) { return InsertLeaf(key, position, out splitString, out splitNode); } splitString = null; splitNode = null; var insertposition = FindAtOrNextPosition(key, false); var insertBufferNumber = m_childBufferNumbers[insertposition]; if (insertBufferNumber==BplusTreeLong.Nullbuffernumber) { throw new BplusTreeException("key not followed by buffer number in non-leaf"); } // insert in subtree var insertChild = MaterializeNodeAtIndex(insertposition); BplusNode childSplit; string childSplitString; var childInsert = insertChild.Insert(key, position, out childSplitString, out childSplit); // if there was a split the node must expand if (childSplit!=null) { // insert the child Soil(); // redundant -- a child will have a change so this node will need to be copied var newChildPosition = insertposition+1; var dosplit = false; // if there is no free space we must do a split if (m_childBufferNumbers[m_size]!=BplusTreeLong.Nullbuffernumber) { dosplit = true; PrepareForSplit(); } // bubble over the current values to make space for new child for (var i=m_childKeys.Length-2; i>=newChildPosition-1; i--) { var i1 = i+1; var i2 = i1+1; m_childKeys[i1] = m_childKeys[i]; m_childBufferNumbers[i2] = m_childBufferNumbers[i1]; var childNode = m_materializedChildNodes[i2] = m_materializedChildNodes[i1]; } // record the new child m_childKeys[newChildPosition-1] = childSplitString; //this.MaterializedChildNodes[newChildPosition] = childSplit; //this.ChildBufferNumbers[newChildPosition] = childSplit.myBufferNumber; childSplit.Reparent(this, newChildPosition); // split, if needed if (dosplit) { var splitpoint = m_materializedChildNodes.Length/2-1; splitString = m_childKeys[splitpoint]; splitNode = new BplusNode(m_owner, m_parent, -1, IsLeaf); // make copy of expanded node structure var materialized = m_materializedChildNodes; var buffernumbers = m_childBufferNumbers; var keys = m_childKeys; // repair the expanded node m_childKeys = new string[m_size]; m_materializedChildNodes = new BplusNode[m_size+1]; m_childBufferNumbers = new long[m_size+1]; Clear(); Array.Copy(materialized, 0, m_materializedChildNodes, 0, splitpoint+1); Array.Copy(buffernumbers, 0, m_childBufferNumbers, 0, splitpoint+1); Array.Copy(keys, 0, m_childKeys, 0, splitpoint); // initialize the new node splitNode.Clear(); // redundant. var remainingKeys = m_size-splitpoint; Array.Copy(materialized, splitpoint+1, splitNode.m_materializedChildNodes, 0, remainingKeys+1); Array.Copy(buffernumbers, splitpoint+1, splitNode.m_childBufferNumbers, 0, remainingKeys+1); Array.Copy(keys, splitpoint+1, splitNode.m_childKeys, 0, remainingKeys); // fix pointers in materialized children of splitnode splitNode.ReParentAllChildren(); // store the new node splitNode.DumpToFreshBuffer(); splitNode.CheckIfTerminal(); splitNode.Soil(); CheckIfTerminal(); } // fix pointers in children ReParentAllChildren(); } if (insertposition==0) { // the smallest key may have changed return childInsert; } return null; // no change in smallest key }
public void RemoveKey(string key) { if (m_root==null) { throw new BplusTreeKeyMissing("tree is empty: cannot delete"); } bool mergeMe; var theroot = m_root; theroot.Delete(key, out mergeMe); // if the root is not a leaf and contains only one child (no key), reroot if (mergeMe && !m_root.IsLeaf && m_root.SizeInUse()==0) { m_root = m_root.FirstChild(); m_rootSeek = m_root.MakeRoot(); theroot.Free(); } }
public void RecordTerminalNode(BplusNode terminalNode) { if (terminalNode==m_root) { return; // never record the root node } if (m_terminalNodeToId.ContainsKey(terminalNode) ) { return; // don't record it again } var id = m_terminalNodeCount; m_terminalNodeCount++; m_terminalNodeToId[terminalNode] = id; m_idToTerminalNode[id] = terminalNode; }
public void ForgetTerminalNode(BplusNode nonterminalNode) { if (!m_terminalNodeToId.ContainsKey(nonterminalNode)) { // silently ignore (?) return; } var id = (int) m_terminalNodeToId[nonterminalNode]; if (id == m_lowerTerminalNodeCount) { m_lowerTerminalNodeCount++; } m_idToTerminalNode.Remove(id); m_terminalNodeToId.Remove(nonterminalNode); }
/// <summary> /// Forget all changes since last commit /// </summary> public void Abort() { // deallocate allocated blocks var toFree = new ArrayList(); foreach (DictionaryEntry d in FreeBuffersOnAbort) { toFree.Add(d.Key); } toFree.Sort(); toFree.Reverse(); foreach (var thing in toFree) { var buffernumber = (long) thing; DeallocateBuffer(buffernumber); } var freehead = m_freeHeadSeek; // reread the header (except for freelist head) ReadHeader(); // restore the root if (m_rootSeek==Nullbuffernumber) { m_root = null; // nothing was committed } else { m_root.LoadFromBuffer(m_rootSeek); } ResetBookkeeping(); m_freeHeadSeek = freehead; SetHeader(); // store new freelist head Fromfile.Flush(); }
public long this[string key] { get { long valueFound; var test = ContainsKey(key, out valueFound); if (!test) { throw new BplusTreeKeyMissing("no such key found: "+key); } return valueFound; } set { if (!BplusNode.KeyOK(key, this)) { throw new BplusTreeBadKeyValue("null or too large key cannot be inserted into tree: "+key); } var rootinit = false; if (m_root==null) { // allocate root m_root = new BplusNode(this, null, -1, true); rootinit = true; //this.rootSeek = root.DumpToFreshBuffer(); } // insert into root... string splitString; BplusNode splitNode; m_root.Insert(key, value, out splitString, out splitNode); if (splitNode!=null) { // split of root: make a new root. rootinit = true; var oldRoot = m_root; m_root = BplusNode.BinaryRoot(oldRoot, splitString, splitNode, this); } if (rootinit) { m_rootSeek = m_root.DumpToFreshBuffer(); } // check size in memory ShrinkFootprint(); } }
public BplusTreeLong(Stream fromfile, int nodeSize, int keyLength, long startSeek, int cultureId) { CultureContext = new CultureInfo(cultureId); m_cmp = CultureContext.CompareInfo; Fromfile = fromfile; NodeSize = nodeSize; SeekStart = startSeek; // add in key prefix overhead KeyLength = keyLength + BufferFile.Shortstorage; m_rootSeek = Nullbuffernumber; m_root = null; m_freeHeadSeek = Nullbuffernumber; SanityCheck(); }
BplusNode MaterializeNodeAtIndex(int myposition) { if (this.isLeaf) { throw new BplusTreeException("cannot materialize child for leaf"); } long childBufferNumber = this.ChildBufferNumbers[myposition]; if (childBufferNumber==BplusTreeLong.NULLBUFFERNUMBER) { throw new BplusTreeException("can't search null subtree at position "+myposition+" in "+this.myBufferNumber); } // is it already materialized? BplusNode result = this.MaterializedChildNodes[myposition]; if (result!=null) { return result; } // otherwise read it in... result = new BplusNode(this.owner, this, myposition, true); // dummy isLeaf value result.LoadFromBuffer(childBufferNumber); this.MaterializedChildNodes[myposition] = result; // no longer terminal this.owner.ForgetTerminalNode(this); return result; }
/// <summary> /// Grow to this.size+1 in preparation for insertion and split /// </summary> void PrepareForSplit() { int supersize = this.Size+1; long[] positions = new long[supersize+1]; string[] keys = new string[supersize]; BplusNode[] materialized = new BplusNode[supersize+1]; Array.Copy(this.ChildBufferNumbers, 0, positions, 0, this.Size+1); positions[this.Size+1] = BplusTreeLong.NULLBUFFERNUMBER; Array.Copy(this.ChildKeys, 0, keys, 0, this.Size); keys[this.Size] = null; Array.Copy(this.MaterializedChildNodes, 0, materialized, 0, this.Size+1); materialized[this.Size+1] = null; this.ChildBufferNumbers = positions; this.ChildKeys = keys; this.MaterializedChildNodes = materialized; }
/// <summary> /// insert key/position entry in self (as leaf) /// </summary> /// <param name="key">Key to associate with the leaf</param> /// <param name="position">position associated with key in external structure</param> /// <param name="splitString">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns>smallest key value in keys, or null if no change</returns> public string InsertLeaf(string key, long position, out string splitString, out BplusNode splitNode) { splitString = null; splitNode = null; var dosplit = false; if (!IsLeaf) { throw new BplusTreeException("bad call to InsertLeaf: this is not a leaf"); } Soil(); var insertposition = FindAtOrNextPosition(key, false); if (insertposition>=m_size) { //throw new BplusTreeException("key too big and leaf is full"); dosplit = true; PrepareForSplit(); } else { // if it's already there then change the value at the current location (duplicate entries not supported). if (m_childKeys[insertposition]==null || m_owner.Compare(m_childKeys[insertposition], key)==0) // this.ChildKeys[insertposition].Equals(key) { m_childBufferNumbers[insertposition] = position; m_childKeys[insertposition] = key; if (insertposition==0) { return key; } else { return null; } } } // check for a null position var nullindex = insertposition; while (nullindex<m_childKeys.Length && m_childKeys[nullindex]!=null) { nullindex++; } if (nullindex>=m_childKeys.Length) { if (dosplit) { throw new BplusTreeException("can't split twice!!"); } //throw new BplusTreeException("no space in leaf"); dosplit = true; PrepareForSplit(); } // bubble in the new info XXXX THIS SHOULD BUBBLE BACKWARDS var nextkey = m_childKeys[insertposition]; var nextposition = m_childBufferNumbers[insertposition]; m_childKeys[insertposition] = key; m_childBufferNumbers[insertposition] = position; while (nextkey!=null) { key = nextkey; position = nextposition; insertposition++; nextkey = m_childKeys[insertposition]; nextposition = m_childBufferNumbers[insertposition]; m_childKeys[insertposition] = key; m_childBufferNumbers[insertposition] = position; } // split if needed if (dosplit) { var splitpoint = m_childKeys.Length/2; var splitlength = m_childKeys.Length - splitpoint; splitNode = new BplusNode(m_owner, m_parent, -1, IsLeaf); // copy the split info into the splitNode Array.Copy(m_childBufferNumbers, splitpoint, splitNode.m_childBufferNumbers, 0, splitlength); Array.Copy(m_childKeys, splitpoint, splitNode.m_childKeys, 0, splitlength); Array.Copy(m_materializedChildNodes, splitpoint, splitNode.m_materializedChildNodes, 0, splitlength); splitString = splitNode.m_childKeys[0]; // archive the new node splitNode.DumpToFreshBuffer(); // store the node data temporarily var buffernumbers = m_childBufferNumbers; var keys = m_childKeys; var nodes = m_materializedChildNodes; // repair current node, copy in the other part of the split m_childBufferNumbers = new long[m_size+1]; m_childKeys = new string[m_size]; m_materializedChildNodes = new BplusNode[m_size+1]; Array.Copy(buffernumbers, 0, m_childBufferNumbers, 0, splitpoint); Array.Copy(keys, 0, m_childKeys, 0, splitpoint); Array.Copy(nodes, 0, m_materializedChildNodes, 0, splitpoint); for (var i=splitpoint; i<m_childKeys.Length; i++) { m_childKeys[i] = null; m_childBufferNumbers[i] = BplusTreeLong.Nullbuffernumber; m_materializedChildNodes[i] = null; } // store the new node //splitNode.DumpToFreshBuffer(); m_owner.RecordTerminalNode(splitNode); splitNode.Soil(); } //return this.ChildKeys[0]; if (insertposition==0) { return key; // smallest key changed. } else { return null; // no change in smallest key } }
/// <summary> /// Find the first key below atIndex, or if no such node traverse to the next key to the right. /// If no such key exists, return nulls. /// </summary> /// <param name="atIndex">where to look in this node</param> /// <param name="FoundInLeaf">leaf where found</param> /// <param name="KeyFound">key value found</param> void TraverseToFollowingKey(int atIndex, out BplusNode FoundInLeaf, out string KeyFound) { FoundInLeaf = null; KeyFound = null; bool LookInParent = false; if (this.isLeaf) { LookInParent = (atIndex>=this.Size) || (this.ChildKeys[atIndex]==null); } else { LookInParent = (atIndex>this.Size) || (atIndex>0 && this.ChildKeys[atIndex-1]==null); } if (LookInParent) { // if it's anywhere it's in the next child of parent if (this.parent!=null && this.indexInParent>=0) { this.parent.TraverseToFollowingKey(this.indexInParent+1, out FoundInLeaf, out KeyFound); return; } else { return; // no such following key } } if (this.isLeaf) { // leaf, we found it. FoundInLeaf = this; KeyFound = this.ChildKeys[atIndex]; return; } else { // nonleaf, look in child (if there is one) if (atIndex==0 || this.ChildKeys[atIndex-1]!=null) { BplusNode thechild = this.MaterializeNodeAtIndex(atIndex); thechild.TraverseToFollowingKey(0, out FoundInLeaf, out KeyFound); } } }
public long makeRoot() { this.parent = null; this.indexInParent = -1; if (this.myBufferNumber==BplusTreeLong.NULLBUFFERNUMBER) { throw new BplusTreeException("no root seek allocated to new root"); } return this.myBufferNumber; }
/// <summary> /// Forget all changes since last commit /// </summary> public void Abort() { // deallocate allocated blocks ArrayList toFree = new ArrayList(); foreach (DictionaryEntry d in this.FreeBuffersOnAbort) { toFree.Add(d.Key); } toFree.Sort(); toFree.Reverse(); foreach (object thing in toFree) { long buffernumber = (long) thing; this.deallocateBuffer(buffernumber); } long freehead = this.freeHeadSeek; // reread the header (except for freelist head) this.readHeader(); // restore the root if (this.rootSeek==NULLBUFFERNUMBER) { this.root = null; // nothing was committed } else { this.root.LoadFromBuffer(this.rootSeek); } this.ResetBookkeeping(); this.freeHeadSeek = freehead; this.setHeader(); // store new freelist head this.fromfile.Flush(); }
public static void Merge(BplusNode left, string KeyBetween, BplusNode right, out string rightLeastKey, out bool DeleteRight) { //System.Diagnostics.Debug.WriteLine("\r\n<br> merging "+right.myBufferNumber+" ("+KeyBetween+") "+left.myBufferNumber); //System.Diagnostics.Debug.WriteLine(left.owner.toHtml()); rightLeastKey = null; // only if DeleteRight if (left.isLeaf || right.isLeaf) { if (!(left.isLeaf&&right.isLeaf)) { throw new BplusTreeException("can't merge leaf with non-leaf"); } MergeLeaves(left, right, out DeleteRight); rightLeastKey = right.ChildKeys[0]; return; } // merge non-leaves DeleteRight = false; string[] allkeys = new string[left.Size*2+1]; long[] allseeks = new long[left.Size*2+2]; BplusNode[] allMaterialized = new BplusNode[left.Size*2+2]; if (left.ChildBufferNumbers[0]==BplusTreeLong.NULLBUFFERNUMBER || right.ChildBufferNumbers[0]==BplusTreeLong.NULLBUFFERNUMBER) { throw new BplusTreeException("cannot merge empty non-leaf with non-leaf"); } int index = 0; allseeks[0] = left.ChildBufferNumbers[0]; allMaterialized[0] = left.MaterializedChildNodes[0]; for (int i=0; i<left.Size; i++) { if (left.ChildKeys[i]==null) { break; } allkeys[index] = left.ChildKeys[i]; allseeks[index+1] = left.ChildBufferNumbers[i+1]; allMaterialized[index+1] = left.MaterializedChildNodes[i+1]; index++; } allkeys[index] = KeyBetween; index++; allseeks[index] = right.ChildBufferNumbers[0]; allMaterialized[index] = right.MaterializedChildNodes[0]; int rightcount = 0; for (int i=0; i<right.Size; i++) { if (right.ChildKeys[i]==null) { break; } allkeys[index] = right.ChildKeys[i]; allseeks[index+1] = right.ChildBufferNumbers[i+1]; allMaterialized[index+1] = right.MaterializedChildNodes[i+1]; index++; rightcount++; } if (index<=left.Size) { // it will all fit in one node //System.Diagnostics.Debug.WriteLine("deciding to forget "+right.myBufferNumber+" into "+left.myBufferNumber); DeleteRight = true; for (int i=0; i<index; i++) { left.ChildKeys[i] = allkeys[i]; left.ChildBufferNumbers[i] = allseeks[i]; left.MaterializedChildNodes[i] = allMaterialized[i]; } left.ChildBufferNumbers[index] = allseeks[index]; left.MaterializedChildNodes[index] = allMaterialized[index]; left.reParentAllChildren(); left.Soil(); right.Free(); return; } // otherwise split the content between the nodes left.Clear(); right.Clear(); left.Soil(); right.Soil(); int leftcontent = index/2; int rightcontent = index-leftcontent-1; rightLeastKey = allkeys[leftcontent]; int outputindex = 0; for (int i=0; i<leftcontent; i++) { left.ChildKeys[i] = allkeys[outputindex]; left.ChildBufferNumbers[i] = allseeks[outputindex]; left.MaterializedChildNodes[i] = allMaterialized[outputindex]; outputindex++; } rightLeastKey = allkeys[outputindex]; left.ChildBufferNumbers[outputindex] = allseeks[outputindex]; left.MaterializedChildNodes[outputindex] = allMaterialized[outputindex]; outputindex++; rightcount = 0; for (int i=0; i<rightcontent; i++) { right.ChildKeys[i] = allkeys[outputindex]; right.ChildBufferNumbers[i] = allseeks[outputindex]; right.MaterializedChildNodes[i] = allMaterialized[outputindex]; outputindex++; rightcount++; } right.ChildBufferNumbers[rightcount] = allseeks[outputindex]; right.MaterializedChildNodes[rightcount] = allMaterialized[outputindex]; left.reParentAllChildren(); right.reParentAllChildren(); }
public void RecordTerminalNode(BplusNode terminalNode) { if (terminalNode==this.root) { return; // never record the root node } if (this.TerminalNodeToId.ContainsKey(terminalNode) ) { return; // don't record it again } int id = this.TerminalNodeCount; this.TerminalNodeCount++; this.TerminalNodeToId[terminalNode] = id; this.IdToTerminalNode[id] = terminalNode; }
public void SerializationCheck() { BplusNode A = new BplusNode(this.owner, null, -1, false); for (int i=0; i<this.Size; i++) { long j = i*((long)0xf0f0f0f0f0f0f01); A.ChildBufferNumbers[i] = j; A.ChildKeys[i] = "k"+i; } A.ChildBufferNumbers[this.Size] = 7; A.TestRebuffer(); A.isLeaf = true; for (int i=0; i<this.Size; i++) { long j = -i*((long)0x3e3e3e3e3e3e666); A.ChildBufferNumbers[i] = j; A.ChildKeys[i] = "key"+i; } A.ChildBufferNumbers[this.Size] = -9097; A.TestRebuffer(); }
public static void MergeLeaves(BplusNode left, BplusNode right, out bool DeleteRight) { DeleteRight = false; string[] allkeys = new string[left.Size*2]; long[] allseeks = new long[left.Size*2]; int index = 0; for (int i=0; i<left.Size; i++) { if (left.ChildKeys[i]==null) { break; } allkeys[index] = left.ChildKeys[i]; allseeks[index] = left.ChildBufferNumbers[i]; index++; } for (int i=0; i<right.Size; i++) { if (right.ChildKeys[i]==null) { break; } allkeys[index] = right.ChildKeys[i]; allseeks[index] = right.ChildBufferNumbers[i]; index++; } if (index<=left.Size) { left.Clear(); DeleteRight = true; for (int i=0; i<index; i++) { left.ChildKeys[i] = allkeys[i]; left.ChildBufferNumbers[i] = allseeks[i]; } right.Free(); left.Soil(); return; } left.Clear(); right.Clear(); left.Soil(); right.Soil(); int rightcontent = index/2; int leftcontent = index - rightcontent; int newindex = 0; for (int i=0; i<leftcontent; i++) { left.ChildKeys[i] = allkeys[newindex]; left.ChildBufferNumbers[i] = allseeks[newindex]; newindex++; } for (int i=0; i<rightcontent; i++) { right.ChildKeys[i] = allkeys[newindex]; right.ChildBufferNumbers[i] = allseeks[newindex]; newindex++; } }
void Destroy() { // make sure the structure is useless, it should no longer be used. this.owner = null; this.parent = null; this.Size = -100; this.ChildBufferNumbers = null; this.ChildKeys = null; this.MaterializedChildNodes = null; this.myBufferNumber = BplusTreeLong.NULLBUFFERNUMBER; this.indexInParent = -100; this.Dirty = false; }
/// <summary> /// insert key/position entry in self /// </summary> /// <param name="key">Key to associate with the leaf</param> /// <param name="position">position associated with key in external structur</param> /// <param name="splitString">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns>null unless the smallest key under this node has changed, in which case it returns the smallest key.</returns> public string Insert(string key, long position, out string splitString, out BplusNode splitNode) { if (this.isLeaf) { return this.InsertLeaf(key, position, out splitString, out splitNode); } splitString = null; splitNode = null; int insertposition = this.FindAtOrNextPosition(key, false, true); long insertBufferNumber = this.ChildBufferNumbers[insertposition]; if (insertBufferNumber==BplusTreeLong.NULLBUFFERNUMBER) { throw new BplusTreeException("key not followed by buffer number in non-leaf"); } // insert in subtree BplusNode InsertChild = this.MaterializeNodeAtIndex(insertposition); BplusNode childSplit; string childSplitString; string childInsert = InsertChild.Insert(key, position, out childSplitString, out childSplit); // if there was a split the node must expand if (childSplit!=null) { // insert the child this.Soil(); // redundant -- a child will have a change so this node will need to be copied int newChildPosition = insertposition+1; bool dosplit = false; // if there is no free space we must do a split if (this.ChildBufferNumbers[this.Size]!=BplusTreeLong.NULLBUFFERNUMBER) { dosplit = true; this.PrepareForSplit(); } // bubble over the current values to make space for new child for (int i=this.ChildKeys.Length-2; i>=newChildPosition-1; i--) { int i1 = i+1; int i2 = i1+1; this.ChildKeys[i1] = this.ChildKeys[i]; this.ChildBufferNumbers[i2] = this.ChildBufferNumbers[i1]; BplusNode childNode = this.MaterializedChildNodes[i2] = this.MaterializedChildNodes[i1]; } // record the new child this.ChildKeys[newChildPosition-1] = childSplitString; //this.MaterializedChildNodes[newChildPosition] = childSplit; //this.ChildBufferNumbers[newChildPosition] = childSplit.myBufferNumber; childSplit.Reparent(this, newChildPosition); // split, if needed if (dosplit) { int splitpoint = this.MaterializedChildNodes.Length/2-1; splitString = this.ChildKeys[splitpoint]; splitNode = new BplusNode(this.owner, this.parent, -1, this.isLeaf); // make copy of expanded node structure BplusNode[] materialized = this.MaterializedChildNodes; long[] buffernumbers = this.ChildBufferNumbers; string[] keys = this.ChildKeys; // repair the expanded node this.ChildKeys = new string[this.Size]; this.MaterializedChildNodes = new BplusNode[this.Size+1]; this.ChildBufferNumbers = new long[this.Size+1]; this.Clear(); Array.Copy(materialized, 0, this.MaterializedChildNodes, 0, splitpoint+1); Array.Copy(buffernumbers, 0, this.ChildBufferNumbers, 0, splitpoint+1); Array.Copy(keys, 0, this.ChildKeys, 0, splitpoint); // initialize the new node splitNode.Clear(); // redundant. int remainingKeys = this.Size-splitpoint; Array.Copy(materialized, splitpoint+1, splitNode.MaterializedChildNodes, 0, remainingKeys+1); Array.Copy(buffernumbers, splitpoint+1, splitNode.ChildBufferNumbers, 0, remainingKeys+1); Array.Copy(keys, splitpoint+1, splitNode.ChildKeys, 0, remainingKeys); // fix pointers in materialized children of splitnode splitNode.reParentAllChildren(); // store the new node splitNode.DumpToFreshBuffer(); splitNode.CheckIfTerminal(); splitNode.Soil(); this.CheckIfTerminal(); } // fix pointers in children this.reParentAllChildren(); } if (insertposition==0) { // the smallest key may have changed return childInsert; } return null; // no change in smallest key }
/// <summary> /// Find near-index of comparekey in leaf under this node. /// </summary> /// <param name="CompareKey">the key to look for</param> /// <param name="inLeaf">the leaf where found</param> /// <param name="LookPastOnly">If true then only look for a greater value, not an exact match.</param> /// <returns>index of match in leaf</returns> int FindAtOrNextPositionInLeaf(string CompareKey, out BplusNode inLeaf, bool LookPastOnly, bool caseSensitive) { int myposition = this.FindAtOrNextPosition(CompareKey, LookPastOnly, caseSensitive); if (this.isLeaf) { inLeaf = this; return myposition; } long childBufferNumber = this.ChildBufferNumbers[myposition]; if (childBufferNumber==BplusTreeLong.NULLBUFFERNUMBER) { throw new BplusTreeException("can't search null subtree"); } BplusNode child = this.MaterializeNodeAtIndex(myposition); return child.FindAtOrNextPositionInLeaf(CompareKey, out inLeaf, LookPastOnly, caseSensitive); }
/// <summary> /// insert key/position entry in self (as leaf) /// </summary> /// <param name="key">Key to associate with the leaf</param> /// <param name="position">position associated with key in external structure</param> /// <param name="splitString">if not null then the smallest key in the new split leaf</param> /// <param name="splitNode">if not null then the node was split and this is the leaf to the right.</param> /// <returns>smallest key value in keys, or null if no change</returns> public string InsertLeaf(string key, long position, out string splitString, out BplusNode splitNode) { splitString = null; splitNode = null; bool dosplit = false; if (!this.isLeaf) { throw new BplusTreeException("bad call to InsertLeaf: this is not a leaf"); } this.Soil(); int insertposition = this.FindAtOrNextPosition(key, false, true); if (insertposition>=this.Size) { //throw new BplusTreeException("key too big and leaf is full"); dosplit = true; this.PrepareForSplit(); } else { // if it's already there then change the value at the current location (duplicate entries not supported). if (this.ChildKeys[insertposition]==null || this.owner.Compare(this.ChildKeys[insertposition], key, true)==0) // this.ChildKeys[insertposition].Equals(key) { this.ChildBufferNumbers[insertposition] = position; this.ChildKeys[insertposition] = key; if (insertposition==0) { return key; } else { return null; } } } // check for a null position int nullindex = insertposition; while (nullindex<this.ChildKeys.Length && this.ChildKeys[nullindex]!=null) { nullindex++; } if (nullindex>=this.ChildKeys.Length) { if (dosplit) { throw new BplusTreeException("can't split twice!!"); } //throw new BplusTreeException("no space in leaf"); dosplit = true; this.PrepareForSplit(); } // bubble in the new info XXXX THIS SHOULD BUBBLE BACKWARDS string nextkey = this.ChildKeys[insertposition]; long nextposition = this.ChildBufferNumbers[insertposition]; this.ChildKeys[insertposition] = key; this.ChildBufferNumbers[insertposition] = position; while (nextkey!=null) { key = nextkey; position = nextposition; insertposition++; nextkey = this.ChildKeys[insertposition]; nextposition = this.ChildBufferNumbers[insertposition]; this.ChildKeys[insertposition] = key; this.ChildBufferNumbers[insertposition] = position; } // split if needed if (dosplit) { int splitpoint = this.ChildKeys.Length/2; int splitlength = this.ChildKeys.Length - splitpoint; splitNode = new BplusNode(this.owner, this.parent, -1, this.isLeaf); // copy the split info into the splitNode Array.Copy(this.ChildBufferNumbers, splitpoint, splitNode.ChildBufferNumbers, 0, splitlength); Array.Copy(this.ChildKeys, splitpoint, splitNode.ChildKeys, 0, splitlength); Array.Copy(this.MaterializedChildNodes, splitpoint, splitNode.MaterializedChildNodes, 0, splitlength); splitString = splitNode.ChildKeys[0]; // archive the new node splitNode.DumpToFreshBuffer(); // store the node data temporarily long[] buffernumbers = this.ChildBufferNumbers; string[] keys = this.ChildKeys; BplusNode[] nodes = this.MaterializedChildNodes; // repair current node, copy in the other part of the split this.ChildBufferNumbers = new long[this.Size+1]; this.ChildKeys = new string[this.Size]; this.MaterializedChildNodes = new BplusNode[this.Size+1]; Array.Copy(buffernumbers, 0, this.ChildBufferNumbers, 0, splitpoint); Array.Copy(keys, 0, this.ChildKeys, 0, splitpoint); Array.Copy(nodes, 0, this.MaterializedChildNodes, 0, splitpoint); for (int i=splitpoint; i<this.ChildKeys.Length; i++) { this.ChildKeys[i] = null; this.ChildBufferNumbers[i] = BplusTreeLong.NULLBUFFERNUMBER; this.MaterializedChildNodes[i] = null; } // store the new node //splitNode.DumpToFreshBuffer(); this.owner.RecordTerminalNode(splitNode); splitNode.Soil(); } //return this.ChildKeys[0]; if (insertposition==0) { return key; // smallest key changed. } else { return null; // no change in smallest key } }
void Reparent(BplusNode newParent, int parentIndex) { // keys and existing parent structure must be updated elsewhere. m_parent = newParent; m_indexInParent = parentIndex; newParent.m_childBufferNumbers[parentIndex] = MyBufferNumber; newParent.m_materializedChildNodes[parentIndex] = this; // parent is no longer terminal m_owner.ForgetTerminalNode(m_parent); }