diff --git a/api/boot.c b/api/boot.c index 92451609..c2e4a4fc 100644 --- a/api/boot.c +++ b/api/boot.c @@ -172,6 +172,9 @@ XS( prima_cleanup) list_delete_all( &prima_guts.static_objects, true); list_destroy( &prima_guts.static_objects); list_destroy( &prima_guts.post_destroys); + hash_destroy( prima_guts.cache, true); + prima_guts.vmt_hash = NULL; + prima_guts.cache = NULL; prima_kill_zombies(); if ( prima_guts.init_ok > 2) window_subsystem_done(); list_first_that( &prima_guts.static_hashes, (void*)kill_hashes, NULL); @@ -375,6 +378,7 @@ if (sizeof(s1) != (s2)) { \ list_create( &prima_guts.post_destroys, 16, 16); prima_guts.objects = hash_create(); prima_guts.vmt_hash = hash_create(); + prima_guts.cache = hash_create(); register_constants(); register_classes(); diff --git a/api/hash.c b/api/hash.c index 5c97fac9..d22c5167 100644 --- a/api/hash.c +++ b/api/hash.c @@ -28,12 +28,15 @@ hash_destroy( PHash h, Bool killAll) static SV *ksv = NULL; -#define ksv_check if ( !ksv) { \ - ksv = newSV( keyLen); \ +#define ksv_init if ( !ksv) { \ + ksv = newSV( 32); \ if (!ksv) croak( "GUTS015: Cannot create SV"); \ - } \ - sv_setpvn( ksv, ( char *) key, keyLen); \ - he = hv_fetch_ent( h, ksv, false, 0) + } \ + +#define ksv_check \ + ksv_init; \ + sv_setpvn( ksv, ( char *) key, keyLen);\ + he = hv_fetch_ent( h, ksv, false, 0) void * @@ -85,6 +88,21 @@ hash_store( PHash h, const void *key, int keyLen, void *val) return true; } +Bool +hash_store_release( PHash h, const void *key, int keyLen, void *val) +{ + HE *he; + ksv_check; + if ( he) { + free( HeVAL(he) ); + HeVAL( he) = &PL_sv_undef; + (void) hv_delete_ent( h, ksv, G_DISCARD, 0); + } + he = hv_store_ent( h, ksv, &PL_sv_undef, 0); + HeVAL( he) = ( SV *) val; + return true; +} + void * hash_first_that( PHash h, PHashProc action, void * params, int * pKeyLen, void ** pKey) { @@ -110,6 +128,109 @@ hash_first_that( PHash h, PHashProc action, void * params, int * pKeyLen, void * return NULL; } +#define INIT_CACHE \ + if ( key_size > MAX_CACHE_KEY_LEN ) croak("cache key too big"); \ + k.type = type; \ + memcpy( k.data, key, key_size) + +#define LOOKUP prima_guts.cache, &k, sizeof(k.type) + key_size + +void +prima_cache_release( int type, void *key, unsigned int key_size) +{ + PrimaCacheKey k; + PrimaValueKey *v; + INIT_CACHE; + if (( v = (PrimaValueKey*) hash_fetch(LOOKUP)) == NULL) + return; + if ( v->refcnt > 0 ) + v->refcnt++; + if ( v-> refcnt == 0) + hash_delete( LOOKUP, true); +} + +PrimaValueKey* +prima_cache_get( int type, void *key, unsigned int key_size) +{ + PrimaCacheKey k; + INIT_CACHE; + return (PrimaValueKey*) hash_fetch(LOOKUP); +} + +void +prima_cache_delete( int type, void *key, unsigned int key_size) +{ + PrimaCacheKey k; + INIT_CACHE; + hash_delete(LOOKUP, true); +} + +void +prima_cache_set( int type, void *key, unsigned int key_size, void* value, unsigned int value_size) +{ + PrimaCacheKey k; + PrimaValueKey *v; + INIT_CACHE; + + if ( !( v = malloc(sizeof(PrimaValueKey) + value_size))) { + warn("not enough memory: %d bytes", value_size); + return; + } + + v->refcnt = 1; + v->size = value_size; + memcpy( v->data, value, value_size); + hash_store(LOOKUP, v); +} + +void +prima_cache_purge( int type, unsigned int max_entries) +{ +#define MAX_HE 1024 + HE **he_ptr, *he_buf[MAX_HE]; + unsigned int count = 0, n_he = 0; + + if (HvKEYS(prima_guts.cache) < max_entries) + return; + + if (max_entries > MAX_HE) { + if ( !( he_ptr = malloc(max_entries * sizeof(HE*)))) + return; + } else + he_ptr = (HE**) &he_buf; + + hv_iterinit(prima_guts.cache); + + for (;;) + { + HE *he; + PrimaCacheKey *key; + if (( he = hv_iternext( prima_guts.cache)) == NULL) + return; + key = (PrimaCacheKey*) HeKEY( he); + if ( key->type != type ) + continue; + count++; + he_buf[n_he++] = he; + } + + if ( count < max_entries ) { + if ( he_ptr != (HE**) &he_buf ) free( he_ptr ); + return; + } + + for ( count = 0; count < n_he; count++) { + HE *he = he_ptr[count]; + ksv_init; + sv_setpvn( ksv, ( char *) HeKEY(he), HeKLEN(he)); + free(HeVAL( he)); + HeVAL( he) = &PL_sv_undef; + (void) hv_delete_ent( prima_guts.cache, ksv, G_DISCARD, 0); + } + if ( he_ptr != (HE**) &he_buf ) free( he_ptr ); +#undef MAX_HE +} + #ifdef __cplusplus } #endif diff --git a/include/apricot.h b/include/apricot.h index 25e99973..2411f92d 100644 --- a/include/apricot.h +++ b/include/apricot.h @@ -523,6 +523,7 @@ typedef struct _PostMsg { #define hash_fetch_key prima_hash_fetch_key #define hash_delete prima_hash_delete #define hash_store prima_hash_store +#define hash_store_release prima_hash_store_release #define hash_count prima_hash_count #define hash_first_that prima_hash_first_that #endif @@ -549,6 +550,9 @@ prima_hash_delete( PHash self, const void *key, int keyLen, Bool kill); extern Bool prima_hash_store( PHash self, const void *key, int keyLen, void *val); +extern Bool +prima_hash_store_release( PHash self, const void *key, int keyLen, void *val); + #define prima_hash_count(hash) (HvKEYS(( HV*) hash)) extern void* diff --git a/include/guts.h b/include/guts.h index b6246dc2..bb2eac26 100644 --- a/include/guts.h +++ b/include/guts.h @@ -35,6 +35,7 @@ typedef struct { PAnyObject kill_chain; PAnyObject ghost_chain; Bool app_is_dead; + PHash cache; } PrimaGuts, *PPrimaGuts; #define P_APPLICATION PApplication(prima_guts.application) @@ -189,6 +190,39 @@ typedef struct { #define IMGDUP_H(d) d.dup #define IMGDUP_CALL(d,f,...) (CIcon(d.dup))->f(d.dup,__VA_ARGS__) +#define MAX_CACHE_KEY_LEN 128 +#pragma pack(1) +typedef struct +{ + int type; + Byte data[MAX_CACHE_KEY_LEN]; +} PrimaCacheKey; + +typedef struct +{ + unsigned int refcnt, size; + Byte data[0]; +} PrimaValueKey; +#pragma pack() + +#define prima_cache_lock(v) if (v) v->refcnt++ +#define prima_cache_unlock_only(v) if (v && v->refcnt > 0) v->refcnt-- + +void +prima_cache_release( int type, void *key, unsigned int key_size); + +PrimaValueKey* +prima_cache_get( int type, void *key, unsigned int key_size); + +void +prima_cache_set( int type, void *key, unsigned int key_size, void* value, unsigned int value_size); + +void +prima_cache_delete( int type, void *key, unsigned int key_size); + +void +prima_cache_purge( int type, unsigned int max_entries); + #ifdef __cplusplus } #endif