#ifndef BTREE_H #define BTREE_H //------------------ // BTreePage // A BTree page //------------------ struct PageHeader { long Order; // maximum # of page links in page long MaxKeys; // maximum # of keys in page long MinKeys; // minimum # of keys in page long NoOfKeys; // actual # of keys in page long KeySize; // maximum # of bytes in a key }; template struct BTreeNode { K m_Key; const D * m_pData; ULONG m_ulHash; BTreeNode * m_pNext; BTreeNode(); BTreeNode(const K& Key, const D* data); ~BTreeNode(); void operator = (const BTreeNode& node); }; template BTreeNode::BTreeNode() : m_pData(NULL), m_pNext(NULL), m_ulHash(0) { } template BTreeNode::BTreeNode(const K& Key, const D* pdata) : m_pData(pdata), m_pNext(NULL) { m_Key = Key; m_ulHash = Hash(Key); } template BTreeNode::~BTreeNode() { m_pNext = NULL; } template void BTreeNode::operator=(const BTreeNode& node) { m_Key = node.m_Key; m_pData = node.m_pData; m_ulHash = node.m_ulHash; m_pNext = node.m_pNext; } template struct BTreePage { PageHeader m_hdr; // header information BTreeNode * m_pNodes; BTreePage** m_ppLinks; BTreePage* m_pParent; BTreePage(long ord); BTreePage(const BTreePage & p); ~BTreePage(); void operator = (const BTreePage & page); void DeleteAllNodes(); void CopyNodes(BTreeNode* pDestNodes, BTreeNode* pSrcNodes, long cnt); }; template BTreePage::BTreePage(long ord) { m_hdr.Order = ord; m_hdr.MaxKeys = ord - 1; m_hdr.MinKeys = ord / 2; m_hdr.NoOfKeys = 0; m_hdr.KeySize = sizeof(K); if (m_hdr.Order == 0) { m_pNodes = NULL; m_ppLinks = NULL; return; } // allocate key array m_pNodes = new BTreeNode [m_hdr.MaxKeys]; ASSERT(m_pNodes, "Couldn't allocate nodes array!"); memset(m_pNodes,0,m_hdr.MaxKeys * sizeof(BTreeNode)); m_ppLinks = new BTreePage*[m_hdr.MaxKeys + 1]; ASSERT(m_ppLinks, "Couldn't allocate limks array!"); memset(m_ppLinks,0,((m_hdr.MaxKeys + 1)* sizeof(BTreePage*))); m_pParent = NULL; } template BTreePage::BTreePage(const BTreePage & pg) { m_hdr = pg.m_hdr; // allocate key array m_pNodes = new BTreeNode[m_hdr.MaxKeys]; ASSERT(m_pNodes, "Couldn't allocate nodes array!"); CopyNodes(m_pNodes, pg.m_pNodes, m_hdr.Order); for (int i = 0; i < m_hdr.MaxKeys + 1; i++) m_ppLinks[i] = pg.m_ppLinks[i]; m_pParent = pg.m_pParent; } template BTreePage::~BTreePage() { // delete old buffers DeleteAllNodes(); delete [] m_ppLinks; } template void BTreePage::operator = (const BTreePage & pg) { // allocate key array if (m_pNodes!= NULL) DeleteAllNodes(); m_pNodes = new BTreeNode [pg.m_hdr.MaxKeys]; ASSERT(m_pNodes, "Couldn't allocate nodes array!"); if (m_ppLinks) delete [] m_ppLinks; m_ppLinks = new BTreePage*[pg.m_hdr.MaxKeys + 1]; ASSERT(m_ppLinks, "Couldn't allocate links array!"); m_hdr = pg.m_hdr; CopyNodes(m_pNodes, pg.m_pNodes, m_hdr.Order); for (int i = 0; i < m_hdr.MaxKeys + 1; i++) m_ppLinks[i] = pg.m_ppLinks[i]; m_pParent = pg.m_pParent; } template void BTreePage::DeleteAllNodes() { for (int i = 0; i < m_hdr.NoOfKeys; i++) { BTreeNode* pIndex = m_pNodes[i].m_pNext; while(pIndex) { BTreeNode* pDel = pIndex; pIndex = pIndex->m_pNext; delete pDel; } } delete [] m_pNodes; m_pNodes = NULL; } template void BTreePage::CopyNodes(BTreeNode* pDestNodes, BTreeNode* pSrcNodes, long cnt) { for (int i = 0; i < cnt; i++) { pDestNodes[i] = pSrcNodes[i]; BTreeNode* pSrcIndex = pSrcNodes[i].m_pNext; BTreeNode* pDestIndex = pDestNodes; while(pSrcIndex) { pDestIndex->m_pNext = new BTreeNode(*pSrcIndex); pSrcIndex = pSrcIndex->m_pNext; pDestIndex = pDestIndex->m_pNext; } } } //----------------------------------------------- // BTree // A DataFile that uses a BTree for indexing // NOTE: No copy semantics will exist for a btree //----------------------------------------------- template class BTree { public: BTree(long ord); // new BTree(long ord, int (*compare)(const K& key1, const K& key2)); ~BTree(); void Insert(const K & key, const D* data); const D* Get(const K & key); void Delete(const K & key); void InOrder(void (* func)(const K & key, const D* pdata, int depth, int index)); void Clear(); private: // data members BTreePage* m_pRoot; // root page (always in memory) void (* TravFunc)(const K & key, const D* pdata, int depth, int index); int (*CompFunc) (const K& key1, const K& key2); // search for a node BOOL Search(BTreePage* ppg, const ULONG& thash, const K& searchkey, BTreePage** ppkeypage, long & pos); // insert node into leaf void InsertKey(const K & inskey, const D* pdata); // promote a key into a parent node void PromoteInternal(BTreePage* ppg, BTreeNode & node, BTreePage* pgrtrpage); // promote a key by creating a new root void PromoteRoot(BTreeNode & node, BTreePage* plesspage, BTreePage* pgrtrpage); // adjust tree if leaf has shrunk in size void AdjustTree(BTreePage* pleafpg); // redistribute keys among siblings and parent void Redistribute(long keypos, BTreePage* plesspage, BTreePage* pparpage, BTreePage* pgrtrpage); // concatenate sibling pages void Concatenate(long keypos, BTreePage* plesspage, BTreePage* pparpage, BTreePage* pgrtrpage); // recursive traversal function used by InOrder void RecurseTraverse(const BTreePage* ppg, int depth); // recursively delete a page and all it's sub pages; void DeletePage(BTreePage* ppg); }; template BTree::BTree(long ord) { CompFunc = NULL; m_pRoot = new BTreePage(ord); } template BTree::BTree(long ord, int (*comp)(const K& key1, const K& key2)) { CompFunc = comp; m_pRoot = new BTreePage(ord); } template BTree::~BTree() { DeletePage(m_pRoot); } template void BTree::Insert(const K & key, const D* pdb) { // store the key in a page InsertKey(key,pdb); } template const D* BTree::Get(const K & key) { BTreePage* pgetpage = NULL; long getpos; if (Search(m_pRoot, Hash(key), key, &pgetpage, getpos)) { BOOL found = FALSE; BTreeNode* pnode = &pgetpage->m_pNodes[getpos]; if (CompFunc) { while(pnode && !found) { if (CompFunc(key, pnode->m_Key) == 0) { found = TRUE; return pnode->m_pData; } pnode = pnode->m_pNext; } } else { while(pnode && !found) { if (key == pnode->m_Key) { found = TRUE; return pnode->m_pData; } pnode = pnode->m_pNext; } } } else { return NULL; } return NULL; } template void BTree::Delete(const K & delkey) { BTreePage* pdelpage = NULL; long delpos; if (!Search(m_pRoot, Hash(delkey), delkey, &pdelpage, delpos)) { return; } if (!pdelpage->m_ppLinks[0]) // is this a leaf page? { //Delete all linked nodes. BOOL bFound = FALSE; BOOL bDelNode = FALSE; BTreeNode* pDelNode = (pdelpage->m_pNodes + delpos); BTreeNode* pIndex = pDelNode; while(pDelNode && !bFound) { if ((CompFunc && (CompFunc(delkey, pDelNode->m_Key) == 0)) || (!CompFunc && (delkey == pDelNode->m_Key))) { //If the node we need to delete is the head node in the list AND it's the only node //then we need to skip to the routine below to delete it from the tree. // + delpos if (pDelNode == (pdelpage->m_pNodes + delpos)) { if (!pDelNode->m_pNext) { bDelNode = TRUE; } else { pDelNode = pDelNode->m_pNext; *pIndex = *pDelNode; delete pDelNode; pDelNode = NULL; } } else { pIndex->m_pNext = pDelNode->m_pNext; delete pDelNode; pDelNode = NULL; } bFound = TRUE; } else { pIndex = pDelNode; pDelNode = pDelNode->m_pNext; } } if (bDelNode) { --pdelpage->m_hdr.NoOfKeys; // remove key from leaf for (long n = delpos; n < pdelpage->m_hdr.NoOfKeys; ++n) { pdelpage->m_pNodes[n] = pdelpage->m_pNodes[n + 1]; } memset((void*)&pdelpage->m_pNodes[pdelpage->m_hdr.NoOfKeys], 0, sizeof(BTreeNode)); // adjust tree if (pdelpage->m_hdr.NoOfKeys < pdelpage->m_hdr.MinKeys) AdjustTree(pdelpage); } } else // delpage is internal { // replace deleted key with immediate successor BTreePage* psucpage = NULL; // find successor psucpage = pdelpage->m_ppLinks[delpos + 1]; while (psucpage->m_ppLinks[0]) psucpage = psucpage->m_ppLinks[0]; //Delete all linked nodes. BOOL bFound = FALSE; BOOL bDelNode = FALSE; BTreeNode* pDelNode = (pdelpage->m_pNodes + delpos); BTreeNode* pIndex = pDelNode; while(pDelNode && !bFound) { if ((CompFunc && (CompFunc(delkey, pDelNode->m_Key) == 0)) || (!CompFunc && (delkey == pDelNode->m_Key))) { //If the node we need to delete is the head node in the list AND it's the only node //then we need to skip to the routine below to delete it from the tree. if (pDelNode == (pdelpage->m_pNodes + delpos)) { if (!pDelNode->m_pNext) { bDelNode = TRUE; } else { pDelNode = pDelNode->m_pNext; pdelpage->m_pNodes[delpos].operator=(*pDelNode); delete pDelNode; } } else { pIndex->m_pNext = pDelNode->m_pNext; delete pDelNode; pDelNode = NULL; } bFound = TRUE; } else { pIndex = pDelNode; pDelNode = pDelNode->m_pNext; } } if (bDelNode) { // first key is the "swappee" pdelpage->m_pNodes[delpos] = psucpage->m_pNodes[0]; // deleted swapped key from sucpage --psucpage->m_hdr.NoOfKeys; for (long n = 0; n < psucpage->m_hdr.NoOfKeys; ++n) { psucpage->m_pNodes[n] = psucpage->m_pNodes[n + 1]; psucpage->m_ppLinks[n + 1] = psucpage->m_ppLinks[n + 2]; } memset((void*)&psucpage->m_pNodes[psucpage->m_hdr.NoOfKeys], 0, sizeof(BTreeNode)); psucpage->m_ppLinks[psucpage->m_hdr.NoOfKeys + 1] = NULL; // adjust tree for leaf node if (psucpage->m_hdr.NoOfKeys < psucpage->m_hdr.MinKeys) AdjustTree(psucpage); } } } template void BTree::InOrder(void (* func)(const K & key, const D* pdata, int depth, int index)) { // save the address of the function to call TravFunc = func; // recurse the tree RecurseTraverse(m_pRoot, 0); } template void BTree::Clear() { DeletePage(m_pRoot); } template BOOL BTree::Search(BTreePage* ppg, const ULONG& thash, const K& searchkey, BTreePage** ppkeypage, long & pos) { BOOL result; pos = 0; for (;;) { if (pos == ppg->m_hdr.NoOfKeys) goto getpage; if (ppg->m_pNodes[pos].m_ulHash == thash) { *ppkeypage = (BTreePage*)ppg; result = TRUE; break; } else { if (ppg->m_pNodes[pos].m_ulHash < thash) ++pos; else { // I know this is a label -- so shoot me! getpage: // if we're in a leaf page, key wasn't found if (!ppg->m_ppLinks[pos]) { *ppkeypage = (BTreePage*)ppg; result = FALSE; } else { result = Search(ppg->m_ppLinks[pos],thash, searchkey,ppkeypage,pos); } break; } } } return result; } template void BTree::InsertKey(const K & inskey, const D* pdata) { BTreePage* pinspage = NULL; long inspos; BTreeNode newnode(inskey, pdata); BOOL bFound = Search(m_pRoot,Hash(inskey), inskey,&pinspage,inspos); if (bFound) { BOOL found = FALSE; BTreeNode* pnode = &(pinspage->m_pNodes[inspos]); BTreeNode* pparent = NULL; if (CompFunc != NULL) { while(pnode && !found) { if (CompFunc(inskey, pnode->m_Key) == 0) { found = TRUE; } pparent = pnode; pnode = pnode->m_pNext; } } else { while(pnode && !found) { if (inskey == pnode->m_Key) { found = TRUE; } pparent = pnode; pnode = pnode->m_pNext; } } if (found) { return; } pparent->m_pNext = new BTreeNode(inskey, pdata); } else { if (pinspage->m_hdr.NoOfKeys == pinspage->m_hdr.MaxKeys) { // temporary arrays BTreeNode* ptempkeys = new BTreeNode[pinspage->m_hdr.MaxKeys + 1]; // copy entries from inspage to temporaries long nt = 0; // index into temporaries long ni = 0; // index into inspage ptempkeys[inspos] = newnode; while (ni < pinspage->m_hdr.MaxKeys) { if (ni == inspos) ++nt; ptempkeys[nt] = pinspage->m_pNodes[ni]; ++ni; ++nt; } // generate a new leaf node BTreePage* psibpage = new BTreePage(pinspage->m_hdr.Order); psibpage->m_pParent = pinspage->m_pParent; // clear # of keys in pages pinspage->m_hdr.NoOfKeys = 0; psibpage->m_hdr.NoOfKeys = 0; // copy appropriate keys from temp to pages for (ni = 0; ni < pinspage->m_hdr.MinKeys; ++ni) { pinspage->m_pNodes[ni] = ptempkeys[ni]; ++pinspage->m_hdr.NoOfKeys; } for (ni = pinspage->m_hdr.MinKeys + 1; ni <= pinspage->m_hdr.MaxKeys; ++ni) { psibpage->m_pNodes[ni - 1 - pinspage->m_hdr.MinKeys] = ptempkeys[ni]; ++(psibpage->m_hdr.NoOfKeys); } // Fill any remaining entries in inspage with null. // Note that sibpage is initialized to null values // by the constructor. for (ni = pinspage->m_hdr.MinKeys; ni < pinspage->m_hdr.MaxKeys; ++ni) { memset((void*)&pinspage->m_pNodes[ni],0,sizeof(BTreeNode)); } // promote key and pointer if (!pinspage->m_pParent) { // we need to create a new root PromoteRoot(ptempkeys[pinspage->m_hdr.MinKeys], pinspage, psibpage); } else { BTreePage* pparpage; pparpage = pinspage->m_pParent; // promote into parent PromoteInternal(pparpage, ptempkeys[pinspage->m_hdr.MinKeys], psibpage); } delete [] ptempkeys; } else // simply insert new key and data ptr { for (long n = pinspage->m_hdr.NoOfKeys; n > inspos; --n) { pinspage->m_pNodes[n] = pinspage->m_pNodes[n - 1]; } pinspage->m_pNodes[inspos] = newnode; ++pinspage->m_hdr.NoOfKeys; } } } template void BTree::PromoteInternal(BTreePage* pinspage, BTreeNode & node, BTreePage* pgrtrpage) { if (pinspage->m_hdr.NoOfKeys == pinspage->m_hdr.MaxKeys) { // temporary arrays BTreeNode * ptempkeys = new BTreeNode[pinspage->m_hdr.MaxKeys + 1]; BTreePage** ptemplnks = new BTreePage*[pinspage->m_hdr.Order + 1]; // copy entries from inspage to temporaries long nt = 0; // index into temporaries long ni = 0; // index into inspage ptemplnks[0] = pinspage->m_ppLinks[0]; long inspos = 0; // find insertion position while ((inspos < pinspage->m_hdr.MaxKeys) && (pinspage->m_pNodes[inspos].m_ulHash < node.m_ulHash)) ++inspos; // store new info ptempkeys[inspos] = node; ptemplnks[inspos + 1] = pgrtrpage; // copy existing keys while (ni < pinspage->m_hdr.MaxKeys) { if (ni == inspos) ++nt; ptempkeys[nt] = pinspage->m_pNodes[ni]; ptemplnks[nt + 1] = pinspage->m_ppLinks[ni + 1]; ++ni; ++nt; } // generate a new leaf node BTreePage* psibpage = new BTreePage(pinspage->m_hdr.Order); psibpage->m_pParent = pinspage->m_pParent; // clear # of keys in pages pinspage->m_hdr.NoOfKeys = 0; psibpage->m_hdr.NoOfKeys = 0; pinspage->m_ppLinks[0] = ptemplnks[0]; // copy appropriate keys from temp to pages for (ni = 0; ni < pinspage->m_hdr.MinKeys; ++ni) { pinspage->m_pNodes[ni] = ptempkeys[ni]; pinspage->m_ppLinks[ni + 1] = ptemplnks[ni + 1]; ++pinspage->m_hdr.NoOfKeys; } psibpage->m_ppLinks[0] = ptemplnks[pinspage->m_hdr.MinKeys + 1]; for (ni = pinspage->m_hdr.MinKeys + 1; ni <= pinspage->m_hdr.MaxKeys; ++ni) { psibpage->m_pNodes[ni - 1 - pinspage->m_hdr.MinKeys] = ptempkeys[ni]; psibpage->m_ppLinks[ni - pinspage->m_hdr.MinKeys] = ptemplnks[ni + 1]; ++psibpage->m_hdr.NoOfKeys; } // Fill any remaining entries in inspage with null. // Note that sibpage is initialized to null values // by the constructor. for (ni = pinspage->m_hdr.MinKeys; ni < pinspage->m_hdr.MaxKeys; ++ni) { memset((void*)&pinspage->m_pNodes[ni],0, sizeof(BTreeNode)); pinspage->m_ppLinks[ni + 1] = NULL; } // update child parent links BTreePage* pchild; for (ni = 0; ni <= psibpage->m_hdr.NoOfKeys; ++ni) { pchild = psibpage->m_ppLinks[ni]; pchild->m_pParent= psibpage; } // promote key and pointer if (!pinspage->m_pParent) { // we need to create a new root PromoteRoot(ptempkeys[pinspage->m_hdr.MinKeys], pinspage, psibpage); } else { BTreePage* pparpage; pparpage = pinspage->m_pParent; // promote into parent PromoteInternal(pparpage, ptempkeys[pinspage->m_hdr.MinKeys], psibpage); } delete [] ptempkeys; delete [] ptemplnks; } else // simply insert new key and data ptr { long inspos = 0; // find insertion position while ((inspos < pinspage->m_hdr.NoOfKeys) && (pinspage->m_pNodes[inspos].m_ulHash < node.m_ulHash)) ++inspos; // shift any keys right for (long n = pinspage->m_hdr.NoOfKeys; n > inspos; --n) { pinspage->m_pNodes[n] = pinspage->m_pNodes[n - 1]; pinspage->m_ppLinks[n + 1] = pinspage->m_ppLinks[n]; } // store new info pinspage->m_pNodes[inspos] = node; pinspage->m_ppLinks[inspos + 1] = pgrtrpage; ++pinspage->m_hdr.NoOfKeys; } } template void BTree::PromoteRoot(BTreeNode & node, BTreePage * plesspage, BTreePage * pgrtrpage) { // create new root page BTreePage* pnewroot = new BTreePage(m_pRoot->m_hdr.Order); // insert key into new root pnewroot->m_pNodes[0] = node; pnewroot->m_ppLinks[0] = plesspage; pnewroot->m_ppLinks[1] = pgrtrpage; pnewroot->m_hdr.NoOfKeys = 1; m_pRoot = pnewroot; plesspage->m_pParent = m_pRoot; pgrtrpage->m_pParent = m_pRoot; } template void BTree::AdjustTree(BTreePage* ppg) { if (!ppg->m_pParent) return; BTreePage* pparpage = ppg->m_pParent; BTreePage* psibless = NULL; BTreePage* psibgrtr = NULL; // find pointer to pg in parent for (long n = 0; pparpage->m_ppLinks[n] != ppg; ++n) ; // read sibling pages if (n < pparpage->m_hdr.NoOfKeys) psibgrtr = pparpage->m_ppLinks[n + 1]; if (n > 0) psibless = pparpage->m_ppLinks[n - 1]; if (!psibgrtr && !psibless) return; // decide to redistribute or concatenate if (!psibgrtr || (psibgrtr && psibless && (psibless->m_hdr.NoOfKeys > psibgrtr->m_hdr.NoOfKeys))) { --n; if (psibless->m_hdr.NoOfKeys > psibless->m_hdr.MinKeys) Redistribute(n,psibless,pparpage,ppg); else Concatenate(n,psibless,pparpage,ppg); } else if (psibgrtr) { if (psibgrtr->m_hdr.NoOfKeys > psibgrtr->m_hdr.MinKeys) Redistribute(n,ppg,pparpage,psibgrtr); else Concatenate(n,ppg,pparpage,psibgrtr); } } template void BTree::Redistribute(long keypos, BTreePage* plesspage, BTreePage* pparpage, BTreePage* pgrtrpage) { // note: this function is ONLY called for leaf nodes! long n; if (!plesspage->m_ppLinks[0]) // working with leaves { if (plesspage->m_hdr.NoOfKeys > pgrtrpage->m_hdr.NoOfKeys) { // slide a key from lesser to greater // move keys in greater to the left by one for (n = pgrtrpage->m_hdr.NoOfKeys; n > 0; --n) { pgrtrpage->m_pNodes[n] = pgrtrpage->m_pNodes[n - 1]; } // store parent separator key in greater page pgrtrpage->m_pNodes[0] = pparpage->m_pNodes[keypos]; // increment greater page's key count ++pgrtrpage->m_hdr.NoOfKeys; // decrement lessor page's key count --plesspage->m_hdr.NoOfKeys; // move last key in less page to parent as separator pparpage->m_pNodes[keypos] = plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys]; // clear last key in less page memset((void*)&plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys], 0, sizeof(BTreeNode)); } else { // slide a key from greater to lessor // add parent key to lessor page plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys] = pparpage->m_pNodes[keypos]; // increment lessor page's key count ++plesspage->m_hdr.NoOfKeys; // insert in parent the lowest key in greater page pparpage->m_pNodes[keypos] = pgrtrpage->m_pNodes[0]; // decrement # of keys in greater page --pgrtrpage->m_hdr.NoOfKeys; // move keys in greater page to left for (n = 0; n < pgrtrpage->m_hdr.NoOfKeys; ++n) { pgrtrpage->m_pNodes[n] = pgrtrpage->m_pNodes[n + 1]; } // make last key blank memset((void*)&pgrtrpage->m_pNodes[n], 0, sizeof(BTreeNode)); } } else { if (plesspage->m_hdr.NoOfKeys > pgrtrpage->m_hdr.NoOfKeys) { // slide a key from lesser to greater // move keys in greater to the left by one for (n = pgrtrpage->m_hdr.NoOfKeys; n > 0; --n) { pgrtrpage->m_pNodes[n] = pgrtrpage->m_pNodes[n - 1]; pgrtrpage->m_ppLinks[n + 1] = pgrtrpage->m_ppLinks[n]; } pgrtrpage->m_ppLinks[1] = pgrtrpage->m_ppLinks[0]; // store parent separator key in greater page pgrtrpage->m_pNodes[0] = pparpage->m_pNodes[keypos]; pgrtrpage->m_ppLinks[0] = plesspage->m_ppLinks[plesspage->m_hdr.NoOfKeys]; // update child link BTreePage* pchild; pchild = pgrtrpage->m_ppLinks[0]; pchild->m_pParent= pgrtrpage; // increment greater page's key count ++pgrtrpage->m_hdr.NoOfKeys; // decrement lessor page's key count --plesspage->m_hdr.NoOfKeys; // move last key in less page to parent as separator pparpage->m_pNodes[keypos] = plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys]; // clear last key in less page memset((void*)&plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys], 0, sizeof(BTreeNode)); plesspage->m_ppLinks[plesspage->m_hdr.NoOfKeys + 1] = NULL; } else { // slide a key from greater to lessor // add parent key to lessor page plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys] = pparpage->m_pNodes[keypos]; plesspage->m_ppLinks[plesspage->m_hdr.NoOfKeys + 1] = pgrtrpage->m_ppLinks[0]; // update child link BTreePage* pchild; pchild = pgrtrpage->m_ppLinks[0]; pchild->m_pParent = plesspage; // increment lessor page's key count ++plesspage->m_hdr.NoOfKeys; // insert in parent the lowest key in greater page pparpage->m_pNodes[keypos] = pgrtrpage->m_pNodes[0]; // decrement # of keys in greater page --pgrtrpage->m_hdr.NoOfKeys; // move keys in greater page to left for (n = 0; n < pgrtrpage->m_hdr.NoOfKeys; ++n) { pgrtrpage->m_pNodes[n] = pgrtrpage->m_pNodes[n + 1]; pgrtrpage->m_ppLinks[n] = pgrtrpage->m_ppLinks[n + 1]; } pgrtrpage->m_ppLinks[n] = pgrtrpage->m_ppLinks[n + 1]; // make last key blank memset((void*)&pgrtrpage->m_pNodes[n], 0, sizeof(BTreeNode)); pgrtrpage->m_ppLinks[n + 1] = NULL; } } if (!pparpage->m_pParent) m_pRoot = pparpage; } template void BTree::Concatenate(long keypos, BTreePage* plesspage, BTreePage* pparpage, BTreePage* pgrtrpage) { long n, ng; // move separator key from parent into lesspage plesspage->m_pNodes[plesspage->m_hdr.NoOfKeys] = pparpage->m_pNodes[keypos]; plesspage->m_ppLinks[plesspage->m_hdr.NoOfKeys + 1] = pgrtrpage->m_ppLinks[0]; ++plesspage->m_hdr.NoOfKeys; // delete separator from parent --pparpage->m_hdr.NoOfKeys; for (n = keypos; n < pparpage->m_hdr.NoOfKeys; ++n) { pparpage->m_pNodes[n] = pparpage->m_pNodes[n + 1]; pparpage->m_ppLinks[n + 1] = pparpage->m_ppLinks[n + 2]; } // clear unused key in parent memset((void*)&pparpage->m_pNodes[n], 0, sizeof(BTreeNode)); pparpage->m_ppLinks[n + 1] = NULL; // copy keys from grtrpage to lesspage ng = 0; n = plesspage->m_hdr.NoOfKeys; while (ng < pgrtrpage->m_hdr.NoOfKeys) { ++plesspage->m_hdr.NoOfKeys; plesspage->m_pNodes[n] = pgrtrpage->m_pNodes[ng]; memset((void*)&pgrtrpage->m_pNodes[ng], 0, sizeof(BTreeNode)); plesspage->m_ppLinks[n + 1] = pgrtrpage->m_ppLinks[ng + 1]; pgrtrpage->m_ppLinks[ng + 1] = NULL; ++ng; ++n; } delete pgrtrpage; // is this a leaf page? if (plesspage->m_ppLinks[0]) { // adjust child pointers to point to less page BTreePage* pchild; for (n = 0; n <= plesspage->m_hdr.NoOfKeys; ++n) { pchild = plesspage->m_ppLinks[n]; pchild->m_pParent = plesspage; } } // write less page and parent if (pparpage->m_hdr.NoOfKeys == 0) { AdjustTree(pparpage); plesspage->m_pParent = pparpage->m_pParent; if (!plesspage->m_pParent) m_pRoot = plesspage; else { for (int n = 0; n <= pparpage->m_pParent->m_hdr.NoOfKeys; n++) { if (pparpage == pparpage->m_pParent->m_ppLinks[n]) { pparpage->m_pParent->m_ppLinks[n] = plesspage; break; } } } delete pparpage; } else { // reset root page, if necessary if (!pparpage->m_pParent) m_pRoot = pparpage; // if parent is too small, adjust tree! if (pparpage->m_hdr.NoOfKeys < pparpage->m_hdr.MinKeys) AdjustTree(pparpage); } } template void BTree::RecurseTraverse(const BTreePage* ppg, int depth) { long n; BTreePage* p = NULL; depth++; // sequence through keys in page, recursively processing links for (n = 0; n < ppg->m_hdr.NoOfKeys; ++n) { // follow each link before processing page if (ppg->m_ppLinks[n]) { p = ppg->m_ppLinks[n]; RecurseTraverse(p, depth); } int index = 0; BTreeNode* p = &ppg->m_pNodes[n]; while(p) { TravFunc(p->m_Key, p->m_pData, depth, index); index++; p = p->m_pNext; } } // handle greatest subtree link if ((ppg->m_ppLinks != NULL) && ppg->m_ppLinks[n]) { p = ppg->m_ppLinks[n]; RecurseTraverse(p, depth); } } template void BTree::DeletePage(BTreePage* ppg) { long n; BTreePage* p = NULL; if (!ppg) return; // sequence through keys in page, recursively processing links for (n = 0; n < ppg->m_hdr.NoOfKeys; ++n) { // follow each link before processing page if (ppg->m_ppLinks[n]) { p = ppg->m_ppLinks[n]; DeletePage(p); ppg->m_ppLinks[n] = NULL; } } // handle greatest subtree link if ((ppg->m_ppLinks != NULL) && ppg->m_ppLinks[n]) { p = ppg->m_ppLinks[n]; DeletePage(p); ppg->m_ppLinks[n] = NULL; } delete ppg; } #endif