/* * Copyright (c) 2014, Oculus VR, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ /// \file DS_BPlusTree.h /// #ifndef __B_PLUS_TREE_CPP #define __B_PLUS_TREE_CPP #include "DS_MemoryPool.hpp" #include "DS_Queue.hpp" #include #include "Export.hpp" // Java // http://www.seanster.com/BplusTree/BplusTree.html // Overview // http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm // Deletion // http://dbpubs.stanford.edu:8090/pub/1995-19 #ifdef _MSC_VER #pragma warning( push ) #endif #include "RakMemoryOverride.hpp" /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. namespace DataStructures { /// Used in the BPlusTree. Used for both leaf and index nodes. /// Don't use a constructor or destructor, due to the memory pool I am using template struct RAK_DLL_EXPORT Page { // We use the same data structure for both leaf and index nodes. // It uses a little more memory for index nodes but reduces // memory fragmentation, allocations, and deallocations. bool isLeaf; // Used for both leaf and index nodes. // For a leaf it means the number of elements in data // For an index it means the number of keys and is one less than the number of children pointers. int size; // Used for both leaf and index nodes. KeyType keys[order]; // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) DataType data[order]; Page *next; Page *previous; // Used only for index nodes. Pointers to the children of this node. Page *children[order+1]; }; /// A BPlus tree /// Written with efficiency and speed in mind. template class RAK_DLL_EXPORT BPlusTree { public: struct ReturnAction { KeyType key1; KeyType key2; enum { NO_ACTION, REPLACE_KEY1_WITH_KEY2, PUSH_KEY_TO_PARENT, SET_BRANCH_KEY, } action; // 0=none, 1=replace key1 with key2 }; BPlusTree(); ~BPlusTree(); void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl bool Get(const KeyType key, DataType &out) const; bool Delete(const KeyType key); bool Delete(const KeyType key, DataType &out); bool Insert(const KeyType key, const DataType &data); void Clear(void); unsigned Size(void) const; bool IsEmpty(void) const; Page *GetListHead(void) const; DataType GetDataHead(void) const; void PrintLeaves(void); void ForEachLeaf(void (*func)(Page * leaf, int index)); void ForEachData(void (*func)(DataType input, int index)); void PrintGraph(void); protected: void ValidateTreeRecursive(Page *cur); void DeleteFromPageAtIndex(const int index, Page *cur); static void PrintLeaf(Page * leaf, int index); void FreePages(void); bool GetIndexOf(const KeyType key, Page *page, int *out) const; void ShiftKeysLeft(Page *cur); bool CanRotateLeft(Page *cur, int childIndex); bool CanRotateRight(Page *cur, int childIndex); void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); Page* GetLeafFromKey(const KeyType key) const; bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); void ShiftNodeLeft(Page *cur); void ShiftNodeRight(Page *cur); MemoryPool > pagePool; Page *root, *leftmostLeaf; }; template BPlusTree::BPlusTree () { RakAssert(order>1); root=0; leftmostLeaf=0; } template BPlusTree::~BPlusTree () { Clear(); } template void BPlusTree::SetPoolPageSize(int size) { pagePool.SetPageSize(size); } template bool BPlusTree::Get(const KeyType key, DataType &out) const { if (root==0) return false; Page* leaf = GetLeafFromKey(key); int childIndex; if (GetIndexOf(key, leaf, &childIndex)) { out=leaf->data[childIndex]; return true; } return false; } template void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) { int i; for (i=index; i < cur->size-1; i++) cur->keys[i]=cur->keys[i+1]; if (cur->isLeaf) { for (i=index; i < cur->size-1; i++) cur->data[i]=cur->data[i+1]; } else { for (i=index; i < cur->size-1; i++) cur->children[i+1]=cur->children[i+2]; } cur->size--; } template bool BPlusTree::Delete(const KeyType key) { DataType temp; return Delete(key, temp); } template bool BPlusTree::Delete(const KeyType key, DataType &out) { if (root==0) return false; ReturnAction returnAction; returnAction.action=ReturnAction::NO_ACTION; int childIndex; bool underflow=false; if (root==leftmostLeaf) { if (GetIndexOf(key, root, &childIndex)==false) return false; out=root->data[childIndex]; DeleteFromPageAtIndex(childIndex,root); if (root->size==0) { pagePool.Release(root, _FILE_AND_LINE_); root=0; leftmostLeaf=0; } return true; } else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) return false; // RakAssert(returnAction.action==ReturnAction::NO_ACTION); if (underflow && root->size==0) { // Move the root down. Page *oldRoot=root; root=root->children[0]; pagePool.Release(oldRoot, _FILE_AND_LINE_); // memset(oldRoot,0,sizeof(root)); } return true; } template bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) { // Get index of child to follow. int branchIndex, childIndex; if (GetIndexOf(key, cur, &childIndex)) branchIndex=childIndex+1; else branchIndex=childIndex; // If child is not a leaf, call recursively if (cur->children[branchIndex]->isLeaf==false) { if (branchIndexsize) rightRootKey=cur->keys[branchIndex]; // Shift right to left else rightRootKey=cur->keys[branchIndex-1]; // Shift center to left if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) return false; // Call again in case the root key changed if (branchIndexsize) rightRootKey=cur->keys[branchIndex]; // Shift right to left else rightRootKey=cur->keys[branchIndex-1]; // Shift center to left if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) { returnAction->action=ReturnAction::NO_ACTION; cur->keys[childIndex]=returnAction->key1; if (branchIndexsize) rightRootKey=cur->keys[branchIndex]; // Shift right to left else rightRootKey=cur->keys[branchIndex-1]; // Shift center to left } } else { // If child is a leaf, get the index of the key. If the item is not found, cancel delete. if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) return false; // Delete: // Remove childIndex from the child at branchIndex out=cur->children[branchIndex]->data[childIndex]; DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); if (childIndex==0) { if (branchIndex>0) cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; if (branchIndex==0) { returnAction->action=ReturnAction::SET_BRANCH_KEY; returnAction->key1=cur->children[0]->keys[0]; } } if (cur->children[branchIndex]->size < order/2) *underflow=true; else *underflow=false; } // Fix underflow: if (*underflow) { *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); } return true; } template bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) { // Borrow from a neighbor that has excess. Page *source; Page *dest; if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) { dest=cur->children[branchIndex]; source=cur->children[branchIndex-1]; // Left has excess ShiftNodeRight(dest); if (dest->isLeaf) { dest->keys[0]=source->keys[source->size-1]; dest->data[0]=source->data[source->size-1]; } else { dest->children[0]=source->children[source->size]; dest->keys[0]=cur->keys[branchIndex-1]; } // Update the parent key for the child (middle) cur->keys[branchIndex-1]=source->keys[source->size-1]; source->size--; // if (branchIndex==0) // { // returnAction->action=ReturnAction::SET_BRANCH_KEY; // returnAction->key1=dest->keys[0]; // } // No underflow return false; } else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) { dest=cur->children[branchIndex]; source=cur->children[branchIndex+1]; // Right has excess if (dest->isLeaf) { dest->keys[dest->size]=source->keys[0]; dest->data[dest->size]=source->data[0]; // The first key in the leaf after shifting is the parent key for the right branch cur->keys[branchIndex]=source->keys[1]; #ifdef _MSC_VER #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant #endif if (order<=3 && dest->size==0) { if (branchIndex==0) { returnAction->action=ReturnAction::SET_BRANCH_KEY; returnAction->key1=dest->keys[0]; } else cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; } } else { if (returnAction->action==ReturnAction::NO_ACTION) { returnAction->action=ReturnAction::SET_BRANCH_KEY; returnAction->key1=dest->keys[0]; } dest->keys[dest->size]=rightRootKey; dest->children[dest->size+1]=source->children[0]; // The shifted off key is the leftmost key for a node cur->keys[branchIndex]=source->keys[0]; } dest->size++; ShiftNodeLeft(source); //cur->keys[branchIndex]=source->keys[0]; // returnAction->action=ReturnAction::SET_BRANCH_KEY; // returnAction->key1=dest->keys[dest->size-1]; // No underflow return false; } else { int sourceIndex; // If no neighbors have excess, merge two branches. // // To merge two leaves, just copy the data and keys over. // // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer if (branchIndexsize) { // Merge right child to current child and delete right child. dest=cur->children[branchIndex]; source=cur->children[branchIndex+1]; } else { // Move current child to left and delete current child dest=cur->children[branchIndex-1]; source=cur->children[branchIndex]; } // Merge if (dest->isLeaf) { for (sourceIndex=0; sourceIndexsize; sourceIndex++) { dest->keys[dest->size]=source->keys[sourceIndex]; dest->data[dest->size++]=source->data[sourceIndex]; } } else { // We want the tree root key of the source, not the current. dest->keys[dest->size]=rightRootKey; dest->children[dest->size++ + 1]=source->children[0]; for (sourceIndex=0; sourceIndexsize; sourceIndex++) { dest->keys[dest->size]=source->keys[sourceIndex]; dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; } } #ifdef _MSC_VER #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant #endif if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; if (branchIndexsize) { // Update the parent key, removing the source (right) DeleteFromPageAtIndex(branchIndex, cur); } else { if (branchIndex>0) { // Update parent key, removing the source (current) DeleteFromPageAtIndex(branchIndex-1, cur); } } if (branchIndex==0 && dest->isLeaf) { returnAction->action=ReturnAction::SET_BRANCH_KEY; returnAction->key1=dest->keys[0]; } if (source==leftmostLeaf) leftmostLeaf=source->next; if (source->isLeaf) { if (source->previous) source->previous->next=source->next; if (source->next) source->next->previous=source->previous; } // Free the source node pagePool.Release(source, _FILE_AND_LINE_); // memset(source,0,sizeof(root)); // Return underflow or not of parent. return cur->size < order/2; } } template void BPlusTree::ShiftNodeRight(Page *cur) { int i; for (i=cur->size; i>0; i--) cur->keys[i]=cur->keys[i-1]; if (cur->isLeaf) { for (i=cur->size; i>0; i--) cur->data[i]=cur->data[i-1]; } else { for (i=cur->size+1; i>0; i--) cur->children[i]=cur->children[i-1]; } cur->size++; } template void BPlusTree::ShiftNodeLeft(Page *cur) { int i; for (i=0; i < cur->size-1; i++) cur->keys[i]=cur->keys[i+1]; if (cur->isLeaf) { for (i=0; i < cur->size; i++) cur->data[i]=cur->data[i+1]; } else { for (i=0; i < cur->size; i++) cur->children[i]=cur->children[i+1]; } cur->size--; } template Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) { int i; if (cur->size < order) { for (i=cur->size; i > insertionIndex; i--) cur->keys[i]=cur->keys[i-1]; if (cur->isLeaf) { for (i=cur->size; i > insertionIndex; i--) cur->data[i]=cur->data[i-1]; } else { for (i=cur->size+1; i > insertionIndex+1; i--) cur->children[i]=cur->children[i-1]; } cur->keys[insertionIndex]=key; if (cur->isLeaf) cur->data[insertionIndex]=leafData; else cur->children[insertionIndex+1]=nodeData; cur->size++; } else { Page* newPage = pagePool.Allocate( _FILE_AND_LINE_ ); newPage->isLeaf=cur->isLeaf; if (cur->isLeaf) { newPage->next=cur->next; if (cur->next) cur->next->previous=newPage; newPage->previous=cur; cur->next=newPage; } int destIndex, sourceIndex; if (insertionIndex>=(order+1)/2) { destIndex=0; sourceIndex=order/2; for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) { newPage->keys[destIndex]=cur->keys[sourceIndex]; } newPage->keys[destIndex++]=key; for (; sourceIndex < order; sourceIndex++, destIndex++) { newPage->keys[destIndex]=cur->keys[sourceIndex]; } destIndex=0; sourceIndex=order/2; if (cur->isLeaf) { for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) { newPage->data[destIndex]=cur->data[sourceIndex]; } newPage->data[destIndex++]=leafData; for (; sourceIndex < order; sourceIndex++, destIndex++) { newPage->data[destIndex]=cur->data[sourceIndex]; } } else { for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) { newPage->children[destIndex]=cur->children[sourceIndex+1]; } newPage->children[destIndex++]=nodeData; // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys // skip past the last child for cur for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) { newPage->children[destIndex]=cur->children[sourceIndex+1]; } // the first key is the middle key. Remove it from the page and push it to the parent returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; returnAction->key1=newPage->keys[0]; for (int j=0; j < destIndex-1; j++) newPage->keys[j]=newPage->keys[j+1]; } cur->size=order/2; } else { destIndex=0; sourceIndex=(order+1)/2-1; for (; sourceIndex < order; sourceIndex++, destIndex++) newPage->keys[destIndex]=cur->keys[sourceIndex]; destIndex=0; if (cur->isLeaf) { sourceIndex=(order+1)/2-1; for (; sourceIndex < order; sourceIndex++, destIndex++) newPage->data[destIndex]=cur->data[sourceIndex]; } else { sourceIndex=(order+1)/2; for (; sourceIndex < order+1; sourceIndex++, destIndex++) newPage->children[destIndex]=cur->children[sourceIndex]; // the first key is the middle key. Remove it from the page and push it to the parent returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; returnAction->key1=newPage->keys[0]; for (int j=0; j < destIndex-1; j++) newPage->keys[j]=newPage->keys[j+1]; } cur->size=(order+1)/2-1; if (cur->size) { bool b = GetIndexOf(key, cur, &insertionIndex); (void) b; RakAssert(b==false); } else insertionIndex=0; InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); } newPage->size=destIndex; return newPage; } return 0; } template bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) { return childIndex>0 && cur->children[childIndex-1]->size void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) { Page *dest = cur->children[childIndex-1]; Page *source = cur->children[childIndex]; returnAction->key1=source->keys[0]; dest->keys[dest->size]=source->keys[0]; dest->data[dest->size]=source->data[0]; dest->size++; for (int i=0; i < source->size-1; i++) { source->keys[i]=source->keys[i+1]; source->data[i]=source->data[i+1]; } source->size--; cur->keys[childIndex-1]=source->keys[0]; returnAction->key2=source->keys[0]; } template bool BPlusTree::CanRotateRight(Page *cur, int childIndex) { return childIndex < cur->size && cur->children[childIndex+1]->size void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) { Page *dest = cur->children[childIndex+1]; Page *source = cur->children[childIndex]; returnAction->key1=dest->keys[0]; for (int i= dest->size; i > 0; i--) { dest->keys[i]=dest->keys[i-1]; dest->data[i]=dest->data[i-1]; } dest->keys[0]=source->keys[source->size-1]; dest->data[0]=source->data[source->size-1]; dest->size++; source->size--; cur->keys[childIndex]=dest->keys[0]; returnAction->key2=dest->keys[0]; } template Page* BPlusTree::GetLeafFromKey(const KeyType key) const { Page* cur = root; int childIndex; while (cur->isLeaf==false) { // When searching, if we match the exact key we go down the pointer after that index if (GetIndexOf(key, cur, &childIndex)) childIndex++; cur = cur->children[childIndex]; } return cur; } template Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) { int childIndex; int branchIndex; if (GetIndexOf(key, cur, &childIndex)) branchIndex=childIndex+1; else branchIndex=childIndex; Page* newPage; if (cur->isLeaf==false) { if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) { if (branchIndex==childIndex+1) { *success=false; return 0; // Already exists } if (CanRotateLeft(cur, branchIndex)) { returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; if (key > cur->children[branchIndex]->keys[0]) { RotateLeft(cur, branchIndex, returnAction); int insertionIndex; GetIndexOf(key, cur->children[branchIndex], &insertionIndex); InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); } else { // Move head element to left and replace it with key,data Page* dest=cur->children[branchIndex-1]; Page* source=cur->children[branchIndex]; returnAction->key1=source->keys[0]; returnAction->key2=key; dest->keys[dest->size]=source->keys[0]; dest->data[dest->size]=source->data[0]; dest->size++; source->keys[0]=key; source->data[0]=data; } cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; return 0; } else if (CanRotateRight(cur, branchIndex)) { returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) { RotateRight(cur, branchIndex, returnAction); int insertionIndex; GetIndexOf(key, cur->children[branchIndex], &insertionIndex); InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); } else { // Insert to the head of the right leaf instead and change our key returnAction->key1=cur->children[branchIndex+1]->keys[0]; InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); returnAction->key2=key; } cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; return 0; } } newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) { if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) cur->keys[branchIndex-1]=returnAction->key2; } if (newPage) { if (newPage->isLeaf==false) { RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); newPage->size--; return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); } else { return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); } } } else { if (branchIndex==childIndex+1) { *success=false; return 0; // Already exists } else { return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); } } return 0; } template bool BPlusTree::Insert(const KeyType key, const DataType &data) { if (root==0) { // Allocate root and make root a leaf root = pagePool.Allocate( _FILE_AND_LINE_ ); root->isLeaf=true; leftmostLeaf=root; root->size=1; root->keys[0]=key; root->data[0]=data; root->next=0; root->previous=0; } else { bool success=true; ReturnAction returnAction; returnAction.action=ReturnAction::NO_ACTION; Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); if (success==false) return false; if (newPage) { KeyType newKey; if (newPage->isLeaf==false) { // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); newKey=returnAction.key1; newPage->size--; } else newKey = newPage->keys[0]; // propagate the root Page* newRoot = pagePool.Allocate( _FILE_AND_LINE_ ); newRoot->isLeaf=false; newRoot->size=1; newRoot->keys[0]=newKey; newRoot->children[0]=root; newRoot->children[1]=newPage; root=newRoot; } } return true; } template void BPlusTree::ShiftKeysLeft(Page *cur) { int i; for (i=0; i < cur->size; i++) cur->keys[i]=cur->keys[i+1]; } template void BPlusTree::Clear(void) { if (root) { FreePages(); leftmostLeaf=0; root=0; } pagePool.Clear(_FILE_AND_LINE_); } template unsigned BPlusTree::Size(void) const { unsigned int count=0; DataStructures::Page *cur = GetListHead(); while (cur) { count+=cur->size; cur=cur->next; } return count; } template bool BPlusTree::IsEmpty(void) const { return root==0; } template bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const { RakAssert(page->size>0); int index, upperBound, lowerBound; upperBound=page->size-1; lowerBound=0; index = page->size/2; #ifdef _MSC_VER #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant #endif while (1) { if (key==page->keys[index]) { *out=index; return true; } else if (keykeys[index]) upperBound=index-1; else lowerBound=index+1; index=lowerBound+(upperBound-lowerBound)/2; if (lowerBound>upperBound) { *out=lowerBound; return false; // No match } } } template void BPlusTree::FreePages(void) { DataStructures::Queue *> queue; DataStructures::Page *ptr; int i; queue.Push(root, _FILE_AND_LINE_ ); while (queue.Size()) { ptr=queue.Pop(); if (ptr->isLeaf==false) { for (i=0; i < ptr->size+1; i++) queue.Push(ptr->children[i], _FILE_AND_LINE_ ); } pagePool.Release(ptr, _FILE_AND_LINE_); // memset(ptr,0,sizeof(root)); }; } template Page *BPlusTree::GetListHead(void) const { return leftmostLeaf; } template DataType BPlusTree::GetDataHead(void) const { return leftmostLeaf->data[0]; } template void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) { int count=0; DataStructures::Page *cur = GetListHead(); while (cur) { func(cur, count++); cur=cur->next; } } template void BPlusTree::ForEachData(void (*func)(DataType input, int index)) { int count=0,i; DataStructures::Page *cur = GetListHead(); while (cur) { for (i=0; i < cur->size; i++) func(cur->data[i], count++); cur=cur->next; } } template void BPlusTree::PrintLeaf(Page * leaf, int index) { int i; RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); for (i=0; i < leaf->size; i++) RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); } template void BPlusTree::PrintLeaves(void) { ForEachLeaf(PrintLeaf); } template void BPlusTree::ValidateTreeRecursive(Page *cur) { RakAssert(cur==root || cur->size>=order/2); if (cur->children[0]->isLeaf) { RakAssert(cur->children[0]->keys[0] < cur->keys[0]); for (int i=0; i < cur->size; i++) { RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); } } else { for (int i=0; i < cur->size+1; i++) ValidateTreeRecursive(cur->children[i]); } } template void BPlusTree::PrintGraph(void) { DataStructures::Queue *> queue; queue.Push(root,_FILE_AND_LINE_); queue.Push(0,_FILE_AND_LINE_); DataStructures::Page *ptr; int i,j; if (root) { RAKNET_DEBUG_PRINTF("%p(", root); for (i=0; i < root->size; i++) { RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); } RAKNET_DEBUG_PRINTF(") "); RAKNET_DEBUG_PRINTF("\n"); } while (queue.Size()) { ptr=queue.Pop(); if (ptr==0) RAKNET_DEBUG_PRINTF("\n"); else if (ptr->isLeaf==false) { for (i=0; i < ptr->size+1; i++) { RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); for (j=0; j < ptr->children[i]->size; j++) RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); RAKNET_DEBUG_PRINTF(") "); queue.Push(ptr->children[i],_FILE_AND_LINE_); } queue.Push(0,_FILE_AND_LINE_); RAKNET_DEBUG_PRINTF(" -- "); } } RAKNET_DEBUG_PRINTF("\n"); } } #ifdef _MSC_VER #pragma warning( pop ) #endif #endif // Code to test this hellish data structure. /* #include "DS_BPlusTree.hpp" #include // Handle underflow on root. If there is only one item left then I can go downwards. // Make sure I keep the leftmost pointer valid by traversing it // When I free a leaf, be sure to adjust the pointers around it. #include "Rand.hpp" void main(void) { DataStructures::BPlusTree btree; DataStructures::List haveList, removedList; int temp; int i, j, index; int testSize; bool b; for (testSize=0; testSize < 514; testSize++) { RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); for (i=0; i < testSize; i++) haveList.Insert(i); for (i=0; i < testSize; i++) { index=i+randomMT()%(testSize-i); temp=haveList[index]; haveList[index]=haveList[i]; haveList[i]=temp; } for (i=0; i