#include "gb-include.h" #include "MemPoolTree.h" MemPoolTree::MemPoolTree () { m_mem = NULL; m_memSize = 0; m_headNode = NULL; m_numUsedNodes = 0; m_nextNode = NULL; m_floor = NULL; } MemPoolTree::~MemPoolTree ( ) { m_mem = NULL; m_memSize = 0; } // "memMax" includes records plus the overhead bool MemPoolTree::init ( char *mem , long memSize ) { // all the mem m_mem = mem; m_memSize = memSize; // the floor m_floor = (MemNode *)(m_mem + m_memSize) ; // next available node m_nextNode = m_floor - 1; return true; } // . used by cache // . wrapper for getNode() MemNode *MemPoolTree::getNode ( MemKey &key ) { // debug msg //log("getting node for k.n1=%lu n0=%llu",key.n1,key.n0); // get the node (about 4 cycles per loop, 80cycles for 1 million items) MemNode *i = m_headNode; while ( i ) { if ( key < i->m_key ) { i = i->m_left ; continue;} if ( key > i->m_key ) { i = i->m_right; continue;} return i; } return NULL; } // . returns node whose key is >= "key" // . returns NULL if none MemNode *MemPoolTree::getNextNode ( MemKey &key ) { // return NULL if tree empty if ( ! m_headNode ) return NULL; // get the node MemNode *parent; MemNode *i = m_headNode ; while ( i ) { parent = i ; if ( key < i->m_key ) { i = i->m_left ; continue; } if ( key > i->m_key ) { i = i->m_right ; continue; } return i; } // if parent's key is > we're done if ( parent->m_key > key ) return parent; // otherwise we must get the node after the parent return getNextNode ( parent ); } // . get the node whose key is <= "key" // . returns -1 if none MemNode *MemPoolTree::getPrevNode ( MemKey &key ) { // return NULL if tree empty if ( ! m_headNode ) return NULL; // get the node MemNode *parent; MemNode *i = m_headNode ; while ( i ) { parent = i ; if ( key < i->m_key ) { i = i->m_left ; continue; } if ( key > i->m_key ) { i = i->m_right ; continue; } return i; } // if parent's key is < we're done if ( parent->m_key < key ) return parent; // otherwise we must get the node after the parent return getPrevNode ( parent ); } // get next node after "node" MemNode *MemPoolTree::getNextNode ( MemNode *i ) { // cruise the kids if we have a right one if ( i->m_right ) { // go to the right kid i = i->m_right; // now go left as much as we can while ( i->m_left ) i = i->m_left; // return that node (it's a leaf or has one right kid) return i; } // now keep getting parents until one has a key bigger than i's key MemNode *p = i->m_parent; // if parent is NULL we're done if ( ! p ) return NULL; // if we're the left kid of the parent, then the parent is the // next biggest node if ( p->m_left == i ) return p; // otherwise keep getting the parent until it has a bigger key // or until we're the LEFT kid of the parent. that's better // cuz comparing keys takes longer. loop is 6 cycles per iteration. while ( p && p->m_key < i->m_key ) p = p->m_parent; // p will be NULL if none are left return p; } MemNode *MemPoolTree::getPrevNode ( MemNode *i ) { // cruise the kids if we have a left one if ( i->m_left ) { // go to the left kid i = i->m_left; // now go right as much as we can while ( i->m_right ) i = i->m_right ; // return that node (it's a leaf or has one left kid) return i; } // now keep getting parents until one has a key bigger than i's key MemNode *p = i->m_parent; // if we're the right kid of the parent, then the parent is the // next least node if ( p->m_right == i ) return p; // keep getting the parent until it has a bigger key // or until we're the RIGHT kid of the parent. that's better // cuz comparing keys takes longer. loop is 6 cycles per iteration. while ( p && p->m_key > i->m_key ) p = p->m_parent; // p will be NULL if none are left return p; } // . returns NULL and sets errno on failure // . returns node ptr we added it to on success // . this will NOT replace any current node with the same key MemNode *MemPoolTree::addNode ( MemKey &key ) { // debug msg //log("adding k.n1=%lu n0=%llu",key.n1,key.n0); // set up vars MemNode *iparent ; // this is NULL iff there are no nodes used in the tree MemNode *i = m_headNode; // . find the parent of node i // . if a node exists with our key then replace it while ( i ) { iparent = i ; if ( key < i->m_key ) i = i->m_left ; else if ( key > i->m_key ) i = i->m_right ; else { errno = EBADENGINEER; log("MemPoolTree::addNode: node already in tree"); return NULL; } } // store in the next available node i = m_nextNode; // if we're the first node we become the head node and our parent is -1 if ( ! m_headNode ) { m_headNode = i; iparent = NULL; } // . the right kid of an empty node is used as a linked list of // empty nodes formed by deleting nodes // . we keep the linked list so we can re-used these vacated nodes MemNode *rightGuy = i->m_right ; // stick ourselves in the next available node, "m_nextNode" i->m_key = key; i->m_parent = iparent; i->m_left = NULL; i->m_right = NULL; i->m_depth = 1; // leave nodes have depth of 1 // make our parent, if any, point to us if ( iparent ) { if ( key < iparent->m_key ) iparent->m_left = i; else if ( key > iparent->m_key ) iparent->m_right = i; else log("MemPoolTree::addNode: bad engineer"); } // . if we weren't recycling a middle node then advance to next // . m_floor is the lowest node number that was never filled // at any one time in the past // . you might call it the brand new housing district // . we advance downwards like a stack to use memory most efficiently if ( m_nextNode == m_floor - 1 ) { m_nextNode--; m_floor--; } // . otherwise, we're in a linked list of vacated used houses // . we have a linked list in the right kid // . make sure the new head doesn't have a left else if ( rightGuy ) m_nextNode = rightGuy; // otherwise point it to the next brand new house (TODO:REMOVE) else { log("MemPoolTree::addNode: bad engineer 2"); sleep(50000); // m_nextNode = m_floor; } // we have one more used node m_numUsedNodes++; // . reset depths starting at our parent and ascending the tree // . will balance if child depths differ by 2 or more setDepths ( iparent ); // return the node we occupied return i; } // . deletes node i from the tree // . i's parent should point to i's left or right kid // . if i has no parent then his left or right kid becomes the new top node void MemPoolTree::deleteNode ( MemNode *i ) { // watch out for NULL ptrs if ( ! i ) { log("\n\n\n\n\nMemPoolTree::deleteNode: NULL MemNode"); return; } // watch out for double deletes if ( i->m_parent == (MemNode *) -2 ) { log("MemPoolTree::deleteNode: caught bad deleteNode() call"); return; } // parents MemNode *iparent ; MemNode *jparent ; // node2 replaces node MemNode *j = i; // . now find a node to replace "node" // . get a node whose key is just to the right or left of node's key // . get node's right kid // . then get that kid's LEFT MOST leaf-node descendant // . this little routine is stolen from getNextNode(i) // . try to pick a kid from the right the same % of time as from left if ( j->m_right && ( m_pickRight || ! j->m_left ) ) { // try to pick a left kid next time m_pickRight = 0; // go to the right kid j =j->m_right ; // now go left as much as we can while ( j->m_left ) j = j->m_left ; // usej (it's a leaf or has a right kid) goto gotReplacement; } // . now get the previous node if i has no right kid // . this little routine is stolen from getPrevNode(i) if ( j->m_left ) { // try to pick a right kid next time m_pickRight = 1; // go to the left kid j = j->m_left; // now go right as much as we can while ( j->m_right ) j =j->m_right; // usej (it's a leaf or has a left kid) goto gotReplacement; } // . come here if i did not have any kids (i's a leaf node) // . get i's parent iparent = i->m_parent; // make parent, if any, disown him if ( iparent ) { if ( iparent->m_left == i ) iparent->m_left = NULL; else iparent->m_right = NULL; } // node now goes to the top of the list of vacated, available homes i->m_right = m_nextNode; // m_nextNode now points to node m_nextNode = i; // his parent is -2 (god) cuz he's dead and available i->m_parent = (MemNode *) -2; // . if we were the head node then, since we didn't have any kids, // the tree must be empty // . one less node in the tree m_numUsedNodes--; // . reset the depths starting at iparent and going up until unchanged // . will balance at pivot nodes that need it setDepths ( iparent ); // return if there are still nodes in the tree if ( m_numUsedNodes > 0 ) return; // otherwise tree must be empty m_headNode = NULL; // this will nullify our linked list of vacated, used homes m_floor = (MemNode *)(m_mem + m_memSize) ; m_nextNode = m_floor - 1; return; // . now replace node #i with node #j // . i should not equal j at this point gotReplacement: // . j's parent should take j's one kid // . that child should likewise point to j's parent // . j should only have <= 1 kid now because of our algorithm above // . if j's parent is i then j keeps his kid jparent = j->m_parent; if ( jparent != i ) { // parent: if j is my left kid, then i take j's right kid // otherwise, if j is my right kid, then i take j's left kid if ( jparent->m_left == j ) { jparent->m_left = j->m_right; if ( j->m_right ) j->m_right->m_parent = jparent; } else { jparent->m_right = j->m_left ; if ( j->m_left ) j->m_left->m_parent = jparent; } } // . j inherits i's children (providing i's child is not j) // . those children's parent should likewise point to j if ( i->m_left != j ) { j->m_left = i->m_left; if ( j->m_left ) j->m_left->m_parent = j; } if ( i->m_right != j ) { j->m_right = i->m_right; if ( j->m_right ) j->m_right->m_parent = j; } // j becomes the kid of i's parent, if any iparent = i->m_parent; if ( iparent ) { if ( iparent->m_left == i ) iparent->m_left = j; else iparent->m_right = j; } // iparent may be -1 j->m_parent = iparent; // if i was the head node now j becomes the head node if ( m_headNode == i ) m_headNode = j; // . i joins the linked list of available used homes // . put it at the head of the list // . "m_nextNode" is the head node of the linked list i->m_right = m_nextNode; m_nextNode = i; // . i's parent should be -2 so we know it's unused in case we're // stepping through the nodes linearly for dumping in RdbDump // . used in getListUnordered() i->m_parent = (MemNode *)-2; // we have one less used node m_numUsedNodes--; // debug step -- check chain from iparent down making sure that // all kids don't have -2 for their parent... seems to be a rare bug //printTree(); // debug msg //fprintf(stderr,"- #%li %lli %li\n",i,m_keys[i].n0,iparent); // our depth becomes that of the node we replaced, unless moving j // up to i decreases the total depth, in which case setDepths() fixes j->m_depth = i->m_depth ; // debug msg //fprintf(stderr,"... replaced %li it with %li (-1 means none)\n",i,j); // . recalculate depths starting at old parent of j // . stops at the first node to have the correct depth // . will balance at pivot nodes that need it if ( jparent != i ) setDepths ( jparent ); else setDepths ( j ); // TODO: register growTree with g_mem to free on demand // do a grow/shrink test and shrink if we need to // return growTable ( ); // done: } // . recompute depths of nodes starting at i and ascending the tree // . call rotateRight/Left() when depth of children differs by 2 or more void MemPoolTree::setDepths ( MemNode *i ) { // inc the depth of all parents if it changes for them while ( i ) { // . compute the new depth for node i // . get depth of left kid // . left/rightDepth is depth of subtree on left/right long leftDepth = 0; long rightDepth = 0; if ( i->m_left ) leftDepth = i->m_left->m_depth; if ( i->m_right ) rightDepth = i->m_right->m_depth; // . get the new depth for node i // . add 1 cuz we include ourself in our m_depth long newDepth ; if ( leftDepth > rightDepth ) newDepth = leftDepth + 1; else newDepth = rightDepth + 1; // if the depth did not change for i then we're done long oldDepth = i->m_depth; // set our new depth i->m_depth = newDepth; // diff can be -2, -1, 0, +1 or +2 long diff = leftDepth - rightDepth; // . if it's -1, 0 or 1 then we don't need to balance // . if rightside is deeper rotate left, i is the pivot // . otherwise, rotate left // . these should set the m_depth[*] for all nodes needing it if ( diff == -2 ) i = rotateLeft ( i ); else if ( diff == 2 ) i = rotateRight ( i ); // . return if our depth was ultimately unchanged // . i may have change if we rotated, but same logic applies if ( i->m_depth == oldDepth ) break; // debug msg //fprintf (stderr,"changed node %li's depth from %li to %li\n", //i,oldDepth,newDepth); // get his parent to continue the ascension i = i->m_parent; } } /* // W , X and B are SUBTREES. // B's subtree was 1 less in depth than W or X, then a new node was added to // W or X triggering the imbalance. // However, if B gets deleted W and X can be the same size. // // Right rotation if W subtree depth is >= X subtree depth: // // A N // / \ / \ // / \ / \ // N B ---> W A // / \ / \ // W X X B // // Right rotation if W subtree depth is < X subtree depth: // A X // / \ / \ // / \ / \ // N B ---> N A // / \ / \ / \ // W X W Q T B // / \ // Q T */ // . we come here when A's left subtree is deeper than it's right subtree by 2 // . this rotation operation causes left to lose 1 depth and right to gain one // . the type of rotation depends on which subtree is deeper, W or X // . W or X must deeper by the other by exactly one // . if they were equal depth then how did adding a node inc the depth? // . if their depths differ by 2 then N would have been rotated first! // . the parameter "i" is the node # for A in the illustration above // . return the node # that replaced A so the balance() routine can continue // . TODO: check our depth modifications below MemNode *MemPoolTree::rotateRight ( MemNode *i ) { // i's left kid's right kid takes his place MemNode *A = i; MemNode *N = A->m_left ; MemNode *W = N->m_left ; MemNode *X = N->m_right; MemNode *Q = NULL; MemNode *T = NULL; if ( X ) { Q = X->m_left ; T = X->m_right; } // let AP be A's parent MemNode *AP = A->m_parent ; // whose the bigger subtree, W or X? (depth includes W or X itself) long Wdepth = 0; long Xdepth = 0; if ( W ) Wdepth = W->m_depth; if ( X ) Xdepth = X->m_depth; // debug msg //fprintf(stderr,"A=%li AP=%li N=%li W=%li X=%li Q=%li T=%li " //"Wdepth=%li Xdepth=%li\n",A,AP,N,W,X,Q,T,Wdepth,Xdepth); // goto Xdeeper if X is deeper if ( Wdepth < Xdepth ) goto Xdeeper; // N's parent becomes A's parent N->m_parent = AP; // A's parent becomes N A->m_parent = N; // X's parent becomes A if ( X ) X->m_parent = A; // A's parents kid becomes N if ( AP ) { if ( AP->m_left == A ) AP->m_left = N; else AP->m_right = N; } // if A had no parent, it was the headNode else { //fprintf(stderr,"changing head node from %li to %li\n", //m_headNode,N); m_headNode = N; } // N's right kid becomes A N->m_right = A; // A's left kid becomes X A->m_left = X; // . compute A's depth from it's X and B kids // . it should be one less if Xdepth smaller than Wdepth // . might set m_depth[A] to computeDepth(A) if we have problems if ( Xdepth < Wdepth ) A->m_depth -= 2; else A->m_depth -= 1; // N gains a depth iff W and X were of equal depth if ( Wdepth == Xdepth ) N->m_depth += 1; // now we're done, return the new pivot that replaced A return N; // come here if X is deeper Xdeeper: // X's parent becomes A's parent X->m_parent = AP; // A's parent becomes X A->m_parent = X; // N's parent becomes X N->m_parent = X; // Q's parent becomes N if ( Q ) Q->m_parent = N; // T's parent becomes A if ( T ) T->m_parent = A; // A's parent's kid becomes X if ( AP ) { if ( AP->m_left == A ) AP->m_left = X; else AP->m_right = X; } // if A had no parent, it was the headNode else { //fprintf(stderr,"changing head node2 from %li to %li\n", //m_headNode,X); m_headNode = X; } // A's left kid becomes T A->m_left = T; // N's right kid becomes Q N->m_right = Q; // X's left kid becomes N X->m_left = N; // X's right kid becomes A X->m_right = A; // X's depth increases by 1 since it gained 1 level of 2 new kids X->m_depth += 1; // N's depth decreases by 1 N->m_depth -= 1; // A's depth decreases by 2 A->m_depth -= 2; // now we're done, return the new pivot that replaced A return X; } MemNode *MemPoolTree::rotateLeft ( MemNode *i ) { // i's left kid's right kid takes his place MemNode *A = i; MemNode *N = A->m_right ; MemNode *W = N->m_right ; MemNode *X = N->m_left; MemNode *Q = NULL; MemNode *T = NULL; if ( X ) { Q = X->m_right ; T = X->m_left ; } // let AP be A's parent MemNode *AP = A->m_parent ; // whose the bigger subtree, W or X? (depth includes W or X itself) long Wdepth = 0; long Xdepth = 0; if ( W ) Wdepth = W->m_depth; if ( X ) Xdepth = X->m_depth; // debug msg //fprintf(stderr,"A=%li AP=%li N=%li W=%li X=%li Q=%li T=%li " //"Wdepth=%li Xdepth=%li\n",A,AP,N,W,X,Q,T,Wdepth,Xdepth); // goto Xdeeper if X is deeper if ( Wdepth < Xdepth ) goto Xdeeper; // N's parent becomes A's parent N->m_parent = AP; // A's parent becomes N A->m_parent = N; // X's parent becomes A if ( X ) X->m_parent = A; // A's parents kid becomes N if ( AP ) { if ( AP->m_right == A ) AP->m_right = N; else AP->m_left = N; } // if A had no parent, it was the headNode else { //fprintf(stderr,"changing head node from %li to %li\n", //m_headNode,N); m_headNode = N; } // N's right kid becomes A N->m_left = A; // A's left kid becomes X A->m_right = X; // . compute A's depth from it's X and B kids // . it should be one less if Xdepth smaller than Wdepth // . might set m_depth[A] to computeDepth(A) if we have problems if ( Xdepth < Wdepth ) A->m_depth -= 2; else A->m_depth -= 1; // N gains a depth iff W and X were of equal depth if ( Wdepth == Xdepth ) N->m_depth += 1; // now we're done, return the new pivot that replaced A return N; // come here if X is deeper Xdeeper: // X's parent becomes A's parent X->m_parent = AP; // A's parent becomes X A->m_parent = X; // N's parent becomes X N->m_parent = X; // Q's parent becomes N if ( Q ) Q->m_parent = N; // T's parent becomes A if ( T ) T->m_parent = A; // A's parent's kid becomes X if ( AP ) { if ( AP->m_right == A ) AP->m_right = X; else AP->m_left = X; } // if A had no parent, it was the headNode else { //fprintf(stderr,"changing head node2 from %li to %li\n", //m_headNode,X); m_headNode = X; } // A's left kid becomes T A->m_right = T; // N's right kid becomes Q N->m_left = Q; // X's left kid becomes N X->m_right = N; // X's right kid becomes A X->m_left = A; // X's depth increases by 1 since it gained 1 level of 2 new kids X->m_depth += 1; // N's depth decreases by 1 N->m_depth -= 1; // A's depth decreases by 2 A->m_depth -= 2; // now we're done, return the new pivot that replaced A return X; }