+#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;
+ }
+ }
+ }
+}