renamed pvector to vector and changed into generalized code
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Sun, 26 Jun 2022 01:01:52 +0000 (11:01 +1000)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Sun, 26 Jun 2022 01:01:52 +0000 (11:01 +1000)
14 files changed:
pvector/Makefile [deleted file]
pvector/example-hashvector.c [deleted file]
pvector/example-pvector.c [deleted file]
pvector/hashvector.c [deleted file]
pvector/hashvector.h [deleted file]
pvector/pvector.c [deleted file]
pvector/pvector.h [deleted file]
pvector/qvector.c [deleted file]
pvector/qvector.h [deleted file]
vector/Makefile [new file with mode: 0644]
vector/hashvector.c [new file with mode: 0644]
vector/hashvector.h [new file with mode: 0644]
vector/vector.c [new file with mode: 0644]
vector/vector.h [new file with mode: 0644]

diff --git a/pvector/Makefile b/pvector/Makefile
deleted file mode 100644 (file)
index be1ab86..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-default: libpvector.a 
-
-all: default example-hashvector example-pvector
-
-#.INTERMEDIATE: pvector.o
-pvector.o: CFLAGS = -Wall -g
-pvector.o: pvector.c | pvector.h
-CLEANRM += pvector.o
-
-#.INTERMEDIATE: qvector.o
-qvector.o: CFLAGS = -Wall -g
-qvector.o: qvector.c | qvector.h
-CLEANRM += qvector.o
-
-#.INTERMEDIATE: hashvector.o
-hashvector.o: CFLAGS = -Wall -g
-hashvector.o: hashvector.c | pvector.h hashvector.h
-
-libpvector.a: pvector.o qvector.o hashvector.o
-       $(AR) r $@ $^
-CLEANRM += libpvector.a
-
-#.INTERMEDIATE: example-pvector.o
-example-pvector: CFLAGS = -Wall -g
-example-pvector: LDLIBS = libpvector.a
-example-pvector: example-pvector.o libpvector.a
-CLEANRM += example-pvector example-pvector.o
-
-#.INTERMEDIATE: example-hashvector.o
-example-hashvector: CFLAGS = -Wall -g ${TEST}
-example-hashvector: LDLIBS = libpvector.a
-example-hashvector: example-hashvector.o libpvector.a
-CLEANRM += example-hashvector example-hashvector.o
-
-clean:
-       rm -f $(CLEANRM)
diff --git a/pvector/example-hashvector.c b/pvector/example-hashvector.c
deleted file mode 100644 (file)
index 0d64c9e..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "hashvector.h"
-
-typedef struct _ipslot {
-    int family;
-    unsigned char data[32];
-    unsigned int bits;
-} ipslot;
-
-static unsigned long voidp_hashcode(void *key) {
-    return hashvector_hashcode( key, sizeof( ipslot ) );
-}
-
-static void* voidp_itemkey(void *item) {
-    return item;
-}
-
-static int voidp_haskey(void *item,void *key) {
-    return memcmp( item, key, sizeof( ipslot ) ) == 0;
-}
-
-static struct {
-    hashvector hv;
-    long fill;
-} table = {
-    .hv = {
-       .table = { 12000, 0 },
-       .fill = 0,
-       .holes = 0,
-       .keyhashcode = voidp_hashcode,
-       .itemkey = voidp_itemkey,
-       .haskey = voidp_haskey
-    },
-    .fill = 0
-};    
-
-#define BUFSZ 10000
-static struct {
-    char data[ BUFSZ ];
-    int cur;
-    int end;
-} stream;
-
-static int readline(int fd,char **outp) {
-    for ( ;; ) {
-       char *curp = stream.data + stream.cur;
-       char *endp = stream.data + stream.end;
-       char *top = curp;
-       while  ( curp < endp ) {
-           if ( *(curp++) == '\n' ) {
-               stream.cur = curp - stream.data;
-               (*outp) = top;
-               return curp - top;
-           }
-       }
-       if ( top != stream.data ) {
-           curp = stream.data;
-           while ( top < endp ) {
-               *(curp++) = *(top++);
-           }
-           endp = curp;
-           stream.end = endp - stream.data;
-       }
-       stream.cur = 0;
-       ssize_t n = read( fd, endp, BUFSZ - stream.end );
-       if ( n <= 0 ) {
-           if ( stream.end == 0 ) {
-               return -1; // No more data
-           }
-           (*outp) = stream.data;
-           return stream.end;
-       }
-       stream.end += n;
-    }
-    //unreachable
-}
-
-// Scan to NUL, CR or c. Return pointer not including character.
-static char *scanto(char *p, char c) {
-    while ( *p && *p != '\n' && *p != c ) {
-       p++;
-    }
-    return p;
-}
-
-static int parse_addr(char *line,ipslot *addr) {
-    char *end = scanto( line, '\n' );
-    char *slash = scanto( line, '/' );
-    *slash = 0;
-    *end = 0;
-    if ( inet_pton( AF_INET6, line, addr->data ) == 1 ) {
-       addr->bits = 128;
-       addr->family = AF_INET6;
-    } else if ( inet_pton( AF_INET, line, addr->data ) == 1 ) {
-       addr->bits = 32;
-       addr->family = AF_INET;
-    } else {
-       return 1;
-    }
-    if ( slash != end && sscanf( slash+1, "%u", &addr->bits ) != 1 ) {
-       return 2;
-    }
-    return 0;
-}
-
-
-static void add_entry(ipslot *tmp) {
-    ipslot *p = (ipslot *) malloc( sizeof( ipslot ) );
-    memmove( p, tmp, sizeof( ipslot ) );
-    hashvector_add( &table.hv, p );
-    table.fill++;
-#if 0
-    pvector *pv = &table.hv.table;
-    if ( pv->size == table.fill ) {
-       (void) pvector_resize( pv, table.fill + 256, 0, 0 );
-    }
-    pvector_set( pv, table.fill++, p );
-#endif
-}
-
-static void load_file(const char *filename) {
-    int fd = open( filename, O_RDONLY );
-    if ( fd < 0 ) {
-       perror( filename );
-       exit( errno );
-    }
-    char *line;
-    int n;
-    while ( ( n = readline( fd, &line ) ) >= 0 ) {
-       ipslot addr;
-       if ( parse_addr( line, &addr ) ) {
-           fprintf( stderr, "Bad address: %s\n", line );
-           continue;
-       }
-       add_entry( &addr );
-    }
-}
-
-static int int_reclaim(pvector *pv,unsigned long index,void *item,void *data) {
-    return 0;
-}
-
-static int dumpitem(const unsigned long 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 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;
-}
-
-static int compare_ipslot(const void *ax,const void *bx) {
-    ipslot *a = (ipslot *) ax;
-    ipslot *b = (ipslot *) bx;
-    int x = b->family - a->family;
-    if ( x ) {
-       return ( x > 0 )? 1 : -1;
-    }
-    x = a->bits < b->bits? a->bits : b->bits;
-    unsigned char *ap = a->data;
-    unsigned char *bp = b->data;
-    int d;
-    for ( ; x >= 8; x -= 8 ) {
-       d = *(bp++) - *(ap++);
-       if ( d ) {
-           return ( d > 0 )? 1 : -1;
-       }
-    }
-    if ( x ) {
-       x = 7 - x;
-       d = ( (*bp) >> x ) - ( (*ap) >> x );
-       if ( d ) {
-           return ( d > 0 )? 1 : -1;
-       }
-    }
-    x = b->bits - a->bits;
-    if ( x ) {
-       return ( x > 0 )? 1 : -1;
-    }
-    return 0;
-}
-
-static int shrink(pvector *pv,unsigned long index,void *item,void *data) {
-    if ( item ) {
-       if ( item == HV_HOLE ) {
-           ((hashvector*) data)->holes--;
-       } else {
-           free( item );
-           ((hashvector*) data)->fill--;
-       }
-    }
-    return 0;
-}
-
-int main(int argc,char **argv) {
-#if TEST0
-    pvector test = { 0 };
-    pvector_resize( &test, 100, 0, 0 );
-    pvector_set( &test, 5, (void*) 500 );
-    pvector_set( &test, 55, (void*) 600 );
-    //pvector_set( &test, 550, (void*) 800 );
-    pvector_resize( &test, 300, 0, 0 );
-    pvector_set( &test, 55, (void*) 650 );
-    pvector_resize( &test, 30000, 0, 0 );
-    pvector_set( &test, 22255, (void*) 26 );
-    pvector_dump( &test, dumpitem );
-    pvector_resize( &test, 100, int_reclaim, 0 );
-    pvector_set( &test, 5, (void*) 2 );
-    pvector_dump( &test, dumpitem );
-    pvector_resize( &test, 0, int_reclaim, 0 ); // clear out the pvector
-
-    int i;
-    for ( i = 1; i < argc; i++ ) {
-       load_file( argv[ i ] );
-    }
-    fprintf( stdout, "---- hashvector after filling it %ld/%ld/%ld\n",
-            table.hv.fill, table.hv.holes, table.hv.table.size );
-    pvector_dump( &table.hv.table, dump_ipslot );
-    if ( hashvector_contents( &table.hv, &test ) < 0 ) {
-       fprintf( stdout, "test is not empty\n" );
-    }
-    fprintf( stdout, "---- hashvector contents in hash order\n" );
-    pvector_dump( &test, dump_ipslot );
-    pvector_qsort( &test, compare_ipslot );
-    fprintf( stdout, "---- contents after sorting\n" );
-    pvector_dump( &test, dump_ipslot );
-#endif
-#if TEST1
-    hashvector hv = {
-       .table = { 4, 0 },
-       .fill = 0,
-       .holes = 0,
-       .keyhashcode = voidp_hashcode,
-       .itemkey = voidp_itemkey,
-       .haskey = voidp_haskey
-    };
-    int i = 0;
-    for ( ; i < 259; i++ ) {
-       ipslot *item = (ipslot*) calloc( 1, sizeof( ipslot ) );
-       if ( i > 250 ) {
-           int n = i;
-           i = n;
-       }
-       item->family = i;
-       memcpy( item->data, "10.10.10.1", 10 );
-       hashvector_add( &hv, item );
-    }
-    pvector_resize( &hv.table, 256, shrink, &hv );
-#endif
-    return 0;
-}
diff --git a/pvector/example-pvector.c b/pvector/example-pvector.c
deleted file mode 100644 (file)
index d1eb474..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "pvector.h"
-
-typedef struct _ipslot {
-    char data[16];
-    unsigned int bits;
-} ipslot;
-
-static struct {
-    pvector data;
-    int fill;
-} table;
-
-#define BUFSZ 10000
-static struct {
-    char data[ BUFSZ ];
-    int cur;
-    int end;
-} stream;
-
-static int readline(int fd,char **outp) {
-    for ( ;; ) {
-       char *curp = stream.data + stream.cur;
-       char *endp = stream.data + stream.end;
-       char *top = curp;
-       while  ( curp < endp ) {
-           if ( *(curp++) == '\n' ) {
-               stream.cur = curp - stream.data;
-               (*outp) = top;
-               return curp - top;
-           }
-       }
-       if ( top != stream.data ) {
-           curp = stream.data;
-           while ( top < endp ) {
-               *(curp++) = *(top++);
-           }
-           endp = curp;
-           stream.end = endp - stream.data;
-       }
-       stream.cur = 0;
-       ssize_t n = read( fd, endp, BUFSZ - stream.end );
-       if ( n <= 0 ) {
-           if ( stream.end == 0 ) {
-               return -1; // No more data
-           }
-           (*outp) = stream.data;
-           return stream.end;
-       }
-       stream.end += n;
-    }
-    //unreachable
-}
-
-// Scan to NUL, CR or c. Return pointer not including character.
-static char *scanto(char *p, char c) {
-    while ( *p && *p != '\n' && *p != c ) {
-       p++;
-    }
-    return p;
-}
-
-static int parse_addr(char *line,ipslot *addr) {
-    char *end = scanto( line, '\n' );
-    char *slash = scanto( line, '/' );
-    *slash = 0;
-    if ( inet_pton( AF_INET, line, addr->data ) == 0 ) {
-       addr->bits = 32;
-    } if ( inet_pton( AF_INET6, line, addr->data ) == 0 ) {
-       addr->bits = 128;
-    } else {
-       return 1;
-    }
-    if ( slash != end && sscanf( slash+1, "%u", &addr->bits ) != 1 ) {
-       return 1;
-    }
-    return 0;
-}
-
-static void add_entry(ipslot *tmp) {
-    ipslot *p = (ipslot *) malloc( sizeof( ipslot ) );
-    memmove( p, tmp, sizeof( ipslot ) );
-    if ( table.data.size == table.fill ) {
-       (void) pvector_resize( &table.data, table.fill + 256, 0, 0 );
-    }
-    pvector_set( &table.data, table.fill++, p );
-}
-
-static void load_file(const char *filename) {
-    int fd = open( filename, O_RDONLY );
-    if ( fd < 0 ) {
-       perror( filename );
-       exit( errno );
-    }
-    char *line;
-    int n;
-    while ( ( n = readline( fd, &line ) ) >= 0 ) {
-       ipslot addr;
-       if ( parse_addr( line, &addr ) ) {
-           fprintf( stderr, "Bad address: %s\n", line );
-           continue;
-       }
-       add_entry( &addr );
-    }
-}
-
-static int int_reclaim(pvector *pv,unsigned long index,void *item,void *data) {
-    return 0;
-}
-
-static int dumpitem(const unsigned long 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 char buffer[100];
-    ipslot *ip = (ipslot*) item;
-    const char *p = inet_ntop( (ip->bits <= 32)? AF_INET : AF_INET6,
-                        ip->data, buffer, 100 );
-    
-    fprintf( stdout, "[%ld] %s/%d\n", index, p, ip->bits );
-    return 0;
-}
-
-int main(int argc,char **argv) {
-    pvector test = { 0 };
-    pvector_resize( &test, 100, 0, 0 );
-    pvector_set( &test, 5, (void*) 500 );
-    pvector_set( &test, 55, (void*) 600 );
-    //pvector_set( &test, 550, (void*) 800 );
-    pvector_resize( &test, 300, 0, 0 );
-    pvector_set( &test, 55, (void*) 650 );
-    pvector_resize( &test, 30000, 0, 0 );
-    pvector_set( &test, 22255, (void*) 26 );
-    pvector_dump( &test, dumpitem );
-    pvector_resize( &test, 100, int_reclaim, 0 );
-    pvector_set( &test, 5, (void*) 2 );
-    pvector_dump( &test, dumpitem );
-
-    int i;
-    for ( i = 1; i < argc; i++ ) {
-       load_file( argv[ i ] );
-    }
-    pvector_dump( &table.data, dump_ipslot );
-
-    return 0;
-}
diff --git a/pvector/hashvector.c b/pvector/hashvector.c
deleted file mode 100644 (file)
index 11fdae8..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "hashvector.h"
-
-// 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;
-    void **hole = 0;
-    void **p = 0;
-    for ( ;; ) {
-       p = pvector_entry(&hv->table, i);
-       if ( p == 0 ) {
-           return 0; // This basically means OOM, and is a failure condition.
-       }
-       if ( *p == 0 ) {
-           break; // Not found
-       }
-       if ( (*p) == HV_HOLE ) {
-           if ( hole == 0 ) {
-               hole = p; // Remember the first hole
-           }
-       } else if ( hv->haskey( *p, key ) ) {
-           return p; // Found
-       }
-       if ( ++i == hv->table.size ) {
-           i = 0; // Roll-around the table
-       }
-       if ( i == index ) {
-           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 );
-    if ( p && *p && *p != HV_HOLE ) {
-       if ( x ) {
-           *x = *p;
-       }
-       return 1;
-    }
-    return 0;
-}
-
-static int capture_item(pvector *pv,unsigned long ix,void *item,void *data) {
-    if ( item != HV_HOLE ) {
-       hashvector_add( (hashvector *) data, item );
-    }
-    return 0;
-}
-
-static int iter_item(unsigned long ix,void *item,void *data) {
-    if ( item && item != HV_HOLE ) {
-       hashvector_add( (hashvector *) data, item );
-    }
-    return 0;
-}
-
-static void hashvector_resize(hashvector *hv,unsigned long new_size) {
-    hashvector tmp = *hv;
-    hv->table.size = new_size;
-    hv->table.entries = 0;
-    hv->fill = 0;
-    hv->holes = 0;
-    if ( new_size < hv->table.size ) {
-       pvector_resize( &tmp.table, 0, capture_item, hv );
-    } else {
-       pvector_iterate( &tmp.table, iter_item, hv );
-    }
-}
-    
-// Add the given element.
-int hashvector_add(hashvector *hv,void *item) {
-    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
-    if ( p == 0 ) {
-       return -1; // OOM or overfull hashvector
-    }
-    if ( *p ) {
-       if ( *p != HV_HOLE ) {
-           return 0; // Already added.
-       }
-       hv->holes--; // about to reuse a hole
-    }
-    *p = item;
-    hv->fill++;
-    if ( hv->fill + hv->holes > hv->table.size / 2 ) {
-       hashvector_resize( hv, hv->table.size * 2 );
-    }
-    return 1;
-}
-
-// Delete the given item
-int hashvector_delete(hashvector *hv,void *item) {
-    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
-    if ( p == 0 ) {
-       return -1;
-    }
-    if ( *p != item ) {
-       return 0;
-    }
-    *p = HV_HOLE;
-    hv->holes++;
-    hv->fill--;
-    if ( hv->table.size > 256 ) {
-       if ( hv->fill < hv->table.size / 4 ) {
-           hashvector_resize( hv, hv->table.size / 2 );
-       }
-    }
-    return 1;
-}
-
-// Copy items into a pvector. Returns 0 on success and -1 on failure.
-int hashvector_contents(hashvector *hv,pvector *pv) {
-    if ( pvector_resize( pv, hv->fill, 0, 0 ) ) {
-       return -1;
-    }
-    unsigned long from = 0;
-    unsigned long to = 0;
-    while ( to < hv->fill ) {
-       void **slot = pvector_next_used( &hv->table, &from, 0, 0 );
-       if ( slot ) {
-           if ( *slot != HV_HOLE ) {
-               pvector_set( pv, to++, *slot );
-           }
-           from++;
-       } else {
-           break;
-       }
-    }
-    return 0;
-}
-
-// A simple binary hashcode, (before modulo table size)
-unsigned long hashvector_hashcode(unsigned char *key,unsigned long n) {
-    unsigned long value = 5381;
-    while ( n-- ) {
-       value += ( value << 5 ) + *(key++);
-    }
-    return value;
-}
-
diff --git a/pvector/hashvector.h b/pvector/hashvector.h
deleted file mode 100644 (file)
index a5b426b..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#ifndef hashvector_H
-#define hashvector_H
-
-/**
- * hashvector is a use of pvector 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
- * or not a given item has a given key. The hashvector manipulations
- * uses those for locating and placing items into the vector; the
- * hascode is the primary place for an item, but then scanning upwards
- * with a rolling index in case of collisions.
- *
- * The vector holds 0 pointers for free slots, (void*)1 pointers (aka
- * HV_HOLE) to indicate slots of deleted items, and otherwise item
- * 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%.
- */
-
-#include "pvector.h"
-
-/*!
- * Type: hashvector
- * This combines a pvector (for contents) with fill and hole counters
- * and the three functions.
- */
-typedef struct _hashvector {
-    pvector 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
-} hashvector;
-
-/*!
- * Macro: HV_HOLE
- * The representation of a deleted item.
- */
-#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.
- */
-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.
- */
-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.
- */
-int hashvector_delete(hashvector *hv,void *item);
-
-/*!
- * Function: int hashvector_contents(hashvector *hv,pvector *pv)
- *
- * Copy content items into a pvector, which must be empty. The
- * function returns -1 if the resizing of the pvector to the
- * hashvector fill fails, otherwise 0 after having copied the
- * hashvector items in their internal order of appearance into the
- * pvector.
- */
-int hashvector_contents(hashvector *hv,pvector *pv);
-
-/*!
- * Function unsigned long hashvector_hashcode(
- *              unsigned char *key,unsigned long n)
- *
- * Computes and returns a hashcode for a block of bytes.
- */
-unsigned long hashvector_hashcode(unsigned char *key,unsigned long n);
-
-#endif
diff --git a/pvector/pvector.c b/pvector/pvector.c
deleted file mode 100644 (file)
index 0838954..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-#include <stdlib.h>
-#include "pvector.h"
-
-/**
- * Representing a vector of void* accessible via an indexing structure
- * as levels of same-size pages. A "pvector_page" is a contiguous
- * array of 256 void*.
- */
-
-/**
- * Advances a pvector index to the next used slot at or below the
- * given level, starting from the indexed entry (inclusive) and up.
- * 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 **pvector_level_next_used(
-    pvector_page *page,unsigned long *index,int level,unsigned long end) {
-    void **p = (void**)&(*page)[ ((pvector_index*)index)->level[ 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 = ((pvector_index*)index)->level[ level - 1 ] == 0;
-           void **x = pvector_level_next_used( *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 ( ++(((pvector_index*)index)->level[ level ]) == 0 ) {
-           break; // cycling this level => nothing found
-       }
-    }
-    return 0;
-}
-
-static unsigned int pvector_levels(unsigned long S) {
-    if ( S <= 1 ) {
-       return 1;
-    }
-    return (unsigned int) ( 39 - __builtin_clz( S - 1 ) ) / 8;
-}
-
-// Find the next used slot at given index or later. Returns pointer to
-// the slot.
-void **pvector_next_used(
-    pvector *pv,unsigned long *index,
-    int (*reclaim)(pvector *pv,unsigned long index,void *item,void *data),
-    void *data )
-{
-    if ( pv->entries == 0 ) {
-       *index = pv->size;
-       return 0;
-    }
-    int levels = pvector_levels( pv->size );
-    for ( ; *index < pv->size; (*index)++ ) {
-       void **slot = pvector_level_next_used(
-           pv->entries, index, levels - 1, pv->size ) ;
-       if ( slot == 0 ) {
-           // reached the end of the vector
-           *index = pv->size;
-           break;
-       }
-       if ( *slot ) {
-           // Try reclaiming the slot,
-           if ( reclaim && reclaim( pv, *index, *slot, data ) == 0 ) {
-               *slot = 0;
-           } else {
-               return slot;
-           }
-       }
-    }
-    return 0;
-}
-
-// Reclaim tree of unused pages
-static void pvector_reclaim(pvector_page *page,unsigned int level) {
-    int i = 0;
-    if ( level > 0 ) {
-       for ( ; i < 256; i++ ) {
-           if ( (*page)[i] ) {
-               pvector_reclaim( (pvector_page *) (*page)[i], level - 1 );
-           }
-       }
-    }
-    free( page );
-}
-
-// Resize vector, using the reclaim function as needed, to handle any
-// excess items or to veto the resize. Returns the index of the veto, if
-// any, or <0 otherwise, with -1 indicating success and -2 indicating
-// OOM while growing.
-//
-// Nothe that resizing may result in the introduction/removal of
-// indexing levels and pages, so as to keep the leveling accurate for
-// the size.
-int pvector_resize(
-    pvector *pv,unsigned long new_size,
-    int (*reclaim)(pvector *pv,unsigned long 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 255 slots to the one slot
-    // of no index page. Level 1 adds 255*256 slots, level 2 adds
-    // 255*(256^2), and generically level i adds 255*(256^i) slots.
-    static int level_delta[8];
-    if ( level_delta[ 0 ] == 0 ) {
-       int d = 1;
-       int i;
-       for ( i = 0; i < 8; i++ ) {
-           level_delta[ i ] = 255 * d;
-           d = 256 * d;
-       }
-    }
-    struct {
-       int old;
-       int new;
-    } level = {
-       pvector_levels( pv->size ),
-       pvector_levels( new_size )
-    };
-    if ( pv->entries == 0 ) {
-       pv->size = new_size;
-       return 0;
-    }
-    // A shrinking pvector might be veto-ed
-    if ( new_size < pv->size ) {
-       unsigned long index = new_size;
-       void **slot = pvector_next_used( pv, &index, reclaim, data );
-       if ( slot ) {
-           return index;
-       }
-       // 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.
-       pvector_page *entries;
-       pvector_page **pp = &pv->entries;
-       while ( level.old-- > level.new ) {
-           pp = (pvector_page **)(*pp)[0];
-       }
-       if ( pp != &pv->entries ) {
-           entries = pv->entries;
-           pv->entries = *pp;
-           *pp = 0;
-           pvector_reclaim( entries, level.old );
-       }
-       if ( new_size == 0 ) {
-           free( pv->entries );
-           pv->entries = 0;
-       }
-    } else {
-       // pvector is growing. Maybe insert levels.
-       while ( level.old < level.new ) {
-           pvector_page *p = (pvector_page *)
-               calloc( 1, sizeof( pvector_page ) );
-           if ( p == 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->size = new_size;
-    return -1;
-}
-
-// Return a pointer to the indexed item the given page level, adding
-// intermediate pages if requested. Returns 0 if addition fails (OOM),
-// or if not requested and page is missing.
-// Level 0 = pointer to the item entry itself.
-// Level PVECTORLEVELS( pv->size ) - 1 = 
-static void **pvector_access(
-    pvector *pv,unsigned long index,int level,int add)
-{
-    if ( index >= pv->size ) {
-       return 0;
-    }
-    void **page = (void**) &pv->entries;
-    int i = pvector_levels( pv->size );
-    while (  i-- > level ) {
-       if ( add && (*page) == 0 ) {
-           (*page) = calloc( 256, sizeof( void* ) );
-       }
-       page = (*page);
-       if ( page == 0 ) {
-           return 0;
-       }
-       page += ((pvector_index)index).level[ i ];
-    }
-    return page;
-}
-
-// Map index into a value slot
-void **pvector_entry(pvector *pv,unsigned long index) {
-    return pvector_access( pv, index, 0, 1 );
-}
-
-inline void pvector_set(pvector *pv,unsigned long index,void *value) {
-    void **p = pvector_entry( pv, index );
-    *p = value;
-}
-
-inline void *pvector_get(pvector *pv,unsigned long index) {
-    return *(pvector_entry( pv, index ));
-}
-
-int pvector_reclaim_any(pvector *pv,unsigned long ix,void *item,void *data) {
-    free( item );
-    return 0;
-}
-
-void pvector_append(pvector *pv,void *value) {
-    pvector_resize( pv, pv->size + 1, 0, 0 );
-    pvector_set( pv, pv->size - 1, value );
-}
-
-// copy block of n items from src[si] to dst[di]
-// no efficiency hacks
-void pvector_copy(pvector *dst,unsigned long di,
-             pvector *src,unsigned long si,unsigned long n) {
-    if ( dst != src || di < si ) {
-       while ( n-- != 0 ) {
-           pvector_set( dst, di++, pvector_get( src, si++ ) );
-       }
-    } else if ( di > si ){
-       di += n - 1;
-       si += n - 1;
-       while ( n-- != 0 ) {
-           pvector_set( dst, di--, pvector_get( src, si-- ) );
-       }
-    }
-}
-
-void pvector_dump(pvector *pv,
-                 int (*itemdump)(const unsigned long,const void *)) {
-    unsigned long index = 0;
-    for ( ; index < pv->size; index++ ) {
-       void **slot = pvector_next_used( pv, &index, 0, 0 );
-       if ( slot == 0 ) {
-           break;
-       }
-       itemdump( index, *slot );
-    }
-}
-
-//// Quicksort
-
-// Returns 1 for "in order", 0 for equal, and -1 for "wrong order"
-typedef int (*comparfn)(const void *,const void *);
-
-static void pvector_qsort_part(
-    pvector *pv,comparfn compar,
-    unsigned long low,unsigned long high)
-{
-    if ( low >= high ) {
-       return;
-    }
-    unsigned long lo = low;
-    unsigned long m = high - 1;
-    
-    if ( lo >= m  ) {
-       return;
-    }
-
-    unsigned long hi = m - 1;
-    void **mp = pvector_entry( pv, m );
-    void **lop, **hip;
-    for ( ;; ) {
-       // Find index of first item "above" mp scanning from lo and up
-       for ( ; lo < m; lo++ ) {
-           lop = pvector_entry( pv, lo );
-           if ( compar( *lop, *mp ) < 0 ) {
-               break;
-           }
-       }
-       // if  lo == m, then lop is wrong!!
-       // Find index of first item "below" mp scanning from hi and down
-       for ( ; hi > lo; hi-- ) {
-           hip = pvector_entry( pv, hi );
-           if ( compar( *mp, *hip ) < 0 ) {
-               break;
-           }
-       }
-       if ( lo >= hi ) {
-           if ( lo < m ) {
-               void *x = *lop;
-               *lop = *mp;
-               *mp = x;
-               m = lo;
-           }
-           break;
-       }
-       void *x = *lop;
-       *lop = *hip;
-       *hip = x;
-    }
-    pvector_qsort_part( pv, compar, low, m );
-    pvector_qsort_part( pv, compar, m+1, high );
-}
-
-void pvector_qsort(pvector *pv,comparfn compar) {
-    pvector_qsort_part( pv, compar, 0, pv->size );
-}
-
-void pvector_iterate(pvector *pv,
-                    int (*itemfn)(unsigned long,void*,void*),
-                    void *data )
-{
-    unsigned long index = 0;
-    while ( index < pv->size ) {
-       void **slot = pvector_next_used( pv, &index, 0, 0 );
-       if ( slot == 0 ) {
-           break;
-       }
-       int i = index & 0xff;
-       for ( ; i < 256 && index < pv->size; i++, index++, slot++ ) {
-           if ( itemfn( index, *slot, data ) ) {
-               return;
-           }
-       }
-    }
-}
diff --git a/pvector/pvector.h b/pvector/pvector.h
deleted file mode 100644 (file)
index f2236f3..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#ifndef pvector_H
-#define pvector_H
-
-/**
- * A pvector is a dynamic pointer array implemented as an access tree
- * of index pages of 256 pointers.
- */
-
-/*!
- * Type: pvector_page
- *
- * A pvector_page is an array of 256 void* items.
- */
-typedef void* pvector_page[256];
-
-/*!
- * Type: pvector_index
- *
- * A pvector index is ether viewed in whole as an unsigned 64-bit
- * integer, or in levels as 8 unsigned char level indexes. This
- * implementation assumes LE integer layout.
- */
-typedef union {
-    unsigned long whole; // 64-bit unsigned integer
-    unsigned char level[8];
-} pvector_index;
-
-/*!
- * Type: pvector
- *
- * A pvector is a compound of a size and a pvector_page pointer, which
- * when non-null points out the top-most page of the pvector. The
- * number of levels is derived from its size with level 0 being the
- * leaf level of actual content. E.g., a pvector larger than 256
- * items, has at least two levels, and generally N levels may span up
- * to 256^N content entries.
- */
-typedef struct _pvector {
-    unsigned long size;     //!< Limit for the logical entries[]
-    pvector_page *entries; //!< Pointer to entries indexing
-} pvector;
-
-// Number of slots for page S
-#define PV_LEVEL_SIZE(S) ((int)(exp( 256, (S) )))
-
-// The indexing part for level part p in index i
-#define PV_PART(p,i) (((unsigned char*)&i)[p])
-
-/*!
- * Find the next used slot at given index or later. With a reclaim
- * function, it will be invoked for verifying that the item is
- * actually in use, in which case it returns 1. Otherwise it should
- * reclaim any memory for the item and return 0;
- */
-void **pvector_next_used(
-    pvector *pv,unsigned long *index,
-    int (*reclaim)(pvector *pv,unsigned long index,void *item,void *data),
-    void *data );
-
-/*!
- * Function: int pvector_resize(
- *              pvector *pv,unsigned long new_size,
- *              int (*reclaim)(pvector *,unsigned long,void *item,void *data),
- *              void *data )
- * \param pv
- * \param new_size
- * \param reclaim
- * \param data
- *
- * Tries to resize the given pvector to a new size. This may result in
- * the introduction or removal of indexing pages, so that the leveling
- * is consistent with the pvector size. Thus, if it grows into a new
- * 256^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.
- *
- * 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 pvector size
- * change. The data argument is passed on to the reclaim function.
- *
- * The pvector_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.
- */
-int pvector_resize(
-    pvector *pv, unsigned long new_size,
-    int (*reclaim)(pvector *pv,unsigned long index,void *item,void *data),
-    void *data );
-
-/*!
- * Function: void **pvector_entry(pvector *pv,unsigned long index)
- * \param pv - the pvector record
- * \param index - the slot index
- *
- * [pgix,epix] = modulo( index, pv->page );
- *
- * \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 pvector size is
- * unchanged.
- */
-extern void **pvector_entry(pvector *pv,unsigned long index); 
-
-/*!
- * Function: unsigned long pvector_size(pvector *pv)
- * \param pv - the pvector record
- * \returns the size of the pvector.
- */
-inline unsigned long pvector_size(pvector *pv) {
-    return pv->size;
-}
-
-void pvector_set(pvector *pv,unsigned long index,void *value);
-
-void *pvector_get(pvector *pv,unsigned long index);
-
-void pvector_append(pvector *pv,void *value);
-
-void pvector_copy(pvector *dst,unsigned long di,
-                 pvector *src,unsigned long si,unsigned long n);
-
-void pvector_dump(pvector *pv,
-                 int (*itemdump)(const unsigned long ,const void *));
-
-void pvector_qsort(pvector *pv,int (*compar)(const void *,const void *));
-
-void pvector_iterate(pvector *pv,
-                    int (*itemfn)(unsigned long,void*,void*),
-                    void*);
-
-#endif
diff --git a/pvector/qvector.c b/pvector/qvector.c
deleted file mode 100644 (file)
index 1fc34ed..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-#include <stdlib.h>
-#include "qvector.h"
-
-/**
- * Representing a vector of void* accessible via an indexing structure
- * as levels of same-size pages. A "qvector_page" is a contiguous
- * array of 16 void*.
- */
-
-/**
- * Advances a qvector index to the next used slot at or below the
- * given level, starting from the indexed entry (inclusive) and up.
- * 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 **qvector_level_next_used(
-    qvector_page *page,qvector_index *index,int level,qvector_index end) {
-    void **p = (void**)&(*page)[ PV_PART( 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 = PV_PART( index, level - 1 ) == 0;
-           void **x = qvector_level_next_used( *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 ( ++PV_PART( 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
-// qvector)
-static unsigned int qvector_levels(qvector_index S) {
-    unsigned int N = 0;
-    do {
-       N++;
-       S /= QV_SLOTS;
-    } while ( S );
-    return N;
-}
-
-// Find the next used slot at given index or later. Returns pointer to
-// the slot.
-void **qvector_next_used(
-    qvector *pv,qvector_index *index,
-    int (*reclaim)(qvector *pv,qvector_index index,void *item,void *data),
-    void *data )
-{
-    if ( pv->entries == 0 ) {
-       *index = pv->size;
-       return 0;
-    }
-    int levels = qvector_levels( pv->size );
-    for ( ; *index < pv->size; (*index)++ ) {
-       void **slot = qvector_level_next_used(
-           pv->entries, index, levels - 1, pv->size ) ;
-       if ( slot == 0 ) {
-           // reached the end of the vector
-           *index = pv->size;
-           break;
-       }
-       if ( *slot ) {
-           // Try reclaiming the slot,
-           if ( reclaim && reclaim( pv, *index, *slot, data ) == 0 ) {
-               *slot = 0;
-           } else {
-               return slot;
-           }
-       }
-    }
-    return 0;
-}
-
-// Reclaim tree of unused pages
-static void qvector_reclaim(qvector_page *page,unsigned int level) {
-    int i = 0;
-    if ( level > 0 ) {
-       for ( ; i < QV_SLOTS; i++ ) {
-           if ( (*page)[i] ) {
-               qvector_reclaim( (qvector_page *) (*page)[i], level - 1 );
-           }
-       }
-    }
-    free( page );
-}
-
-// Resize vector, using the reclaim function as needed, to handle any
-// excess items or to veto the resize. Returns the index of the veto, if
-// any, or <0 otherwise, with -1 indicating success and -2 indicating
-// OOM while growing.
-//
-// Nothe that resizing may result in the introduction/removal of
-// indexing levels and pages, so as to keep the leveling accurate for
-// the size.
-int qvector_resize(
-    qvector *pv,qvector_index new_size,
-    int (*reclaim)(qvector *pv,qvector_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[ QV_INDEX_FIELDS ];
-    if ( level_delta[ 0 ] == 0 ) {
-       int d = 1;
-       int i;
-       for ( i = 0; i < QV_INDEX_FIELDS; i++ ) {
-           level_delta[ i ] = 15 * d;
-           d = 16 * d;
-       }
-    }
-    struct {
-       int old;
-       int new;
-    } level = {
-       qvector_levels( pv->size ),
-       qvector_levels( new_size )
-    };
-    if ( pv->entries == 0 ) {
-       pv->size = new_size;
-       return 0;
-    }
-    // A shrinking qvector might be veto-ed
-    if ( new_size < pv->size ) {
-       qvector_index index = new_size;
-       void **slot = qvector_next_used( pv, &index, reclaim, data );
-       if ( slot ) {
-           return index;
-       }
-       // 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.
-       qvector_page *entries;
-       qvector_page **pp = &pv->entries;
-       while ( level.old-- > level.new ) {
-           pp = (qvector_page **)(*pp)[0];
-       }
-       if ( pp != &pv->entries ) {
-           entries = pv->entries;
-           pv->entries = *pp;
-           *pp = 0;
-           qvector_reclaim( entries, level.old );
-       }
-       if ( new_size == 0 ) {
-           free( pv->entries );
-           pv->entries = 0;
-       }
-    } else {
-       // qvector is growing. Maybe insert levels.
-       while ( level.old < level.new ) {
-           qvector_page *p = (qvector_page *)
-               calloc( 1, sizeof( qvector_page ) );
-           if ( p == 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->size = new_size;
-    return -1;
-}
-
-// Return a pointer to the indexed item the given page level, adding
-// intermediate pages if requested. Returns 0 if addition fails (OOM),
-// or if not requested and page is missing.
-// Level 0 = pointer to the item entry itself.
-// Level QVECTORLEVELS( pv->size ) - 1 = 
-static void **qvector_access(
-    qvector *pv,qvector_index index,int level,int add)
-{
-    if ( index >= pv->size ) {
-       return 0;
-    }
-    void **page = (void**) &pv->entries;
-    int i = qvector_levels( pv->size );
-    while (  i-- > level ) {
-       if ( add && (*page) == 0 ) {
-           (*page) = calloc( QV_SLOTS, sizeof( void* ) );
-       }
-       page = (*page);
-       if ( page == 0 ) {
-           return 0;
-       }
-       page += PV_PART( &index, i );
-    }
-    return page;
-}
-
-// Map index into a value slot
-void **qvector_entry(qvector *pv,qvector_index index) {
-    return qvector_access( pv, index, 0, 1 );
-}
-
-inline void qvector_set(qvector *pv,qvector_index index,void *value) {
-    void **p = qvector_entry( pv, index );
-    *p = value;
-}
-
-inline void *qvector_get(qvector *pv,qvector_index index) {
-    return *(qvector_entry( pv, index ));
-}
-
-int qvector_reclaim_any(qvector *pv,qvector_index ix,void *item,void *data) {
-    free( item );
-    return 0;
-}
-
-void qvector_append(qvector *pv,void *value) {
-    qvector_resize( pv, pv->size + 1, 0, 0 );
-    qvector_set( pv, pv->size - 1, value );
-}
-
-// copy block of n items from src[si] to dst[di]
-// no efficiency hacks
-void qvector_copy(qvector *dst,qvector_index di,
-             qvector *src,qvector_index si,qvector_index n) {
-    if ( dst != src || di < si ) {
-       while ( n-- != 0 ) {
-           qvector_set( dst, di++, qvector_get( src, si++ ) );
-       }
-    } else if ( di > si ){
-       di += n - 1;
-       si += n - 1;
-       while ( n-- != 0 ) {
-           qvector_set( dst, di--, qvector_get( src, si-- ) );
-       }
-    }
-}
-
-void qvector_dump(qvector *pv,
-                 int (*itemdump)(const qvector_index,const void *)) {
-    qvector_index index = 0;
-    for ( ; index < pv->size; index++ ) {
-       void **slot = qvector_next_used( pv, &index, 0, 0 );
-       if ( slot == 0 ) {
-           break;
-       }
-       itemdump( index, *slot );
-    }
-}
-
-//// Quicksort
-
-// Returns 1 for "in order", 0 for equal, and -1 for "wrong order"
-typedef int (*comparfn)(const void *,const void *);
-
-static void qvector_qsort_part(
-    qvector *pv,comparfn compar,
-    qvector_index low,qvector_index high)
-{
-    if ( low >= high ) {
-       return;
-    }
-    qvector_index lo = low;
-    qvector_index m = high - 1;
-    
-    if ( lo >= m  ) {
-       return;
-    }
-
-    qvector_index hi = m - 1;
-    void **mp = qvector_entry( pv, m );
-    void **lop, **hip;
-    for ( ;; ) {
-       // Find index of first item "above" mp scanning from lo and up
-       for ( ; lo < m; lo++ ) {
-           lop = qvector_entry( pv, lo );
-           if ( compar( *lop, *mp ) < 0 ) {
-               break;
-           }
-       }
-       // if  lo == m, then lop is wrong!!
-       // Find index of first item "below" mp scanning from hi and down
-       for ( ; hi > lo; hi-- ) {
-           hip = qvector_entry( pv, hi );
-           if ( compar( *mp, *hip ) < 0 ) {
-               break;
-           }
-       }
-       if ( lo >= hi ) {
-           if ( lo < m ) {
-               void *x = *lop;
-               *lop = *mp;
-               *mp = x;
-               m = lo;
-           }
-           break;
-       }
-       void *x = *lop;
-       *lop = *hip;
-       *hip = x;
-    }
-    qvector_qsort_part( pv, compar, low, m );
-    qvector_qsort_part( pv, compar, m+1, high );
-}
-
-void qvector_qsort(qvector *pv,comparfn compar) {
-    qvector_qsort_part( pv, compar, 0, pv->size );
-}
-
-void qvector_iterate(qvector *pv,
-                    int (*itemfn)(qvector_index,void*,void*),
-                    void *data )
-{
-    qvector_index index = 0;
-    while ( index < pv->size ) {
-       void **slot = qvector_next_used( pv, &index, 0, 0 );
-       if ( slot == 0 ) {
-           break;
-       }
-       int i = index & 0xff;
-       for ( ; i < QV_SLOTS && index < pv->size; i++, index++, slot++ ) {
-           if ( itemfn( index, *slot, data ) ) {
-               return;
-           }
-       }
-    }
-}
diff --git a/pvector/qvector.h b/pvector/qvector.h
deleted file mode 100644 (file)
index dfdad4b..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-#ifndef qvector_H
-#define qvector_H
-
-/**
- * A qvector is a dynamic pointer array implemented as an access tree
- * of index pages. The indexing is done using "unsigned long" indexes.
- */
-
-/*!
- * Type: qvector_index
- * This is the general indexing used for qvector access.
- */
-typedef unsigned long qvector_index;
-
-/*!
- * Macro: QV_LEVEL_BITS
- * This defines the number of bits in the indexing bit field.
- */
-#define QV_LEVEL_BITS 4
-
-/*!
- * Macro: QV_INDEX_BITS
- * This defines the number of bits of a qvector index
- */
-#define QV_INDEX_BITS sizeof( qvector_index )
-
-/*!
- * Macro: QV_INDEX_FIELDS
- * This defines the number of bit fields in an qvector index
- */
-#define QV_INDEX_FIELDS ( ( QV_INDEX_BITS - 1 ) / QV_LEVEL_BITS + 1 )
-
-/*!
- * Macro: QV_SLOTS
- * This defines the number of slots spanned by an index level
- */
-#define QV_SLOTS ( 1 << QV_LEVEL_BITS )
-
-/*!
- * Type: qvector_page
- *
- * A qvector_page is an array of 16 void* items.
- */
-typedef void* qvector_page[ QV_SLOTS ];
-
-/*!
- * Type: qvector_field
- * This is QV_LEVEL_BITS size bit field
- */
-typedef struct { int bits:QV_LEVEL_BITS; } qvector_field;
-
-/*!
- * Type: qvector_indexing
- *
- * A qvector index is ether viewed in whole as an QV_INDEX_BITS wide
- * unsigned, or in levels as a packed array of qvector_field index
- * parts. This implementation assumes LE integer layout.
- */
-typedef union {
-    qvector_index whole;                     // as a whole
-    qvector_field level[ QV_INDEX_FIELDS ];  // qua bits fields
-} qvector_indexing;
-
-/*!
- * Type: qvector
- *
- * A qvector is a compound of a size and a qvector_page pointer, which
- * when non-null points out the top-most page of the qvector. The
- * number of levels is derived from its size with level 0 being the
- * leaf level of actual content. E.g., a qvector larger than 16
- * items, has at least two levels, and generally N levels may span up
- * to 16^N content entries.
- */
-typedef struct _qvector {
-    qvector_index size;     //!< Limit for the logical entries[]
-    qvector_page *entries; //!< Pointer to entries indexing
-} qvector;
-
-// Number of slots for page S
-#define PV_LEVEL_SIZE(S) ((int)(exp( 16, (S) )))
-
-// The indexing part for level part p in index i
-#define PV_PART(i,p) (((qvector_indexing*)(i))->level[ p ].bits)
-/*!
- * Find the next used slot at given index or later. With a reclaim
- * function, it will be invoked for verifying that the item is
- * actually in use, in which case it returns 1. Otherwise it should
- * reclaim any memory for the item and return 0;
- */
-void **qvector_next_used(
-    qvector *pv,qvector_index *index,
-    int (*reclaim)(qvector *pv,qvector_index index,void *item,void *data),
-    void *data );
-
-/*!
- * Function: int qvector_resize(
- *              qvector *pv,qvector_index new_size,
- *              int (*reclaim)(qvector *,qvector_index,void *item,void *data),
- *              void *data )
- * \param pv
- * \param new_size
- * \param reclaim
- * \param data
- *
- * Tries to resize the given qvector to a new size. This may result in
- * the introduction or removal of indexing pages, so that the leveling
- * is consistent with the qvector 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.
- *
- * 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 qvector size
- * change. The data argument is passed on to the reclaim function.
- *
- * The qvector_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.
- */
-int qvector_resize(
-    qvector *pv, qvector_index new_size,
-    int (*reclaim)(qvector *pv,qvector_index index,void *item,void *data),
-    void *data );
-
-/*!
- * Function: void **qvector_entry(qvector *pv,qvector_index index)
- * \param pv - the qvector record
- * \param index - the slot index
- *
- * [pgix,epix] = modulo( index, pv->page );
- *
- * \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 qvector size is
- * unchanged.
- */
-extern void **qvector_entry(qvector *pv,qvector_index index); 
-
-/*!
- * Function: qvector_index qvector_size(qvector *pv)
- * \param pv - the qvector record
- * \returns the size of the qvector.
- */
-inline qvector_index qvector_size(qvector *pv) {
-    return pv->size;
-}
-
-void qvector_set(qvector *pv,qvector_index index,void *value);
-
-void *qvector_get(qvector *pv,qvector_index index);
-
-void qvector_append(qvector *pv,void *value);
-
-void qvector_copy(qvector *dst,qvector_index di,
-                 qvector *src,qvector_index si,qvector_index n);
-
-void qvector_dump(qvector *pv,
-                 int (*itemdump)(const qvector_index ,const void *));
-
-void qvector_qsort(qvector *pv,int (*compar)(const void *,const void *));
-
-void qvector_iterate(qvector *pv,
-                    int (*itemfn)(qvector_index,void*,void*),
-                    void*);
-
-#endif
diff --git a/vector/Makefile b/vector/Makefile
new file mode 100644 (file)
index 0000000..f8b05a7
--- /dev/null
@@ -0,0 +1,26 @@
+LIBRARY = libvector.a
+LIBOBJS = vector.o hashvector.o
+
+# This is overridable on command line
+VECTOR_LEVEL_BITS = 4
+
+default: $(LIBRARY)
+
+all: default
+
+CFLAGS = -Wall -g -fmax-errors=1 -DVECTOR_LEVEL_BITS=$(VECTOR_LEVEL_BITS)
+
+define STDCC
+.INTERMEDIATE: $1.o
+CLEANRM += $1.o
+$1.o: $1.c | $1.h
+endef
+
+$(foreach OBJ,$(LIBOBJS:.o=),$(eval $(call STDCC,$(OBJ))))
+
+CLEANRM += $(LIBRARY)
+$(LIBRARY): $(LIBOBJS)
+       $(AR) r $@ $^
+
+clean:
+       rm -f $(CLEANRM)
diff --git a/vector/hashvector.c b/vector/hashvector.c
new file mode 100644 (file)
index 0000000..1afae80
--- /dev/null
@@ -0,0 +1,144 @@
+#include "hashvector.h"
+
+// 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;
+    void **hole = 0;
+    void **p = 0;
+    for ( ;; ) {
+       p = vector_entry(&hv->table, i);
+       if ( p == 0 ) {
+           return 0; // This basically means OOM, and is a failure condition.
+       }
+       if ( *p == 0 ) {
+           break; // Not found
+       }
+       if ( (*p) == HV_HOLE ) {
+           if ( hole == 0 ) {
+               hole = p; // Remember the first hole
+           }
+       } else if ( hv->haskey( *p, key ) ) {
+           return p; // Found
+       }
+       if ( ++i == hv->table.size ) {
+           i = 0; // Roll-around the table
+       }
+       if ( i == index ) {
+           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 );
+    if ( p && *p && *p != HV_HOLE ) {
+       if ( x ) {
+           *x = *p;
+       }
+       return 1;
+    }
+    return 0;
+}
+
+static int capture_item(vector *pv,unsigned long ix,void *item,void *data) {
+    if ( item != HV_HOLE ) {
+       hashvector_add( (hashvector *) data, item );
+    }
+    return 0;
+}
+
+static int iter_item(unsigned long ix,void *item,void *data) {
+    if ( item && item != HV_HOLE ) {
+       hashvector_add( (hashvector *) data, item );
+    }
+    return 0;
+}
+
+static void hashvector_resize(hashvector *hv,unsigned long new_size) {
+    hashvector tmp = *hv;
+    hv->table.size = new_size;
+    hv->table.entries = 0;
+    hv->fill = 0;
+    hv->holes = 0;
+    if ( new_size < hv->table.size ) {
+       vector_resize( &tmp.table, 0, capture_item, hv );
+    } else {
+       vector_iterate( &tmp.table, iter_item, hv );
+    }
+}
+    
+// Add the given element.
+int hashvector_add(hashvector *hv,void *item) {
+    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
+    if ( p == 0 ) {
+       return -1; // OOM or overfull hashvector
+    }
+    if ( *p ) {
+       if ( *p != HV_HOLE ) {
+           return 0; // Already added.
+       }
+       hv->holes--; // about to reuse a hole
+    }
+    *p = item;
+    hv->fill++;
+    if ( hv->fill + hv->holes > hv->table.size / 2 ) {
+       hashvector_resize( hv, hv->table.size * 2 );
+    }
+    return 1;
+}
+
+// Delete the given item
+int hashvector_delete(hashvector *hv,void *item) {
+    void **p = hashvector_find_slot( hv, hv->itemkey( item ) );
+    if ( p == 0 ) {
+       return -1;
+    }
+    if ( *p != item ) {
+       return 0;
+    }
+    *p = HV_HOLE;
+    hv->holes++;
+    hv->fill--;
+    if ( hv->table.size > VECTOR_SLOTS && hv->fill < hv->table.size / 4 ) {
+       hashvector_resize( hv, hv->table.size / 2 );
+    }
+    return 1;
+}
+
+// Copy items into a vector. Returns 0 on success and -1 on failure.
+int hashvector_contents(hashvector *hv,vector *pv) {
+    if ( vector_resize( pv, hv->fill, 0, 0 ) ) {
+       return -1;
+    }
+    unsigned long from = 0;
+    unsigned long to = 0;
+    while ( to < hv->fill ) {
+       void **slot = vector_next_used( &hv->table, &from, 0, 0 );
+       if ( slot ) {
+           if ( *slot != HV_HOLE ) {
+               vector_set( pv, to++, *slot );
+           }
+           from++;
+       } else {
+           break;
+       }
+    }
+    return 0;
+}
+
+// A simple binary hashcode, (before modulo table size)
+unsigned long hashvector_hashcode(unsigned char *key,unsigned long n) {
+    unsigned long value = 5381;
+    while ( n-- ) {
+       value += ( value << 5 ) + *(key++);
+    }
+    return value;
+}
+
diff --git a/vector/hashvector.h b/vector/hashvector.h
new file mode 100644 (file)
index 0000000..71b6253
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef hashvector_H
+#define 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
+ * or not a given item has a given key. The hashvector manipulations
+ * uses those for locating and placing items into the vector; the
+ * hascode is the primary place for an item, but then scanning upwards
+ * with a rolling index in case of collisions.
+ *
+ * The vector holds 0 pointers for free slots, (void*)1 pointers (aka
+ * HV_HOLE) to indicate slots of deleted items, and otherwise item
+ * 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%.
+ */
+
+#include "vector.h"
+
+/*!
+ * Type: hashvector
+ * This combines a vector (for contents) with fill and hole counters
+ * and the three functions.
+ */
+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
+} hashvector;
+
+/*!
+ * Macro: HV_HOLE
+ * The representation of a deleted item.
+ */
+#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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+int hashvector_contents(hashvector *hv,vector *pv);
+
+/*!
+ * Function unsigned long hashvector_hashcode(
+ *              unsigned char *key,unsigned long n)
+ *
+ * Computes and returns a hashcode for a block of bytes.
+ */
+unsigned long hashvector_hashcode(unsigned char *key,unsigned long n);
+
+#endif
diff --git a/vector/vector.c b/vector/vector.c
new file mode 100644 (file)
index 0000000..8dadf00
--- /dev/null
@@ -0,0 +1,338 @@
+#include <stdlib.h>
+#include "vector.h"
+
+/**
+ * Representing a vector of void* accessible via an indexing structure
+ * as levels of same-size pages. A "vector_page" is a contiguous array
+ * void*, and an index is "unsigned long" (64 bits).
+ */
+
+/**
+ * Advances a vector index to the next used slot at or below the
+ * given level, starting from the indexed entry (inclusive) and up.
+ * 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_next_used(
+    vector_page *page,vector_index *index,int level,vector_index end) {
+    void **p = (void**)&(*page)[ VECTOR_INDEX_PART( 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 );
+           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 ( ++VECTOR_INDEX_PART( 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.
+void **vector_next_used(
+    vector *pv,vector_index *index,
+    int (*reclaim)(vector *pv,vector_index index,void *item,void *data),
+    void *data )
+{
+    if ( pv->entries == 0 ) {
+       *index = pv->size;
+       return 0;
+    }
+    int levels = vector_levels( pv->size );
+    for ( ; *index < pv->size; (*index)++ ) {
+       void **slot = vector_level_next_used(
+           pv->entries, index, levels - 1, pv->size ) ;
+       if ( slot == 0 ) {
+           // reached the end of the vector
+           *index = pv->size;
+           break;
+       }
+       if ( *slot ) {
+           // Try reclaiming the slot,
+           if ( reclaim && reclaim( pv, *index, *slot, data ) == 0 ) {
+               *slot = 0;
+           } else {
+               return slot;
+           }
+       }
+    }
+    return 0;
+}
+
+// Reclaim tree of unused pages
+static void vector_reclaim(vector_page *page,unsigned int level) {
+    int i = 0;
+    if ( level > 0 ) {
+       for ( ; i < VECTOR_SLOTS; i++ ) {
+           if ( (*page)[i] ) {
+               vector_reclaim( (vector_page *) (*page)[i], level - 1 );
+           }
+       }
+    }
+    free( page );
+}
+
+// Resize vector, using the reclaim function as needed, to handle any
+// excess items or to veto the resize. Returns the index of the veto, if
+// any, or <0 otherwise, with -1 indicating success and -2 indicating
+// OOM while growing.
+//
+// Nothe that resizing may result in the introduction/removal of
+// indexing levels and pages, so as to keep the leveling accurate for
+// the size.
+int vector_resize(
+    vector *pv,vector_index new_size,
+    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 )
+    };
+    if ( pv->entries == 0 ) {
+       pv->size = new_size;
+       return 0;
+    }
+    // A shrinking vector might be veto-ed
+    if ( new_size < pv->size ) {
+       vector_index index = new_size;
+       void **slot = vector_next_used( pv, &index, reclaim, data );
+       if ( slot ) {
+           return index;
+       }
+       // 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 ) {
+           pp = (vector_page **)(*pp)[0];
+       }
+       if ( pp != &pv->entries ) {
+           entries = pv->entries;
+           pv->entries = *pp;
+           *pp = 0;
+           vector_reclaim( entries, level.old );
+       }
+       if ( new_size == 0 ) {
+           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 ) {
+               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->size = new_size;
+    return -1;
+}
+
+// Return a pointer to the indexed item the given page level, adding
+// intermediate pages if requested. Returns 0 if addition fails (OOM),
+// or if not requested and page is missing.
+// Level 0 = pointer to the item entry itself.
+// Level VECTORLEVELS( pv->size ) - 1 = 
+static 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 );
+    while (  i-- > level ) {
+       if ( add && (*page) == 0 ) {
+           (*page) = calloc( VECTOR_SLOTS, sizeof( void* ) );
+       }
+       page = (*page);
+       if ( page == 0 ) {
+           return 0;
+       }
+       page += VECTOR_INDEX_PART( &index, i );
+    }
+    return page;
+}
+
+// Map index into a value slot
+void **vector_entry(vector *pv,vector_index index) {
+    return vector_access( pv, index, 0, 1 );
+}
+
+inline void vector_set(vector *pv,vector_index index,void *value) {
+    void **p = vector_entry( pv, index );
+    *p = value;
+}
+
+inline void *vector_get(vector *pv,vector_index index) {
+    return *(vector_entry( pv, index ));
+}
+
+int vector_reclaim_any(vector *pv,vector_index ix,void *item,void *data) {
+    free( item );
+    return 0;
+}
+
+void vector_append(vector *pv,void *value) {
+    vector_resize( pv, pv->size + 1, 0, 0 );
+    vector_set( pv, pv->size - 1, value );
+}
+
+// copy block of n items from src[si] to dst[di]
+// no efficiency hacks
+void vector_copy(vector *dst,vector_index di,
+             vector *src,vector_index si,vector_index n) {
+    if ( dst != src || di < si ) {
+       while ( n-- != 0 ) {
+           vector_set( dst, di++, vector_get( src, si++ ) );
+       }
+    } else if ( di > si ){
+       di += n - 1;
+       si += n - 1;
+       while ( n-- != 0 ) {
+           vector_set( dst, di--, vector_get( src, si-- ) );
+       }
+    }
+}
+
+void vector_dump(vector *pv,
+                 int (*itemdump)(const vector_index,const void *)) {
+    vector_index index = 0;
+    for ( ; index < pv->size; index++ ) {
+       void **slot = vector_next_used( pv, &index, 0, 0 );
+       if ( slot == 0 ) {
+           break;
+       }
+       itemdump( index, *slot );
+    }
+}
+
+//// Quicksort
+
+// Returns 1 for "in order", 0 for equal, and -1 for "wrong order"
+typedef int (*comparfn)(const void *,const void *);
+
+static void vector_qsort_part(
+    vector *pv,comparfn compar,
+    vector_index low,vector_index high)
+{
+    if ( low >= high ) {
+       return;
+    }
+    vector_index lo = low;
+    vector_index m = high - 1;
+    
+    if ( lo >= m  ) {
+       return;
+    }
+
+    vector_index hi = m - 1;
+    void **mp = vector_entry( pv, m );
+    void **lop, **hip;
+    for ( ;; ) {
+       // Find index of first item "above" mp scanning from lo and up
+       for ( ; lo < m; lo++ ) {
+           lop = vector_entry( pv, lo );
+           if ( compar( *lop, *mp ) < 0 ) {
+               break;
+           }
+       }
+       // if  lo == m, then lop is wrong!!
+       // Find index of first item "below" mp scanning from hi and down
+       for ( ; hi > lo; hi-- ) {
+           hip = vector_entry( pv, hi );
+           if ( compar( *mp, *hip ) < 0 ) {
+               break;
+           }
+       }
+       if ( lo >= hi ) {
+           if ( lo < m ) {
+               void *x = *lop;
+               *lop = *mp;
+               *mp = x;
+               m = lo;
+           }
+           break;
+       }
+       void *x = *lop;
+       *lop = *hip;
+       *hip = x;
+    }
+    vector_qsort_part( pv, compar, low, m );
+    vector_qsort_part( pv, compar, m+1, high );
+}
+
+void vector_qsort(vector *pv,comparfn compar) {
+    vector_qsort_part( pv, compar, 0, pv->size );
+}
+
+void vector_iterate(vector *pv,
+                    int (*itemfn)(vector_index,void*,void*),
+                    void *data )
+{
+    vector_index index = 0;
+    while ( index < pv->size ) {
+       void **slot = vector_next_used( pv, &index, 0, 0 );
+       if ( slot == 0 ) {
+           break;
+       }
+       int i = index & 0xff;
+       for ( ; i < VECTOR_SLOTS && index < pv->size; i++, index++, slot++ ) {
+           if ( itemfn( index, *slot, data ) ) {
+               return;
+           }
+       }
+    }
+}
diff --git a/vector/vector.h b/vector/vector.h
new file mode 100644 (file)
index 0000000..1227860
--- /dev/null
@@ -0,0 +1,168 @@
+#ifndef vector_H
+#define 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.
+ */
+
+/*!
+ * Macro: VECTOR_LEVEL_BITS
+ * This defines the number of bits in the indexing bit field.
+ */
+#define VECTOR_LEVEL_BITS 4
+
+/*!
+ * 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 )
+
+/*!
+ * Macro: VECTOR_INDEX_FIELDS
+ * This defines the number of bit fields in an vector index
+ */
+#define VECTOR_INDEX_FIELDS \
+    ( ( VECTOR_INDEX_BITS - 1 ) / VECTOR_LEVEL_BITS + 1 )
+
+/*!
+ * Macro: VECTOR_SLOTS
+ * This defines the number of slots spanned by an index level
+ */
+#define VECTOR_SLOTS ( 1 << VECTOR_LEVEL_BITS )
+
+/*!
+ * Type: vector_page
+ *
+ * A vector_page is an array of 16 void* items.
+ */
+typedef void* vector_page[ VECTOR_SLOTS ];
+
+/*!
+ * Type: vector_field
+ * This is VECTOR_LEVEL_BITS size bit field
+ */
+typedef struct { int bits:VECTOR_LEVEL_BITS; } vector_field;
+
+/*!
+ * Type: vector_indexing
+ *
+ * A vector index is ether viewed in whole as an VECTOR_INDEX_BITS wide
+ * unsigned, or in levels as a packed array of vector_field index
+ * parts. This implementation assumes LE integer layout.
+ */
+typedef union {
+    vector_index whole;                     // as a whole
+    vector_field level[ VECTOR_INDEX_FIELDS ];  // qua bits fields
+} vector_indexing;
+
+// The indexing part for level part p in index i
+#define VECTOR_INDEX_PART(i,p) (((vector_indexing*)(i))->level[ p ].bits)
+
+/*!
+ * Type: vector
+ *
+ * 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 next used slot at given index or later. With a reclaim
+ * function, it will be invoked for verifying that the item is
+ * actually in use, in which case it returns 1. Otherwise it should
+ * reclaim any memory for the item and return 0;
+ */
+void **vector_next_used(
+    vector *pv,vector_index *index,
+    int (*reclaim)(vector *pv,vector_index index,void *item,void *data),
+    void *data );
+
+/*!
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ */
+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
+ *
+ * [pgix,epix] = modulo( index, pv->page );
+ *
+ * \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.
+ */
+extern void **vector_entry(vector *pv,vector_index index); 
+
+/*!
+ * Function: vector_index vector_size(vector *pv)
+ * \param pv - the vector record
+ * \returns the size of the vector.
+ */
+inline vector_index vector_size(vector *pv) {
+    return pv->size;
+}
+
+void vector_set(vector *pv,vector_index index,void *value);
+
+void *vector_get(vector *pv,vector_index index);
+
+void vector_append(vector *pv,void *value);
+
+void vector_copy(vector *dst,vector_index di,
+                 vector *src,vector_index si,vector_index n);
+
+void vector_dump(vector *pv,
+                 int (*itemdump)(const vector_index ,const void *));
+
+void vector_qsort(vector *pv,int (*compar)(const void *,const void *));
+
+void vector_iterate(vector *pv,
+                    int (*itemfn)(vector_index,void*,void*),
+                    void*);
+
+#endif