rework to handle vector variants 0-3
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Thu, 30 Jun 2022 11:48:03 +0000 (21:48 +1000)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Thu, 30 Jun 2022 11:48:03 +0000 (21:48 +1000)
tests/Makefile
tests/example-hashvector.c
tests/example-vector.c
tests/vectortests.c
vector/Makefile
vector/hashvector.c
vector/hashvector.h
vector/vector.c
vector/vector.h

index b050eda33cc87e465f55d3558c8a2a05945d58c8..6da4dd77f1d9d2cba77b8b9004ddb6b0b30cf425 100644 (file)
@@ -5,7 +5,7 @@ BIN = ${SRC:.c=}
 default: ${BIN}
 
 CFLAGS = -Wall -I../vector -g -fmax-errors=1
-LDLIBS = -L../vector -lvector
+LDLIBS = -L../vector -lvector -lm
 
 .INTERMEDIATE: ${OBJ}
 
index 7bd57a5f53a3018aa7a101b501553d64db30919b..516cd07393cc8e790e6b872a32453e767e18e7b2 100644 (file)
@@ -8,15 +8,15 @@ typedef struct _ipslot {
     unsigned int bits;
 } ipslot;
 
-static unsigned long voidp_hashcode(void *key) {
+static unsigned long voidp_hashcode(itemkeyfun *this,void *key) {
     return hashvector_hashcode( key, sizeof( ipslot ) );
 }
 
-static void* voidp_itemkey(void *item) {
+static void* voidp_itemkey(itemkeyfun *this,void *item) {
     return item;
 }
 
-static int voidp_haskey(void *item,void *key) {
+static int voidp_haskey(itemkeyfun *this,void *item,void *key) {
     return memcmp( item, key, sizeof( ipslot ) ) == 0;
 }
 
@@ -33,13 +33,16 @@ static int shrink(vector *pv,unsigned long index,void *item,void *data) {
 }
 
 int main(int argc,char **argv) {
+    itemkeyfun voidpfun = {
+       .hashcode = voidp_hashcode,
+       .itemkey = voidp_itemkey,
+       .haskey = voidp_haskey
+    };
     hashvector hv = {
        .table = { 4, 0 },
        .fill = 0,
        .holes = 0,
-       .keyhashcode = voidp_hashcode,
-       .itemkey = voidp_itemkey,
-       .haskey = voidp_haskey
+       .type = &voidpfun
     };
     int i = 0;
     for ( ; i < 259; i++ ) {
index 48403fcb106302ee50e0804c92c8f2d9c973f28b..c191d2dd4402544901f7ccd6f2fd9b2822edcfe4 100644 (file)
@@ -126,17 +126,15 @@ static int int_reclaim(vector *pv,unsigned long index,void *item,void *data) {
     return 0;
 }
 
-static int dumpitem(const unsigned long index,const void *item) {
+static void dumpitem(const vector_index index,const void *item) {
     fprintf( stdout, "[%ld] %p\n", index, item );
-    return 0;
 }
 
-static int dump_ipslot(const unsigned long index,const void *item) {
+static void dump_ipslot(const vector_index index,const void *item) {
     static char buffer[100];
     ipslot *ip = (ipslot*) item;
     const char *p = inet_ntop( ip->family, ip->data, buffer, 100 );
     fprintf( stdout, "[%ld] %s/%d\n", index, p, ip->bits );
-    return 0;
 }
 
 int main(int argc,char **argv) {
index 30169dc26e59d272382de61101fbb13dfa3ef76b..6f8cdbd9205ade136d243b8e940a263b208c0286 100644 (file)
@@ -57,151 +57,176 @@ static void my_vector_dump(
     vector_dump( vp, itemdump );
 }
 
+static char *item25 = "this is first item";
+static char *item75 = "this is second item";
+
 int main(int argc,char **argv) {
-    OUT( "VECTOR_LEVEL_BITS = %d\n", VECTOR_LEVEL_BITS );
-    OUT( "sizeof( vector_index ) = %ld\n", sizeof( vector_index ) );
-    OUT( "VECTOR_INDEX_BITS - %ld\n", VECTOR_INDEX_BITS );
-    OUT( "VECTOR_INDEX_FIELDS = %ld\n", VECTOR_INDEX_FIELDS );
-    OUT( "VECTOR_SLOTS = %d\n", VECTOR_SLOTS );
-    OUT( "sizeof( vector_page ) = %ld\n", sizeof( vector_page ) );
-    OUT( "sizeof( vector ) = %ld\n", sizeof( vector ) );
-
-    vector v = { 100, 0 }; // Create an empty vector of 100 slots.
-    void ** slot;
-    void *item;
-    int i;
+    int variant = 0;
+    for ( ; variant < 4; variant++ ) {
+       OUT( "variant = %d ========================\n", variant );
+       vector v = { variant, 100, 0 }; // Create an empty vector of 100 slots.
+
+       //OUT( "VECTOR_LEVEL_BITS = %d\n", VECTOR_LEVEL_BITS );
+       //OUT( "sizeof( vector_index ) = %ld\n", sizeof( vector_index ) );
+       //OUT( "VECTOR_INDEX_BITS - %ld\n", VECTOR_INDEX_BITS );
+       //OUT( "VECTOR_INDEX_FIELDS = %ld\n", VECTOR_INDEX_FIELDS );
+       OUT( "VECTOR_SLOTS = %ld\n", VECTOR_SLOTS( &v ) );
+       // OUT( "sizeof( vector_page ) = %ld\n", sizeof( vector_page ) );
+       OUT( "sizeof( vector ) = %ld\n", sizeof( vector ) );
+
+       void ** slot;
+       void *item;
+       int i;
     
-    OUT( "vector v has 100 empty slots\n" );
-
-    // void vector_dump(vector *pv,
-    //   int (*itemdump)(const vector_index ,const void *));
-    // void **vector_next_used(vector *pv,vector_index *index);
-    my_vector_dump( &v, itemdump );
-
-    //void vector_set(vector *pv,vector_index index,void *value);
-    vector_set( &v, 25, "this is first item" );
-
-    // void **vector_prev_used(vector *pv,vector_index *index);
-    int t0[6] = { 0, 25, 50, 99, 100, 1000 };
-    OUT( "vector_next_used:\n" );
-    for ( i = 0; i < 6; i++ ) {
-       vector_index index = t0[i];
-       slot = vector_next_used( &v, &index );
-       OUT( " [%d] => [%ld] %p\n", t0[i], index, slot );
-    }
-
-    OUT( "vector_prev_used:\n" );
-    for ( i = 0; i < 6; i++ ) {
-       vector_index index = t0[i];
-       slot = vector_prev_used( &v, &index );
-       OUT( " [%d] => [%ld] %p\n", t0[i], index, slot );
-    }
-
-    vector_set( &v, 75, "this is second item" );
-    OUT( "vector v has 2 non-empty slots\n" );
-    my_vector_dump( &v, itemdump );
+       OUT( "vector v has 100 empty slots\n" );
+
+       // void vector_dump(vector *pv,
+       //        int (*itemdump)(const vector_index ,const void *));
+       // void **vector_next_used(vector *pv,vector_index *index);
+       my_vector_dump( &v, itemdump );
+
+       //void vector_set(vector *pv,vector_index index,void *value);
+       item = item25;
+       vector_set( &v, 25, item );
+       OUT( "assigned 25 to %p %s\n", item, (char*)item );
+
+       // void **vector_prev_used(vector *pv,vector_index *index);
+       int t0[6] = { 0, 25, 50, 99, 100, 1000 };
+       OUT( "vector_next_used:\n" );
+       for ( i = 0; i < 6; i++ ) {
+           vector_index index = t0[i];
+           slot = vector_next_used( &v, &index );
+           OUT( " [%d] => [%ld] %p\n", t0[i], index, slot? *slot : 0 );
+       }
+
+       OUT( "vector_prev_used:\n" );
+       for ( i = 0; i < 6; i++ ) {
+           vector_index index = t0[i];
+           slot = vector_prev_used( &v, &index );
+           OUT( " [%d] => [%ld] %p\n", t0[i], index, slot? *slot : 0 );
+       }
+
+       item = item75;
+       vector_set( &v, 75, item );
+       OUT( "assigned 75 to %p %s\n", item, (char*)item );
+
+       my_vector_dump( &v, itemdump );
     
-    OUT( "vector_next_used:\n" );
-    for ( i = 0; i < 6; i++ ) {
-       vector_index index = t0[i];
-       slot = vector_next_used( &v, &index );
-       OUT( " [%d] => [%ld] %p\n", t0[i], index, slot );
-    }
-
-    OUT( "vector_prev_used:\n" );
-    for ( i = 0; i < 6; i++ ) {
-       vector_index index = t0[i];
-       slot = vector_prev_used( &v, &index );
-       OUT( " [%d] => [%ld] %p\n", t0[i], index, slot );
-    }
-
-    OUT( "shrinking the vector:\n" );
-    // int vector_resize(
-    //     vector *pv, vector_index new_size,
-    //     int (*reclaim)(vector *pv,vector_index index,void *item,void *data),
-    //     void *data );
-    i = vector_resize( &v, 50, itemreclaim, (void*)1 );
-    OUT( "shrink to 50 (reclaim refused) = %d\n", i );
+       OUT( "vector_next_used:\n" );
+       for ( i = 0; i < 6; i++ ) {
+           vector_index index = t0[i];
+           slot = vector_next_used( &v, &index );
+           OUT( " [%d] => [%ld] %p\n", t0[i], index, slot? *slot : 0 );
+       }
+
+       OUT( "vector_prev_used:\n" );
+       for ( i = 0; i < 6; i++ ) {
+           vector_index index = t0[i];
+           slot = vector_prev_used( &v, &index );
+           OUT( " [%d] => [%ld] %p\n", t0[i], index, slot? *slot : 0 );
+       }
+
+       OUT( "shrinking the vector:\n" );
+       // int vector_resize(
+       //     vector *pv, vector_index new_size,
+       //     int (*reclaim)(vector *pv,vector_index index,
+       //                    void *item, void *data),
+       //     void *data );
+       i = vector_resize( &v, 50, itemreclaim, (void*)1 );
+       OUT( "shrink to 50 (reclaim refused) = %d\n", i );
         
 
-    i = vector_resize( &v, 50, itemreclaim, (void*)0 );
-    OUT( "shrink to 50 (accept reclaim) = %d\n", i );
+       i = vector_resize( &v, 50, itemreclaim, (void*)0 );
+       OUT( "shrink to 50 (accept reclaim) = %d\n", i );
 
-    i = vector_resize( &v, 508, 0, 0 );
-    OUT( "grow to 508 (no reclaim) = %d\n", i );
+       i = vector_resize( &v, 508, 0, 0 );
+       OUT( "grow to 508 (no reclaim) = %d\n", i );
 
-    // void **vector_entry(vector *pv,vector_index index); 
+       // void **vector_entry(vector *pv,vector_index index); 
 #define SLOTSTR(slot) (slot? ((*slot)? *slot : "(nil)") : "(unassigned)" )
-    slot = vector_entry( &v, 24 );
-    itemdump( 24, SLOTSTR(slot) );
-
-    slot = vector_entry( &v, 25 );
-    itemdump( 25, SLOTSTR(slot) );
-
-    slot = vector_entry( &v, 300 );
-    itemdump( 300, SLOTSTR( slot ) );
-
-    //#define vector_size(pv) ((vector_index) (pv)->size)
-    OUT( "vector size: %ld\n", vector_size( &v ) );
-
-    // void *vector_get_set(vector *pv,vector_index index,void *value);
-    item = vector_get_set( &v, 25, "another value" );
-    // void *vector_get(vector *pv,vector_index index);
-    OUT( "old: \"%s\", new: \"%s\"\n",
-        (char*)item, (char*)vector_get( &v, 25 ) );
-
-    // void vector_append(vector *pv,void *value);
-    vector_append( &v, "the very last item" );
-    OUT( "vector size: %ld\n", vector_size( &v ) );
-    my_vector_dump( &v, itemdump );
-
-    vector v2 = { 200, 0 };
-    // void vector_copy(
-    //        vector *dst,vector_index di,
-    //        vector *src,vector_index si,
-    //        vector_index n);
-    vector_copy( &v2, 20, &v, 10, 20 );
-    my_vector_dump( &v2, itemdump );
-
-    vector_resize( &v2, 0, itemreclaim, 0 ); // Reset vector v2
-    my_vector_dump( &v2, itemdump );
-
-    vector_append( &v2, "9 the very last item" );
-    vector_append( &v2, "3 the very last item" );
-    vector_append( &v2, "4 the very last item" );
-    vector_append( &v2, "6 the very last item" );
-    vector_append( &v2, "5 the very last item" );
-    vector_resize( &v2, vector_size( &v2 ) + 3, 0, 0 );
-    vector_append( &v2, "2 the very last item" );
-    vector_append( &v2, "8 the very last item" );
-    vector_append( &v2, "1 the very last item" );
-    vector_append( &v2, 0 );
-    vector_append( &v2, "7 the very last item" );
-    vector_append( &v2, "0 the very last item" );
-    my_vector_dump( &v2, itemdump );
-
-    // void vector_qsort(vector *pv,int (*compar)(const void *,const void *));
-    OUT( "sorted:" );
-    vector_qsort( &v2, itemcmp );
-    my_vector_dump( &v2, itemdump );
-
-    // void vector_iterate(vector *pv,
-    //     vector_index start,
-    //     int (*itemfn)(vector_index,void *item,void *data),
-    //     void *data);
-    OUT( "showing all slots\n" );
-    vector_iterate( &v2, 4, itemiter, 0 );
-
-    // void *vector_bsearch(vector *pv,vector_index *index,const void *key,
-    //      int (*compare)(const void *key, const void *item));
-    char *pfx[5] = { "4", "9", "0", "3", "10" };
-    for ( i = 0; i < ( sizeof( pfx ) / sizeof( char* ) ); i++ ) {
-       char *prefix = pfx[i];
-       vector_index index = 0;
-       OUT( "lookup prefix \"%s\":\n", prefix );
-       item = vector_bsearch( &v2, &index, prefix, itemfind );
-       OUT( "[%ld] %p %s\n", index, item, ( item? (char*)item : "(null)" ) );
+       slot = vector_entry( &v, 24 );
+       itemdump( 24, SLOTSTR(slot) );
+
+       slot = vector_entry( &v, 25 );
+       itemdump( 25, SLOTSTR(slot) );
+
+       slot = vector_entry( &v, 300 );
+       itemdump( 300, SLOTSTR( slot ) );
+
+       //#define vector_size(pv) ((vector_index) (pv)->size)
+       OUT( "vector size: %ld\n", vector_size( &v ) );
+
+       // void *vector_get(vector *pv,vector_index index);
+       // void *vector_get_set(vector *pv,vector_index index,void *value);
+       item = vector_get( &v, 25 );
+       OUT( "old item 25 is %p %s\n", item, (char*)item );
+       item = "another value";
+       OUT( "new item 25 is %p %s\n", item, (char*)item );
+       item = vector_get_set( &v, 25, item );
+       OUT( "got item 25 as %p %s\n", item, (char*)item );
+       item = vector_get( &v, 25 );
+       OUT( "now item 25 is %p %s\n", item, (char*)item );
+
+       // void vector_append(vector *pv,void *value);
+       item = "the very last item";
+       OUT( "appending %p %s\n", item, (char*)item );
+       vector_append( &v, item );
+
+       OUT( "vector size: %ld\n", vector_size( &v ) );
+       my_vector_dump( &v, itemdump );
+
+       vector v2 = { variant, 200, 0 };
+       // void vector_copy(
+       //        vector *dst,vector_index di,
+       //        vector *src,vector_index si,
+       //        vector_index n);
+       vector_copy( &v2, 20, &v, 10, 20 );
+       my_vector_dump( &v2, itemdump );
+
+       vector_resize( &v2, 0, itemreclaim, 0 ); // Reset vector v2
+       my_vector_dump( &v2, itemdump );
+
+       vector_append( &v2, "9 the very last item" );
+       vector_append( &v2, "3 the very last item" );
+       vector_append( &v2, "4 the very last item" );
+       vector_append( &v2, "6 the very last item" );
+       vector_append( &v2, "5 the very last item" );
+       vector_resize( &v2, vector_size( &v2 ) + 3, 0, 0 );
+       vector_append( &v2, "2 the very last item" );
+       vector_append( &v2, "8 the very last item" );
+       vector_append( &v2, "1 the very last item" );
+       vector_append( &v2, 0 );
+       vector_append( &v2, "7 the very last item" );
+       vector_append( &v2, "0 the very last item" );
+       my_vector_dump( &v2, itemdump );
+
+       // void vector_qsort(vector*,int (*compar)(const void *,const void *));
+       OUT( "sorted:" );
+       vector_qsort( &v2, itemcmp );
+       my_vector_dump( &v2, itemdump );
+
+       // void vector_iterate(vector *pv,
+       //          vector_index start,
+       //          int (*itemfn)(vector_index,void *item,void *data),
+       //          void *data);
+       OUT( "showing all slots\n" );
+       vector_iterate( &v2, 4, itemiter, 0 );
+
+       // void *vector_bsearch(vector *pv,vector_index *index,const void *key,
+       //           int (*compare)(const void *key, const void *item));
+       char *pfx[5] = { "4", "9", "0", "3", "10" };
+       for ( i = 0; i < ( sizeof( pfx ) / sizeof( char* ) ); i++ ) {
+           char *prefix = pfx[i];
+           vector_index index = 0;
+           OUT( "lookup prefix \"%s\":\n", prefix );
+           item = vector_bsearch( &v2, &index, prefix, itemfind );
+           OUT( "[%ld] %p %s\n", index, item,
+                ( item? (char*)item : "(null)" ) );
+       }
+
+       // Clear out the vectors
+        (void) vector_resize( &v, 0, itemreclaim, (void*)0 );
+        (void) vector_resize( &v2, 0, itemreclaim, (void*)0 );
     }
-
     return 0;
 }
index 0fb332e827317d0684e92bef571a038b3c5083b5..7c5268d0b7aa65ce6991d857489795c7c24d75f1 100644 (file)
@@ -1,11 +1,13 @@
 LIBRARY = libvector.a
 LIBOBJS = vector.o hashvector.o
+LIBOBJS += integeritem.o  stringitem.o  tupleitem.o
 
 default: $(LIBRARY)
 
 all: default
 
 CFLAGS = -Wall -g -fmax-errors=1 -I.
+LDLIBS = -lm
 
 define STDCC
 .INTERMEDIATE: $1.o
index bd6fb97b8697c1594414301a5cf3d28fead0b23e..27c2d65e5d63bec9bedbe5f33ff5006599c72873 100644 (file)
@@ -1,43 +1,65 @@
 #include "hashvector.h"
 
+#define SELF hv->type
+
 // Find the slot for the keyed element, and return pointer to it, or
 // to the first of holes encountered while considering collisions.
 // Returns a pointer to the place for the item, or 0 in case of OOM or
 // overfull hashvector (i.e. 0 shouldn't happen).
-static void **hashvector_find_slot(hashvector *hv,void *key) {
-    unsigned long index = hv->keyhashcode( key ) % hv->table.size;
-    unsigned long i = index;
+// If itemkey is set, then the itmekey callback function is used for
+// obtaining a temporary key from the item.
+static void **hashvector_find_slot(
+    hashvector *hv, void *key, unsigned long *i, int itemkey )
+{
+    if ( itemkey ) {
+        // Get actual key from keying item
+       key = hv->type->itemkey( SELF, key );
+    }
+    unsigned long index = hv->type->hashcode( SELF, key ) % hv->table.size;
+    *i = index;
     void **hole = 0;
     void **p = 0;
     for ( ;; ) {
-       p = vector_entry(&hv->table, i);
+       p = vector_entry( &hv->table, (*i) );
        if ( p == 0 ) {
+           if ( itemkey ) {
+               hv->type->releasekey( SELF, key );
+           }
            return 0; // This basically means OOM, and is a failure condition.
        }
-       if ( *p == 0 ) {
-           break; // Not found
+       if ( (*p) == 0 ) {
+           if ( itemkey ) {
+               hv->type->releasekey( SELF, key );
+           }
+           return ( hole )? hole : p; // Not found; it's place is here.
        }
        if ( (*p) == HV_HOLE ) {
            if ( hole == 0 ) {
                hole = p; // Remember the first hole
            }
-       } else if ( hv->haskey( *p, key ) ) {
+       } else if ( hv->type->haskey( SELF, (*p), key ) ) {
+           if ( itemkey ) {
+               hv->type->releasekey( SELF, key );
+           }
            return p; // Found
        }
-       if ( ++i == hv->table.size ) {
-           i = 0; // Roll-around the table
+       if ( ++(*i) == hv->table.size ) {
+           (*i) = 0; // Roll-around the table
        }
-       if ( i == index ) {
+       if ( (*i) == index ) {
+           if ( itemkey ) {
+               hv->type->releasekey( SELF, key );
+           }
            return 0; // Overfull hashvector!
        }
     }
-    return ( hole )? hole : p;
 }
 
 // Find the keyed element, and assign the x pointer, or assign 0.
 // Returns 1 if element is found and 0 otherwise.
 int hashvector_find(hashvector *hv,void *key,void **x) {
-    void **p = hashvector_find_slot( hv, key );
+    unsigned long i;
+    void **p = hashvector_find_slot( hv, key, &i, 0 );
     if ( p && *p && *p != HV_HOLE ) {
        if ( x ) {
            *x = *p;
@@ -76,7 +98,8 @@ static void hashvector_resize(hashvector *hv,unsigned long new_size) {
     
 // Add the given element.
 int hashvector_add(hashvector *hv,void *item) {
-    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
+    unsigned long i;
+    void **p = hashvector_find_slot( hv, item, &i, 1 );
     if ( p == 0 ) {
        return -1; // OOM or overfull hashvector
     }
@@ -96,17 +119,28 @@ int hashvector_add(hashvector *hv,void *item) {
 
 // Delete the given item
 int hashvector_delete(hashvector *hv,void *item) {
-    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
+    unsigned long i;
+    void **p = hashvector_find_slot( hv, item, &i, 1 );
     if ( p == 0 ) {
        return -1;
     }
     if ( *p != item ) {
        return 0;
     }
-    *p = HV_HOLE;
-    hv->holes++;
+    // Check if subsequent slot is occupied
+    if ( ++i >= hv->table.size ) {
+       i = 0;
+    }
+    void **q = vector_access( &hv->table, i, 0, 0 );
+    if ( q && (*q) ) {
+       *p = HV_HOLE;
+       hv->holes++;
+    } else {
+       *p = 0;
+    }
     hv->fill--;
-    if ( hv->table.size > VECTOR_SLOTS && hv->fill < hv->table.size / 4 ) {
+    if ( hv->table.size > VECTOR_SLOTS( &hv->table ) &&
+        hv->fill < hv->table.size / 4 ) {
        hashvector_resize( hv, hv->table.size / 2 );
     }
     return 1;
index 1994b66546024e2725ff418e8ba15473b1cb41a4..d0f9e12f23114f0ec5aecac85c289e9641d49c85 100644 (file)
@@ -1,7 +1,12 @@
 #ifndef hashvector_H
 #define hashvector_H
 
+#include <vector.h>
+#include <itemkeyfun.h>
+
 /**
+ * \file hashvector.h
+ *
  * A hashvector is a use of vector as a hashtable. The hashvector
  * includes three functions to, respectively, obtain the hashcode of a
  * given key, to obtain the key of a given item, and to tell whether
  * pointers. Thus, deleting an item results in av HV_HOLE et the
  * item's slot.
  *
- * The hashvector grows to double in size, with rehashing, when the
- * sum of items and holes exceeds 50% fill, and it shrinks to half
- * size when item numbers reduces below 50%.
+ * The hashvector grows to double in size, with rehashing, when an
+ * item is added to make the sum of fill and holes exceed 50% of the
+ * size, and the hashvector shrinks to half size when an item is
+ * deleted to make fill reduce below 25% of the size.
  */
 
-#include <vector.h>
-
-/*!
- * Type: hashvector
- * This combines a vector (for contents) with fill and hole counters
- * and the three functions.
+/**
+ * \struct hashvector
+ *
+ * hashvector combines a vector (for contents) with fill and hole
+ * counters, and itemkeyfun callback functions for abstracting items
+ * as being pairs of key and payload.
  */
-typedef struct _hashvector {
-    vector table;
-    unsigned long fill;                   // number of added elements
-    unsigned long holes;                  // number of deleted
-    unsigned long (*keyhashcode)(void *key); // The hashcode of a key
-    void *(*itemkey)(void *item);        // the key of ain item
-    int (*haskey)(void *item,void *key); // whether an item has a given
+typedef struct {
+    /**
+     * This is the backing vector for the hashvector. Items are placed
+     * in the vector by means of their key hashcodes, at the first
+     * unused slot cyclically after the hashcode index.
+     */
+    vector table;        // the table space for items
+    /**
+     * This is the fill count, i.e., how many key-distinct items there
+     * are in the backing vector.
+     */
+    unsigned long fill;  // current number of contained items
+    /**
+     * This is a count of HV_HOLE markers in the backing vector, which
+     * hold the position of deleted items so as to not break the
+     * lookup sequence for items placed cyclically after their
+     * hashcode index due to hash collisions.
+     */
+    unsigned long holes; // current number of slots marking deleted items
+    /**
+     * This is a pointer to the itemkeyfun record that provides the
+     * key-and-payload abstraction for items.
+     */
+    itemkeyfun *type;    // the key type for the items
 } hashvector;
 
-/*!
- * Macro: HV_HOLE
- * The representation of a deleted item.
+/**
+ * HV_HOLE is the representation of a deleted item. This supports the
+ * hashtable algoritm where hashcode collisions are resolved by
+ * placing later items compactly cyclically after their hashcode
+ * index. When an item is removed from the table, its slot is set as a
+ * HV_HOLE slot instead of being cleared.
+ *
+ * \related hashvector
  */
 #define HV_HOLE ((void*) 1)
 
-/*!
- * Function: int hashvector_find(hashvector *hv,void *key,void **x)
- *
+/**
  * Find the item, if any, with the given key and assign *x with its
  * address. Returns 1 on success and 0 on failure to find the keyed
  * item. Note that when the function returns 0, *x is unchanged.
+ *
+ * \related hashvector
  */
 int hashvector_find(hashvector *hv,void *key,void **x);
 
-/*!
- * Function: void hashvector_add(hashvector *hv,void *item)
- *
+/**
  * Add the given item into the hashvector, growing the hashvector as
  * needed to ensure that its fill+holes is is no more than half the
  * size. Note that this function first locates any item of the same
  * key, and then doesn't add if there is already an item that has the
  * same key. Returns 1 when an item is added, 0 when the item key
  * is already present, and -1 upon OOM or ovefull hashvector.
+ *
+ * \related hashvector
  */
 int hashvector_add(hashvector *hv,void *item);
 
-/*!
- * Function: void hashvector_delete(hashvector *hv,void *item)
- *
+/**
  * Delete the given item, and shrink the hashvector so that its size
  * is at most double its fill, though at least 256 slots. Returns 1
  * when the item gets deleted (by replacing its slot with a HV_HOLE
  * value, 0 if the hashvector has either no item or a different item
  * for the item key, and -1 in case of OOM or overfull hashvector.
+ *
+ * \related hashvector
  */
 int hashvector_delete(hashvector *hv,void *item);
 
-/*!
- * Function: int hashvector_contents(hashvector *hv,vector *pv)
- *
+/**
  * Copy content items into a vector, which must be empty. The
  * function returns -1 if the resizing of the vector to the
  * hashvector fill fails, otherwise 0 after having copied the
  * hashvector items in their internal order of appearance into the
  * vector.
+ *
+ * \related hashvector
  */
 int hashvector_contents(hashvector *hv,vector *pv);
 
-/*!
- * Function unsigned long hashvector_hashcode(
- *              unsigned char *key,unsigned long n)
+/**
+ * This is a utility function to compute and return a hashcode for a
+ * block of bytes.
  *
- * Computes and returns a hashcode for a block of bytes.
+ * \related hashvector
  */
 unsigned long hashvector_hashcode(unsigned char *key,unsigned long n);
 
index 5b3848842b30ec1058b4c39fb7cd38901de6836c..a9c6b63f87e787ec10c90247caf349a3dfee2b17 100644 (file)
@@ -1,4 +1,5 @@
-#include <assert.h>
+#include <string.h>
+#include <math.h>
 #include <stdlib.h>
 #include "vector.h"
 
  */
 
 /** ============================================================ **/
-#if VECTOR_LEVEL_BITS == 4
-typedef union {
-    vector_index as_whole;
-    struct {
-       unsigned int msb:4; unsigned int lsb:4;
-    } __attribute__ ((__packed__)) as_byte[8];
-} vector_indexing;
 
-#define VECTOR_LEVEL_MASK ( VECTOR_SLOTS - 1 )
+static int VECTOR_BITS[4] = { 8, 4, 2, 64 };
+
+typedef struct {
+    unsigned int a:2;
+    unsigned int b:2;
+    unsigned int c:2;
+    unsigned int d:2;
+} bitpair;
+
+typedef struct {
+    unsigned int a:4;
+    unsigned int b:4;
+} nibble;
 
-#define VECTOR_PART_BYTE(i,p) ((vector_indexing*)(i))->as_byte[ (p)/2 ]
+typedef struct {
+    unsigned int a:8;
+} byte;
 
-static int VECTOR_INDEX_PART(vector_index *index,int part) {
-    if ( part & 1 ) {
-       return VECTOR_PART_BYTE(index,part).lsb;
+/**
+ * Return the index part for the given level of the vector's leveling
+ * variant.
+ *
+ * The vector variant indicates whether indexing uses 8, 4, or 2 bits
+ * per level.
+ */
+unsigned long VECTOR_INDEX_PART(vector *pv,vector_index *index, int level) {
+    unsigned char *px = (unsigned char *) index;
+    switch ( pv->variant ) {
+    case 0: {
+       byte *pp = (byte*)(px + level);
+       return pp->a;
     }
-    return VECTOR_PART_BYTE(index,part).msb;
+    case 1: {
+       nibble *pp = (nibble*)(px + ( level / 2 ));
+       switch ( level & 1 ) {
+       case 0: return pp->a;
+       case 1: return pp->b;
+       }
+       break;
+    }
+    case 2: {
+       bitpair *pp = (bitpair*)(px + ( level / 4 ));
+       switch ( level & 3 ) {
+       case 0: return pp->a;
+       case 1: return pp->b;
+       case 2: return pp->c;
+       case 3: return pp->d;
+       }
+       break;
+    }
+    case 3:
+       return (*index);
+    }
+    return 0;
 }
 
-static int VECTOR_INDEX_PART_INC(vector_index *index,int part) {
-    if ( part & 1 ) {
-       return ++VECTOR_PART_BYTE(index,part).lsb;
+/**
+ * Increment the index part at the indivated level, cyclic but not
+ * carrying over to the upper level. Returns the new level index.
+ */
+static unsigned long VECTOR_INDEX_PART_INC(
+    vector *pv,vector_index *index, int level)
+{
+    unsigned char *px = (unsigned char *) index;
+    switch ( pv->variant ) {
+    case 0: {
+       byte *pp = (byte*)( px + level );
+       return ++(pp->a);
     }
-    return ++VECTOR_PART_BYTE(index,part).msb;
+    case 1: {
+       nibble *pp = (nibble*)( px + ( level / 2 ) );
+       switch ( level & 1 ) {
+       case 0: return ++(pp->a);
+       case 1: return ++(pp->b);
+       }
+       break;
+    }
+    case 2: {
+       bitpair *pp = (bitpair*)( px + level / 4 );
+       switch ( level & 0xf ) {
+       case 0: return ++(pp->a);
+       case 1: return ++(pp->b);
+       case 2: return ++(pp->c);
+       case 3: return ++(pp->d);
+       }
+       break;
+    }
+    case 3:
+       return ++(*index);
+    }
+    return 0;
 }
 
-static int VECTOR_INDEX_PART_INC(vector_index *index,int part) {
-    if ( part & 1 ) {
-       return VECTOR_PART_BYTE(index,part).lsb--;
+/**
+ * Decrement the index part at the indicated level, cyclic but not
+ * carrying over to the upper level. Returns the prior level index.
+ */
+static unsigned long VECTOR_INDEX_PART_DEC(
+    vector *pv,vector_index *index, int level)
+{
+    unsigned char *px = (unsigned char *) index;
+    switch ( pv->variant ) {
+    case 0: {
+       byte *pp = (byte*)( px + level );
+       return (pp->a)--;
+    }
+    case 1: {
+       nibble *pp = (nibble*)( px + ( level / 2 ) );
+       switch ( level & 1 ) {
+       case 0: return (pp->a)--;
+       case 1: return (pp->b)--;
+       }
+       break;
+    }
+    case 2: {
+       bitpair *pp = (bitpair*)( px + level / 4 );
+       switch ( level & 0xf ) {
+       case 0: return (pp->a)--;
+       case 1: return (pp->b)--;
+       case 2: return (pp->c)--;
+       case 3: return (pp->d)--;
+       }
+       break;
+    }
+    case 3:
+       return (*index)--;
     }
-    return VECTOR_PART_BYTE(index,part).msb--;
+    return 0;
 }
 
-/** ============================================================ **/
-#elif VECTOR_LEVEL_BITS == 8
-
-#define VECTOR_LEVEL_MASK ( VECTOR_SLOTS - 1 )
+#define ONES  (~((vector_index) 0))
 
-typedef union {
-    vector_index as_whole;
-    unsigned char as_byte[8];
-} vector_indexing;
+// Set index to last value for all index parts at level and lower.
+static void VECTOR_INDEX_FIRST(vector *pv,vector_index *index, int level) {
+    (*index) &= ONES << ( VECTOR_BITS[ pv->variant ] * level );
+}
 
-#define VECTOR_INDEX_PART(i,p) (((vector_indexing*)(i))->as_byte[p])
+// Set index to last value for all index parts at level and lower.
+static void VECTOR_INDEX_LAST(vector *pv,vector_index *index, int level) {
+    (*index) |= ONES >> ( 64 - VECTOR_BITS[ pv->variant ] * level );
+}
 
-#define VECTOR_INDEX_PART_INC(i,p) (++VECTOR_INDEX_PART(i,p))
+// Return number of slots for a vector variant.
+unsigned long VECTOR_SLOTS(vector *pv) {
+    switch ( pv->variant ) {
+    case 0: return 256;
+    case 1: return 16;
+    case 2: return 4;
+    case 3: return pv->size;
+    }
+    return 0;
+}
 
-#define VECTOR_INDEX_PART_DEC(i,p) (VECTOR_INDEX_PART(i,p)--)
+// The number of levels to span vector pv wrt its size and variant
+static unsigned int vector_levels(vector *pv,unsigned int size) {
+    if ( size < 4 ) {
+       return 1;
+    }
+    switch ( pv->variant ) {
+    case 0: return ((int)(log2( size - 1 ) / 8)) + 1;
+    case 1: return ((int)(log2( size - 1 ) / 4)) + 1;
+    case 2: return ((int)(log2( size - 1 ) / 2)) + 1;
+    case 3: return 1;
+    }
+    return 0;
+}
 
-#endif
 /** ============================================================ **/
 
 /**
@@ -70,44 +189,36 @@ typedef union {
  * pointer to the used slot, if any, and 0 otherwise.
  */
 static void **vector_level_next_used(
-    vector_page *page,vector_index *index,int level,vector_index end) {
-    void **p = (void**)&(*page)[ VECTOR_INDEX_PART( index, level ) ];
+    vector *pv,
+    vector_page *page,
+    vector_index *index,
+    int level,
+    vector_index end )
+{
+    void **p = (void**)&(*page)[ VECTOR_INDEX_PART( pv, index, level ) ];
     for( ; *index < end; p++ ) {
        if ( *p ) {
            if ( level == 0 ) {
                return p; // This is a used entry
            }
            // *p is an index that needs to be inspected recursively
-           int whole = VECTOR_INDEX_PART( index, level - 1 ) == 0;
-           void **x = vector_level_next_used( *p, index, level - 1, end );
+           void **x = vector_level_next_used( pv, *p, index, level - 1, end );
            if ( x ) {
                return x; // Used slot was found; return it.
            }
-           // The page *p is all empty, so can/should be reclaimed.
-           if ( whole ) {
-               free( *p );
-               *p = 0;
+           // If the page *p is all empty, so can/should be reclaimed.
+       } else {
+           if ( level > 0 ) {
+               VECTOR_INDEX_FIRST( pv, index, level );
            }
        }
-       if ( VECTOR_INDEX_PART_INC( index, level ) == 0 ) {
+       if ( VECTOR_INDEX_PART_INC( pv, index, level ) == 0 ) {
            break; // cycling this level => nothing found
        }
     }
     return 0;
 }
 
-
-// The least number of levels to span index S (typically the size of a
-// vector)
-static unsigned int vector_levels(vector_index S) {
-    unsigned int N = 0;
-    do {
-       N++;
-       S /= VECTOR_SLOTS;
-    } while ( S );
-    return N;
-}
-
 // Find the next used slot at given index or later. Returns pointer to
 // the slot. This allows for a reclaim function that may reclaim slot
 // items on the way to next used slot.
@@ -116,10 +227,10 @@ void **vector_next_used(vector *pv,vector_index *index) {
        *index = pv->size;
        return 0;
     }
-    int levels = vector_levels( pv->size );
+    int levels = vector_levels( pv, pv->size );
     for ( ; *index < pv->size; (*index)++ ) {
        void **slot = vector_level_next_used(
-           pv->entries, index, levels - 1, pv->size ) ;
+           pv, pv->entries, index, levels - 1, pv->size ) ;
        if ( slot == 0 ) {
            *index = pv->size; // reached the end of the vector
        } else  if ( *slot == 0 ) {
@@ -130,13 +241,112 @@ void **vector_next_used(vector *pv,vector_index *index) {
     return 0;
 }
 
-// Reclaim tree of unused pages
-static void vector_reclaim(vector_page *page,unsigned int level) {
+#if 1
+/**
+ * Advances a vector index to the prior used slot at or below the
+ * given level, starting from the indexed entry (inclusive) and down.
+ * The function will free any empty pages it discovers, and then
+ * update the index slots accordingly. The given index is advanced
+ * cyclically to match the found slot. The function returns a slot
+ * pointer to the used slot, if any, and 0 otherwise.
+ */
+static void **vector_level_prev_used(
+    vector *pv,
+    vector_page *page,
+    vector_index *index,
+    int level )
+{
+    void **p = (void**)&(*page)[ VECTOR_INDEX_PART( pv, index, level ) ];
+    do {
+       if ( *p ) {
+           if ( level == 0 ) {
+               return p; // This is a used entry
+           }
+           // *p is an index that needs to be inspected recursively
+           void **x = vector_level_prev_used( pv, *p, index, level - 1 );
+           if ( x ) {
+               return x; // Used slot was found; return it.
+           }
+           // If the page *p is all empty, so can/should be reclaimed.
+       } else {
+           if ( level > 0 ) {
+               VECTOR_INDEX_LAST( pv, index, level );
+           }
+       }
+       p--;
+    } while ( VECTOR_INDEX_PART_DEC( pv, index, level ) != 0 );
+    return 0;
+}
+
+// Find the next used slot at given index or later. Returns pointer to
+// the slot. This allows for a reclaim function that may reclaim slot
+// items on the way to next used slot.
+void **vector_prev_used(vector *pv,vector_index *index) {
+    if ( pv->entries == 0 || *index >= pv->size ) {
+       *index = pv->size;
+       return 0;
+    }
+    int levels = vector_levels( pv, pv->size );
+    do {
+       void **slot = vector_level_prev_used(
+           pv, pv->entries, index, levels - 1 ) ;
+       if ( slot == 0 ) {
+           break; // reached the end of the vector
+       }
+       if ( *slot ) {
+           return slot;
+       }
+    } while ( (*index)-- != 0 );
+    *index = pv->size;
+    return 0;
+}
+
+#endif
+
+#if 0
+// Find the first in-use slot at or before the index, at the level
+static void **vector_prev_used_level(vector *pv,vector_index *index,int lv) {
+    void **slot = vector_access( pv, *index, lv, 0 );
+    if ( slot == 0 ) {
+       return 0;
+    }
+    do {
+       if ( *slot ) {
+           if ( lv == 0 ) {
+               return slot;
+           }
+           void **sub = vector_prev_used_level( pv, index, lv - 1 );
+           if ( sub ) {
+               return sub;
+           }
+       }
+       slot--;
+    } while ( VECTOR_INDEX_PART_DEC( pv, index, lv ) != 0 );
+    return 0;
+}
+
+// Find nearest used slot at or prior to the given index.
+void **vector_prev_used(vector *pv,vector_index *index) {
+    if ( pv->entries == 0 || *index >= pv->size ) {
+       *index = pv->size;
+       return 0;
+    }
+    void **slot = vector_prev_used_level(
+       pv, index, vector_levels( pv, pv->size ) - 1 );
+    if ( slot == 0 ) {
+       *index = pv->size;
+    }
+    return slot;
+}
+#endif
+
+// Reclaim tree of unused pages for a given level
+static void vector_reclaim(vector *pv,vector_page *page,unsigned int level) {
     int i = 0;
     if ( level > 0 ) {
-       for ( ; i < VECTOR_SLOTS; i++ ) {
+       for ( ; i < VECTOR_SLOTS( pv ); i++ ) {
            if ( (*page)[i] ) {
-               vector_reclaim( (vector_page *) (*page)[i], level - 1 );
+               vector_reclaim( pv, (vector_page *) (*page)[i], level - 1 );
            }
        }
     }
@@ -156,27 +366,14 @@ int vector_resize(
     int (*reclaim)(vector *pv,vector_index index,void *item,void *data),
     void *data )
 {
-    // Table of number of slots for a level above that of the number
-    // at the prior lower level. The first level (i.e., level 0) adds
-    // 15 slots to the one slot of no index page. Level 1 adds 15*16
-    // slots, level 2 adds 15*(16^2), and generically level i adds
-    // 15*(16^i) slots.
-    static int level_delta[ VECTOR_INDEX_FIELDS ];
-    if ( level_delta[ 0 ] == 0 ) {
-       int d = 1;
-       int i;
-       for ( i = 0; i < VECTOR_INDEX_FIELDS; i++ ) {
-           level_delta[ i ] = ( VECTOR_SLOTS - 1 ) * d;
-           d = VECTOR_SLOTS * d;
-       }
-    }
     struct {
        int old;
        int new;
     } level = {
-       vector_levels( pv->size ),
-       vector_levels( new_size )
+       vector_levels( pv, pv->size ),
+       vector_levels( pv, new_size )
     };
+    vector_page *entries = 0;
     if ( pv->entries == 0 ) {
        pv->size = new_size;
        return 0;
@@ -196,40 +393,61 @@ int vector_resize(
        // At this point we know that there are no slots used after
        // the new_size size, so now it's time to remove and reclaim
        // any superflouous top level pages.
-       vector_page *entries;
-       vector_page **pp = &pv->entries;
-       while ( level.old-- > level.new ) {
-           if ( pp ) {
-               pp = (vector_page **)(*pp)[0];
+       if ( pv->variant == 3 ) { // Follow vector size using realloc
+           if ( new_size > 0 ) {
+               entries = (vector_page*)
+                   realloc( pv->entries, new_size * sizeof( void* ) );
+               if ( entries == 0 ) {
+                   return -2; // OOM
+               }
            }
-       }
-       if ( pp != &pv->entries ) {
-           entries = pv->entries;
-           if ( pp ) {
-               pv->entries = *pp;
-               *pp = 0; // Detach subtree
-           } else {
+           pv->entries = entries;
+       } else {
+           vector_page **pp = &pv->entries;
+           int i = level.old;
+           while ( i-- > level.new ) {
+               if ( pp ) {
+                   pp = (vector_page **)(*pp);
+               }
+           }
+           if ( pp != &pv->entries ) {
+               entries = pv->entries;
+               if ( pp ) {
+                   pv->entries = *pp;
+                   *pp = 0; // Detach subtree
+               } else {
+                   pv->entries = 0;
+               }
+               vector_reclaim( pv, entries, level.old - 1 );
+           }
+           if ( new_size == 0 && pv->entries ) {
+               free( pv->entries );
                pv->entries = 0;
            }
-           vector_reclaim( entries, level.old );
-       }
-       if ( new_size == 0 && pv->entries ) {
-           free( pv->entries );
-           pv->entries = 0;
        }
     } else {
        // vector is growing. Maybe insert levels.
-       while ( level.old < level.new ) {
-           vector_page *p = (vector_page *)
-               calloc( 1, sizeof( vector_page ) );
-           if ( p == 0 ) {
+       if ( pv->variant == 3 ) { // Follow vector size using realloc
+           entries = (vector_page *)realloc(
+               pv->entries, new_size * sizeof( void* ) );
+           if ( entries == 0 ) {
                return -2; // OOM
            }
-           (*p)[0] = pv->entries;
-           pv->entries = p;
-           pv->size += level_delta[ level.old++ ];
-           // Note that the last level addition might make the size
-           // larger than requested, which gets corrected below.
+           pv->entries = entries;
+           memset( &(*entries)[ pv->size ], 0,
+                   ( new_size - pv->size ) * sizeof( void* ) );
+       } else {
+           for ( ; level.old < level.new; level.old++ ) {
+               vector_page *p = (vector_page *)
+                   calloc( VECTOR_SLOTS( pv ), sizeof( void* ) );
+               if ( p == 0 ) {
+                   return -2; // OOM
+               }
+               (*p)[0] = pv->entries;
+               pv->entries = p;
+               // Should maybe change the size to match the level?
+               // otherwise recovery from OOM is impossible
+           }
        }
     }
     pv->size = new_size;
@@ -239,62 +457,25 @@ int vector_resize(
 // Return pointer to the indexed page slot at the requested level, and
 // adding intermediate index pages if so requested. Returns 0 if
 // addition fails (OOM), or if not requested and page is missing.
-static void **vector_access(
-    vector *pv,vector_index index,int level,int add)
-{
+void **vector_access(vector *pv,vector_index index,int level,int add) {
     if ( index >= pv->size ) {
        return 0;
     }
     void **page = (void**) &pv->entries;
-    int i = vector_levels( pv->size );
+    int i = vector_levels( pv, pv->size );
     while (  i-- > level ) {
        if ( add && (*page) == 0 ) {
-           (*page) = calloc( VECTOR_SLOTS, sizeof( void* ) );
+           (*page) = calloc( VECTOR_SLOTS( pv ), sizeof( void* ) );
        }
        page = (*page);
        if ( page == 0 ) {
            return 0;
        }
-       page += VECTOR_INDEX_PART( &index, i );
+       page += VECTOR_INDEX_PART( pv, &index, i );
     }
     return page;
 }
 
-// Find the first in-use slot at or before the index, at the level
-static void **vector_prev_used_level(vector *pv,vector_index *index,int lv) {
-    void **slot = vector_access( pv, *index, lv, 0 );
-    if ( slot == 0 ) {
-       return 0;
-    }
-    do {
-       if ( *slot ) {
-           if ( lv == 0 ) {
-               return slot;
-           }
-           void **sub = vector_prev_used_level( pv, index, lv - 1 );
-           if ( sub ) {
-               return sub;
-           }
-       }
-       slot--;
-    } while ( VECTOR_INDEX_PART_DEC( index, lv ) != 0 );
-    return 0;
-}
-
-// Find nearest used slot at or prior to the given index.
-void **vector_prev_used(vector *pv,vector_index *index) {
-    if ( pv->entries == 0 || *index >= pv->size ) {
-       *index = pv->size;
-       return 0;
-    }
-    void **slot = vector_prev_used_level(
-       pv, index, vector_levels( pv->size ) - 1 );
-    if ( slot == 0 ) {
-       *index = pv->size;
-    }
-    return slot;
-}
-
 // Map index into a value slot
 void **vector_entry(vector *pv,vector_index index) {
     return vector_access( pv, index, 0, 1 );
@@ -302,7 +483,6 @@ void **vector_entry(vector *pv,vector_index index) {
 
 inline void vector_set(vector *pv,vector_index index,void *value) {
     void **p = vector_entry( pv, index );
-    //assert( p != 0 );
     *p = value;
 }
 
@@ -428,8 +608,9 @@ void vector_iterate(vector *pv,
        if ( slot == 0 ) {
            break;
        }
-       int i = index & VECTOR_LEVEL_MASK ;
-       for ( ; i < VECTOR_SLOTS && index < pv->size; i++, index++, slot++ ) {
+       int end = VECTOR_SLOTS( pv );
+       int i = index & ( end - 1 );
+       for ( ; i < end && index < pv->size; i++, index++, slot++ ) {
            if ( itemfn( index, *slot, data ) ) {
                return;
            }
@@ -462,3 +643,31 @@ void *vector_bsearch(vector *pv,vector_index *index,const void *key,
     }
     return 0;
 }
+
+// Iterator callback.
+static int checkunused(vector_index index,void *item,void *data) {
+    vector_index *last = (vector_index*) data;
+    if ( item == 0 ) {
+       (*last) = index;
+       return 1;
+    }
+    if ( *last > index ) {
+       // Only on the first iteration,  with *last = vector_sie
+       if ( index == 0 ) {
+           (*last) = 1;
+           return 0;
+       }
+       *last = 0;
+    } else if ( index == (*last) ) {
+       (*last)++;
+       return 0;
+    }
+    return 1;
+}
+
+// Scan forward for the next unused vector slot
+vector_index vector_next_unused(vector *pv,vector_index index) {
+    vector_index unused = vector_size( pv );
+    vector_iterate( pv, index, checkunused, &unused );
+    return unused;
+}
index 62e119529ce01adc98bd48935b33d9cf467d5755..d3f99b699ac584214caf667210c2b780427efe18 100644 (file)
 #ifndef vector_H
 #define vector_H
 
-/**
+/** \file vector.h
+ *
  * A vector is a dynamic pointer array implemented as an access tree
- * of index pages. The indexing is done using "unsigned long" indexes.
- */
-
-#ifndef VECTOR_LEVEL_BITS
-/*!
- * Macro: VECTOR_LEVEL_BITS
- * This defines the number of bits in the indexing bit field.
+ * of index pages. The indexing is done using "unsigned long" indexes,
+ * and the level 0 index corresponds to actual items.
+ *
+ * Actual vectors are assigned a leveling variant which defines the
+ * index page size for the vector. This must not be changed for a
+ * vector with entries.
+ *
+ * \subsubsection variantlist Variants:
+ *
+ * - 0 is 8-bit indexing parts and index pages with 256 pointers
+ * - 1 is 4-bit indexing parts and index pages with 16 pointers
+ * - 2 is 2-bit indexing parts and index pages with 4 pointers
+ * - 3 is for a single page sized as the vector.
+ *
+ * Variants 0-2 are managed by adding/removing full pages of the
+ * indexing tree upon resize and access. Variant 3 is managed by using
+ * realloc upon resize. In all cases shrinking a vector may mean to
+ * reclaim "lost" items, if any, via a provided item reclaim callback
+ * function which also may veto the shrinking.
  */
-#define VECTOR_LEVEL_BITS 8
-#endif
 
-/*!
- * Type: vector_index
+/**
  * This is the general indexing used for vector access.
  */
 typedef unsigned long vector_index;
 
-/*!
- * Macro: VECTOR_INDEX_BITS
- * This defines the number of bits of a vector index
- */
-#define VECTOR_INDEX_BITS ( sizeof( vector_index ) * 8 )
-
-/*!
- * Macro: VECTOR_INDEX_FIELDS
- * This defines the number of bit fields in an vector index
+/**
+ * A vector_page is an array of void* items. Its size depends on the
+ * applicable vector variant: 2^(8-variant)
  */
-#define VECTOR_INDEX_FIELDS \
-    ( ( VECTOR_INDEX_BITS - 1 ) / VECTOR_LEVEL_BITS + 1 )
+typedef void* vector_page[];
 
-/*!
- * Macro: VECTOR_SLOTS
- * This defines the number of slots spanned by an index level
+/**
+ * A vector is a compound of a size and a vector_page pointer, which
+ * when non-null points out the top-most page of the vector indexing
+ * tree. The number of levels is derived from its size with level 0
+ * being the leaf level of actual content. E.g., a vector larger than
+ * 256 items, has at least two levels, and generally N levels may span
+ * up to 256^N content entries.
  */
-#define VECTOR_SLOTS ( 1 << VECTOR_LEVEL_BITS )
+typedef struct {
+    /**
+     * The indexing variant. 0 = 8-bit, 1 = 4-bit, and 2 = 2-bit
+     * indexing parts. This gives 256, 16 or 4 slots per index page.
+     * Note that variant should not be changed after initialization.
+     */
+    int variant;
+    /**
+     * The size of the vector.
+     */
+    vector_index size;
+    /**
+     * The root page of the indexing tree.
+     */
+    vector_page *entries;
+} vector;
 
-/*!
- * Type: vector_page
+/**
+ * \brief Return the number of slots spanned by an index level for the
+ * given vector variant.
  *
- * A vector_page is an array of 16 void* items.
+ * - 0 indicates 8-bit index parts, and 256 page slots
+ * - 1 indicates 4-bit index parts, and 16 page slots
+ * - 2 indicates 2-bit index parts, and 4 page slots
+ * - 3 indicates 64-bit index parts, and 1 page level following the size
+ * 
+ * The type 3 vector is managed by using realloc.
  */
-typedef void* vector_page[ VECTOR_SLOTS ];
+extern unsigned long VECTOR_SLOTS(vector *pv);
 
-/*!
- * Type: vector
+/**
+ * \brief Find the nearest used (non-null) slot at given or higher
+ * index.
  *
- * A vector is a compound of a size and a vector_page pointer, which
- * when non-null points out the top-most page of the vector. The
- * number of levels is derived from its size with level 0 being the
- * leaf level of actual content. E.g., a vector larger than 16
- * items, has at least two levels, and generally N levels may span up
- * to 16^N content entries.
- */
-typedef struct _vector {
-    vector_index size;     //!< Limit for the logical entries[]
-    vector_page *entries; //!< Pointer to entries indexing
-} vector;
-
-/*!
- * Find the nearest used (non-null) slot at given or higher index.
+ * \param pv is the vector concerned.
+ *
+ * \param index is the index to change.
+ *
+ * \returns a pointer to the first non-null vector slot from the given
+ * index, and *index set accordingly. If no non-null slot is found,
+ * the 0 is returned and *index is set to the vector size.
+ *
+ * \related vector
  */
 extern void **vector_next_used(vector *pv,vector_index *index);
 
-/*!
- * Find the nearest used (non-null) slot at given or lower index.
+/**
+ * \brief Find the nearest used (non-null) slot at given or lower
+ * index.
+ *
+ * \param pv is the vector concerned.
+ *
+ * \param index is the index to change.
+ *
+ * \returns a pointer to the first non-null vector slot from the given
+ * index, and *index set accordingly. If no non-null slot is found,
+ * the 0 is returned and *index is set to the vector size.
+ *
+ * \related vector
  */
 extern void **vector_prev_used(vector *pv,vector_index *index);
 
-/*!
- * Function: int vector_resize(
- *              vector *pv,vector_index new_size,
- *              int (*reclaim)(vector *,vector_index,void *item,void *data),
- *              void *data )
- * \param pv
- * \param new_size
- * \param reclaim
- * \param data
- *
- * Tries to resize the given vector to a new size. This may result in
- * the introduction or removal of indexing pages, so that the leveling
- * is consistent with the vector size. Thus, if it grows into a new
- * 16^N level, then one or more new upper level pages are inserted as
- * needed. If it shrinks below the current level, then top-level pages
- * are remove.
+/**
+ * \brief Resize a vector.
+ *
+ * \param pv is the vector concerned.
+ *
+ * \param new_size is the new size it should have,
+ *
+ * \param reclaim is used upon shrinking in size for handling any
+ * current items above the new size, or vetoing the attempted resize.
+ *
+ * \param data is passed on the the reclaim function to use as context
+ * as needed.
+ *
+ * \returns the index of a resizing veto any, or <0 otherwise, with -1
+ * indicating success and -2 indicating OOM.
+ *
+ * This function attempts to resize the given vector to a new size.
+ * This may result in the introduction or removal of indexing pages,
+ * so that the index tree leveling is consistent with the vector size.
+ * Thus, if it grows into a new level, then one or more new upper
+ * level pages are inserted as needed. If it shrinks below the current
+ * level, then top-level pages are removed.
  *
  * Also, if the new size is smaller than currently, then the now
  * excess tail of entries is scanned for any used slots and the given
  * reclaim function is invoked successively for these. The reclaim
  * function must, in addition to memory-managing the entry, return 0
- * upon success and non-zero to veto the attempted vector size
- * change. The data argument is passed on to the reclaim function.
+ * upon success, or non-zero to veto the attempted vector size change.
+ * The data argument is passed on to the reclaim function as given.
  *
- * The vector_resize function returns 0 on success, with the size
- * duly changed. Otherwise the function retains the current size and
- * returns -index-1 for the index of the veto-ed entry.
+ * \related vector
  */
 extern int vector_resize(
     vector *pv, vector_index new_size,
     int (*reclaim)(vector *pv,vector_index index,void *item,void *data),
     void *data );
 
-/*!
- * Function: void **vector_entry(vector *pv,vector_index index)
- * \param pv - the vector record
- * \param index - the slot index
+/** 
+ * \brief Return pointer to the indexed page slot at the requested
+ * level, and adding intermediate index pages if so requested.
+ *
+ * \param pv is the vector concerned.
+ *
+ * \param index is the slot index.
+ *
+ * \param level is the indexing level to access. Level 0 is the leaf
+ * level that holds the slots for the items; level 1 is one level up,
+ * for vectors larger than 256 items; ans so on.
+ *
+ * \param add is a flag to indicate (with 1) that missing index pages
+ * should be added, or (with 0) that the function should simply return
+ * null if an index page to access at any level is missing.
+ *
+ * \returns a pointer to the slot for the indexed item (level 0), or
+ * (for higher levels) the slot for the index page on the access path
+ * to the indexed item. The function returns 0 if the access path is
+ * broken by a missing index page, or (with add==1) the allocation of
+ * a new index page fails.
+ *
+ * \note The index tree for the vector is populated on demand only
+ * where access has been requested.
+ *
+ * \related vector
+ */
+extern void **vector_access(vector *pv,vector_index index,int level,int add);
+
+/**
+ * \brief Return the slot value at the given index.
  *
- * [pgix,epix] = modulo( index, pv->page );
+ * \param pv is the vector concerned.
+ *
+ * \param index is the slot index.
  *
  * \returns a direct pointer to the slot of the given index in the
- * array, or 0 if the index is beyond the array limits (0-limit). Note
- * that slot pointers are only valid while the vector size is
- * unchanged.
+ * array, or 0 if the index is beyond the array limits (0-limit).
+ *
+ * \note Note that slot pointers are only valid while the vector size
+ * is unchanged.
+ *
+ * \related vector
  */
 extern void **vector_entry(vector *pv,vector_index index); 
 
-/*!
- * Macro: vector_size(v)
- * \param v - the vector record
+/**
+ * \param pv - the vector concerned
  * \returns the size of the vector.
+ * \related vector
  */
 #define vector_size(pv) ((vector_index) (pv)->size)
 
+/**
+ * \brief Set the vector value at the given index.
+ *
+ * \param pv is the vector concerned
+ * \param index is the index for the slot to assign
+ * \param value is the new slot value
+ *
+ * \note An assignment of 0 will be treated as an unused slot.
+ *
+ * \related vector
+ */
 extern void vector_set(vector *pv,vector_index index,void *value);
 
-// Set value at index but return the old value
+/**
+ * \brief Set the vector value at the given index and return the prior
+ * value.
+ *
+ * \param pv is the vector concerned
+ * \param index is the index for the slot to assign
+ * \param value is the new slot value
+ *
+ * \note An assignment of 0 will be treated as an unused slot.
+ *
+ * \related vector
+ */
 extern void *vector_get_set(vector *pv,vector_index index,void *value);
 
+/**
+ * \brief Get the vector value at the given index.
+ *
+ * \param pv is the vector concerned
+ * \param index is the index for the slot to assign
+ *
+ * \note This function will allocate all traversed indeex tree pages
+ * even for accessing an unassigned slot.
+ *
+ * \related vector
+ */
 extern void *vector_get(vector *pv,vector_index index);
 
+/**
+ * \brief Grow the vector by one and assign the added slot.
+ *
+ * \param pv is the vector concerned
+ * \param value is the new slot value
+ *
+ * \related vector
+ */
 extern void vector_append(vector *pv,void *value);
 
+/**
+ * \brief Copy a consecutive region from one vector into another, or
+ * possibly the same vector.
+ *
+ * \param pv is the vector concerned
+ * \param value is the new slot value
+ *
+ * \note This transfers all slots from the source region to the
+ * destination region, including zero slots. The vectors must be large
+ * enough for the transfer, which is carried out from lowest to
+ * highest or highest to lowest index depending on wther the move is
+ * to higher index or to lower index respectively.
+ *
+ * \related vector
+ */
 extern void vector_copy(
     vector *dst,vector_index di,
     vector *src,vector_index si,
     vector_index n);
 
 /**
- * Invoking the itemdup function for all used (non-null) slots.
+ * \brief Utility function that invokes the itemdump function for all
+ * used (non-null) slots.
+ *
+ * \related vector
+ * \seealso vector_iterate
  */
 extern void vector_dump(
     vector *pv,
@@ -148,22 +279,56 @@ extern void vector_dump(
 
 extern void vector_qsort(vector *pv,int (*compar)(const void *,const void *));
 
-/*!
- * Function: void vector_iterate(vector *pv,
- *                  vector_index start,
- *                  int (*itemfn)(vector_index,void*,void*),
- *                  void*);
- *
+/**
  * Steps through the vector item by item invoking the given function
  * for each. Continues stepping while the item function returns 0.
+ *
+ * \related vector
  */
 extern void vector_iterate(
     vector *pv, vector_index start,
     int (*itemfn)(vector_index,void *item,void *data),
     void *data);
 
+/**
+ * \brief Binary search in a sorted vector for an item of the given
+ * key, with a callback function providing the sorting order.
+ *
+ * \param pv is the vector concerned.
+ *
+ * \param index is a vector_index pointer for returning the index of
+ * the found item.
+ *
+ * \param key is the lookup key to find.
+ *
+ * \param compare is a callback function that should return the search
+ * direction given a key and an item. It should return 0 if the key is
+ * a match for the item, <0 if the sought item is expected at a higher
+ * index, and >0 if the sought item is expected at a lower index.
+ *
+ * \return a pointer to the found item and *index set to its index. If
+ * there is no matching item, then 0 is returned, and the index is set
+ * to the vector size.
+ *
+ * \related vector
+ */
 extern void *vector_bsearch(
     vector *pv, vector_index *index, const void *key,
     int (*compare)(const void *key, const void *item));
 
+/**
+ * \brief Find the next used slot at or after the given index.
+ *
+ * \param pv the vector concerned.
+ * \param index pointer to the index to advance.
+ * \return the new index, or the vector size if no unused slot is
+ * found.
+ *
+ * Scans forward in the vector for the first unused (null) vector slot
+ * at or after the given index. Returns pv->size if full.
+ *
+ * \related vector
+ */
+extern vector_index vector_next_unused(vector *pv,vector_index index);
+
 #endif