diff --git a/btree.cpp b/btree.cpp index f404337..98f026f 100644 --- a/btree.cpp +++ b/btree.cpp @@ -1,23 +1,130 @@ +#include #include #include #include +#include #include "btree.h" +#include "btree_types.h" #include "btree_consts.h" +#include "btree_standardVFS.h" bool operator ==(uPtr u1, uPtr u2) { return (u1.offset == u2.offset); } // overloaded operator == -bTree::bTree(char * filename, uInt32 nodeSize, treeTypes tt, bTreeVFS * virtualFS) { - filename = filename; - nodeSize = nodeSize; - tt = tt; +bTree::bTree(const char * filename, uInt32 nodeSize, treeTypes tt, bTreeVFS * virtualFS) { + memoryTree = (filename != NULL && *filename != '\0'); + + info = new bTreeInfo; + assert(info); + memset(info, 0, sizeof(struct bTreeInfo)); + vfs = virtualFS; + + info->magic = B_NODE_TREEINFO; + info->root.offset = 0; // should be set with setNull() !!! + info->firstDeleted.offset = 0; // should be set with setNull() !!! + info->nodes = 0; + info->height = 0; + info->keys = 0; + info->bNodeSize = nodeSize; + info->treeType = tt; + + if (!memoryTree) { + + if (vfs == NULL) vfs = new StandardVFS(); + + if (vfs->fExist(filename)) { + if (!vfs->fOpen(filename)) exit(42); + if (!vfs->fRead(info, sizeof(bTreeInfo))) exit(42); + } else { + if (!vfs->fCreate(filename)) exit(42); + if (!vfs->fWrite(info, sizeof(bTreeInfo))) exit(42); + } + + } // if not memoryTree + + switch (info->treeType) { + case BT_CUSTOM: + InstallUserFunctions(compareKeyNull, copyKeyNull, keySizeNull); + break; + case BT_PCHAR: + InstallUserFunctions(compareKeyPChar, copyKeyPChar, keySizePChar); + break; + case BT_STRING: + InstallUserFunctions(compareKeyString, copyKeyString, keySizeString); + break; + case BT_SINGLE: + InstallUserFunctions(compareKeySingle, copyKeySingle, keySizeSingle); + break; + case BT_DOUBLE: + InstallUserFunctions(compareKeyDouble, copyKeyDouble, keySizeDouble); + break; + case BT_INT32: + InstallUserFunctions(compareKeyInt32, copyKeyInt32, keySizeInt32); + break; + case BT_INT64: + InstallUserFunctions(compareKeyInt64, copyKeyInt64, keySizeInt64); + break; + default: + InstallUserFunctions(compareKeyNull, copyKeyNull, keySizeNull); + break; + } // switch + + treeChanged = false; return; } // bTree::bTree +bTree::bTree(const char * filename, bTreeVFS * virtualFS) { + info = NULL; + memoryTree = false; + treeChanged = false; + InstallUserFunctions(compareKeyNull, copyKeyNull, keySizeNull); + + vfs = virtualFS; + + if (vfs == NULL) vfs = new StandardVFS(); + if (!vfs->fExist(filename)) { + assert(false); + return; + } // if filename doesn't exist + + info = new bTreeInfo; + memset(info, 0, sizeof(bTreeInfo)); + if (!vfs->fOpen(filename)) exit(42); + if (!vfs->fRead(info, sizeof(bTreeInfo))) exit(42); + if (info->magic != B_NODE_TREEINFO) exit(42); + + switch (info->treeType) { + case BT_CUSTOM: + InstallUserFunctions(compareKeyNull, copyKeyNull, keySizeNull); + break; + case BT_PCHAR: + InstallUserFunctions(compareKeyPChar, copyKeyPChar, keySizePChar); + break; + case BT_STRING: + InstallUserFunctions(compareKeyString, copyKeyString, keySizeString); + break; + case BT_SINGLE: + InstallUserFunctions(compareKeySingle, copyKeySingle, keySizeSingle); + break; + case BT_DOUBLE: + InstallUserFunctions(compareKeyDouble, copyKeyDouble, keySizeDouble); + break; + case BT_INT32: + InstallUserFunctions(compareKeyInt32, copyKeyInt32, keySizeInt32); + break; + case BT_INT64: + InstallUserFunctions(compareKeyInt64, copyKeyInt64, keySizeInt64); + break; + default: + InstallUserFunctions(compareKeyNull, copyKeyNull, keySizeNull); + break; + } // switch +} // bTree::bTree + int bTree::align(int keyLength) { return ((sizeof(uPtr) + keyLength + 7) >> 3) << 3; @@ -71,8 +178,11 @@ for (unsigned int curNode = 1; curNode <= node->numKeys; curNode++) { size += align(keySize(&dataNode->key)); -//mjikaboom: possibly this doesn't adjust by bytes. - dataNode += align(keySize(&dataNode->key)); + + uInt8 * tmpPtr = (uInt8 *)dataNode; + tmpPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *)tmpPtr; + } // for curNode return size; } // bTree::calcSize @@ -84,7 +194,7 @@ node->magic = B_NODE_OPEN; // these should really be setNull() calls, but, as mentioned elsewhere - // GNU is evil and crappy. + // GNU is evil and likes to put kittens in blenders. node->left.offset = 0; node->right.offset = 0; @@ -98,6 +208,91 @@ free(node); } // bTree::deAllocNode +uPtr +bTree::deleteEntry(uPtr u, void * key, uPtr value) { + TbNode * bnode; + TbNode * tmpNode; + TbNode * neighbor; + TbNode * parent; + + TbNodeData * dataNode; + TbNodeData * nextDataNode; + TbNodeData * tmpDataNode; + + void * src; + void * dst; + + uInt8 * convPtr = NULL; + uPtr result, tmpLink; + + uInt32 nodeSize, tmpTag; + unsigned int curNode; + int res, size, neighborSize; + + bool found; + + setNull(result); + + if (isNull(u)) return result; + + bnode = loadPage(u); + + assert(bnode->numKeys != 0); // this should never happen + + if (bnode->magic == B_NODE_LEAF) { + dataNode = &bnode->data; + curNode = 0; + + do { + res = compareKeys(&dataNode->key, key); + if (res >= 0) break; + convPtr = (uInt8 *)dataNode; + convPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *)convPtr; + curNode++; + } while (curNode != bnode->numKeys); + + if (res != 0) { +// cerr << "Could not find in leaf node" << endl; + discardPage(bnode); + return result; + } // if res != 0 + + // Since we found the entry, we will have to save the superblock + // at the end. Set treeChanged to true. + treeChanged = true; + result = dataNode->link; + + nodeSize = align(keySize(&dataNode->key)); + + convPtr = (uInt8 *) dataNode; + convPtr += nodeSize; + nextDataNode = (TbNodeData *) dataNode; + + memmove(dataNode, nextDataNode, bnode->size - ((int)nextDataNode - (int)bnode)); + bnode->numKeys--; + bnode->size -= nodeSize; + dataNode = (TbNodeData *)((int)bnode + bnode->size); + memset(dataNode, 0, nodeSize); + setKeys(getKeys() - 1); + + if (getAddress(bnode) == getRoot()) { + if (bnode->numKeys == 0) { + info->root.offset = 0; // should be a setNull() call + setRoot(info->root); + setHeight(getHeight() - 1); + deAllocNode(bnode); + return result; + } + } else { // not root + if (bnode->size < (info->bNodeSize / 2) + } // else not root + } else { + + } // else bnode->magic != B_NODE_LEAF + return result; +} // bTree::deleteEntry + void bTree::discardPage(TbNode * node) { if (!memoryTree) @@ -105,9 +300,203 @@ } // bTree::discardPage bool -bTree::isNull(uPtr u) { - return (u.offset == 0); -} // bTree::isNull +bTree::fetchFirstKey(uPtr u, bTreeSearch & search) { + TbNode * node; + TbNodeData * dataNode; + bool result = false; + + if (isNull(u)) return false; + node = loadPage(u); + if (node == NULL) return false; + + dataNode = &node->data; + + if (node->magic == B_NODE_LEAF) { + search.value = dataNode->link; + search.keySize = keySize(&dataNode->key); + search.key = calloc(1, align(search.keySize)); + if (search.key == NULL) return result; + copyKey(&dataNode->key, search.key); + result = true; + } else { + return fetchFirstKey(dataNode->link, search); + } // else not B_NODE_LEAF + discardPage(node); + return result; +} // bTree::fetchFirstKey + +bool +bTree::fetchLastKey(uPtr u, bTreeSearch & search) { + TbNode * node; + TbNodeData * dataNode; + bool result = false; + + if (isNull(u)) return false; + node = loadPage(u); + if (node == NULL) return false; + + dataNode = &node->data; + + if (node->magic == B_NODE_LEAF) { + for (unsigned int curNode = 1; curNode < node->numKeys; curNode++) { + + uInt8 * tmpPtr = (uInt8 *)dataNode; + tmpPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *)tmpPtr; + + } // for curNode + + search.value = dataNode->link; + search.keySize = keySize(&dataNode->key); + search.key = calloc(1, align(search.keySize)); + copyKey(&dataNode->key, search.key); + result = true; + } else { + for (unsigned int curNode = 1; curNode <= node->numKeys; curNode++) { + + uInt8 * tmpPtr = (uInt8 *)dataNode; + tmpPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *)tmpPtr; + + } // for curNode + result = fetchLastKey(dataNode->link, search); + } // else not B_NODE_LEAF + discardPage(node); + return result; +} // bTree::fetchLastKey + +uPtr +bTree::findLeafNode(uPtr u, void * key) { + TbNode * bnode; + TbNodeData * dataNode; + uPtr link; + uPtr result; + + setNull(result); + if (key == NULL) return result; + if (isNull(u)) return result; + + bnode = loadPage(u); + + if (bnode->magic == B_NODE_LEAF) { + discardPage(bnode); + return u; + } else { + dataNode = &bnode->data; + for (unsigned int curNode = 0; curNode < bnode->numKeys; curNode++) { + if (compareKeys(key, &dataNode->key) <= 0) { + link = dataNode->link; + discardPage(bnode); + return findLeafNode(link, key); + } +// This code: +// dataNode += align(keySize(&dataNode->key)); +// Becomes the following because I can't do manual byte adjusts: + uInt8 * tmpPtr = (uInt8 *)dataNode; + tmpPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *)tmpPtr; + } // for curnode + link = dataNode->link; + discardPage(bnode); + return findLeafNode(link, key); + } // else not B_NODE_LEAF + return result; +} // bTree::findLeafNode + +void +bTree::freeAll(void) { + if (!memoryTree) return; + freePage(getRoot()); + // setNull(info->root); + info->root.offset = 0; // this really should be a setNull() call. Die GNU. +} // bTree::freeAll + +void +bTree::freePage(uPtr u) { + TbNode * node; + TbNodeData * dataNode; + + if (isNull(u)) return; + + node = u.bPtr; + if (node->magic = B_NODE_INDEX) { + dataNode = &node->data; + for (unsigned int curNode = 0; curNode < node->numKeys; curNode++) { + freePage(dataNode->link); + + uInt8 * tmpPtr = (uInt8 *) dataNode; + tmpPtr += align(keySize(&dataNode->key)); + dataNode = (TbNodeData *) tmpPtr; + + } // for curNode + freePage(dataNode->link); + } // if index node + free(node); +} // bTree::freePage + +void +bTree::getNeighbor(TbNode * bnode, TbNode ** neighbor, int & neighborSize) { + TbNode * l; + TbNode * r; + // a check to see if neighbor is null is probably not needed as + // this function is only used by deleteEntry() and we pass variables in. + *neighbor = NULL; + neighborSize = 0; + + l = loadPage(bnode->left); + r = loadPage(bnode->right); + + if (l == NULL) { + // this next check may be unnecessary + if (r->parent == bnode->parent) { + *neighbor = r; + neighborSize = r->size; + } + return; + } // if l == null + + if (r == NULL) { + // this next check may be unnecessary + if (l->parent == bnode->parent) { + *neighbor = l; + neighborSize = l->size; + } + return; + } // if r == null + + if (l->parent == r->parent) { // both are children of the same parent + + if (l->size < r->size) { + *neighbor = l; + neighborSize = l->size; + discardPage(r); + return; + } else { + *neighbor = r; + neighborSize = r->size; + discardPage(l); + return; + } // else l->size >= r->size + + } else { // one of the neighbors has a different parent + + if (l->parent == bnode->parent) { + *neighbor = l; + neighborSize = l->size; + discardPage(r); + return; + } else { + *neighbor = r; + neighborSize = r->size; + discardPage(l); + return; + } // else l->parent != bnode->parent + + } // else + + discardPage(l); + discardPage(r); +} // bTree::getNeighbor uPtr bTree::getAddress(TbNode * node) { @@ -183,6 +572,11 @@ return info->root; } // bTree::getRoot +bool +bTree::isNull(uPtr u) { + return (u.offset == 0); +} // bTree::isNull + TbNode * bTree::loadPage(uPtr u) { TbNode * node; @@ -318,9 +712,27 @@ u.offset = 0; } // bTree::setNull +/*************************************/ +/* Public Methods */ +/*************************************/ + +uInt32 +bTree::GetKeyCount(void) { + if (info != NULL) + return getKeys(); + else + return 0; +} // bTree::GetKeyCount + +uInt32 +bTree::GetTreeHeight(void) { + return getHeight(); +} // bTree::GetTreeHeight + + void bTree::InstallUserFunctions(compareKeyFunc cmpFunc, copyKeyProc copyProc, keySizeFunc ksFunc) { - compareKey = cmpFunc; + compareKeys = cmpFunc; copyKey = copyProc; keySize = ksFunc; return; diff --git a/btree.h b/btree.h index bd13d53..962dc4b 100644 --- a/btree.h +++ b/btree.h @@ -7,7 +7,7 @@ class bTree { protected: - compareKeyFunc compareKey; + compareKeyFunc compareKeys; copyKeyProc copyKey; keySizeFunc keySize; bTreeInfo * info; @@ -19,19 +19,28 @@ uPtr allocEmptyNode(void); int calcSize(TbNode *); void deAllocNode(TbNode *); + uPtr deleteEntry(uPtr, void *, uPtr); void discardPage(TbNode *); - bool isNull(uPtr); + bool fetchFirstKey(uPtr, bTreeSearch &); + bool fetchLastKey(uPtr, bTreeSearch &); + uPtr findLeafNode(uPtr, void *); + void freeAll(void); + void freePage(uPtr); uPtr getAddress(TbNode *); uPtr getFirstDeleted(void); uInt32 getNodes(void); uInt32 getHeight(void); uInt32 getKeys(void); + void getNeighbor(TbNode *, TbNode **, int &); uPtr getRoot(void); + bool isNull(uPtr); + TbNode * loadPage(uPtr); void loadSuperBlock(void); void savePage(TbNode *); void saveSuperBlock(void); + void setFirstDeleted(uPtr); void setNodes(uInt32); void setHeight(uInt32); @@ -39,12 +48,15 @@ void setLeft(uPtr, uPtr); void setParent(uPtr, uPtr); void setRight(uPtr, uPtr); - void setRoot(uPtr); void setNull(uPtr &); + void setRoot(uPtr); public: - bTree(char *, uInt32, treeTypes, bTreeVFS *); + bTree(const char *, uInt32, treeTypes, bTreeVFS *); + bTree(const char *, bTreeVFS *); + uInt32 GetKeyCount(void); + uInt32 GetTreeHeight(void); void InstallUserFunctions(compareKeyFunc, copyKeyProc, keySizeFunc); virtual ~bTree(void); };// bTree diff --git a/btree_consts.h b/btree_consts.h index b22c7c8..c4c761e 100644 --- a/btree_consts.h +++ b/btree_consts.h @@ -1,9 +1,9 @@ #ifndef BTREE_CONSTS_H #define BTREE_CONSTS_H -#define B_NODE_LEAF 0x4641454C; // LEAF -#define B_NODE_INDEX 0x58444E49; // INDX -#define B_NODE_OPEN 0x4E45504F; // OPEN -#define B_NODE_TREEINFO 0x4F464E4945455254; // TREEINFO +#define B_NODE_LEAF 0x4641454CL // LEAF +#define B_NODE_INDEX 0x58444E49L // INDX +#define B_NODE_OPEN 0x4E45504FL // OPEN +#define B_NODE_TREEINFO 0x4F464E4945455254LL // TREEINFO #endif diff --git a/btree_standardVFS.h b/btree_standardVFS.h index 44079b2..9e2fea1 100644 --- a/btree_standardVFS.h +++ b/btree_standardVFS.h @@ -4,7 +4,7 @@ #include #include "btree_vfs.h" -class StandardVFS : bTreeVFS { +class StandardVFS : public bTreeVFS { protected: FILE * dataFile; bool openFile; diff --git a/lists.pas b/lists.pas index f5a2342..9326e96 100755 --- a/lists.pas +++ b/lists.pas @@ -585,7 +585,6 @@ *) constructor bTree.init; -var lResult:longint; begin memoryTree := fileName = ''; treeName := filename; @@ -605,7 +604,7 @@ keys := 0; bNodeSize := nodeSize; treeType := tt; - end; {with} + end; // with if not (memoryTree) then begin @@ -640,8 +639,6 @@ end; // bTree.init constructor bTree.open; -var - lResult:longint; label safeExit; begin info := NIL; @@ -677,7 +674,7 @@ BT_INT32 : InstallUserFunctions(compareKeyInt32, copyKeyInt32, keySizeInt32); BT_INT64 : InstalluserFunctions(compareKeyInt64, copyKeyInt64, keySizeInt64); end; // case -safeExit: +safeexit: end; // bTree.open function bTree.align; @@ -785,7 +782,7 @@ if (l = NIL) then begin - // this next check may be unecessary + // this next check may be unnecessary if (r^.parent = bnode^.parent) then begin neighbor := r; @@ -796,7 +793,7 @@ if (r = NIL) then begin - // this next check may be unecessary + // this next check may be unnecessary if (l^.parent = bnode^.parent) then begin neighbor := l; @@ -909,7 +906,7 @@ end else // not root begin - if (bnode^.size < (info^.bNodeSize / 2)) then + if (bnode^.size < (info^.bNodeSize div 2)) then begin getNeighbor; if (neighbor <> NIL) and (neighborSize <> 0) then @@ -1661,7 +1658,8 @@ // source key is bigger than dest key if ((deltaSize + node^.size) > info^.bNodeSize) then begin -// writeln(f, 'Source key would cause split'); + writeln('Source key would cause split'); + runerror(1024); end else // source key will fit begin @@ -1811,11 +1809,11 @@ discardPage(node); end; {bTree.fetchLastKey} -procedure bTree.findLeafNode; +function bTree.findLeafNode; var bNode:PbNode; dataNode:PbNodeData; - i:integer; + curNode:integer; link:uPtr; begin setNull(result); @@ -1834,7 +1832,7 @@ else begin dataNode := @bnode^.data; - for i:=0 to bnode^.numKeys -1 do + for curNode:=0 to bnode^.numKeys -1 do begin if (compareKeys(key, @dataNode^.key) <= 0) then begin