cbtree: instead C-strings, use key + length
authorMarko Kreen <markokr@gmail.com>
Thu, 24 Sep 2009 09:56:43 +0000 (12:56 +0300)
committerMarko Kreen <markokr@gmail.com>
Tue, 8 Dec 2009 12:03:17 +0000 (14:03 +0200)
This allows to be more flexible on what data to use as key.

usual/cbtree.c
usual/cbtree.h

index 0525fcb173aa347321bd25529a28bea16debacc6..a4eca52efad27c21626dc242447c93ebb49e64ad 100644 (file)
@@ -44,7 +44,7 @@ struct Node {
 
 struct CBTree {
        struct Node *root;
-       const char *(*get_key)(void *obj);
+       cbtree_getkey_func get_key;
 };
 
 #define SAME_KEY 0xFFFFFFFF
@@ -73,7 +73,7 @@ static inline void *get_external(void *extval)
 }
 
 /* get specific bit from string */
-static inline unsigned get_bit(unsigned bitpos, const char *key, unsigned klen)
+static inline unsigned get_bit(unsigned bitpos, const unsigned char *key, unsigned klen)
 {
        unsigned pos = bitpos / 8;
        unsigned bit = 7 - (bitpos % 8);
@@ -81,29 +81,47 @@ static inline unsigned get_bit(unsigned bitpos, const char *key, unsigned klen)
 }
 
 /* use callback to get key for a stored object */
-static inline const char *get_key(struct CBTree *tree, void *obj)
+static inline unsigned get_key(struct CBTree *tree, void *obj, const void **key_p)
 {
-       return tree->get_key(obj);
+       return tree->get_key(obj, key_p);
 }
 
-/* Find first differing bit on 2 zero-terminated strings.  */
-static unsigned find_crit_bit(const char *a, const char *b)
+/* check if object key matches argument */
+static inline bool key_matches(struct CBTree *tree, void *obj, const void *key, unsigned klen)
 {
-       unsigned i, c, pos;
+       const void *o_key;
+       unsigned o_klen;
+       o_klen = get_key(tree, obj, &o_key);
+       return (o_klen == klen) && (memcmp(key, o_key, klen) == 0);
+}
 
-       /* find differing byte */
-       for (i = 0; ; i++) {
-               /* this handles also size difference */
-               if (a[i] != b[i])
-                       break;
+/* Find first differing bit on 2 strings.  */
+static unsigned find_crit_bit(const unsigned char *a, unsigned alen, const unsigned char *b, unsigned blen)
+{
+       unsigned i, c, pos, av, bv;
+       unsigned minlen = (alen > blen) ? blen : alen;
+       unsigned maxlen = (alen > blen) ? alen : blen;
+
+       /* find differing byte in common data */
+       for (i = 0; i < minlen; i++) {
+               av = a[i];
+               bv = b[i];
+               if (av != bv)
+                       goto found;
+       }
 
-               /* equal strings? */
-               if (a[i] == 0)
-                       return SAME_KEY;
+       /* find differing byte when one side is zero-filled */
+       for (; i < maxlen; i++) {
+               av = (i < alen) ? a[i] : 0;
+               bv = (i < blen) ? b[i] : 0;
+               if (av != bv)
+                       goto found;
        }
+       return SAME_KEY;
 
+found:
        /* calculate bits that differ */
-       c = a[i] ^ b[i];
+       c = av ^ bv;
 
        /* find the first one */
        pos = 8 - fls(c);
@@ -117,7 +135,7 @@ static unsigned find_crit_bit(const char *a, const char *b)
  */
 
 /* walk nodes until external pointer is found */
-static void *raw_lookup(struct CBTree *tree, const char *key, unsigned klen)
+static void *raw_lookup(struct CBTree *tree, const void *key, unsigned klen)
 {
        struct Node *node = tree->root;
        unsigned bit;
@@ -129,20 +147,18 @@ static void *raw_lookup(struct CBTree *tree, const char *key, unsigned klen)
 }
 
 /* actual lookup.  returns obj ptr or NULL of not found */
-void *cbtree_lookup(struct CBTree *tree, const char *key)
+void *cbtree_lookup(struct CBTree *tree, const void *key, unsigned klen)
 {
-       unsigned klen;
        void *obj;
 
        if (!tree->root)
                return NULL;
 
        /* find match based on bits we know about */
-       klen = strlen(key);
        obj = raw_lookup(tree, key, klen);
 
        /* need to check if the object actually matches */
-       if (strcmp(key, get_key(tree, obj)) == 0)
+       if (key_matches(tree, obj, key, klen))
                return obj;
 
        return NULL;
@@ -169,7 +185,7 @@ static bool insert_first(struct CBTree *tree, void *obj)
 }
 
 /* insert into specific bit-position */
-static bool insert_at(struct CBTree *tree, unsigned newbit, const char *key, unsigned klen, void *obj)
+static bool insert_at(struct CBTree *tree, unsigned newbit, const void *key, unsigned klen, void *obj)
 {
        /* location of current node/obj pointer under examination */
        struct Node **pos = &tree->root;
@@ -195,20 +211,22 @@ static bool insert_at(struct CBTree *tree, unsigned newbit, const char *key, uns
 /* actual insert: returns true -> insert ok or key found, false -> malloc failure */
 bool cbtree_insert(struct CBTree *tree, void *obj)
 {
-       const char *key;
-       unsigned newbit, klen;
+       const void *key, *old_key;
+       unsigned newbit, klen, old_klen;
        void *old_obj;
 
        if (!tree->root)
                return insert_first(tree, obj);
 
-       /* match bits we know about */
-       key = get_key(tree, obj);
-       klen = strlen(key);
+       /* current key */
+       klen = get_key(tree, obj, &key);
+
+       /* nearest key in tree */
        old_obj = raw_lookup(tree, key, klen);
+       old_klen = get_key(tree, obj, &old_key);
 
        /* first differing bit is the target position */
-       newbit = find_crit_bit(key, get_key(tree, old_obj));
+       newbit = find_crit_bit(key, klen, old_key, old_klen);
        if (newbit == SAME_KEY)
                return true;
        return insert_at(tree, newbit, key, klen, obj);
@@ -220,11 +238,10 @@ bool cbtree_insert(struct CBTree *tree, void *obj)
  */
 
 /* true -> object was found and removed, false -> not found */
-bool cbtree_delete(struct CBTree *tree, const char *key)
+bool cbtree_delete(struct CBTree *tree, const void *key, unsigned klen)
 {
        void *obj, *tmp;
        unsigned bit = 0;
-       unsigned klen = strlen(key);
        /* location of current node/obj pointer under examination */
        struct Node **pos = &tree->root;
        /* if 'pos' has user obj, prev_pos has internal node pointing to it */
@@ -242,7 +259,7 @@ bool cbtree_delete(struct CBTree *tree, const char *key)
 
        /* does the key actually matches */
        obj = get_external(*pos);
-       if (strcmp(key, get_key(tree, obj)) != 0)
+       if (!key_matches(tree, obj, key, klen))
                return false;
 
        /* drop the internal node pointing to our key */
index cf62e48608f13176b766f0209111093b471ce4a7..a8ba46c44b839d58ea1d7b8470a4a7dffd6f735e 100644 (file)
@@ -21,7 +21,8 @@
 
 #include <usual/base.h>
 
-typedef const char *(*cbtree_getkey_func)(void *obj);
+/* returns length of the key */
+typedef unsigned int (*cbtree_getkey_func)(void *obj, const void **dst_p);
 
 struct CBTree;
 
@@ -29,9 +30,9 @@ struct CBTree *cbtree_create(cbtree_getkey_func get_key_fn);
 void cbtree_destroy(struct CBTree *tree);
 
 bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK;
-bool cbtree_delete(struct CBTree *tree, const char *key);
+bool cbtree_delete(struct CBTree *tree, const void *key, unsigned klen);
 
-void *cbtree_lookup(struct CBTree *tree, const char *key);
+void *cbtree_lookup(struct CBTree *tree, const void *key, unsigned klen);
 
 #endif