ccn_btree.c

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