ccn_btree.c

Go to the documentation of this file.
00001 /**
00002  * BTree implementation
00003  */ 
00004 /* (Will be) Part of the CCNx C Library.
00005  *
00006  * Copyright (C) 2011, 2012 Palo Alto Research Center, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or modify it
00009  * under the terms of the GNU Lesser General Public License version 2.1
00010  * as published by the Free Software Foundation.
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014  * Lesser General Public License for more details. You should have received
00015  * a copy of the GNU Lesser General Public License along with this library;
00016  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00017  * Fifth Floor, Boston, MA 02110-1301 USA.
00018  */
00019  
00020 #include <sys/types.h>
00021 #include <stdio.h>
00022 #include <stdint.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 
00026 #include <ccn/charbuf.h>
00027 #include <ccn/hashtb.h>
00028 
00029 #include <ccn/btree.h>
00030 
00031 #ifndef MYFETCH
00032 #define MYFETCH(p, f) ccn_btree_fetchval(&((p)->f[0]), sizeof((p)->f))
00033 #endif
00034 unsigned
00035 ccn_btree_fetchval(const unsigned char *p, int size)
00036 {
00037     int i;
00038     unsigned v;
00039     
00040     for (v = 0, i = 0; i < size; i++)
00041         v = (v << 8) + p[i];
00042     return(v);
00043 }
00044 
00045 #ifndef MYSTORE
00046 #define MYSTORE(p, f, v) ccn_btree_storeval(&((p)->f[0]), sizeof((p)->f), (v))
00047 #endif
00048 void
00049 ccn_btree_storeval(unsigned char *p, int size, unsigned v)
00050 {
00051     int i;
00052     
00053     for (i = size; i > 0; i--, v >>= 8)
00054         p[i-1] = v;
00055 }
00056 
00057 /**
00058  *  Minimum size of a non-empty node
00059  */
00060 #define MIN_NODE_BYTES (sizeof(struct ccn_btree_node_header) + sizeof(struct ccn_btree_entry_trailer))
00061 
00062 /**
00063  * Find the entry trailer associated with entry i of the btree node.
00064  *
00065  * Sets node->corrupt if a problem with the node's structure is discovered.
00066  * @returns entry trailer pointer, or NULL if there is a problem.
00067  */
00068 static struct ccn_btree_entry_trailer *
00069 seek_trailer(struct ccn_btree_node *node, int i)
00070 {
00071     struct ccn_btree_entry_trailer *t;
00072     unsigned last;
00073     unsigned ent;
00074     
00075     if (node->corrupt || node->buf->length < MIN_NODE_BYTES)
00076         return(NULL);
00077     t = (struct ccn_btree_entry_trailer *)(node->buf->buf +
00078         (node->buf->length - sizeof(struct ccn_btree_entry_trailer)));
00079     last = MYFETCH(t, entdx);
00080     ent = MYFETCH(t, entsz) * CCN_BT_SIZE_UNITS;
00081     if (ent < sizeof(struct ccn_btree_entry_trailer))
00082         return(node->corrupt = __LINE__, NULL);
00083     if (ent * (last + 1) >= node->buf->length)
00084         return(node->corrupt = __LINE__, NULL);
00085     if ((unsigned)i > last)
00086         return(NULL);
00087     t = (struct ccn_btree_entry_trailer *)(node->buf->buf + node->buf->length
00088         - (ent * (last - i))
00089         - sizeof(struct ccn_btree_entry_trailer));
00090     if (MYFETCH(t, entdx) != i)
00091         return(node->corrupt = __LINE__, NULL);
00092     return(t);
00093 }
00094 
00095 /**
00096  * Get the address of the indexed entry within the node.
00097  *
00098  * payload_bytes must be divisible by CCN_BT_SIZE_UNITS.
00099  *
00100  * @returns NULL in case of error.
00101  */
00102 void *
00103 ccn_btree_node_getentry(size_t payload_bytes, struct ccn_btree_node *node, int i)
00104 {
00105     struct ccn_btree_entry_trailer *t;
00106     size_t entry_bytes;
00107     
00108     entry_bytes = payload_bytes + sizeof(struct ccn_btree_entry_trailer);
00109     t = seek_trailer(node, i);
00110     if (t == NULL)
00111         return(NULL);
00112     if (MYFETCH(t, entsz) * CCN_BT_SIZE_UNITS != entry_bytes)
00113         return(node->corrupt = __LINE__, NULL);
00114     return(((unsigned char *)t) + sizeof(*t) - entry_bytes);    
00115 }
00116 
00117 /**
00118  * Get the address of entry within an internal (non-leaf) node.
00119  */
00120 static struct ccn_btree_internal_payload *
00121 seek_internal(struct ccn_btree_node *node, int i)
00122 {
00123     struct ccn_btree_internal_payload *ans;
00124     
00125     ans = ccn_btree_node_getentry(sizeof(*ans), node, i);
00126     if (ans == NULL)
00127         return(NULL);
00128     if (MYFETCH(ans, magic) != CCN_BT_INTERNAL_MAGIC)
00129         return(node->corrupt = __LINE__, NULL);
00130     return(ans);
00131 }
00132 
00133 /**
00134  * Number of entries within the btree node
00135  *
00136  * @returns number of entries, or -1 for error
00137  */
00138 int
00139 ccn_btree_node_nent(struct ccn_btree_node *node)
00140 {
00141     struct ccn_btree_entry_trailer *t;
00142 
00143     if (node->corrupt)
00144         return(-1);
00145     if (node->buf->length < MIN_NODE_BYTES)
00146         return(0);
00147     t = (struct ccn_btree_entry_trailer *)(node->buf->buf +
00148         (node->buf->length - sizeof(struct ccn_btree_entry_trailer)));
00149     return(MYFETCH(t, entdx) + 1);
00150 }
00151 
00152 /**
00153  * Size, in bytes, of entries within the node
00154  *
00155  * If there are no entries, returns 0.
00156  * This size includes the entry trailer.
00157  *
00158  * @returns size, or -1 for error
00159  */
00160 int
00161 ccn_btree_node_getentrysize(struct ccn_btree_node *node)
00162 {
00163     struct ccn_btree_entry_trailer *t;
00164 
00165     if (node->corrupt)
00166         return(-1);
00167     if (node->buf->length < MIN_NODE_BYTES)
00168         return(0);
00169     t = (struct ccn_btree_entry_trailer *)(node->buf->buf +
00170         (node->buf->length - sizeof(struct ccn_btree_entry_trailer)));
00171     return(MYFETCH(t, entsz) * CCN_BT_SIZE_UNITS);
00172 }
00173 
00174 /**
00175  * Size, in bytes, of payloads within the node
00176  *
00177  * If there are no entries, returns 0.
00178  * This does not include the entry trailer, but will include padding
00179  * to a multiple of CCN_BT_SIZE_UNITS.
00180  *
00181  * @returns size, or -1 for error
00182  */
00183 int
00184 ccn_btree_node_payloadsize(struct ccn_btree_node *node)
00185 {
00186     int ans;
00187     
00188     ans = ccn_btree_node_getentrysize(node);
00189     if (ans >= sizeof(struct ccn_btree_entry_trailer))
00190         ans -= sizeof(struct ccn_btree_entry_trailer);
00191     return(ans);
00192 }
00193 
00194 /** 
00195  * Node level (leaves are at level 0)
00196  * @returns the node level, or -1 for error
00197  */
00198 int ccn_btree_node_level(struct ccn_btree_node *node)
00199 {
00200     struct ccn_btree_node_header *hdr = NULL;
00201 
00202     if (node->corrupt || node->buf->length < sizeof(struct ccn_btree_node_header))
00203         return(-1);
00204     hdr = (struct ccn_btree_node_header *)(node->buf->buf);
00205     return(MYFETCH(hdr, level));
00206 }
00207 
00208 /**
00209  * Fetch the key within the indexed entry of node
00210  * @returns -1 in case of error
00211  */
00212 int
00213 ccn_btree_key_fetch(struct ccn_charbuf *dst,
00214                     struct ccn_btree_node *node,
00215                     int i)
00216 {
00217     dst->length = 0;
00218     return(ccn_btree_key_append(dst, node, i));
00219 }
00220 
00221 /**
00222  * Append the key within the indexed entry of node to dst
00223  * @returns -1 in case of error
00224  */
00225 int
00226 ccn_btree_key_append(struct ccn_charbuf *dst,
00227                      struct ccn_btree_node *node,
00228                      int i)
00229 {
00230     struct ccn_btree_entry_trailer *p = NULL;
00231     unsigned koff = 0;
00232     unsigned ksiz = 0;
00233 
00234     p = seek_trailer(node, i);
00235     if (p == NULL)
00236         return(-1);
00237     koff = MYFETCH(p, koff0);
00238     ksiz = MYFETCH(p, ksiz0);
00239     if (koff > node->buf->length)
00240         return(node->corrupt = __LINE__, -1);
00241     if (ksiz > node->buf->length - koff)
00242         return(node->corrupt = __LINE__, -1);
00243     ccn_charbuf_append(dst, node->buf->buf + koff, ksiz);
00244     koff = MYFETCH(p, koff1);
00245     ksiz = MYFETCH(p, ksiz1);
00246     if (koff > node->buf->length)
00247         return(node->corrupt = __LINE__, -1);
00248     if (ksiz > node->buf->length - koff)
00249         return(node->corrupt = __LINE__, -1);
00250     ccn_charbuf_append(dst, node->buf->buf + koff, ksiz);
00251     return(0);
00252 }
00253 
00254 /**
00255  * Compare given key with the key in the indexed entry of the node
00256  *
00257  * The comparison is a standard lexicographic one on unsigned bytes; that is,
00258  * there is no assumption of what the bytes actually encode.
00259  *
00260  * The special return value -9999 indicates the key is a strict prefix.
00261  * This does not matter to the btree lookup, but is useful for higher levels.
00262  *
00263  * @returns negative, zero, or positive to indicate less, equal, or greater
00264  */
00265 int
00266 ccn_btree_compare(const unsigned char *key,
00267                   size_t size,
00268                   struct ccn_btree_node *node,
00269                   int i)
00270 {
00271     struct ccn_btree_entry_trailer *p = NULL;
00272     size_t cmplen;
00273     unsigned koff = 0;
00274     unsigned ksiz = 0;
00275     int res;
00276     
00277     p = seek_trailer(node, i);
00278     if (p == NULL)
00279         return(i < 0 ? 999 : -999);
00280     koff = MYFETCH(p, koff0);
00281     ksiz = MYFETCH(p, ksiz0);
00282     if (koff > node->buf->length)
00283         return(node->corrupt = __LINE__, -1);
00284     if (ksiz > node->buf->length - koff)
00285         return(node->corrupt = __LINE__, -1);
00286     cmplen = size;
00287     if (cmplen > ksiz)
00288         cmplen = ksiz;
00289     res = memcmp(key, node->buf->buf + koff, cmplen);
00290     if (res != 0)
00291         return(res);
00292     if (size < ksiz)
00293         return(-9999); /* key is a strict prefix */
00294     /* Compare the other part of the key */
00295     key += cmplen;
00296     size -= cmplen;
00297     koff = MYFETCH(p, koff1);
00298     ksiz = MYFETCH(p, ksiz1);
00299     if (koff > node->buf->length)
00300         return(node->corrupt = __LINE__, -1);
00301     if (ksiz > node->buf->length - koff)
00302         return(node->corrupt = __LINE__, -1);
00303     cmplen = size;
00304     if (cmplen > ksiz)
00305         cmplen = ksiz;
00306     res = memcmp(key, node->buf->buf + koff, cmplen);
00307     if (res != 0)
00308         return(res);
00309     if (size < ksiz)
00310         return(-9999); /* key is a strict prefix */
00311     return(size > ksiz);
00312 }
00313 
00314 /**
00315  * Search the node for the given key
00316  *
00317  * The return value is encoded as 2 * index + (found ? 1 : 0); that is, a
00318  * successful search returns an odd number and an unsuccessful search returns
00319  * an even number.  In the case of an unsuccessful search, the index indicates
00320  * where the item would go if it were to be inserted.
00321  *
00322  * Uses a binary search, so the keys in the node must be sorted and unique.
00323  *
00324  * @returns CCN_BT_ENCRES(index, success) indication, or -1 for an error.
00325  */
00326 int
00327 ccn_btree_searchnode(const unsigned char *key,
00328                      size_t size,
00329                      struct ccn_btree_node *node)
00330 {
00331     int i, j, mid, res;
00332     
00333     if (node->corrupt)
00334         return(-1);
00335     i = 0;
00336     j = ccn_btree_node_nent(node);
00337     while (i < j) {
00338         mid = (i + j) >> 1;
00339         res =  ccn_btree_compare(key, size, node, mid);
00340         // printf("node = %u, i = %d, j = %d, mid = %d, res = %d\n", (int)node->nodeid, i, j, mid, res);
00341         if (res == 0)
00342             return(CCN_BT_ENCRES(mid, 1));
00343         if (res < 0)
00344             j = mid;
00345         else
00346             i = mid + 1;
00347     }
00348     if (i != j) {
00349         abort();
00350     }
00351     return(CCN_BT_ENCRES(i, 0));
00352 }
00353 
00354 /**
00355  * Do a btree lookup, starting from the default root.
00356  *
00357  * In the absence of errors, if *leafp is not NULL the handle for the
00358  * appropriate leaf node will be stored.  See ccn_btree_getnode() for
00359  * warning about lifetime of the resulting pointer.
00360  *
00361  * The return value is encoded as for ccn_btree_searchnode().
00362  *
00363  * @returns CCN_BT_ENCRES(index, success) indication, or -1 for an error.
00364  */
00365 int
00366 ccn_btree_lookup(struct ccn_btree *btree,
00367                  const unsigned char *key, size_t size,
00368                  struct ccn_btree_node **leafp)
00369 {
00370     struct ccn_btree_node *node = NULL;
00371     node = ccn_btree_getnode(btree, 1, 0);
00372     if (node == NULL || node->corrupt)
00373         return(-1);
00374     return(ccn_btree_lookup_internal(btree, node, 0, key, size, leafp));
00375 }
00376 
00377 /**
00378  * Do a btree lookup, starting from the provided root and stopping
00379  * at stoplevel.
00380  *
00381  * In the absence of errors, if *ansp is not NULL the handle for the
00382  * appropriate node will be stored.  See ccn_btree_getnode() for
00383  * warning about lifetime of the resulting pointer.
00384  *
00385  * The return value is encoded as for ccn_btree_searchnode().
00386  *
00387  * @returns CCN_BT_ENCRES(index, success) indication, or -1 for an error.
00388  */
00389 int
00390 ccn_btree_lookup_internal(struct ccn_btree *btree,
00391                           struct ccn_btree_node *root, int stoplevel,
00392                           const unsigned char *key, size_t size,
00393                           struct ccn_btree_node **ansp)
00394 {
00395     struct ccn_btree_node *node = NULL;
00396     struct ccn_btree_node *child = NULL;
00397     struct ccn_btree_internal_payload *e = NULL;
00398     ccn_btnodeid childid;
00399     ccn_btnodeid parent;
00400     int entdx;
00401     int level;
00402     int newlevel;
00403     int srchres;
00404     
00405     node = root;
00406     if (node == NULL || node->corrupt)
00407         return(-1);
00408     parent = node->nodeid;
00409     level = ccn_btree_node_level(node);
00410     if (level < stoplevel)
00411         return(-1);
00412     srchres = ccn_btree_searchnode(key, size, node);
00413     if (srchres < 0)
00414         return(-1);
00415     while (level > stoplevel) {
00416         entdx = CCN_BT_SRCH_INDEX(srchres) + CCN_BT_SRCH_FOUND(srchres) - 1;
00417         if (entdx < 0)
00418             abort();
00419         e = seek_internal(node, entdx);
00420         if (e == NULL)
00421             return(-1);
00422         childid = MYFETCH(e, child);
00423         child = ccn_btree_getnode(btree, childid, node->nodeid);
00424         if (child == NULL)
00425             return(-1);
00426         newlevel = ccn_btree_node_level(child);
00427         if (newlevel != level - 1) {
00428             ccn_btree_note_error(btree, __LINE__);
00429             node->corrupt = __LINE__;
00430             return(-1);
00431         }
00432         node = child;
00433         level = newlevel;
00434         srchres = ccn_btree_searchnode(key, size, node);
00435     }
00436     if (ansp != NULL)
00437         *ansp = node;
00438     return(srchres);
00439 }
00440 
00441 /* See if we can reuse a leading portion of the key */
00442 static void
00443 scan_reusable(const unsigned char *key, size_t keysize,
00444              struct ccn_btree_node *node, int ndx, unsigned reuse[2])
00445 {
00446     /* this is an optimization - leave out for now */
00447     /* but this is a good place to do this check... */
00448     if (ndx == 0 && keysize > 0 && ccn_btree_node_level(node) != 0) {
00449         abort();
00450     }
00451 }
00452 
00453 /**
00454  *  Insert a new entry into a node
00455  *
00456  * The caller is responsible for providing the correct index i, which
00457  * will become the index of the new entry.
00458  *
00459  * The caller is also responsible for triggering a split.
00460  *
00461  * @returns the new entry count, or -1 in case of error.
00462  */
00463 int
00464 ccn_btree_insert_entry(struct ccn_btree_node *node, int i,
00465                        const unsigned char *key, size_t keysize,
00466                        void *payload, size_t payload_bytes)
00467 {
00468     size_t k, grow, minnewsize, pb, pre, post, org;
00469     unsigned char *to = NULL;
00470     unsigned char *from = NULL;
00471     struct ccn_btree_entry_trailer space = {};
00472     struct ccn_btree_entry_trailer *t = &space;
00473     unsigned reuse[2] = {0, 0};
00474     int j, n;
00475     
00476     if (node->freelow == 0)
00477         ccn_btree_chknode(node);
00478     if (node->corrupt)
00479         return(-1);
00480     if (keysize > CCN_BT_MAX_KEY_SIZE)
00481         return(-1);
00482     pb = (payload_bytes + CCN_BT_SIZE_UNITS - 1)
00483          / CCN_BT_SIZE_UNITS
00484          * CCN_BT_SIZE_UNITS;
00485     n = ccn_btree_node_nent(node);
00486     if (i > n)
00487         return(-1);
00488     if (n == 0) {
00489         org = node->buf->length;
00490         k = pb + sizeof(struct ccn_btree_entry_trailer);
00491     }
00492     else {
00493         unsigned char *x = ccn_btree_node_getentry(pb, node, 0);
00494         if (x == NULL) return(-1);
00495         org = x - node->buf->buf;
00496         k = ccn_btree_node_getentrysize(node);
00497     }
00498     if (k != pb + sizeof(struct ccn_btree_entry_trailer))
00499         return(-1);
00500     scan_reusable(key, keysize, node, i, reuse);
00501     if (reuse[1] != 0) {
00502         MYSTORE(t, koff0, reuse[0]);
00503         MYSTORE(t, ksiz0, reuse[1]);
00504         MYSTORE(t, koff1, node->freelow);
00505         MYSTORE(t, ksiz1, keysize - reuse[1]);
00506     }
00507     else {
00508         MYSTORE(t, koff0, node->freelow);
00509         MYSTORE(t, ksiz0, keysize);
00510     }
00511     MYSTORE(t, level, ccn_btree_node_level(node));
00512     MYSTORE(t, entsz, k / CCN_BT_SIZE_UNITS);
00513     if (keysize != reuse[1] && node->clean > node->freelow)
00514         node->clean = node->freelow;
00515     minnewsize = (n + 1) * k + node->freelow + keysize - reuse[1];
00516     minnewsize = (minnewsize + CCN_BT_SIZE_UNITS - 1)
00517                  / CCN_BT_SIZE_UNITS
00518                  * CCN_BT_SIZE_UNITS;
00519     pre = i * k;        /* # bytes of entries before the new one */
00520     post = (n - i) * k; /* # bytes of entries after the new one */
00521     if (minnewsize <= node->buf->length) {
00522         /* no expansion needed, but need to slide pre bytes down */
00523         to = node->buf->buf + org - k;
00524         if (node->clean > org - k)
00525             node->clean = org - k;
00526         memmove(to, to + k, pre);
00527         /* Set pointer to empty space for new entry */
00528         to += pre;
00529     }
00530     else {
00531         /* Need to expand */
00532         grow = minnewsize - node->buf->length;
00533         if (NULL == ccn_charbuf_reserve(node->buf, grow))
00534             return(-1);
00535         to = node->buf->buf + minnewsize - (pre + k + post);
00536         from = node->buf->buf + org;
00537         if (node->clean > org)
00538             node->clean = org;
00539         node->buf->length = minnewsize;
00540         memmove(to + pre + k, from + pre, post);
00541         memmove(to, from, pre);
00542         /* Rarely, we move pre down and post up - skip this fill if so. */
00543         if (to > from)
00544             memset(from, 0x33, to - from);
00545         to = to + pre;
00546     }
00547     /* Copy in bits of new entry */
00548     memset(to, 0, k);
00549     memmove(to, payload, payload_bytes);
00550     memmove(to + pb, t, sizeof(*t));
00551     /* Fix up the entdx in the relocated entries */
00552     for (j = i, to = to + pb; j <= n; j++, to += k) {
00553         t = (void*)to;
00554         MYSTORE(t, entdx, j);
00555     }
00556     /* Finally, copy the (non-shared portion of the) key */
00557     to = node->buf->buf + node->freelow;
00558     memmove(to, key + reuse[0], keysize - reuse[1]);
00559     node->freelow += keysize - reuse[1];
00560     return(n + 1);
00561 }
00562 
00563 #if 0
00564 #define MSG(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
00565 #else
00566 #define MSG(fmt, ...) ((void)0)
00567 #endif
00568 
00569 /**
00570  *  Given an old root, add a level to the tree to prepare for a split.
00571  *
00572  *  @returns node with a new nodeid, new singleton root, and the old contents.
00573  */
00574 static struct ccn_btree_node *
00575 ccn_btree_grow_a_level(struct ccn_btree *btree, struct ccn_btree_node *node)
00576 {
00577     struct ccn_btree_internal_payload link = {{CCN_BT_INTERNAL_MAGIC}};
00578     struct ccn_btree_node *child = NULL;
00579     struct ccn_charbuf *t = NULL;
00580     int level;
00581     int res;
00582     
00583     level = ccn_btree_node_level(node);
00584     if (level < 0)
00585         return(NULL);
00586     child = ccn_btree_getnode(btree, btree->nextnodeid++, node->nodeid);
00587     if (child == NULL)
00588         return(NULL);
00589     res = ccn_btree_prepare_for_update(btree, child);
00590     if (res < 0)
00591         ccn_btree_note_error(btree, __LINE__);
00592     res = ccn_btree_prepare_for_update(btree, node);
00593     if (res < 0)
00594         ccn_btree_note_error(btree, __LINE__);
00595     child->clean = 0;
00596     node->clean = 0;
00597     t = child->buf;
00598     child->buf = node->buf;
00599     node->buf = t;
00600     res = ccn_btree_init_node(node, level + 1, 'R', 0); // XXX - arbitrary extsz
00601     if (res < 0)
00602         ccn_btree_note_error(btree, __LINE__);
00603     MYSTORE(&link, child, child->nodeid);
00604     res = ccn_btree_insert_entry(node, 0, NULL, 0, &link, sizeof(link));
00605     if (res < 0)
00606         ccn_btree_note_error(btree, __LINE__);
00607     child->parent = node->nodeid;
00608     MSG("New root %u at level %d over node %u (%d errors)",
00609         (unsigned)node->nodeid, level + 1,
00610         (unsigned)child->nodeid, btree->errors);
00611     return(child);
00612 }
00613 
00614 /**
00615  * Test for an oversize node
00616  *
00617  * This takes into account both the size of a node and the count of
00618  * entries.
00619  *
00620  * @returns a boolean result.
00621  */
00622 int
00623 ccn_btree_oversize(struct ccn_btree *btree, struct ccn_btree_node *node)
00624 {
00625     int n;
00626     
00627     n = ccn_btree_node_nent(node);
00628     if (n > 4 && btree->nodebytes != 0 &&
00629         node->buf->length > btree->nodebytes)
00630         return(1);
00631     if (ccn_btree_node_level(node) == 0 && btree->full0 > 0)
00632         return(n > btree->full0);
00633     return(n > btree->full);
00634 }
00635 
00636 int
00637 ccn_btree_split(struct ccn_btree *btree, struct ccn_btree_node *node)
00638 {
00639     int i, j, k, n, pb, res;
00640     struct ccn_btree_node newnode = {};
00641     struct ccn_btree_node *a[2] = {NULL, NULL};
00642     struct ccn_btree_node *chld = NULL;
00643     struct ccn_btree_node *parent = NULL;
00644     void *payload = NULL;
00645     struct ccn_charbuf *key = NULL;
00646     struct ccn_btree_internal_payload link = {{CCN_BT_INTERNAL_MAGIC}};
00647     struct ccn_btree_internal_payload *olink = NULL;
00648     int level;
00649     
00650     if (btree->nextsplit == node->nodeid)
00651         btree->nextsplit = 0;
00652     n = ccn_btree_node_nent(node);
00653     if (n < 4)
00654         return(-1);
00655     res = ccn_btree_prepare_for_update(btree, node);
00656     if (res < 0)
00657         return(-1);
00658     if (node->nodeid == 1) {
00659         node = ccn_btree_grow_a_level(btree, node);
00660         if (node == NULL)
00661             abort();
00662         if (node->nodeid == 1 || node->parent != 1 || ccn_btree_node_nent(node) != n)
00663             abort();
00664     }
00665     parent = ccn_btree_getnode(btree, node->parent, 0);
00666     if (parent == NULL || ccn_btree_node_nent(parent) < 1)
00667         return(node->corrupt = __LINE__, -1); /* Must have a parent to split. */
00668     if (ccn_btree_node_payloadsize(parent) != sizeof(link))
00669         return(node->corrupt = __LINE__, -1);
00670     res = ccn_btree_prepare_for_update(btree, parent);
00671     if (res < 0)
00672         return(-1);
00673     pb = ccn_btree_node_payloadsize(node);
00674     level = ccn_btree_node_level(node);
00675     MSG("Splitting %d entries of node %u, child of %u", n,
00676         (unsigned)node->nodeid, (unsigned)node->parent);
00677     /* Create two new nodes to hold the split-up content */
00678     /* One of these is temporary, and will get swapped in for original node */
00679     newnode.buf = ccn_charbuf_create();
00680     if (newnode.buf == NULL)
00681         goto Bail;
00682     newnode.nodeid = node->nodeid;
00683     a[0] = &newnode;
00684     /* The other new node is created anew */
00685     a[1] = ccn_btree_getnode(btree, btree->nextnodeid++, 0);
00686     if (a[1] == NULL)
00687         goto Bail;
00688     res = ccn_btree_prepare_for_update(btree, a[1]);
00689     if (res < 0)
00690         return(-1);
00691     for (k = 0; k < 2; k++) {
00692         if (ccn_btree_node_nent(a[k]) != 0)
00693             goto Bail;
00694         res = ccn_btree_init_node(a[k], ccn_btree_node_level(node), 0, 0);
00695         if (res < 0)
00696             goto Bail;
00697         a[k]->parent = node->parent;
00698     }
00699     /* Distribute the entries into the two new nodes */
00700     key = ccn_charbuf_create();
00701     if (key == NULL) goto Bail;
00702     for (i = 0, j = 0, k = 0, res = 0; i < n; i++, j++) {
00703         res = ccn_btree_key_fetch(key, node, i);
00704         if (i == n / 2) {
00705             k = 1; j = 0; /* switch to second half */
00706             if (level > 0)
00707                 key->length = 0; /* internal nodes need one fewer key */
00708         }
00709         payload = ccn_btree_node_getentry(pb, node, i);
00710         if (res < 0 || payload == NULL)
00711             goto Bail;
00712         res = ccn_btree_insert_entry(a[k], j, key->buf, key->length, payload, pb);
00713         MSG("Splitting [%u %d] into [%u %d] (res = %d)",
00714             (unsigned)node->nodeid, i, (unsigned)a[k]->nodeid, j, res);
00715         if (res < 0)
00716             goto Bail;
00717         if (level > 0) {
00718             /* Fix up the cached parent pointer if necessary */
00719             chld = NULL;
00720             olink = payload;
00721             if (MYFETCH(olink, magic) == CCN_BT_INTERNAL_MAGIC)
00722                 chld = ccn_btree_rnode(btree, MYFETCH(olink, child));
00723             if (chld != NULL) {
00724                 if (chld->parent != a[k]->nodeid) {
00725                     MSG("Parent of %u changed from %u to %u",
00726                         (unsigned)chld->nodeid,
00727                         (unsigned)chld->parent,
00728                         (unsigned)a[k]->nodeid);
00729                 }
00730                 chld->parent = a[k]->nodeid;
00731             }
00732         }
00733     }
00734     /* Link the new node into the parent */
00735     res = ccn_btree_key_fetch(key, node, n / 2); /* Splitting key. */
00736     if (res < 0)
00737         goto Bail;
00738     /*
00739      * Note - we could abbreviate the splitting key to something less than
00740      * the first key of the subtree under a[1] and greater than
00741      * the last key of the subtree under a[0].  But we don't do that yet.
00742      */
00743     MYSTORE(&link, child, a[1]->nodeid);
00744     res = ccn_btree_searchnode(key->buf, key->length, parent);
00745     if (res < 0)
00746         goto Bail;
00747     if (CCN_BT_SRCH_FOUND(res) && key->length != 0)
00748         goto Bail;
00749     i = CCN_BT_SRCH_INDEX(res);
00750     olink = ccn_btree_node_getentry(sizeof(link), parent, i - 1);
00751     if (olink == NULL || MYFETCH(olink, child) != a[0]->nodeid) {
00752         node->corrupt = __LINE__;
00753         parent->corrupt = __LINE__;
00754         goto Bail;
00755     }
00756     /* It look like we are in good shape to commit the changes */
00757     res = ccn_btree_insert_entry(parent, i,
00758                                  key->buf, key->length,
00759                                  &link, sizeof(link));
00760     if (res < 0) {
00761         parent->corrupt = __LINE__;
00762         goto Bail;
00763     }
00764     else if (ccn_btree_oversize(btree, parent)) {
00765         btree->missedsplit = btree->nextsplit;
00766         btree->nextsplit = parent->nodeid;
00767     }
00768     node->clean = 0;
00769     ccn_charbuf_destroy(&node->buf);
00770     node->buf = newnode.buf;
00771     newnode.buf = NULL;
00772     res = ccn_btree_chknode(node); /* Update freelow */
00773     if (res < 0)
00774         goto Bail;
00775     ccn_charbuf_destroy(&key);
00776     return(0);
00777 Bail:
00778     ccn_charbuf_destroy(&newnode.buf);
00779     ccn_charbuf_destroy(&key);
00780     ccn_btree_note_error(btree, __LINE__);
00781     return(-1);
00782 }
00783 #undef MSG
00784 
00785 /**
00786  * Find the leaf that comes after the given node
00787  *
00788  * This may be used to walk though the leaf nodes in order.
00789  * If success, sets *ansp to a leaf pointer or NULL
00790  * @returns 0 if at end, 1 if *ansp is not NULL, -1 if error.
00791  */
00792 int
00793 ccn_btree_next_leaf(struct ccn_btree *btree,
00794                     struct ccn_btree_node *node,
00795                     struct ccn_btree_node **ansp)
00796 {
00797     struct ccn_btree_internal_payload *e = NULL;
00798     struct ccn_btree_node *p = NULL;
00799     struct ccn_btree_node *q = NULL;
00800     struct ccn_btree_node *parent = NULL;
00801     int i;
00802     int n;
00803     int ans;
00804     int res;
00805     struct ccn_charbuf *key = NULL;
00806     
00807     ans = -1;
00808     key = ccn_charbuf_create();
00809     p = node;
00810     n = ccn_btree_node_nent(p);
00811     if (n < 1)
00812         goto Bail;
00813     while (p->parent != 0) {
00814         res = ccn_btree_key_fetch(key, p, n - 1);
00815         if (res < 0)
00816             goto Bail;
00817         parent = ccn_btree_getnode(btree, p->parent, 0);
00818         if (parent == NULL)
00819             goto Bail;
00820         res = ccn_btree_searchnode(key->buf, key->length, parent);
00821         if (res < 0)
00822             goto Bail;
00823         n = ccn_btree_node_nent(parent);
00824         if (n < 1)
00825             goto Bail;
00826         i = CCN_BT_SRCH_INDEX(res) + CCN_BT_SRCH_FOUND(res) - 1;
00827         if (i < n - 1) {
00828             /* We have found the ancestor that has the leaf we are after. */
00829             q = NULL;
00830             e = ccn_btree_node_getentry(sizeof(*e), parent, i + 1);
00831             q = ccn_btree_getnode(btree, MYFETCH(e, child), parent->nodeid);
00832             if (q == NULL)
00833                 goto Bail;
00834             res = ccn_btree_lookup_internal(btree, q, 0, key->buf, 0, ansp);
00835             if (res < 0)
00836                 goto Bail;
00837             ans = 1;
00838             break;
00839         }
00840         p = parent;
00841         /* n is aleady set to ccn_btree_node_nent(p) */
00842     }
00843     if (ans != 1) {
00844         *ansp = NULL;
00845         ans = 0;
00846     }
00847 Bail:
00848     ccn_charbuf_destroy(&key);
00849     return(ans);
00850 }
00851 
00852 /**
00853  * Find the leaf that comes before the given node
00854  *
00855  * This may be used to walk though the leaf nodes in reverse order.
00856  * If success, sets *ansp to a leaf pointer or NULL
00857  * @returns 0 if at beginning, 1 if *ansp is not NULL, -1 if error.
00858  */
00859 int
00860 ccn_btree_prev_leaf(struct ccn_btree *btree,
00861                     struct ccn_btree_node *node,
00862                     struct ccn_btree_node **ansp)
00863 {
00864     struct ccn_btree_internal_payload *e = NULL;
00865     struct ccn_btree_node *p = NULL;
00866     struct ccn_btree_node *q = NULL;
00867     struct ccn_btree_node *parent = NULL;
00868     int ans;
00869     int i;
00870     int n;
00871     
00872     ans = -1;
00873     p = node;
00874     while (p->parent != 0) {
00875         parent = ccn_btree_getnode(btree, p->parent, 0);
00876         if (parent == NULL)
00877             goto Bail;
00878         n = ccn_btree_node_nent(parent);
00879         if (n < 0)
00880             goto Bail;
00881         /* Set i to our index in parent */
00882         for (i = n - 1; i > 0; i--) {
00883             e = ccn_btree_node_getentry(sizeof(*e), parent, i);
00884             if (MYFETCH(e, child) == p->nodeid)
00885                 break;
00886         }
00887         if (i > 0) {
00888             /* we can stop walking up the tree now, and walk down instead */
00889             for (q = parent; ccn_btree_node_level(q) != 0;) {
00890                 e = ccn_btree_node_getentry(sizeof(*e), q, i - 1);
00891                 q = ccn_btree_getnode(btree, MYFETCH(e, child), q->nodeid);
00892                 if (q == NULL)
00893                     goto Bail;
00894                 i = ccn_btree_node_nent(q);
00895             }
00896             *ansp = q;
00897             ans = 1;
00898             break;
00899         }
00900         p = parent;
00901     }
00902     if (ans != 1) {
00903         *ansp = NULL;
00904         ans = 0;
00905     }
00906 Bail:
00907     return(ans);
00908 }
00909 
00910 #define CCN_BTREE_MAGIC 0x53ade78
00911 #define CCN_BTREE_VERSION 1
00912 
00913 /**
00914  *  Write out any pending changes, mark the node clean, and release node iodata
00915  *
00916  * Retains the cached node data in memory.
00917  *
00918  * @returns 0 for success or -1 for error.
00919  */
00920 int
00921 ccn_btree_close_node(struct ccn_btree *btree, struct ccn_btree_node *node)
00922 {
00923     int res = 0;
00924     struct ccn_btree_io *io = btree->io;
00925     
00926     if (node->corrupt)
00927         res = -1;
00928     else if (node->iodata != NULL && io != NULL) {
00929         res = io->btwrite(io, node);
00930         if (res < 0)
00931             ccn_btree_note_error(btree, __LINE__);
00932         else
00933             node->clean = node->buf->length;
00934         res |= io->btclose(io, node);
00935         if (res < 0)
00936             ccn_btree_note_error(btree, __LINE__);
00937     }
00938     else if (io != NULL && node->clean != node->buf->length) {
00939         res = -1;
00940         ccn_btree_note_error(btree, __LINE__);
00941     }
00942     return(res);
00943 }
00944 
00945 static void
00946 finalize_node(struct hashtb_enumerator *e)
00947 {
00948     struct ccn_btree *btree = hashtb_get_param(e->ht, NULL);
00949     struct ccn_btree_node *node = e->data;
00950     
00951     if (btree->magic != CCN_BTREE_MAGIC)
00952         abort();
00953     ccn_btree_close_node(btree, node);
00954     ccn_charbuf_destroy(&node->buf);
00955 }
00956 
00957 /**
00958  * Keep count of noticed errors
00959  *
00960  * Do this in one place so it is easy to set a breakpoint.
00961  */
00962 void
00963 ccn_btree_note_error(struct ccn_btree *bt, int info)
00964 {
00965     bt->errors++;
00966 }
00967 
00968 /**
00969  * Create a new btree handle, not attached to any external files
00970  * @returns new handle, or NULL in case of error.
00971  */
00972 struct ccn_btree *
00973 ccn_btree_create(void)
00974 {
00975     struct ccn_btree *ans;
00976     struct hashtb_param param = {0};
00977     
00978     ans = calloc(1, sizeof(*ans));
00979     if (ans != NULL) {
00980         ans->magic = CCN_BTREE_MAGIC;
00981         param.finalize_data = ans;
00982         param.finalize = &finalize_node;
00983         ans->resident = hashtb_create(sizeof(struct ccn_btree_node), &param);
00984         if (ans->resident == NULL) {
00985             free(ans);
00986             return(NULL);
00987         }
00988         ans->errors = 0;
00989         ans->io = NULL;
00990         ans->nextnodeid = 1;  /* This will be the root */
00991         ans->full = ans->full0 = 19;
00992     }
00993     return(ans);
00994 }
00995 
00996 /**
00997  * Destroys a btree handle, shutting things down cleanly.
00998  * @returns a negative value in case of error.
00999  */
01000 int
01001 ccn_btree_destroy(struct ccn_btree **pbt)
01002 {
01003     struct ccn_btree *bt = *pbt;
01004     int res = 0;
01005     
01006     if (bt == NULL)
01007         return(0);
01008     *pbt = NULL;
01009     if (bt->magic != CCN_BTREE_MAGIC)
01010         abort();
01011     hashtb_destroy(&bt->resident);
01012     if (bt->errors != 0)
01013         res = -(bt->errors & 1023);
01014     if (bt->io != NULL)
01015         res |= bt->io->btdestroy(&bt->io);
01016     free(bt);
01017     return(res);
01018 }
01019 
01020 /**
01021  *  Initialize the btree node
01022  *
01023  * It is the caller's responsibility to be sure that the node does not
01024  * contain any useful information.
01025  *
01026  * @returns -1 for error, 0 for success
01027  */
01028 int
01029 ccn_btree_init_node(struct ccn_btree_node *node,
01030                     int level, unsigned char nodetype, unsigned char extsz)
01031 {
01032     struct ccn_btree_node_header *hdr = NULL;
01033     size_t bytes;
01034     
01035     if (node->corrupt)
01036         return(-1);
01037     bytes = sizeof(*hdr) + extsz * CCN_BT_SIZE_UNITS;
01038     node->clean = 0;
01039     node->buf->length = 0;
01040     hdr = (struct ccn_btree_node_header *)ccn_charbuf_reserve(node->buf, bytes);
01041     if (hdr == NULL) return(-1);
01042     MYSTORE(hdr, magic, CCN_BTREE_MAGIC);
01043     MYSTORE(hdr, version, CCN_BTREE_VERSION);
01044     MYSTORE(hdr, nodetype, nodetype);
01045     MYSTORE(hdr, level, level);
01046     MYSTORE(hdr, extsz, extsz);
01047     node->buf->length += bytes;
01048     return(0);
01049 }
01050 
01051 #define CCN_BTREE_MAX_NODE_BYTES (8U<<20)
01052 
01053 /**
01054  * Access a btree node, creating or reading it if necessary
01055  *
01056  * Care should be taken to not store the node handle in data structures,
01057  * since it will become invalid when the node gets flushed from the
01058  * resident cache.
01059  *
01060  * @returns node handle
01061  */
01062 struct ccn_btree_node *
01063 ccn_btree_getnode(struct ccn_btree *bt,
01064                   ccn_btnodeid nodeid,
01065                   ccn_btnodeid parentid)
01066 {
01067     struct hashtb_enumerator ee;
01068     struct hashtb_enumerator *e = &ee;
01069     struct ccn_btree_node *node = NULL;
01070     int res;
01071 
01072     if (bt->magic != CCN_BTREE_MAGIC)
01073         abort();
01074     hashtb_start(bt->resident, e);
01075     res = hashtb_seek(e, &nodeid, sizeof(nodeid), 0);
01076     node = e->data;
01077     if (res == HT_NEW_ENTRY) {
01078         node->nodeid = nodeid;
01079         node->buf = ccn_charbuf_create();
01080         bt->cleanreq++;
01081         if (node->buf == NULL) {
01082             ccn_btree_note_error(bt, __LINE__);
01083             node->corrupt = __LINE__;
01084         }
01085         if (bt->io != NULL) {
01086             res = bt->io->btopen(bt->io, node);
01087             if (res < 0) {
01088                 ccn_btree_note_error(bt, __LINE__);
01089                 node->corrupt = __LINE__;
01090             }
01091             else {
01092                 res = bt->io->btread(bt->io, node, CCN_BTREE_MAX_NODE_BYTES);
01093                 if (res < 0)
01094                     ccn_btree_note_error(bt, __LINE__);
01095                 else {
01096                     node->clean = node->buf->length;
01097                     if (-1 == ccn_btree_chknode(node))
01098                         ccn_btree_note_error(bt, __LINE__);
01099                     node->activity = CCN_BT_ACTIVITY_READ_BUMP;
01100                     if (bt->io->openfds >= CCN_BT_OPEN_NODES_LIMIT) {
01101                         /* having read in the node, it is safe to close it */
01102                         res = bt->io->btclose(bt->io, node);
01103                          if (res < 0)
01104                             ccn_btree_note_error(bt, __LINE__);
01105                     }
01106                 }
01107             }
01108         }
01109     }
01110     if (node != NULL && node->nodeid != nodeid)
01111         abort();
01112     hashtb_end(e);
01113     if (node != NULL && node->parent == 0)
01114         node->parent = parentid;
01115     node->activity += CCN_BT_ACTIVITY_REFERENCE_BUMP;
01116     return(node);
01117 }
01118 
01119 /**
01120  * Access a btree node that is already resident
01121  *
01122  * Care should be taken to not store the node handle in data structures,
01123  * since it will become invalid when the node gets flushed from the
01124  * resident cache.
01125  *
01126  * This call does not bump the activity counter.
01127  *
01128  * @returns node handle, or NULL if the node is not currently resident.
01129  */
01130 struct ccn_btree_node *
01131 ccn_btree_rnode(struct ccn_btree *bt, ccn_btnodeid nodeid)
01132 {
01133     return(hashtb_lookup(bt->resident, &nodeid, sizeof(nodeid)));
01134 }
01135 
01136 /**
01137  * Check a node for internal consistency
01138  *
01139  * Sets or clears node->corrupt as appropriate.
01140  * In case of success, sets the correct value for node->freelow
01141  *
01142  * @returns old value of node->corrupt if the node looks OK, otherwise -1
01143  */
01144 int
01145 ccn_btree_chknode(struct ccn_btree_node *node)
01146 {
01147     unsigned freelow = 0;
01148     unsigned freemax = 0;
01149     unsigned strbase = sizeof(struct ccn_btree_node_header);
01150     struct ccn_btree_node_header *hdr = NULL;
01151     unsigned lev = 0;
01152     unsigned entsz = 0;
01153     unsigned saved_corrupt;
01154     struct ccn_btree_entry_trailer *p = NULL;
01155     int i;
01156     int nent;
01157     unsigned koff;
01158     unsigned ksiz;
01159     
01160     if (node == NULL)
01161         return(-1);
01162     saved_corrupt = node->corrupt;
01163     node->corrupt = 0;
01164     if (node->buf == NULL)
01165         return(node->corrupt = __LINE__, -1);
01166     if (node->buf->length == 0)
01167         return(node->freelow = 0, node->corrupt = 0, 0);
01168     if (node->buf->length < sizeof(struct ccn_btree_node_header))
01169         return(node->corrupt = __LINE__, -1);
01170     hdr = (struct ccn_btree_node_header *)node->buf->buf;
01171     if (MYFETCH(hdr, magic) != CCN_BTREE_MAGIC)
01172         return(node->corrupt = __LINE__, -1);
01173     if (MYFETCH(hdr, version) != CCN_BTREE_VERSION)
01174         return(node->corrupt = __LINE__, -1);
01175     /* nodetype values are not checked at present */
01176     lev = MYFETCH(hdr, level);
01177     strbase += MYFETCH(hdr, extsz) * CCN_BT_SIZE_UNITS;
01178     if (strbase > node->buf->length)
01179         return(node->corrupt = __LINE__, -1);
01180     if (strbase == node->buf->length)
01181         return(node->freelow = strbase, saved_corrupt); /* no entries */
01182     nent = ccn_btree_node_nent(node);
01183     for (i = 0; i < nent; i++) {
01184         unsigned e;
01185         p = seek_trailer(node, i);
01186         if (p == NULL)
01187             return(-1);
01188         e = MYFETCH(p, entsz);
01189         if (i == 0) {
01190             freemax = ((unsigned char *)p) - node->buf->buf;
01191             entsz = e;
01192         }
01193         if (e != entsz)
01194             return(node->corrupt = __LINE__, -1);
01195         if (MYFETCH(p, level) != lev)
01196             return(node->corrupt = __LINE__, -1);
01197         koff = MYFETCH(p, koff0);
01198         ksiz = MYFETCH(p, ksiz0);
01199         if (koff < strbase && ksiz != 0)
01200             return(node->corrupt = __LINE__, -1);
01201         if (koff > freemax)
01202             return(node->corrupt = __LINE__, -1);
01203         if (ksiz > freemax - koff)
01204             return(node->corrupt = __LINE__, -1);
01205         if (koff + ksiz > freelow)
01206             freelow = koff + ksiz;
01207         koff = MYFETCH(p, koff1);
01208         ksiz = MYFETCH(p, ksiz1);
01209         if (koff < strbase && ksiz != 0)
01210             return(node->corrupt = __LINE__, -1);
01211         if (koff > freemax)
01212             return(node->corrupt = __LINE__, -1);
01213         if (ksiz > freemax - koff)
01214             return(node->corrupt = __LINE__, -1);
01215         if (koff + ksiz > freelow)
01216             freelow = koff + ksiz;
01217     }
01218     if (node->freelow != freelow)
01219         node->freelow = freelow; /* set a break here to check for fixups */
01220     return(saved_corrupt);
01221 }
01222 
01223 /**
01224  *  Get ready to update a btree node
01225  *
01226  * If applicable, open the node so that it will be
01227  * in a good state to write later on.
01228  *
01229  * @returns 0 if OK, -1 for error.
01230  */
01231 int
01232 ccn_btree_prepare_for_update(struct ccn_btree *bt, struct ccn_btree_node *node)
01233 {
01234     int res = 0;
01235     
01236     if (node->freelow == 0)
01237         ccn_btree_chknode(node);
01238     if (node->corrupt)
01239         return(-1);
01240     if (bt->io != NULL && node->iodata == NULL) {
01241         bt->cleanreq++;
01242         res = bt->io->btopen(bt->io, node);
01243         if (res < 0) {
01244             ccn_btree_note_error(bt, __LINE__);
01245             node->corrupt = __LINE__;
01246         }
01247     }
01248     node->activity += CCN_BT_ACTIVITY_UPDATE_BUMP;
01249     return(res);
01250 }
01251 
01252 static int
01253 compare_lexical(struct ccn_charbuf *a, struct ccn_charbuf *b)
01254 {
01255     int al, bl; /* won't work for huge keys, but OK for here */
01256     int res;
01257     
01258     al = a->length;
01259     bl = b->length;
01260     res = memcmp(a->buf, b->buf, al < bl ? al : bl);
01261     if (res == 0)
01262         res = (al - bl);
01263     return(res);
01264 }
01265 
01266 static void
01267 ccn_charbuf_append_escaped(struct ccn_charbuf *dst, struct ccn_charbuf *src)
01268 {
01269     size_t i, n;
01270     int c;
01271     
01272     n = src->length;
01273     ccn_charbuf_reserve(dst, n);
01274     for (i = 0; i < n; i++) {
01275         c = src->buf[i];
01276         if (c < ' ' || c > '~' || c == '\\' || c == '(' || c == ')' || c == '"')
01277             ccn_charbuf_putf(dst, "\\%03o", c);
01278         else
01279             ccn_charbuf_append_value(dst, c, 1);
01280     }
01281 }
01282 
01283 #define MSG(fmt, ...) if (outfp != NULL) fprintf(outfp, fmt "\n", __VA_ARGS__)
01284 
01285 /**
01286  *  Check the structure of the btree for consistency.
01287  *
01288  * If outfp is not NULL, information about structure will be written.
01289  * @returns -1 if an error was found.
01290  */
01291 int
01292 ccn_btree_check(struct ccn_btree *btree, FILE *outfp) {
01293     struct ccn_btree_node *node;
01294     struct ccn_btree_node *child;
01295     ccn_btnodeid stack[40] = {};
01296     int kstk[40] = {};
01297     int sp = 0;
01298     struct ccn_charbuf *buf[3];
01299     struct ccn_charbuf *q;
01300     int pp = 0;  /* for ping-pong buffers */
01301     int res;
01302     int i, k;
01303     struct ccn_btree_internal_payload *e = NULL;
01304     const char *indent = "\t\t\t\t\t\t\t\t"; /* 8 tabs for indentation */
01305     
01306     //unsigned long nodecount = 0;
01307     if (0) return(0);
01308     
01309     for (i = 0; i < 3; i++)
01310         buf[i] = ccn_charbuf_create();
01311     q = buf[2]; /* Scratch buffer for quoting */
01312     MSG("%%I start ccn_btree_check %d %u %u %d",
01313         hashtb_n(btree->resident),
01314         (unsigned)btree->nextsplit,
01315         (unsigned)btree->missedsplit,
01316         btree->errors);
01317     if (btree->missedsplit != 0 || btree->errors != 0) {
01318         MSG("%%W %s", "reset error indications");
01319         btree->missedsplit = 0;
01320         btree->errors = 0;
01321     }
01322     node = ccn_btree_getnode(btree, 1, 0);
01323     if (node == NULL) {
01324         MSG("%%E %s", "no root node!");
01325         goto Bail;
01326     }
01327     k = 0;
01328     res = 0;
01329     while (node != NULL && res >= 0) {
01330         int l = ccn_btree_node_level(node);
01331         int n = ccn_btree_node_nent(node);
01332         if (k == 0) {
01333             res = ccn_btree_chknode(node);
01334             if (res < 0) {
01335                 MSG("%%E ccn_btree_chknode(%u) error (%d)",
01336                     (unsigned)node->nodeid, node->corrupt);
01337                 ccn_btree_note_error(btree, __LINE__);
01338             }
01339             else if (res != 0) {
01340                 MSG("%%W ccn_btree_chknode(%u) returned %d",
01341                     (unsigned)node->nodeid, node->corrupt);
01342             }
01343         }
01344         if (k == n) {
01345             /* Done with this node, release scarce resources */
01346             res = ccn_btree_close_node(btree, node);
01347             if (res < 0)
01348                 MSG("%%W close of node %u failed", (unsigned)node->nodeid);
01349             /* Pop our stack to continue processing our parent */
01350             if (sp == 0) (k = 0, node = NULL);
01351             else (sp--, k = kstk[sp], node = ccn_btree_getnode(btree, stack[sp], 0));
01352         }
01353         else {
01354             if (k == 0 && l > 0) {
01355                 /* Key 0 of a non-leaf should be empty */
01356                 if (ccn_btree_compare(NULL, 0, node, k) != 0) {
01357                     ccn_btree_key_fetch(q, node, k);
01358                     i = q->length;
01359                     ccn_charbuf_append_escaped(q, q);
01360                     MSG("%%E Key [%u 0] %d not empty: (%s)",
01361                         (unsigned)node->nodeid, l, ccn_charbuf_as_string(q) + i);
01362                     ccn_btree_note_error(btree, __LINE__);
01363                 }
01364             }
01365             else {
01366                 pp ^= 1; /* swap ping-pong buffers */
01367                 res = ccn_btree_key_fetch(buf[pp], node, k);
01368                 if (res < 0) {
01369                     MSG("%%E could not fetch key %d of node %u",
01370                         k, (unsigned)node->nodeid);
01371                 }
01372                 else {
01373                     res = compare_lexical(buf[pp ^ 1], buf[pp]);
01374                     if (res < 0 || (res == 0 && k == 0 && l == 0)) {
01375                         /* Keys are in correct order */
01376                         res = 0;
01377                     }
01378                     else {
01379                         MSG("%%E Keys are out of order! [%u %d]",
01380                             (unsigned)node->nodeid, k);
01381                         ccn_btree_note_error(btree, __LINE__);
01382                         res = -(btree->errors > 10);
01383                     }
01384                     q->length = 0;
01385                     ccn_charbuf_append_escaped(q, buf[pp]);
01386                     MSG("%s(%s) [%u %d] %d %s", indent + 8 - sp % 8,
01387                         ccn_charbuf_as_string(q), (unsigned)node->nodeid, k, l,
01388                         l == 0 ? "leaf" : "node");
01389                 }
01390             }
01391             if (l == 0)
01392                 k++;
01393             else {
01394                 stack[sp] = node->nodeid;
01395                 kstk[sp] = k + 1;
01396                 sp++;
01397                 if (sp == 40) goto Bail;
01398                 e = ccn_btree_node_getentry(sizeof(*e), node, k);
01399                 if (e == NULL) goto Bail;
01400                 child = ccn_btree_getnode(btree, MYFETCH(e, child), node->nodeid);
01401                 if (child == NULL) goto Bail;
01402                 if (child->parent != node->nodeid) {
01403                     /* This is an error, but we can repair it */
01404                     MSG("%%E child->parent != node->nodeid (%u!=%u)",
01405                         (unsigned)child->parent, (unsigned)node->nodeid);
01406                     ccn_btree_note_error(btree, __LINE__);
01407                     child->parent = node->nodeid;
01408                 }
01409                 node = child;
01410                 k = 0;            
01411             }
01412         }
01413     }
01414     if (res <= 0 && btree->errors == 0) {
01415         for (i = 0; i < 3; i++)
01416             ccn_charbuf_destroy(&buf[i]);
01417         return(0);
01418     }
01419 Bail:
01420     ccn_btree_note_error(btree, __LINE__);
01421     MSG("%%W finish ccn_btree_check %d %u %u %d",
01422         hashtb_n(btree->resident),
01423         (unsigned)btree->nextsplit,
01424         (unsigned)btree->missedsplit,
01425         btree->errors);
01426     for (i = 0; i < 3; i++)
01427         ccn_charbuf_destroy(&buf[i]);
01428     return(-1);
01429 }
01430 #undef MSG

Generated on Thu Feb 16 00:43:59 2012 for Content-Centric Networking in C by  doxygen 1.5.6