// http://www.cs.msstate.edu/~cs2314/global/BTreeAnimation/algorithm.html #include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <iostream> #include <assert.h> #include "btree.h" #include "ubixfs.h" using namespace std; #define VERIFY(x, y, z, n) if ((x) != (y)) { cout << "verify " << z << " failed" << endl; PrintWholeTree(); } bTree::bTree(UbixFS * filesystem, fileDescriptor * myfd) { size_t result = 0; root = NULL; tag = 0; fs = filesystem; fd = myfd; header = new bTreeHeader; assert(header); memset(header, 0, sizeof(bTreeHeader)); assert(fs); result = fs->vfs_read(fd, header, 0, sizeof(bTreeHeader)); assert(result == sizeof(bTreeHeader)); // If there are any files in this dir, load the first node of the b+tree if (header->treeLeafCount != 0) { assert(header->firstNodeOffset != 0); root = new bNode; assert(root); result = fs->vfs_read(fd, root, header->firstNodeOffset, sizeof(bNode)); assert(result == sizeof(bNode)); } // if } // bTree::bTree bTree::bTree(const char * key, ubixfsInode * inode) { /* once the FS and the bTree are interfaced, this should go away */ root = NULL; tag = 0; header = new bTreeHeader; assert(header); memset(header, 0, sizeof(bTreeHeader)); header->treeDepth = 1; header->treeWidth = 0; header->treeLeafCount = 0; header->firstDeleted = -1; header->firstNodeOffset = sizeof(bTreeHeader); if (inode == NULL) return; root = allocEmptyNode(); if (root == NULL) return; root->used = 1; root->parent.bPtr = NULL; root->leaf = true; root->childCount[1] = 1; // cout << "---Creating " << inode->name << "@" << inode << endl; strncpy(root->keys[0], key, B_MAX_NAME_LENGTH); // insert pointer to data page to the right of the data root->head[1].iPtr = inode; root->tail[1].iPtr = inode; root->present[1] = true; if (inode != NULL) { inode->next.bPtr = inode->prev.bPtr = NULL; } // if return; } // bTree:bTree bool bTree::Insert(const char * key, ubixfsInode * inode) { bNode * bnode = root; ubixfsInode * tmpInode = NULL; unsigned int curSlot = 0; if (inode == NULL) return false; // note: this code is right out of the constructor if (root == NULL) { if (header == NULL) header = new bTreeHeader; assert(header); memset(header, 0, sizeof(bTreeHeader)); header->treeDepth = 1; header->treeWidth = 0; header->treeLeafCount = 0; header->firstDeleted = -1; header->firstNodeOffset = sizeof(bTreeHeader); root = allocEmptyNode(); assert(root); if (root == NULL) return false; root->used = 1; root->parent.bPtr = NULL; root->leaf = true; root->childCount[1] = 1; strncpy(root->keys[0], key, B_MAX_NAME_LENGTH); // insert pointer to data page to the right of the data root->head[1].iPtr = inode; root->tail[1].iPtr = inode; root->present[1] = true; inode->next.iPtr = inode->prev.iPtr = NULL; return true; } // if tmpInode = Find(key); if (tmpInode != NULL) return false; // PrintWholeTree(); // cout << "Insert(" << key << ")" << endl; //Info(bnode); ++header->treeLeafCount; /* * Find the leaf node the inode goes into */ assert(bnode->used); // cout << "---Inserting " << inode->name << " @ " << inode << endl; while (bnode != NULL && !bnode->leaf) { if (strcmp(key, bnode->keys[0]) < 0) { bnode = bnode->head[0].bPtr; } else { if (strcmp(key, bnode->keys[bnode->used-1]) >= 0) { bnode = bnode->head[bnode->used].bPtr; } else { for (unsigned int i = 1; i < bnode->used; i++) { if (strcmp(key, bnode->keys[i]) < 0) { bnode = bnode->head[i].bPtr; break; } // if } // for i } // else } } // while /* * */ assert(bnode); if (bnode->leaf != true) cout << "leafnode!=true" << endl; assert(inode); if (strcmp(key, bnode->keys[curSlot = 0]) < 0) tmpInode = bnode->head[curSlot].iPtr; else if (strcmp(key, bnode->keys[(curSlot = bnode->used)-1]) >= 0) tmpInode = bnode->head[bnode->used].iPtr; else { for (curSlot = 1; curSlot < bnode->used; curSlot++) { if (strcmp(key, bnode->keys[curSlot]) < 0) { tmpInode = bnode->head[curSlot].iPtr; break; } // if } // for curSlot tmpInode = bnode->head[curSlot].iPtr; } // else if (tmpInode == NULL) { /* * This is the first node in this leaf */ bnode->head[curSlot].iPtr = bnode->tail[curSlot].iPtr = inode; bnode->present[curSlot] = true; if (curSlot == 0) { if (bnode->head[1].iPtr != NULL) { ubixfsInode * iptr = bnode->head[1].iPtr; inode->prev.iPtr = iptr->prev.iPtr; inode->next.iPtr = iptr; iptr->prev.iPtr = inode; if (inode->prev.iPtr != NULL) inode->prev.iPtr->next.iPtr = inode; } else { inode->next.iPtr = inode->prev.iPtr = NULL; } // else } else { ++bnode->used; } // else } else { /* * Add node to leaf page. Scan through to find where it goes. */ if (strcmp(key, bnode->head[curSlot].iPtr->name) < 0) { inode->next.iPtr = bnode->head[curSlot].iPtr; inode->prev.iPtr = inode->next.iPtr->prev.iPtr; inode->next.iPtr->prev.iPtr = inode; if (inode->prev.iPtr != NULL) inode->prev.iPtr->next.iPtr = inode; bnode->head[curSlot].iPtr = inode; } else { if (strcmp(key, bnode->tail[curSlot].iPtr->name) > 0) { inode->prev.iPtr = bnode->tail[curSlot].iPtr; inode->next.iPtr = inode->prev.iPtr->next.iPtr; inode->prev.iPtr->next.iPtr = inode; if (inode->next.iPtr != NULL) inode->next.iPtr->prev.iPtr = inode; bnode->tail[curSlot].iPtr = inode; } else { ubixfsInode * tmpInode = bnode->head[curSlot].iPtr; for (unsigned int i = 0; i < bnode->childCount[curSlot]; i++) { if (strcmp(key, tmpInode->name) < 0) { inode->next.iPtr = tmpInode; inode->prev.iPtr = tmpInode->prev.iPtr; inode->next.iPtr->prev.iPtr = inode; inode->prev.iPtr->next.iPtr = inode; break; } // if tmpInode = tmpInode->next.iPtr; } // for i } // else } // else } // else if (++bnode->childCount[curSlot] == B_MAX_CHILD_COUNT) { // cout << "---- before split ----" << endl; // Info(bnode); if (curSlot != bnode->used) { int shift = bnode->used - curSlot +1; memmove(&bnode->head[curSlot+1], &bnode->head[curSlot], sizeof(bnode->head[0]) * shift); memmove(&bnode->tail[curSlot+1], &bnode->tail[curSlot], sizeof(bnode->tail[0]) * shift); memmove(&bnode->present[curSlot+1], &bnode->present[curSlot], sizeof(bnode->present[0]) * shift); memmove(&bnode->childCount[curSlot+1], &bnode->childCount[curSlot], sizeof(bnode->childCount[0]) * shift); memmove(&bnode->keys[curSlot+1], &bnode->keys[curSlot], sizeof(bnode->keys[0]) * (shift-1)); memset(bnode->keys[curSlot], 0, B_MAX_NAME_LENGTH); } else { bnode->head[curSlot+1] = bnode->head[curSlot]; bnode->tail[curSlot+1] = bnode->tail[curSlot]; bnode->childCount[curSlot+1] = bnode->childCount[curSlot]; bnode->present[curSlot+1] = bnode->present[curSlot]; } // else ubixfsInode * tmpInode = bnode->head[curSlot].iPtr; for (unsigned int i = 0; i < (B_MAX_CHILD_COUNT+1) >> 1; i++) { assert(tmpInode); tmpInode = tmpInode->next.iPtr; } // for i strncpy(bnode->keys[curSlot], tmpInode->name, B_MAX_NAME_LENGTH); bnode->head[curSlot+1].iPtr = tmpInode; bnode->tail[curSlot].iPtr = tmpInode->prev.iPtr; bnode->childCount[curSlot] = (B_MAX_CHILD_COUNT+1) >> 1; bnode->childCount[curSlot+1] -= bnode->childCount[curSlot]; bnode->present[curSlot] = true; ++header->treeWidth; if (++bnode->used == B_MAX_KEYS) splitNode(bnode); } // if leaf is full // Info(bnode); return true; } // bTree::Insert void bTree::splitNode(bNode * oldNode) { ubixfsInode * tmpInode = NULL; assert(oldNode); if (oldNode == NULL) return; if (oldNode->used != B_MAX_KEYS) return; bNode * newNode = allocEmptyNode(); if (newNode == NULL) return; unsigned int shift = B_MAX_KEYS >> 1; unsigned int splitLoc = B_MAX_KEYS - shift; ++ shift; // cout << "oldNode before split: " << endl; // Info(oldNode); // cout << "splitLoc: " << splitLoc << endl; // cout << "shift: " << shift << endl; newNode->used = oldNode->used = B_MAX_KEYS >> 1; newNode->parent.bPtr = oldNode->parent.bPtr; newNode->leaf = oldNode->leaf; // cout << "newNode->used: " << newNode->used << endl; // cout << "oldNode->used: " << oldNode->used << endl; memcpy(&newNode->keys[0], &oldNode->keys[splitLoc], sizeof(newNode->keys[0]) * (shift-1)); memset(&oldNode->keys[splitLoc], 0, sizeof(newNode->keys[0]) * (shift-1)); memcpy(&newNode->present[0], &oldNode->present[splitLoc], sizeof(newNode->present[0]) * shift); memset(&oldNode->present[splitLoc], 0, sizeof(newNode->present[0]) * shift); memcpy(&newNode->head[0], &oldNode->head[splitLoc], sizeof(newNode->head[0]) * shift); memset(&oldNode->head[splitLoc], 0, sizeof(newNode->head[0]) * shift); memcpy(&newNode->tail[0], &oldNode->tail[splitLoc], sizeof(newNode->tail[0]) * shift); memset(&oldNode->tail[splitLoc], 0, sizeof(newNode->tail[0]) * shift); memcpy(&newNode->childCount[0], &oldNode->childCount[splitLoc], sizeof(newNode->childCount[0]) * shift); memset(&oldNode->childCount[splitLoc], 0, sizeof(newNode->childCount[0]) * shift); if (!newNode->leaf) { for (unsigned int i = 0; i <= newNode->used; i++) { newNode->head[i].bPtr->parent.bPtr = newNode; } // for i } // if newNode isn't a leaf tmpInode = GetFirstNode(newNode); assert(tmpInode); if (oldNode == root) { // allocate a new root node ++header->treeDepth; root = allocEmptyNode(); oldNode->parent.bPtr = root; newNode->parent.bPtr = root; // strncpy(root->keys[0], newNode->keys[0], B_MAX_NAME_LENGTH); strncpy(root->keys[0], tmpInode->name, B_MAX_NAME_LENGTH); root->head[0].bPtr = oldNode; root->tail[0].bPtr = root->tail[1].bPtr = NULL; root->head[1].bPtr = newNode; root->used = 1; root->leaf = false; root->present[0] = root->present[1] = true; root->childCount[0] = root->childCount[1] = 0; // root->childCount[0] = oldNode->used; // root->childCount[1] = newNode->used; // cout << "parent" << endl; // Info(newNode->parent); // cout << "oldNode" << endl; // Info(oldNode); // cout << "-----" << endl; // cout << "newNode" << endl; // Info(newNode); // cout << "-----" << endl; } else { insertNode(newNode->parent.bPtr, tmpInode->name, newNode); // if (oldNode->parent->used == B_MAX_KEYS) splitNode(oldNode->parent); } // else return; } // bTree::splitNode void bTree::insertNode(bNode * node, const char * key, bNode * headPtr) { unsigned int curSlot = 0; if (node == NULL || key == NULL) return; if (strcmp(key, node->keys[node->used-1]) >= 0){ curSlot = node->used; memset(node->keys[curSlot], 0, B_MAX_NAME_LENGTH); strncpy(node->keys[curSlot], key, B_MAX_NAME_LENGTH); node->head[curSlot+1].bPtr = headPtr; node->tail[curSlot+1].bPtr = NULL; node->present[curSlot+1] = true; node->childCount[node->used] = 0; // maybe? } else { for (curSlot = 0; curSlot < node->used; curSlot++) { if (strcmp(key, node->keys[curSlot]) < 0) break; } // for /* * note that there is one more item for everything but keys * So, make the shift count +1 and just subtract it from the key shift * later */ int shift = node->used - curSlot +1; memmove(&node->head[curSlot+1], &node->head[curSlot], sizeof(node->head[0]) * shift); memmove(&node->tail[curSlot+1], &node->tail[curSlot], sizeof(node->tail[0]) * shift); memmove(&node->present[curSlot+1], &node->present[curSlot], sizeof(node->present[0]) * shift); memmove(&node->childCount[curSlot+1], &node->childCount[curSlot], sizeof(node->childCount[0]) * shift); memmove(&node->keys[curSlot+1], &node->keys[curSlot], sizeof(node->keys[0]) * (shift-1)); memset(node->keys[curSlot], 0, B_MAX_NAME_LENGTH); strncpy(node->keys[curSlot], key, B_MAX_NAME_LENGTH); node->head[curSlot+1].bPtr = headPtr; node->tail[curSlot+1].bPtr = NULL; node->present[curSlot+1] = true; // node->childCount[node->used] = ?; } // else if (++node->used == B_MAX_KEYS) splitNode(node); return; } // bTree::insertNode bNode * bTree::allocEmptyNode(void) { bNode * newNode = new bNode; memset(newNode, 0, sizeof(bNode)); newNode->magic1 = B_NODE_MAGIC_1; newNode->magic2 = B_NODE_MAGIC_2; newNode->parent.bPtr = NULL; newNode->tag = ++tag; // this will start at 1 (0 is the header node) return newNode; } // bTree::allocEmptyNode void bTree::Info(const bNode * node) { ubixfsInode * inode = NULL; if (node == NULL || root == NULL) return; cout << node << " | " << node->parent.bPtr << endl; for (unsigned int i = 0; i <= node->used; i++) { inode = node->head[i].iPtr; // cout << "(" << node->childCount[i] << ")"; for (unsigned int k = 0; k < node->childCount[i]; k++) { cout << "[" << inode->name << "]"; inode = inode->next.iPtr; } // for k if (i != node->used) cout << " {" << node->keys[i] << "} "; } // for i cout << endl; return; #if 0 for (unsigned int i = 0; i < node->used; i++) { cout << "keys[" << i << "]: " << node->keys[i] << " "; } // for i cout << endl; cout << "node->used: " << node->used << endl; cout << "leaf: " << node->leaf << endl; for (unsigned int i = 0; i <= node->used; i++) { inode = (ubixfsInode *)node->head[i]; cout << "node->childCount["<<i<<"]: " << node->childCount[i] << endl; for (unsigned int j = 0; j < node->childCount[i]; j++) { assert(inode); cout << "[" << i << "].[" << j << "]->" << inode->name << endl; inode = inode->next; } // for j } // for i #endif } // bTree::Info void bTree::Info(void) { ubixfsInode * inode = NULL; cout << "tree depth: " << header->treeDepth << endl; cout << "tree width: " << header->treeWidth << endl; cout << "tree leaf count: " << header->treeLeafCount << endl; cout << "tag: " << tag << endl; if (root == NULL) return; for (unsigned int i = 0; i <= root->used; i++) { cout << "CC[" << i << "]: " << root->childCount[i] << " "; } // for i cout << endl; for (unsigned int i = 0; i <= root->used; i++) { cout << "CH[" << i << "]: " << root->head[i].bPtr << " "; } // for i cout << endl; for (unsigned int i = 0; i <=root->used; i++) { cout << "CT[" << i << "]: " << root->tail[i].bPtr << " "; } // for i cout << endl; for (unsigned int i = 0; i < root->used; i++) { cout << "keys[" << i << "]: " << root->keys[i] << " "; } // for i cout << endl; cout << "root->used: " << root->used << endl; for (unsigned int i = 0; i <= root->used; i++) { inode = root->head[i].iPtr; cout << "root->childCount["<<i<<"]: " << root->childCount[i] << endl; if (root->leaf) { cout << "root contains leaf node" << endl; for (unsigned int j = 0; j < root->childCount[i]; j++) { assert(inode); cout << "[" << i << "].[" << j << "]->" << inode->name << endl; inode = inode->next.iPtr; } // for j } // if root->leaf } // for i } // bTree::Info void bTree::Print(void) { ubixfsInode * node = GetFirstNode(); while (node != NULL) { cout << node->name << endl; node = node->next.iPtr; } } // bTree::Print ubixfsInode * bTree::Find(const char * key) { /* ubixfsInode * tmp = GetFirstNode(); while (tmp!=NULL) { if (strcmp(tmp->name, key) == 0) return tmp; tmp = tmp->next.iPtr; } return NULL; */ return treeSearch(root, key); } // bTree::Find ubixfsInode * bTree::inodeSearch(ubixfsInode * inode, const char * key) { if (inode == NULL || key == NULL) return NULL; int result = strcmp(inode->name, key); if (result == 0) return inode; if (result < 0) { inode = inode->next.iPtr; while (inode != NULL && ((result = strcmp(inode->name, key)) < 0)) { inode = inode->next.iPtr; } // while } else { inode = inode->prev.iPtr; while (inode != NULL && ((result = strcmp(inode->name, key)) > 0)) { inode = inode->prev.iPtr; } // while } // else return (result == 0 ? inode : NULL); } // bTree::inodeSearch ubixfsInode * bTree::treeSearch(bNode * bnode, const char * key) { if (bnode == NULL || key == NULL || bnode->used == 0) return NULL; if (bnode->leaf) return inodeSearch(GetFirstNode(bnode), key); if (strcmp(key, bnode->keys[0]) < 0) { return treeSearch(bnode->head[0].bPtr, key); } // if if (strcmp(key, bnode->keys[bnode->used-1]) >= 0) { return treeSearch(bnode->head[bnode->used].bPtr, key); } // if for (unsigned int i = 1; i < bnode->used; i++) { if (strcmp(key, bnode->keys[i]) < 0) { return treeSearch(bnode->head[i].bPtr, key); } // if } // for i return NULL; } // bTree::treeSearch ubixfsInode * bTree::GetFirstNode(void) { return GetFirstNode(root); } // bTree::GetFirstNode ubixfsInode * bTree::GetFirstNode(bNode * node) { bNode * tmpNode = node; if (tmpNode == NULL) return NULL; while (!tmpNode->leaf) { for (unsigned int i = 0; i < tmpNode->used; i++) { if (tmpNode->head[i].bPtr != NULL) { tmpNode = tmpNode->head[i].bPtr; break; } // if } // for i } // while for (unsigned int i = 0; i < tmpNode->used; i++) { if (tmpNode->head[i].iPtr != NULL) return tmpNode->head[i].iPtr; } // for i return NULL; } // bTree::GetFirstNode bNode * bTree::findLeafNode(bNode * node, const char * key) { assert(node); assert(key); if (node == NULL || key == NULL) return NULL; assert(node->used); if (node->leaf) return node; if (strcmp(key, node->keys[0]) < 0) return findLeafNode(node->head[0].bPtr, key); if (strcmp(key, node->keys[node->used-1]) >= 0) return findLeafNode(node->head[node->used].bPtr, key); for (unsigned int i = 1; i < node->used; i++) { if (strcmp(key, node->keys[i]) < 0) return findLeafNode(node->head[i].bPtr, key); } // for i return NULL; } // bTree::findLeafNode void bTree::saveNode(FILE * fd, bNode * node, void * tmpPtr) { bNode * ptr = (bNode *)tmpPtr; assert(tmpPtr); assert(fd); assert(node); cout << "writing tag: " << node->tag << endl; memcpy(tmpPtr, node, sizeof(bNode)); if (node->parent.bPtr != NULL) ptr->parent.offset = node->parent.bPtr->tag * sizeof(bNode); else ptr->parent.offset = 0; for (unsigned int i = 0; i <= node->used; i++) { bNode * bPtr = node->head[i].bPtr; if (bPtr != NULL) ptr->head[i].offset = bPtr->tag * sizeof(bNode); else ptr->head[i].offset = ~0; ptr->present[i] = false; } // for i if (node->leaf) { for (unsigned int i = 0; i <= node->used; i++) { // ubixfsInode * inode = node->head[i].iPtr; // mji if (inode != NULL) tmp->head[i] = inode-> } // for i } else { for (unsigned int i = 0; i <= node->used; i++) { if (node->head[i].bPtr != NULL) saveNode(fd, node->head[i].bPtr, tmpPtr); } // for i } // else return; } // bTree::saveNode bool bTree::Save(const char * filename) { ubixfsInode * uPtr = NULL; if (filename == NULL) return false; FILE * fd = NULL; if ((fd = fopen(filename, "wb+")) == NULL) return false; cout << "tags: " << tag << endl; lseek(fileno(fd), tag * sizeof(bNode), SEEK_END); header->firstNodeOffset = sizeof(bNode); header->firstDeleted = -1; void * tmpPtr = malloc(sizeof(bNode)); assert(tmpPtr); uPtr = (ubixfsInode *)tmpPtr; memset(tmpPtr, 0, sizeof(bNode)); fwrite(header, sizeof(bTreeHeader), 1, fd); saveNode(fd, root, tmpPtr); fclose(fd); free(tmpPtr); return true; } // bTree::Save bool bTree::Load(const char * filename) { if (filename == NULL) return false; return true; } // bTree::Load bool bTree::Delete(const char * key) { if (key == NULL) return false; return true; } // bTree::Delete bool bTree::Verify(void) { ubixfsInode * node = GetFirstNode(); if (node == NULL) return true; while (node != NULL) { ubixfsInode * next = node->next.iPtr; if (next != NULL) { // cout << node->name << "::" << node->next->name << ":::" << strcmp(node->name, node->next->name) << endl; if (strcmp(node->name, next->name) > 0) return false; } node = next; } // while return true; } // bTree::Verify void bTree::Print(bNode * node) { if (node == NULL) return; Info(node); if (!node->leaf) for (unsigned int i = 0; i <= node->used; i++) { Print(node->head[i].bPtr); } // for i } // bTree::Print void bTree::PrintWholeTree(void) { Print(root); } // bTree::PrintWholeTree; bTree::~bTree(void) { cout << "tree depth: " << header->treeDepth << endl; cout << "tree width: " << header->treeWidth << endl; cout << "tree leaf count: " << header->treeLeafCount << endl; } // bTree::~bTree