2 * Copyright 2007 Secure Endpoints Inc.
6 * This software has been released under the terms of the IBM Public
7 * License. For details, see the LICENSE file in the top-level source
8 * directory or online at http://www.openafs.org/dl/license10.html
10 * Thanks to Jan Jannink for B+ tree algorithms.
22 /******************* statistics globals *********************************/
23 afs_uint32 bplus_lookup_hits = 0;
24 afs_uint32 bplus_lookup_hits_inexact = 0;
25 afs_uint32 bplus_lookup_misses = 0;
26 afs_uint32 bplus_lookup_ambiguous = 0;
27 afs_uint32 bplus_create_entry = 0;
28 afs_uint32 bplus_remove_entry = 0;
29 afs_uint32 bplus_build_tree = 0;
30 afs_uint32 bplus_free_tree = 0;
31 afs_uint32 bplus_dv_error = 0;
33 afs_uint64 bplus_lookup_time = 0;
34 afs_uint64 bplus_create_time = 0;
35 afs_uint64 bplus_remove_time = 0;
36 afs_uint64 bplus_build_time = 0;
37 afs_uint64 bplus_free_time = 0;
39 /*********************** private functions *************************/
40 static void initFreeNodePool(Tree *B, int quantity);
41 static Nptr getFreeNode(Tree *B);
42 static void putFreeNode(Tree *B, Nptr self);
43 static void cleanupNodePool(Tree *B);
45 static Nptr descendToLeaf(Tree *B, Nptr curr);
46 static int getSlot(Tree *B, Nptr curr);
47 static int findKey(Tree *B, Nptr curr, int lo, int hi);
48 static int bestMatch(Tree *B, Nptr curr, int slot);
50 static Nptr getDataNode(Tree *B, keyT key, dataT data);
51 static Nptr descendSplit(Tree *B, Nptr curr);
52 static void insertEntry(Tree *B, Nptr node, int slot, Nptr sibling, Nptr downPtr);
53 static void placeEntry(Tree *B, Nptr node, int slot, Nptr downPtr);
54 static Nptr split(Tree *B, Nptr node);
55 static void makeNewRoot(Tree *B, Nptr oldRoot, Nptr newNode);
57 static Nptr descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc, Nptr parent);
58 static void collapseRoot(Tree *B, Nptr oldRoot, Nptr newRoot);
59 static void removeEntry(Tree *B, Nptr curr, int slot);
60 static Nptr merge(Tree *B, Nptr left, Nptr right, Nptr anchor);
61 static Nptr shift(Tree *B, Nptr left, Nptr right, Nptr anchor);
63 static void _clrentry(Nptr node, int entry);
64 static void _pushentry(Nptr node, int entry, int offset);
65 static void _pullentry(Nptr node, int entry, int offset);
66 static void _xferentry(Nptr srcNode, int srcEntry, Nptr destNode, int destEntry);
67 static void _setentry(Nptr node, int entry, keyT key, Nptr downNode);
70 static int _isRoot(Tree *B, Nptr n)
72 int flagset = ((n->flags & isROOT) == isROOT);
77 if (flagset && n != getroot(B))
83 static int _isFew(Tree *B, Nptr n)
85 int flagset = ((n->flags & FEWEST) == FEWEST);
86 int fanout = getminfanout(B, n);
87 int entries = numentries(n);
88 int mincnt = entries <= fanout;
93 if (flagset && !mincnt || !flagset && mincnt)
99 static int _isFull(Tree *B, Nptr n)
101 int flagset = ((n->flags & isFULL) == isFULL);
102 int maxcnt = numentries(n) == getfanout(B);
107 if (flagset && !maxcnt || !flagset && maxcnt)
112 #endif /* DEBUG_BTREE */
114 /***********************************************************************\
115 | B+tree Initialization and Cleanup Routines |
116 \***********************************************************************/
118 /******************** Set up B+tree structure **********************/
119 Tree *initBtree(unsigned int poolsz, unsigned int fanout, KeyCmp keyCmp)
123 dataT data = {0,0,0,0};
125 if (fanout > MAX_FANOUT)
128 setbplustree(B, malloc(sizeof(Tree)));
129 memset(B, 0, sizeof(Tree));
130 setfanout(B, fanout);
131 setminfanout(B, (fanout + 1) >> 1);
132 initFreeNodePool(B, poolsz);
134 setleaf(B, getFreeNode(B)); /* set up the first leaf node */
135 setroot(B, getleaf(B)); /* the root is initially the leaf */
136 setflag(getroot(B), isLEAF);
137 setflag(getroot(B), isROOT);
138 setflag(getroot(B), FEWEST);
143 setcomparekeys(B, keyCmp);
146 sprintf(B->message, "INIT: B+tree of fanout %d at %10p.\n", fanout, (void *)B);
147 OutputDebugString(B->message);
153 /******************** Clean up B+tree structure ********************/
155 * dirlock must be write locked
157 void freeBtree(Tree *B)
160 sprintf(B->message, "FREE: B+tree at %10p.\n", (void *) B);
161 OutputDebugString(B->message);
166 memset(B, 0, sizeof(*B));
171 /***********************************************************************\
172 | Find location for data |
173 \***********************************************************************/
175 /********************** top level lookup **********************/
176 Nptr bplus_Lookup(Tree *B, keyT key)
181 sprintf(B->message, "LOOKUP: key %s.\n", key.name);
182 OutputDebugString(B->message);
185 setfunkey(B, key); /* set search key */
186 findNode = descendToLeaf(B, getroot(B)); /* start search from root node */
194 slot = findKey(B, findNode, 1, numentries(findNode));
195 dataNode = getnode(findNode, slot);
196 data = getdatavalue(dataNode);
198 sprintf(B->message, "LOOKUP: %s found on page %d value (%d.%d.%d).\n",
200 getnodenumber(B, findNode),
205 sprintf(B->message, "LOOKUP: not found!\n");
206 OutputDebugString(B->message);
212 /********************** `recurse' down B+tree **********************/
213 static Nptr descendToLeaf(Tree *B, Nptr curr)
220 memset(prev, 0, sizeof(prev));
222 for (depth = 0, slot = getSlot(B, curr); isinternal(curr); depth++, slot = getSlot(B, curr)) {
225 curr = getfirstnode(curr);
227 curr = getnode(curr, slot);
233 if ((slot > 0) && !comparekeys(B)(getfunkey(B), getkey(curr, slot), 0))
234 findNode = curr; /* correct key value found */
236 findNode = NONODE; /* key value not in tree */
241 /******************** find slot for search key *********************/
242 static int getSlot(Tree *B, Nptr curr)
246 entries = numentries(curr); /* need this if root is ever empty */
247 slot = !entries ? 0 : findKey(B, curr, 1, entries);
253 /******************** recursive binary search **********************/
254 static int findKey(Tree *B, Nptr curr, int lo, int hi)
256 int mid, findslot = BTERROR;
259 findslot = bestMatch(B, curr, lo); /* recursion base case */
262 if (findslot == BTERROR) {
263 sprintf(B->message, "Bad key ordering on node %d\n", getnodenumber(B, curr));
264 OutputDebugString(B->message);
268 mid = (lo + hi) >> 1;
269 switch (findslot = bestMatch(B, curr, mid)) {
270 case BTLOWER: /* check lower half of range */
271 findslot = findKey(B, curr, lo, mid - 1); /* never in 2-3+trees */
273 case BTUPPER: /* check upper half of range */
274 findslot = findKey(B, curr, mid + 1, hi);
277 sprintf(B->message, "Bad key ordering on node %d\n", getnodenumber(B, curr));
278 OutputDebugString(B->message);
285 /************ comparison of key with a target key slot *************/
286 static int bestMatch(Tree *B, Nptr curr, int slot)
288 int diff, comp, findslot;
290 diff = comparekeys(B)(getfunkey(B), getkey(curr, slot), 0);
291 if (diff < 0) { /* also check previous slot */
293 ((comp = comparekeys(B)(getfunkey(B), getkey(curr, slot - 1), 0)) >= 0))
297 else if (comp < diff) {
298 findslot = BTERROR; /* inconsistent ordering of keys */
304 findslot = BTLOWER; /* key must be below in node ordering */
306 } else { /* or check following slot */
307 if ((slot == numentries(curr)) ||
308 ((comp = comparekeys(B)(getfunkey(B), getkey(curr, slot + 1), 0)) < 0))
312 else if (comp == 0) {
315 else if (comp > diff) {
316 findslot = BTERROR; /* inconsistent ordering of keys */
322 findslot = BTUPPER; /* key must be above in node ordering */
329 /***********************************************************************\
330 | Insert new data into tree |
331 \***********************************************************************/
334 /********************** top level insert call **********************/
335 void insert(Tree *B, keyT key, dataT data)
340 sprintf(B->message, "INSERT: key %s.\n", key.name);
341 OutputDebugString(B->message);
344 setfunkey(B, key); /* set insertion key */
345 setfundata(B, data); /* a node containing data */
346 setsplitpath(B, NONODE);
347 newNode = descendSplit(B, getroot(B)); /* insertion point search from root */
348 if (newNode != getsplitpath(B)) /* indicates the root node has split */
349 makeNewRoot(B, getroot(B), newNode);
353 /***************** recurse down and split back up ******************/
355 descendSplit(Tree *B, Nptr curr)
357 Nptr downNode = NONODE, sibling = NONODE;
365 setsplitpath(B, NONODE);
366 else if (getsplitpath(B) == NONODE)
367 setsplitpath(B, curr); /* indicates where nodes must split */
369 slot = getSlot(B, curr); /* is null only if the root is empty */
370 if (isinternal(curr)) { /* continue recursion to leaves */
372 downNode = descendSplit(B, getfirstnode(curr));
374 downNode = descendSplit(B, getnode(curr, slot));
375 } else if ((slot > 0) && !comparekeys(B)(getfunkey(B), getkey(curr, slot), 0)) {
376 if (!(gettreeflags(B) & TREE_FLAG_UNIQUE_KEYS)) {
377 downNode = getDataNode(B, getfunkey(B), getfundata(B));
378 getdatanext(downNode) = getnode(curr,slot);
379 setnode(curr, slot, downNode);
382 setsplitpath(B, NONODE);
385 downNode = getDataNode(B, getfunkey(B), getfundata(B)); /* an insertion takes place */
387 if (downNode != NONODE) { /* insert only where necessary */
388 if (getsplitpath(B) != NONODE)
389 sibling = split(B, curr); /* a sibling node is prepared */
390 insertEntry(B, curr, slot, sibling, downNode);
396 /*************** determine location of inserted key ****************/
398 insertEntry(Tree *B, Nptr currNode, int slot, Nptr sibling, Nptr downPtr)
400 int split, i, j, k, x, y;
404 sprintf(B->message, "INSERT: slot %d, down node %d.\n", slot, getnodenumber(B, downPtr));
405 OutputDebugString(B->message);
408 if (sibling == NONODE) { /* no split occurred */
409 placeEntry(B, currNode, slot + 1, downPtr);
411 else { /* split entries between the two */
412 if isinternal(currNode) {
414 split = getfanout(B) - getminfanout(B, currNode);
415 } else if (isroot(currNode)) {
416 /* split the root node and turn it into just a leaf */
418 split = getminfanout(B, currNode);
421 split = getminfanout(B, currNode);
423 j = (slot != split ? 1 : 0);
424 k = (slot >= split ? 1 : 0);
427 * Move entries from the top half of the current node to
428 * to the sibling node.
429 * The number of entries to move is dependent upon where
430 * the new entry is going to be added in relationship to
431 * the split slot. (slots are 1-based). If order to produce
432 * a balanced tree, if the insertion slot is greater than
433 * the split we move one less entry as the new entry will
434 * be inserted into the sibling.
436 * If the node that is being split is an internal node (i != 0)
437 * then we move one less entry due to the extra down pointer
438 * when the split slot is not equal to the insertion slot
440 for (x = split + k + j * i, y = 1; x <= getfanout(B); x++, y++) {
441 xferentry(currNode, x, sibling, y); /* copy entries to sibling */
442 clrentry(currNode, x);
443 decentries(currNode);
447 if (getkey(sibling, numentries(sibling)).name == NULL)
451 clrflag(currNode, isFULL);
452 if (numentries(currNode) == getminfanout(B, currNode))
453 setflag(currNode, FEWEST); /* never happens in even size nodes */
456 if (numentries(sibling) > getfanout(B))
459 if (numentries(sibling) == getfanout(B))
460 setflag(sibling, isFULL); /* only ever happens in 2-3+trees */
462 if (numentries(sibling) > getminfanout(B, sibling))
463 clrflag(sibling, FEWEST);
465 if (i) { /* set first pointer of internal node */
467 setfirstnode(sibling, getnode(currNode, split + k));
468 decentries(currNode);
469 if (numentries(currNode) == getminfanout(B, currNode))
470 setflag(currNode, FEWEST);
472 clrflag(currNode, FEWEST);
475 setfirstnode(sibling, downPtr);
478 if (j) { /* insert new entry into correct spot */
480 placeEntry(B, sibling, slot - split + 1 - i, downPtr);
482 placeEntry(B, currNode, slot + 1, downPtr);
484 /* set key separating nodes */
486 key = getkey(sibling, 1);
488 Nptr leaf = getfirstnode(sibling);
489 while ( isinternal(leaf) )
490 leaf = getfirstnode(leaf);
491 key = getkey(leaf, 1);
496 placeEntry(B, sibling, 1, downPtr);
500 /************ place key into appropriate node & slot ***************/
502 placeEntry(Tree *B, Nptr node, int slot, Nptr downPtr)
512 if (numentries(node) != 0 && getkey(node, numentries(node)).name == NULL)
515 for (x = numentries(node); x >= slot && x != 0; x--) /* make room for new entry */
516 pushentry(node, x, 1);
517 setentry(node, slot, getfunkey(B), downPtr);/* place it in correct slot */
519 incentries(node); /* adjust entry counter */
521 if (getkey(node, numentries(node)).name == NULL)
525 if (numentries(node) == getfanout(B))
526 setflag(node, isFULL);
527 if (numentries(node) > getminfanout(B, node))
528 clrflag(node, FEWEST);
530 setflag(node, FEWEST);
534 /***************** split full node and set flags *******************/
536 split(Tree *B, Nptr node)
540 sibling = getFreeNode(B);
542 setflag(sibling, FEWEST); /* set up node flags */
545 setflag(sibling, isLEAF);
546 setnextnode(sibling, getnextnode(node));/* adjust leaf pointers */
547 setnextnode(node, sibling);
549 if (getsplitpath(B) == node)
550 setsplitpath(B, NONODE); /* no more splitting needed */
553 clrflag(node, isROOT);
559 /********************** build new root node ************************/
561 makeNewRoot(Tree *B, Nptr oldRoot, Nptr newNode)
563 setroot(B, getFreeNode(B));
565 setfirstnode(getroot(B), oldRoot); /* old root becomes new root's child */
566 setentry(getroot(B), 1, getfunkey(B), newNode); /* old root's sibling also */
567 incentries(getroot(B));
569 if (numentries(getroot(B)) > getfanout(B))
573 /* the oldRoot's isROOT flag was cleared in split() */
574 setflag(getroot(B), isROOT);
575 setflag(getroot(B), FEWEST);
576 clrflag(getroot(B), isLEAF);
581 /***********************************************************************\
582 | Delete data from tree |
583 \***********************************************************************/
585 /********************** top level delete call **********************\
587 | The recursive call for deletion carries 5 additional parameters
588 | which may be needed to rebalance the B+tree when removing the key.
589 | These parameters are:
590 | 1. immediate left neighbor of the current node
591 | 2. immediate right neighbor of the current node
592 | 3. the anchor of the current node and left neighbor
593 | 4. the anchor of the current node and right neighbor
594 | 5. the parent of the current node
596 | All of these parameters are simple to calculate going along the
597 | recursive path to the leaf nodes and the point of key deletion.
598 | At that time, the algorithm determines which node manipulations
599 | are most efficient, that is, cause the least rearranging of data,
600 | and minimize the need for non-local key manipulation.
602 \***********************************************************************/
603 void delete(Tree *B, keyT key)
608 sprintf(B->message, "DELETE: key %s.\n", key.name);
609 OutputDebugString(B->message);
612 setfunkey(B, key); /* set deletion key */
613 setmergepath(B, NONODE);
614 newNode = descendBalance(B, getroot(B), NONODE, NONODE, NONODE, NONODE, NONODE);
615 if (isnode(newNode)) {
617 sprintf(B->message, "DELETE: collapsing node %d", getnodenumber(B, newNode));
618 OutputDebugString(B->message);
620 collapseRoot(B, getroot(B), newNode); /* remove root when superfluous */
625 /********************** remove old root node ***********************/
627 collapseRoot(Tree *B, Nptr oldRoot, Nptr newRoot)
631 sprintf(B->message, "COLLAPSE: old %d, new %d.\n", getnodenumber(B, oldRoot), getnodenumber(B, newRoot));
632 OutputDebugString(B->message);
633 showNode(B, "collapseRoot oldRoot", oldRoot);
634 showNode(B, "collapseRoot newRoot", newRoot);
638 setflag(newRoot, isROOT);
639 clrflag(newRoot, FEWEST);
640 putFreeNode(B, oldRoot);
641 dectreeheight(B); /* the height of the tree decreases */
645 /**************** recurse down and balance back up *****************/
647 descendBalance(Tree *B, Nptr curr, Nptr left, Nptr right, Nptr lAnc, Nptr rAnc, Nptr parent)
649 Nptr newMe=NONODE, myLeft=NONODE, myRight=NONODE, lAnchor=NONODE, rAnchor=NONODE, newNode=NONODE;
650 int slot = 0, notleft = 0, notright = 0, fewleft = 0, fewright = 0, test = 0;
653 sprintf(B->message, "descendBalance curr %d, left %d, right %d, lAnc %d, rAnc %d, parent %d\n",
654 curr ? getnodenumber(B, curr) : -1,
655 left ? getnodenumber(B, left) : -1,
656 right ? getnodenumber(B, right) : -1,
657 lAnc ? getnodenumber(B, lAnc) : -1,
658 rAnc ? getnodenumber(B, rAnc) : -1,
659 parent ? getnodenumber(B, parent) : -1);
660 OutputDebugString(B->message);
664 setmergepath(B,NONODE);
665 else if (getmergepath(B) == NONODE)
666 setmergepath(B, curr); /* mark which nodes may need rebalancing */
668 slot = getSlot(B, curr);
670 if (isinternal(curr)) /* set up next recursion call's parameters */
673 newNode = getfirstnode(curr);
674 myLeft = !isnode(left) ? NONODE : getlastnode(left);
678 newNode = getnode(curr, slot);
680 myLeft = getfirstnode(curr);
682 myLeft = getnode(curr, slot - 1);
686 if (slot == numentries(curr)) {
687 myRight = !isnode(right) ? NONODE : getfirstnode(right);
691 myRight = getnode(curr, slot + 1);
694 newMe = descendBalance(B, newNode, myLeft, myRight, lAnchor, rAnchor, curr);
696 else if ((slot > 0) && !comparekeys(B)(getfunkey(B), getkey(curr, slot), 0))
702 newNode = getnode(curr, slot);
703 next = getdatanext(newNode);
706 * We only delete exact matches.
708 if (!comparekeys(B)(getfunkey(B), getdatakey(newNode), EXACT_MATCH)) {
709 /* exact match, free the first entry */
710 setnode(curr, slot, next);
712 if (next == NONODE) {
713 /* delete this key as there are no more data values */
716 /* otherwise, there are more and we leave the key in place */
717 setnode(curr, slot, next);
718 putFreeNode(B, newNode);
720 /* but do not delete the key */
722 setmergepath(B, NONODE);
724 } else if (next == NONODE) {
726 * we didn't find an exact match and there are no more
727 * choices. so we leave it alone and remove nothing.
730 setmergepath(B, NONODE);
732 /* The first data node doesn't match but there are other
733 * options. So we must determine if any of the next nodes
734 * are the one we are looking for.
739 if (!comparekeys(B)(getfunkey(B), getdatakey(next), EXACT_MATCH)) {
740 /* we found the one to delete */
741 getdatanext(prev) = getdatanext(next);
742 putFreeNode(B, next);
746 next = getdatanext(next);
749 /* do not delete the key */
751 setmergepath(B, NONODE);
756 newMe = NONODE; /* no deletion possible, key not found */
757 setmergepath(B, NONODE);
760 /***************** rebalancing tree after deletion *****************\
762 | The simplest B+tree rebalancing consists of the following rules.
764 | If a node underflows:
765 | CASE 1 check if it is the root, and collapse it if it is,
766 | CASE 2 otherwise, check if both of its neighbors are minimum
767 | sized and merge the underflowing node with one of them,
768 | CASE 3 otherwise shift surplus entries to the underflowing node.
770 | The choice of which neighbor to use is optional. However, the
771 | rebalancing rules that follow also ensure whenever possible
772 | that the merges and shifts which do occur use a neighbor whose
773 | anchor is the parent of the underflowing node.
775 | Cases 3, 4, 5 below are more an optimization than a requirement,
776 | and can be omitted, with a change of the action value in case 6,
777 | which actually corresponds to the third case described above.
779 \***********************************************************************/
781 /* begin deletion, working upwards from leaves */
783 if (newMe != NONODE) { /* this node removal doesn't consider duplicates */
785 sprintf(B->message, "descendBalance DELETE: slot %d, node %d.\n", slot, getnodenumber(B, curr));
786 OutputDebugString(B->message);
789 removeEntry(B, curr, slot + (newMe != newNode)); /* removes one of two */
792 showNode(B, "descendBalance curr", curr);
796 if (getmergepath(B) == NONODE)
798 else { /* tree rebalancing rules for node merges and shifts */
799 notleft = !isnode(left);
800 notright = !isnode(right);
802 fewleft = isfew(left); /* only used when defined */
804 fewright = isfew(right);
806 /* CASE 1: prepare root node (curr) for removal */
807 if (notleft && notright) {
808 test = isleaf(curr); /* check if B+tree has become empty */
809 newNode = test ? NONODE : getfirstnode(curr);
811 /* CASE 2: the merging of two nodes is a must */
812 else if ((notleft || fewleft) && (notright || fewright)) {
813 test = (lAnc != parent);
814 newNode = test ? merge(B, curr, right, rAnc) : merge(B, left, curr, lAnc);
816 /* CASE 3: choose the better of a merge or a shift */
817 else if (!notleft && fewleft && !notright && !fewright) {
818 test = (rAnc != parent) && (curr == getmergepath(B));
819 newNode = test ? merge(B, left, curr, lAnc) : shift(B, curr, right, rAnc);
821 /* CASE 4: also choose between a merge or a shift */
822 else if (!notleft && !fewleft && !notright && fewright) {
823 test = !(lAnc == parent) && (curr == getmergepath(B));
824 newNode = test ? merge(B, curr, right, rAnc) : shift(B, left, curr, lAnc);
826 /* CASE 5: choose the more effective of two shifts */
827 else if (lAnc == rAnc) { /* => both anchors are the parent */
828 test = (numentries(left) <= numentries(right));
829 newNode = test ? shift(B, curr, right, rAnc) : shift(B, left, curr, lAnc);
831 /* CASE 6: choose the shift with more local effect */
832 else { /* if omitting cases 3,4,5 use below */
833 test = (lAnc == parent); /* test = (!notleft && !fewleft); */
834 newNode = test ? shift(B, left, curr, lAnc) : shift(B, curr, right, rAnc);
839 sprintf(B->message, "descendBalance returns %d\n", getnodenumber(B, newNode));
840 OutputDebugString(B->message);
846 /**************** remove key and pointer from node *****************/
848 removeEntry(Tree *B, Nptr curr, int slot)
852 putFreeNode(B, getnode(curr, slot)); /* return deleted node to free list */
853 for (x = slot; x < numentries(curr); x++)
854 pullentry(curr, x, 1); /* adjust node with removed key */
856 clrflag(curr, isFULL); /* keep flag information up to date */
857 if (numentries(curr) > getminfanout(B, curr))
858 clrflag(curr, FEWEST);
860 setflag(curr, FEWEST);
864 /******* merge a node pair & set emptied node up for removal *******/
866 merge(Tree *B, Nptr left, Nptr right, Nptr anchor)
871 sprintf(B->message, "MERGE: left %d, right %d.\n", getnodenumber(B, left), getnodenumber(B, right));
872 OutputDebugString(B->message);
873 showNode(B, "pre-merge anchor", anchor);
874 showNode(B, "pre-merge left", left);
875 showNode(B, "pre-merge right", right);
878 if (isinternal(left)) {
879 incentries(left); /* copy key separating the nodes */
881 if (numentries(left) > getfanout(B))
884 setfunkey(B, getkey(right, 1)); /* defined but maybe just deleted */
885 z = getSlot(B, anchor); /* needs the just calculated key */
886 setfunkey(B, getkey(anchor, z)); /* set slot to delete in anchor */
887 setentry(left, numentries(left), getfunkey(B), getfirstnode(right));
890 setnextnode(left, getnextnode(right));
892 for (x = numentries(left) + 1, y = 1; y <= numentries(right); x++, y++) {
895 if (numentries(left) > getfanout(B))
898 xferentry(right, y, left, x); /* transfer entries to left node */
902 if (numentries(left) > getminfanout(B, left))
903 clrflag(left, FEWEST);
904 if (numentries(left) == getfanout(B))
905 setflag(left, isFULL); /* never happens in even size nodes */
907 if (getmergepath(B) == left || getmergepath(B) == right)
908 setmergepath(B, NONODE); /* indicate rebalancing is complete */
911 showNode(B, "post-merge anchor", anchor);
912 showNode(B, "post-merge left", left);
913 showNode(B, "post-merge right", right);
919 /****** shift entries in a node pair & adjust anchor key value *****/
921 shift(Tree *B, Nptr left, Nptr right, Nptr anchor)
926 sprintf(B->message, "SHIFT: left %d, right %d, anchor %d.\n",
927 getnodenumber(B, left),
928 getnodenumber(B, right),
929 getnodenumber(B, anchor));
930 OutputDebugString(B->message);
931 showNode(B, "pre-shift anchor", anchor);
932 showNode(B, "pre-shift left", left);
933 showNode(B, "pre-shift right", right);
936 i = isinternal(left);
938 if (numentries(left) < numentries(right)) { /* shift entries to left */
939 y = (numentries(right) - numentries(left)) >> 1;
940 x = numentries(left) + y;
941 setfunkey(B, getkey(right, y + 1 - i)); /* set new anchor key value */
942 z = getSlot(B, anchor); /* find slot in anchor node */
944 if (z == 0 && !isroot(anchor))
947 if (i) { /* move out old anchor value */
948 decentries(right); /* adjust for shifting anchor */
951 if (numentries(left) > getfanout(B))
954 setentry(left, numentries(left), getkey(anchor, z), getfirstnode(right));
955 setfirstnode(right, getnode(right, y + 1 - i));
957 clrflag(right, isFULL);
958 setkey(anchor, z, getfunkey(B)); /* set new anchor value */
959 for (z = y, y -= i; y > 0; y--, x--) {
960 decentries(right); /* adjust entry count */
963 if (numentries(left) > getfanout(B))
966 xferentry(right, y, left, x); /* transfer entries over */
969 for (x = 1; x <= numentries(right); x++) /* adjust reduced node */
970 pullentry(right, x, z);
972 else if (numentries(left) > numentries(right)) { /* shift entries to right */
973 y = (numentries(left) - numentries(right)) >> 1;
974 x = numentries(left) - y + 1;
976 for (z = numentries(right); z > 0; z--) /* adjust increased node */
977 pushentry(right, z, y);
979 setfunkey(B, getkey(left, x)); /* set new anchor key value */
980 z = getSlot(B, anchor) + 1;
985 if (numentries(right) > getfanout(B))
988 setentry(right, y, getkey(anchor, z), getfirstnode(right));
989 setfirstnode(right, getnode(left, x));
991 clrflag(left, isFULL);
992 setkey(anchor, z, getfunkey(B));
993 for (x = numentries(left) + i, y -= i; y > 0; y--, x--) {
997 if (numentries(right) > getfanout(B))
1000 xferentry(left, x, right, y); /* transfer entries over */
1008 #endif /* DEBUG_BTREE */
1010 if (numentries(left) > getminfanout(B, left)) /* adjust node flags */
1011 clrflag(left, FEWEST); /* never happens in 2-3+trees */
1013 setflag(left, FEWEST);
1014 if (numentries(right) > getminfanout(B, right))
1015 clrflag(right, FEWEST); /* never happens in 2-3+trees */
1017 setflag(right, FEWEST);
1018 setmergepath(B, NONODE);
1021 sprintf(B->message, "SHIFT: left %d, right %d.\n", getnodenumber(B, left), getnodenumber(B, right));
1022 OutputDebugString(B->message);
1023 showNode(B, "post-shift anchor", anchor);
1024 showNode(B, "post-shift left", left);
1025 showNode(B, "post-shift right", right);
1033 _clrentry(Nptr node, int entry)
1035 if (getkey(node,entry).name != NULL) {
1036 free(getkey(node,entry).name);
1037 getkey(node,entry).name = NULL;
1039 getnode(node,entry) = NONODE;
1043 _pushentry(Nptr node, int entry, int offset)
1045 if (getkey(node,entry + offset).name != NULL)
1046 free(getkey(node,entry + offset).name);
1051 getkey(node,entry + offset).name = strdup(getkey(node,entry).name);
1053 if ( getnode(node, entry) == NONODE )
1056 getnode(node,entry + offset) = getnode(node,entry);
1060 _pullentry(Nptr node, int entry, int offset)
1062 if (getkey(node,entry).name != NULL)
1063 free(getkey(node,entry).name);
1064 getkey(node,entry).name = strdup(getkey(node,entry + offset).name);
1066 if ( getnode(node, entry + offset) == NONODE )
1069 getnode(node,entry) = getnode(node,entry + offset);
1073 _xferentry(Nptr srcNode, int srcEntry, Nptr destNode, int destEntry)
1075 if (getkey(destNode,destEntry).name != NULL)
1076 free(getkey(destNode,destEntry).name);
1077 getkey(destNode,destEntry).name = strdup(getkey(srcNode,srcEntry).name);
1079 if ( getnode(srcNode, srcEntry) == NONODE )
1082 getnode(destNode,destEntry) = getnode(srcNode,srcEntry);
1086 _setentry(Nptr node, int entry, keyT key, Nptr downNode)
1088 if (getkey(node,entry).name != NULL)
1089 free(getkey(node,entry).name);
1090 getkey(node,entry).name = strdup(key.name);
1092 if ( downNode == NONODE )
1095 getnode(node,entry) = downNode;
1099 /***********************************************************************\
1100 | Empty Node Utilities |
1101 \***********************************************************************/
1103 /********************* Set up initial pool of free nodes ***********/
1105 initFreeNodePool(Tree *B, int quantity)
1110 setfirstallnode(B, NONODE);
1111 setfirstfreenode(B, NONODE);
1113 for (i = 0, p = NONODE; i < quantity; i++) {
1114 n = malloc(sizeof(*n));
1115 memset(n, 0, sizeof(*n));
1116 setnodenumber(B,n,i);
1119 setnextnode(p, n); /* insert node into free node list */
1122 setfirstfreenode(B, n);
1123 setfirstallnode(B, n);
1127 setnextnode(p, NONODE); /* indicates end of free node list */
1128 setallnode(p, NONODE); /* indicates end of all node list */
1130 setpoolsize(B, quantity);
1134 /******************* Cleanup Free Node Pool **************************/
1137 cleanupNodePool(Tree *B)
1142 for ( i=0, node = getfirstallnode(B); node != NONODE && i<getpoolsize(B); node = next, i++ ) {
1144 if ( getdatakey(node).name ) {
1145 free(getdatakey(node).name);
1146 getdatakey(node).name = NULL;
1148 if ( getdatavalue(node).longname ) {
1149 free(getdatavalue(node).longname);
1150 getdatavalue(node).longname = NULL;
1152 } else { /* data node */
1153 for ( j=1; j<=getfanout(B); j++ ) {
1154 if (getkey(node, j).name)
1155 free(getkey(node, j).name);
1158 next = getallnode(node);
1163 /************** take a free B+tree node from the pool **************/
1165 getFreeNode(Tree *B)
1167 Nptr newNode = getfirstfreenode(B);
1169 if (newNode != NONODE) {
1170 setfirstfreenode(B, getnextnode(newNode)); /* adjust free node list */
1171 setnextnode(newNode, NONODE); /* remove node from list */
1174 newNode = malloc(sizeof(*newNode));
1175 memset(newNode, 0, sizeof(*newNode));
1177 setallnode(newNode, getfirstallnode(B));
1178 setfirstallnode(B, newNode);
1179 setnodenumber(B, newNode, getpoolsize(B));
1180 setpoolsize(B, getpoolsize(B) + 1);
1183 clearflags(newNode); /* Sets MAGIC */
1188 /************* return a deleted B+tree node to the pool ************/
1190 putFreeNode(Tree *B, Nptr node)
1198 if ( getdatakey(node).name )
1199 free(getdatakey(node).name);
1200 } else { /* data node */
1201 for ( i=1; i<=getfanout(B); i++ ) {
1202 if (getkey(node, i).name)
1203 free(getkey(node, i).name);
1207 /* free nodes do not have MAGIC set */
1208 memset(&nAdr(node), 0, sizeof(nAdr(node)));
1209 setnextnode(node, getfirstfreenode(B)); /* add node to list */
1210 setfirstfreenode(B, node); /* set it to be list head */
1214 /******* fill a free data node with a key and associated data ******/
1216 getDataNode(Tree *B, keyT key, dataT data)
1218 Nptr newNode = getFreeNode(B);
1220 setflag(newNode, isDATA);
1221 getdatakey(newNode).name = strdup(key.name);
1222 getdatavalue(newNode) = data;
1223 getdatanext(newNode) = NONODE;
1230 /***********************************************************************\
1231 | B+tree Printing Utilities |
1232 \***********************************************************************/
1234 /*********************** B+tree node printer ***********************/
1235 void showNode(Tree *B, const char * where, Nptr n)
1239 sprintf(B->message, "- -- -- -- -- -- -- -- -- -- -- -- -\n");
1240 OutputDebugString(B->message);
1241 sprintf(B->message, "| %-20s |\n", where);
1242 OutputDebugString(B->message);
1243 sprintf(B->message, "| node %6d ", getnodenumber(B, n));
1244 OutputDebugString(B->message);
1245 sprintf(B->message, " magic %4x |\n", getmagic(n));
1246 OutputDebugString(B->message);
1247 sprintf(B->message, "- -- -- -- -- -- -- -- -- -- -- -- -\n");
1248 OutputDebugString(B->message);
1249 sprintf(B->message, "| flags %1d%1d%1d%1d ", isfew(n), isfull(n), isroot(n), isleaf(n));
1250 OutputDebugString(B->message);
1251 sprintf(B->message, "| keys = %5d ", numentries(n));
1252 OutputDebugString(B->message);
1253 sprintf(B->message, "| node = %6d |\n", getnodenumber(B, getfirstnode(n)));
1254 OutputDebugString(B->message);
1255 for (x = 1; x <= numentries(n); x++) {
1256 sprintf(B->message, "| entry %6d ", x);
1257 OutputDebugString(B->message);
1258 sprintf(B->message, "| key = %6s ", getkey(n, x).name);
1259 OutputDebugString(B->message);
1260 sprintf(B->message, "| node = %6d |\n", getnodenumber(B, getnode(n, x)));
1261 OutputDebugString(B->message);
1263 sprintf(B->message, "- -- -- -- -- -- -- -- -- -- -- -- -\n");
1264 OutputDebugString(B->message);
1267 /****************** B+tree class variable printer ******************/
1268 void showBtree(Tree *B)
1270 sprintf(B->message, "- -- -- -- -- -- -\n");
1271 OutputDebugString(B->message);
1272 sprintf(B->message, "| B+tree %10p |\n", (void *) B);
1273 OutputDebugString(B->message);
1274 sprintf(B->message, "- -- -- -- -- -- -\n");
1275 OutputDebugString(B->message);
1276 sprintf(B->message, "| root %6d |\n", getnodenumber(B, getroot(B)));
1277 OutputDebugString(B->message);
1278 sprintf(B->message, "| leaf %6d |\n", getnodenumber(B, getleaf(B)));
1279 OutputDebugString(B->message);
1280 sprintf(B->message, "| fanout %3d |\n", getfanout(B) + 1);
1281 OutputDebugString(B->message);
1282 sprintf(B->message, "| minfanout %3d |\n", getminfanout(B, getroot(B)) + 1);
1283 OutputDebugString(B->message);
1284 sprintf(B->message, "| height %3d |\n", gettreeheight(B));
1285 OutputDebugString(B->message);
1286 sprintf(B->message, "| freenode %6d |\n", getnodenumber(B, getfirstfreenode(B)));
1287 OutputDebugString(B->message);
1288 sprintf(B->message, "| theKey %6s |\n", getfunkey(B).name);
1289 OutputDebugString(B->message);
1290 sprintf(B->message, "| theData %d.%d.%d |\n", getfundata(B).volume,
1291 getfundata(B).vnode, getfundata(B).unique);
1292 OutputDebugString(B->message);
1293 sprintf(B->message, "- -- -- -- -- -- -\n");
1294 OutputDebugString(B->message);
1298 listBtreeNodes(Tree *B, const char * parent_desc, Nptr node)
1304 if (isntnode(node)) {
1305 sprintf(B->message, "%s - NoNode!!!\n");
1306 OutputDebugString(B->message);
1312 data = getdatavalue(node);
1313 sprintf(B->message, "%s - data node %d (%d.%d.%d)\n",
1314 parent_desc, getnodenumber(B, node),
1315 data.fid.volume, data.fid.vnode, data.fid.unique);
1316 OutputDebugString(B->message);
1319 showNode(B, parent_desc, node);
1321 if ( isinternal(node) || isroot(node) ) {
1322 sprintf(thisnode, "parent %6d", getnodenumber(B , node));
1324 OutputDebugString(B->message);
1325 for ( i= isinternal(node) ? 0 : 1; i <= numentries(node); i++ ) {
1326 listBtreeNodes(B, thisnode, i == 0 ? getfirstnode(node) : getnode(node, i));
1331 /*********************** B+tree data printer ***********************/
1333 listBtreeValues(Tree *B, Nptr n, int num)
1339 for (slot = 1; (n != NONODE) && num && numentries(n); num--) {
1340 if (comparekeys(B)(getkey(n, slot),prev, 0) < 0) {
1341 sprintf(B->message, "BOMB %8s\n", getkey(n, slot).name);
1342 OutputDebugString(B->message);
1345 prev = getkey(n, slot);
1346 data = getdatavalue(getnode(n, slot));
1347 sprintf(B->message, "%8s (%d.%d.%d)\n",
1348 prev.name, data.fid.volume, data.fid.vnode, data.fid.unique);
1349 OutputDebugString(B->message);
1350 if (++slot > numentries(n))
1351 n = getnextnode(n), slot = 1;
1353 sprintf(B->message, "\n\n");
1354 OutputDebugString(B->message);
1357 /******************** entire B+tree data printer *******************/
1359 listAllBtreeValues(Tree *B)
1361 listBtreeValues(B, getleaf(B), BTERROR);
1363 #endif /* DEBUG_BTREE */
1366 findAllBtreeValues(Tree *B)
1369 Nptr n = getleaf(B), l;
1373 for (slot = 1; (n != NONODE) && num && numentries(n); num--) {
1374 if (comparekeys(B)(getkey(n, slot),prev, 0) < 0) {
1375 sprintf(B->message,"BOMB %8s\n", getkey(n, slot).name);
1376 OutputDebugString(B->message);
1381 prev = getkey(n, slot);
1382 l = bplus_Lookup(B, prev);
1385 sprintf(B->message,"BOMB %8s cannot be found\n", prev.name);
1387 sprintf(B->message,"BOMB lookup(%8s) finds wrong node\n", prev.name);
1388 OutputDebugString(B->message);
1394 if (++slot > numentries(n))
1395 n = getnextnode(n), slot = 1;
1400 * the return must be -1, 0, or 1. stricmp() in MSVC 8.0
1401 * does not return only those values.
1404 compareKeys(keyT key1, keyT key2, int flags)
1408 if (flags & EXACT_MATCH)
1409 comp = strcmp(key1.name, key2.name);
1411 comp = stricmp(key1.name, key2.name);
1412 return (comp < 0 ? -1 : (comp > 0 ? 1 : 0));
1415 /* Look up a file name in directory.
1418 op->scp->dirlock is read locked
1421 op->scp->dirlock is read locked
1424 cm_BPlusDirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
1428 Nptr leafNode = NONODE;
1429 LARGE_INTEGER start, end;
1431 if (op->scp->dirBplus == NULL ||
1432 op->dataVersion != op->scp->dirDataVersion) {
1437 QueryPerformanceCounter(&start);
1439 leafNode = bplus_Lookup(op->scp->dirBplus, key);
1440 if (leafNode != NONODE) {
1442 Nptr firstDataNode, dataNode, nextDataNode;
1446 /* Found a leaf that matches the key via a case-insensitive
1447 * match. There may be one or more data nodes that match.
1448 * If we have an exact match, return that.
1449 * If we have an ambiguous match, return an error.
1450 * If we have only one inexact match, return that.
1452 slot = findKey(op->scp->dirBplus, leafNode, 1, numentries(leafNode));
1453 firstDataNode = getnode(leafNode, slot);
1455 for ( dataNode = firstDataNode; dataNode; dataNode = nextDataNode) {
1457 if (!comparekeys(op->scp->dirBplus)(key, getdatakey(dataNode), EXACT_MATCH) ) {
1461 nextDataNode = getdatanext(dataNode);
1465 *cfid = getdatavalue(dataNode).fid;
1467 bplus_lookup_hits++;
1468 } else if (count == 1) {
1469 *cfid = getdatavalue(firstDataNode).fid;
1470 rc = CM_ERROR_INEXACT_MATCH;
1471 bplus_lookup_hits_inexact++;
1473 rc = CM_ERROR_AMBIGUOUS_FILENAME;
1474 bplus_lookup_ambiguous++;
1478 bplus_lookup_misses++;
1481 QueryPerformanceCounter(&end);
1483 bplus_lookup_time += (end.QuadPart - start.QuadPart);
1492 op->scp->dirlock is write locked
1495 op->scp->dirlock is write locked
1497 long cm_BPlusDirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
1502 LARGE_INTEGER start, end;
1505 if (op->scp->dirBplus == NULL ||
1506 op->dataVersion != op->scp->dirDataVersion) {
1511 data.fid.cell = cfid->cell;
1512 data.fid.volume = cfid->volume;
1513 data.fid.vnode = cfid->vnode;
1514 data.fid.unique = cfid->unique;
1515 data.longname = NULL;
1517 QueryPerformanceCounter(&start);
1518 bplus_create_entry++;
1520 insert(op->scp->dirBplus, key, data);
1521 if (!cm_Is8Dot3(entry)) {
1523 dfid.vnode = htonl(data.fid.vnode);
1524 dfid.unique = htonl(data.fid.unique);
1526 cm_Gen8Dot3NameInt(entry, &dfid, shortName, NULL);
1528 key.name = shortName;
1529 data.longname = strdup(entry);
1530 insert(op->scp->dirBplus, key, data);
1533 QueryPerformanceCounter(&end);
1535 bplus_create_time += (end.QuadPart - start.QuadPart);
1544 op->scp->dirlock is write locked
1547 op->scp->dirlock is write locked
1549 int cm_BPlusDirDeleteEntry(cm_dirOp_t * op, char *entry)
1553 Nptr leafNode = NONODE;
1554 LARGE_INTEGER start, end;
1556 if (op->scp->dirBplus == NULL ||
1557 op->dataVersion != op->scp->dirDataVersion) {
1562 QueryPerformanceCounter(&start);
1564 bplus_remove_entry++;
1566 if (op->scp->dirBplus) {
1567 if (!cm_Is8Dot3(entry)) {
1572 leafNode = bplus_Lookup(op->scp->dirBplus, key);
1573 if (leafNode != NONODE) {
1575 Nptr firstDataNode, dataNode, nextDataNode;
1579 /* Found a leaf that matches the key via a case-insensitive
1580 * match. There may be one or more data nodes that match.
1581 * If we have an exact match, return that.
1582 * If we have an ambiguous match, return an error.
1583 * If we have only one inexact match, return that.
1585 slot = findKey(op->scp->dirBplus, leafNode, 1, numentries(leafNode));
1586 firstDataNode = getnode(leafNode, slot);
1588 for ( dataNode = firstDataNode; dataNode; dataNode = nextDataNode) {
1590 if (!comparekeys(op->scp->dirBplus)(key, getdatakey(dataNode), EXACT_MATCH) ) {
1594 nextDataNode = getdatanext(dataNode);
1598 fid = getdatavalue(dataNode).fid;
1600 } else if (count == 1) {
1601 fid = getdatavalue(firstDataNode).fid;
1602 rc = CM_ERROR_INEXACT_MATCH;
1604 rc = CM_ERROR_AMBIGUOUS_FILENAME;
1609 if (rc != CM_ERROR_AMBIGUOUS_FILENAME) {
1610 dfid.vnode = htonl(fid.vnode);
1611 dfid.unique = htonl(fid.unique);
1612 cm_Gen8Dot3NameInt(entry, &dfid, shortName, NULL);
1614 /* delete first the long name and then the short name */
1615 delete(op->scp->dirBplus, key);
1616 key.name = shortName;
1617 delete(op->scp->dirBplus, key);
1620 char * longname = NULL;
1621 /* We need to lookup the 8dot3 name to determine what the
1622 * matching long name is
1624 leafNode = bplus_Lookup(op->scp->dirBplus, key);
1625 if (leafNode != NONODE) {
1627 Nptr firstDataNode, dataNode, nextDataNode;
1631 /* Found a leaf that matches the key via a case-insensitive
1632 * match. There may be one or more data nodes that match.
1633 * If we have an exact match, return that.
1634 * If we have an ambiguous match, return an error.
1635 * If we have only one inexact match, return that.
1637 slot = findKey(op->scp->dirBplus, leafNode, 1, numentries(leafNode));
1638 firstDataNode = getnode(leafNode, slot);
1640 for ( dataNode = firstDataNode; dataNode; dataNode = nextDataNode) {
1642 if (!comparekeys(op->scp->dirBplus)(key, getdatakey(dataNode), EXACT_MATCH) ) {
1646 nextDataNode = getdatanext(dataNode);
1650 longname = getdatavalue(dataNode).longname;
1652 } else if (count == 1) {
1653 longname = getdatavalue(firstDataNode).longname;
1654 rc = CM_ERROR_INEXACT_MATCH;
1656 rc = CM_ERROR_AMBIGUOUS_FILENAME;
1660 if (rc != CM_ERROR_AMBIGUOUS_FILENAME) {
1662 key.name = longname;
1663 delete(op->scp->dirBplus, key);
1666 delete(op->scp->dirBplus, key);
1671 QueryPerformanceCounter(&end);
1673 bplus_remove_time += (end.QuadPart - start.QuadPart);
1681 int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
1682 void *dummy, osi_hyper_t *entryOffsetp)
1684 keyT key = {dep->name};
1688 data.fid.cell = scp->fid.cell;
1689 data.fid.volume = scp->fid.volume;
1690 data.fid.vnode = ntohl(dep->fid.vnode);
1691 data.fid.unique = ntohl(dep->fid.unique);
1692 data.longname = NULL;
1694 /* the Write lock is held in cm_BPlusDirBuildTree() */
1695 insert(scp->dirBplus, key, data);
1696 if (!cm_Is8Dot3(dep->name)) {
1698 dfid.vnode = dep->fid.vnode;
1699 dfid.unique = dep->fid.unique;
1701 cm_Gen8Dot3NameInt(dep->name, &dfid, shortName, NULL);
1703 key.name = shortName;
1704 data.longname = strdup(dep->name);
1705 insert(scp->dirBplus, key, data);
1709 findAllBtreeValues(scp->dirBplus);
1716 * scp->dirlock must be writeLocked before call
1718 * scp->mutex must not be held
1720 long cm_BPlusDirBuildTree(cm_scache_t *scp, cm_user_t *userp, cm_req_t* reqp)
1724 LARGE_INTEGER start, end;
1726 osi_assert(scp->dirBplus == NULL);
1728 QueryPerformanceCounter(&start);
1731 if (scp->dirBplus == NULL) {
1732 scp->dirBplus = initBtree(64, MAX_FANOUT, compareKeys);
1734 if (scp->dirBplus == NULL) {
1738 thyper.HighPart = 0;
1739 rc = cm_ApplyDir(scp, cm_BPlusDirFoo, NULL, &thyper, userp, reqp, NULL);
1742 QueryPerformanceCounter(&end);
1744 bplus_build_time += (end.QuadPart - start.QuadPart);
1749 int cm_MemDumpBPlusStats(FILE *outputFile, char *cookie, int lock)
1754 sprintf(output, "%s - B+ Lookup Hits: %-8d\r\n", cookie, bplus_lookup_hits);
1755 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1756 sprintf(output, "%s - Inexact Hits: %-8d\r\n", cookie, bplus_lookup_hits_inexact);
1757 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1758 sprintf(output, "%s - Ambiguous Hits: %-8d\r\n", cookie, bplus_lookup_ambiguous);
1759 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1760 sprintf(output, "%s - Misses: %-8d\r\n", cookie, bplus_lookup_misses);
1761 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1762 sprintf(output, "%s - Create: %-8d\r\n", cookie, bplus_create_entry);
1763 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1764 sprintf(output, "%s - Remove: %-8d\r\n", cookie, bplus_remove_entry);
1765 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1766 sprintf(output, "%s - Build Tree: %-8d\r\n", cookie, bplus_build_tree);
1767 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1768 sprintf(output, "%s - Free Tree: %-8d\r\n", cookie, bplus_free_tree);
1769 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1770 sprintf(output, "%s - DV Error: %-8d\r\n", cookie, bplus_dv_error);
1771 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1773 sprintf(output, "%s - B+ Time Lookup: %-16I64d\r\n", cookie, bplus_lookup_time);
1774 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1775 sprintf(output, "%s - Create: %-16I64d\r\n", cookie, bplus_create_time);
1776 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1777 sprintf(output, "%s - Remove: %-16I64d\r\n", cookie, bplus_remove_time);
1778 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1779 sprintf(output, "%s - Build: %-16I64d\r\n", cookie, bplus_build_time);
1780 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1781 sprintf(output, "%s - Free: %-16I64d\r\n", cookie, bplus_free_time);
1782 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1787 void cm_BPlusDumpStats(void)
1789 afsi_log("B+ Lookup Hits: %-8d", bplus_lookup_hits);
1790 afsi_log(" Inexact Hits: %-8d", bplus_lookup_hits_inexact);
1791 afsi_log(" Ambiguous Hits: %-8d", bplus_lookup_ambiguous);
1792 afsi_log(" Misses: %-8d", bplus_lookup_misses);
1793 afsi_log(" Create: %-8d", bplus_create_entry);
1794 afsi_log(" Remove: %-8d", bplus_remove_entry);
1795 afsi_log(" Build Tree: %-8d", bplus_build_tree);
1796 afsi_log(" Free Tree: %-8d", bplus_free_tree);
1797 afsi_log(" DV Error: %-8d", bplus_dv_error);
1799 afsi_log("B+ Time Lookup: %-16I64d", bplus_lookup_time);
1800 afsi_log(" Create: %-16I64d", bplus_create_time);
1801 afsi_log(" Remove: %-16I64d", bplus_remove_time);
1802 afsi_log(" Build: %-16I64d", bplus_build_time);
1803 afsi_log(" Free: %-16I64d", bplus_free_time);
1805 #endif /* USE_BPLUS */