From: Ralph Ronnquist Date: Thu, 30 Jun 2022 11:48:03 +0000 (+1000) Subject: rework to handle vector variants 0-3 X-Git-Tag: 0.1~4 X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=c1aae73b1fafd372c4f38a1a205e20a9f22a2fa0;p=rrq%2Frrqmisc.git rework to handle vector variants 0-3 --- diff --git a/tests/Makefile b/tests/Makefile index b050eda..6da4dd7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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} diff --git a/tests/example-hashvector.c b/tests/example-hashvector.c index 7bd57a5..516cd07 100644 --- a/tests/example-hashvector.c +++ b/tests/example-hashvector.c @@ -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++ ) { diff --git a/tests/example-vector.c b/tests/example-vector.c index 48403fc..c191d2d 100644 --- a/tests/example-vector.c +++ b/tests/example-vector.c @@ -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) { diff --git a/tests/vectortests.c b/tests/vectortests.c index 30169dc..6f8cdbd 100644 --- a/tests/vectortests.c +++ b/tests/vectortests.c @@ -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; } diff --git a/vector/Makefile b/vector/Makefile index 0fb332e..7c5268d 100644 --- a/vector/Makefile +++ b/vector/Makefile @@ -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 diff --git a/vector/hashvector.c b/vector/hashvector.c index bd6fb97..27c2d65 100644 --- a/vector/hashvector.c +++ b/vector/hashvector.c @@ -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; diff --git a/vector/hashvector.h b/vector/hashvector.h index 1994b66..d0f9e12 100644 --- a/vector/hashvector.h +++ b/vector/hashvector.h @@ -1,7 +1,12 @@ #ifndef hashvector_H #define hashvector_H +#include +#include + /** + * \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 @@ -15,81 +20,104 @@ * 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 - -/*! - * 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); diff --git a/vector/vector.c b/vector/vector.c index 5b38488..a9c6b63 100644 --- a/vector/vector.c +++ b/vector/vector.c @@ -1,4 +1,5 @@ -#include +#include +#include #include #include "vector.h" @@ -9,56 +10,174 @@ */ /** ============================================================ **/ -#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; +} diff --git a/vector/vector.h b/vector/vector.h index 62e1195..d3f99b6 100644 --- a/vector/vector.h +++ b/vector/vector.h @@ -1,146 +1,277 @@ #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