#include #include TRIVIAL_EXCEPTION(ht_fill_limit_crossed); void defaultResumptionHandler(ht_fill_limit_crossed &) { printf("default resumption ht_fill_limit_crossed\n"); } void defaultTerminationHandler(ht_fill_limit_crossed &) = void; trait has_hash( otype K ) { size_t hash(K); int ?==?( K, K ); }; trait hkey( otype K, dtype tN | has_hash(K) ) { K key(tN &); }; forall( otype K, dtype tN, dtype tE | $dlistable(tN, tE) | hkey(K, tN) | { void defaultResumptionHandler(ht_fill_limit_crossed &); } ) { struct hashtable { size_t n_buckets; dlist(tN, tE) *buckets; size_t item_count; float ff_next_warn_up; void (*__stashed_defaultResumptionHandler) (ht_fill_limit_crossed &); }; void ?{}( hashtable(K, tN, tE) & this ) = void; // void ?{}( hashtable(K, tN, tE) & this, size_t, dlist(tN, tE)* ) = void; void ?{}( hashtable(K, tN, tE) & this, size_t n_buckets, dlist(tN, tE) *buckets ) { this.n_buckets = n_buckets; this.buckets = buckets; this.item_count = 0; this.ff_next_warn_up = 0.5; this.__stashed_defaultResumptionHandler = defaultResumptionHandler; for ( i; n_buckets ) { ?{}( this.buckets[i] ); } } } forall( otype K, dtype tN, dtype tE | $dlistable(tN, tE) | hkey(K, tN) ) { float fill_frac( hashtable(K, tN, tE) & this ) with(this) { return ((float)item_count) / n_buckets; } size_t bucket_of( hashtable(K, tN, tE) & this, K k ) { return hash(k) % this.n_buckets; } tE & get( hashtable(K, tN, tE) & this, K k ) with (this) { dlist(tN, tE) & bucket = buckets[ bucket_of(this, k) ]; for ( tN * item = & $tempcv_e2n(bucket`first); item != 0p; item = & $tempcv_e2n((*item)`next) ) { if ( key(*item) == k ) { return *item; } } return *0p; } void check_ff_warning( hashtable(K, tN, tE) & this ) with (this) { void (*defaultResumptionHandler) (ht_fill_limit_crossed &) = __stashed_defaultResumptionHandler; if (fill_frac(this) > ff_next_warn_up) { throwResume (ht_fill_limit_crossed){}; ff_next_warn_up *= 2; } } void put( hashtable(K, tN, tE) & this, tE & v ) with (this) { check_ff_warning(this); K k = key( $tempcv_e2n(v) ); dlist(tN, tE) & bucket = buckets[ bucket_of(this, k) ]; for ( tN * item = & $tempcv_e2n(bucket`first); item != 0p; item = & $tempcv_e2n((*item)`next) ) { if ( key(*item) == k ) { remove(*item); break; } } insert_first(bucket, v); this.item_count ++; } } // tactical usage: // HASHTABLE_STATIC(int, item_by_prority, item, n, ht) // // intended equivalent: // hashtable_static(int, item_by_prority, item, Z(n)) ht; #define HASHTABLE_STATIC(K, tN, tE, n_buckets, obj) \ struct __hashtable_static_ ## obj { \ inline hashtable(K, tN, tE); \ dlist(tN, tE) $items[n_buckets]; \ }; \ void ?{}( __hashtable_static_ ## obj & this ) { \ ((hashtable(K, tN, tE) &)this){ n_buckets, this.$items }; \ } \ __hashtable_static_ ## obj obj; trait heaped(dtype T) { T * alloc( size_t ); void free( void * ); }; void __dynamic_defaultResumptionHandler(ht_fill_limit_crossed & ex) { printf("dynamic limit crossed\n"); } forall( otype K, dtype tN, dtype tE | $dlistable(tN, tE) | hkey(K, tN) | heaped( dlist(tN, tE) ) ) { struct hashtable_dynamic { inline hashtable(K, tN, tE); }; void ?{}( hashtable_dynamic(K, tN, tE) & this, size_t n_buckets ) { void (*defaultResumptionHandler) (ht_fill_limit_crossed &) = __dynamic_defaultResumptionHandler; dlist(tN, tE) *buckets = alloc(n_buckets); ((hashtable(K, tN, tE) &)this){ n_buckets, buckets }; } void ^?{}( hashtable_dynamic(K, tN, tE) & this ) { free(this.buckets); } } struct request { unsigned int src_id; unsigned int tgt_id; DLISTED_MGD_EXPL_IN(request, ht_by_src) DLISTED_MGD_EXPL_IN(request, ht_by_tgt) }; DLISTED_MGD_EXPL_OUT(request, ht_by_src) DLISTED_MGD_EXPL_OUT(request, ht_by_tgt) size_t hash( unsigned int k ) { // not really a hash function, not really the point return k; } unsigned int key( request_in_ht_by_src & v ) { return v.src_id; } #include int main() { HASHTABLE_STATIC(unsigned int, request_in_ht_by_src, request, 67, h_src) request & wasnt_found = get(h_src, 17); assert( &wasnt_found == 0p ); request r; r.src_id = 117; r.tgt_id = 998; put(h_src, r); request & found = get(h_src, 117); assert( &found == &r ); & wasnt_found = & get(h_src, 998); assert( &wasnt_found == 0p ); printf( "%f\n", fill_frac(h_src) ); request rs[500]; try { for (i; 500) { rs[i].src_id = 8000 * i; put(h_src, rs[i]); } } catchResume(ht_fill_limit_crossed*) { printf("fill limit tripped with h_src filled at %f\n", fill_frac(h_src)); throwResume; } assert( & get(h_src, 117 ) ); assert( & get(h_src, 8000*25 ) ); assert(! & get(h_src, 8000*25+1) ); dlist(request_in_ht_by_src, request) * (*old_alloc)( size_t ) = alloc; dlist(request_in_ht_by_src, request) * alloc( size_t n ) { dlist(request_in_ht_by_src, request) * ret = old_alloc(n); printf("alloc'ed at %p\n", ret); return ret; } void (*old_free)( void * ) = free; void free( void * o ) { printf("free'ing at %p\n", o); old_free(o); } hashtable_dynamic(unsigned int, request_in_ht_by_src, request) ht2 = { 113 }; request rs2[500]; try { for (i; 500) { if (i % 10 == 0) {printf("%d(%f),", i, fill_frac(ht2));} rs2[i].src_id = 8000 * i; put(ht2, rs2[i]); } } catchResume(ht_fill_limit_crossed*) { printf("fill limit tripped with ht2 filled at %f\n", fill_frac(ht2)); throwResume; } }