| /* $Id$ | |
| * | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to you under the Apache License, Version | |
| * 2.0 (the "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** | |
| * etchrun.c -- runtime data management. | |
| * the runtime cache database is where we cache object vtables etc. | |
| */ | |
| #include <stdio.h> | |
| #include "etch_global.h" | |
| #define ETCH_CACHE_USES_ALPHA_KEY | |
| #define CACHE_DEBUG FALSE | |
| /* | |
| * ETCH_CACHE_USES_ALPHA_KEY switches on alpha cache keys rather than the normal | |
| * 4-byte integer. experiencing a problem with either the hfirst or hnext | |
| * hashtable functionality, this was an attempt to determine if the 4-byte keys | |
| * were causing the problem. it appears not, so we can probably use int keys again. | |
| */ | |
| #ifdef ETCH_CACHE_USES_ALPHA_KEY | |
| #define ETCHCACHEKEYBUFLEN 20 | |
| char ckey[ETCHCACHEKEYBUFLEN]; | |
| /* | |
| * make_cache_key() | |
| * given 32-bit key constructs alpha key for the cache returning key byte length | |
| */ | |
| int make_cache_key(const unsigned int ikey) | |
| { | |
| char* pkey = ckey; | |
| /* memcpy(pkey+=4,"ca$h",4); */ | |
| _itoa_s(ikey, pkey, ETCHCACHEKEYBUFLEN, 10); | |
| return (int)strlen(ckey); | |
| } | |
| /* | |
| * cache_populate_out() | |
| * populate caller's out struct | |
| */ | |
| void cache_populate_out(etch_hashitem* useritem, etch_hashitem* curritem) | |
| { | |
| useritem->key = curritem->key; | |
| useritem->value = curritem->value; | |
| useritem->hash = curritem->hash; | |
| } | |
| /* | |
| * cache_findxl() | |
| */ | |
| void* cache_findxl(char* key, unsigned keylen, void** out) | |
| { | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| int result = jenkins_find | |
| (global_cache->realtable, key, keylen, 0, &founditem); | |
| #if CACHE_DEBUG | |
| #pragma warning(disable:4313) | |
| if (result == 0) | |
| printf("cache_found key %s len %d addr %08x\n", | |
| key, keylen, founditem->value); | |
| else printf("cache_notfound key %s len %d\n", key, keylen); | |
| #endif | |
| if (result == -1) return NULL; | |
| if (out) | |
| cache_populate_out(*out, founditem); | |
| return founditem->value; | |
| } | |
| /* | |
| * cache_find() | |
| * locate cached object with specified key, returning it or NULL | |
| */ | |
| void* cache_find(const unsigned int objtype, void** out) | |
| { | |
| unsigned keylen = make_cache_key(objtype); | |
| return cache_findxl(ckey, keylen, out); | |
| } | |
| /* | |
| * cache_findx() | |
| * locate cached object with specified ansi char key, returning it or NULL | |
| */ | |
| void* cache_findx(char* key, void** out) | |
| { | |
| unsigned keylen = (unsigned)strlen(key); | |
| return cache_findxl(key, keylen, out); | |
| } | |
| /* | |
| * cache_find_by_hash() | |
| * locate cached object with specified hashkey, returning it or NULL | |
| */ | |
| void* cache_find_by_hash(const unsigned hash, void** out) | |
| { | |
| int result; | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| result = jenkins_findh(global_cache->realtable, hash, 0, &founditem); | |
| if (result == -1) return NULL; | |
| if (out) | |
| cache_populate_out(*out, founditem); | |
| return founditem->value; | |
| } | |
| /* | |
| * cache_current() | |
| * return cached object at current position | |
| */ | |
| etch_hashitem* cache_current() | |
| { | |
| int result; | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| result = jenkins_current(global_cache->realtable, 0, &founditem); | |
| return (result == -1)? NULL: founditem; | |
| } | |
| /* | |
| * cache_delxl() | |
| * Remove specified object from cache given ansi char key and length. | |
| * Return pointer to cached object, which is not freed here. | |
| */ | |
| void* cache_delxl (char* key, const unsigned keylen) | |
| { | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| int result = jenkins_remove | |
| (global_cache->realtable, ckey, keylen, 0, &founditem); | |
| if (result == -1) return NULL; | |
| free(founditem->key); /* free 4-byte key allocated in cache_add() */ | |
| return founditem->value; | |
| } | |
| /* | |
| * cache_del() | |
| * Remove specified object from cache given integer key. | |
| * Return pointer to cached object, which is not freed here. | |
| */ | |
| void* cache_del (const unsigned int objtype) | |
| { | |
| unsigned keylen = make_cache_key(objtype); | |
| return cache_delxl(ckey, keylen); | |
| } | |
| /* | |
| * cache_delx() | |
| * Remove specified object from cache given ansi char key. | |
| * Return pointer to cached object, which is not freed here. | |
| */ | |
| void* cache_delx (char* key) | |
| { | |
| unsigned keylen = (unsigned)strlen(key); | |
| return cache_delxl(key, keylen); | |
| } | |
| /* | |
| * cache_insertxl() | |
| * Add specified object to cache given ansi char key and char length, | |
| * with existence test optional. | |
| * Return hash of supplied key, or zero. | |
| */ | |
| int cache_insertxl (char* key, const unsigned keylen, void* data, const int is_check) | |
| { | |
| int result = 0, keylent = 0; | |
| char* pkey = NULL; | |
| void* pval = NULL; | |
| etch_hashitem hashbucket; | |
| etch_hashitem* inserteditem = &hashbucket; | |
| memset(&hashbucket, 0, sizeof(etch_hashitem)); | |
| if (is_check) | |
| { pval = cache_findxl(key, keylen, &inserteditem); | |
| if (pval) return inserteditem->hash; /* entry exists */ | |
| } | |
| keylent = keylen + 1;/* add new entry */ | |
| pkey = malloc(keylent); | |
| strcpy_s(pkey, keylent, key); | |
| #if CACHE_DEBUG | |
| #pragma warning(disable:4313) | |
| printf("cache_insertxl key %s len %d addr %08x\n", pkey, keylen, data); | |
| #endif | |
| result = jenkins_insert | |
| (global_cache->realtable, pkey, keylen, data, 0, 0, &inserteditem); | |
| /* cache_dump(); */ | |
| return inserteditem->hash; | |
| } | |
| /* | |
| * cache_insert() | |
| * Add specified object to cache with existence test optional. | |
| * Return inserted item hash, or zero. | |
| */ | |
| int cache_insert (const unsigned int key, void* data, const int is_check) | |
| { | |
| int keylen = 0; | |
| void* pval = NULL; | |
| keylen = make_cache_key(key); | |
| return cache_insertxl(ckey, keylen, data, is_check); | |
| } | |
| /* | |
| * cache_insertx() | |
| * Add specified object to cache with existence test optional. | |
| * Return inserted item hash, or zero. | |
| */ | |
| int cache_insertx (char* key, void* data, const int is_check) | |
| { | |
| unsigned keylen = (unsigned)strlen(key); | |
| return cache_insertxl(key, keylen, data, is_check); | |
| } | |
| /* | |
| * cache_add() | |
| * Add specified object to cache given integer key. | |
| * Return 0 or -1. | |
| */ | |
| int cache_add (const unsigned int objtype, void* data) | |
| { | |
| return cache_insert (objtype, data, TRUE); | |
| } | |
| /* | |
| * cache_addx() | |
| * Add specified object to cache. | |
| * Return 0 or -1. | |
| */ | |
| int cache_addx(char* key, void* data) | |
| { | |
| return cache_insertx(key, data, TRUE); | |
| } | |
| #else // #ifdef ETCH_CACHE_USES_ALPHA_KEY | |
| /* | |
| * cache_find() | |
| * locate cached object with specified key, returning it or NULL | |
| */ | |
| void* cache_find(const unsigned int objtype) | |
| { | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| int result = jenkins_find | |
| (global_cache->realtable, (char*)&objtype, sizeof(int), 0, &founditem); | |
| return result == -1? NULL: founditem->value; | |
| } | |
| /* | |
| * cache_del() | |
| * Remove specified object from cache. | |
| * Return pointer to cached object, which is not freed here. | |
| */ | |
| void* cache_del (const unsigned int objtype) | |
| { | |
| etch_hashitem hashbucket; | |
| etch_hashitem* founditem = &hashbucket; | |
| int result = jenkins_remove | |
| (global_cache->realtable, (char*)&objtype, sizeof(int), 0, &founditem); | |
| if (result == -1) return NULL; | |
| free(founditem->key); /* free 4-byte key allocated in cache_add() */ | |
| return founditem->value; | |
| } | |
| * | |
| * cache_add() | |
| * Add specified object to cache. | |
| * Return pointer to item's data entry, which is not freed here. | |
| */ | |
| int cache_add (const unsigned int objtype, void* data) | |
| { | |
| int result = 0; | |
| char* pkey = 0; | |
| void* pval = cache_find(objtype); | |
| if (pval) return 0; /* entry exists */ | |
| pkey = malloc(sizeof(int)); /* add new entry */ | |
| memcpy(pkey, &objtype, sizeof(int)); | |
| result = jenkins_insert | |
| (global_cache->realtable, pkey, sizeof(int), data, 0, 0, 0); | |
| return result; | |
| } | |
| #endif // #ifdef ETCH_CACHE_USES_ALPHA_KEY | |
| /* | |
| * cache_create() | |
| * create the global cache as an untracked hashtable | |
| */ | |
| etch_hashtable* cache_create() | |
| { | |
| global_cache = new_systemhashtable(INITIAL_CACHE_SIZE_ITEMS); | |
| return global_cache; | |
| } | |
| /* | |
| * cache_clear() | |
| * remove all objects from the runtime list, freeing both key memory and | |
| * data object memory for each. Returns the count of buckets so cleared. | |
| */ | |
| int cache_clear() | |
| { | |
| int result = jenkins_clear(global_cache->realtable, TRUE, TRUE, 0, 0); | |
| return result; | |
| } | |
| /* | |
| * cache_destroy() | |
| * clear the cache and destroy the cache object | |
| */ | |
| int cache_destroy() | |
| { | |
| int result = cache_clear(); | |
| result = jenkins_destroy(global_cache->realtable, 0, 0); | |
| return result; | |
| } | |
| /* | |
| * cache_count() | |
| * return number of items in the runtime cache | |
| */ | |
| int cache_count() | |
| { | |
| return jenkins_count(global_cache->realtable, 0, 0); | |
| } | |
| #pragma warning(disable:4311) | |
| /* | |
| * cache_dump() | |
| * debug console display of pointers to each entry in the cache | |
| */ | |
| int cache_dump() | |
| { | |
| etch_hashitem hashbucket; | |
| etch_hashitem* myentry = &hashbucket; | |
| const int count = jenkins_count(global_cache->realtable, 0, 0); | |
| printf("\n%d entries in cache\n",count); | |
| if (count == 0) return 0; | |
| if (0 == jenkins_first(global_cache->realtable, NULL, &myentry)) | |
| printf("%08x: %08x\n", *(unsigned*)myentry->key, (unsigned)myentry->value); | |
| else printf("cache.first() failed\n"); | |
| while(0 == jenkins_next(global_cache->realtable, NULL, &myentry)) | |
| printf("%08x: %08x\n", *(unsigned*)myentry->key, (unsigned)myentry->value); | |
| return count; | |
| } | |
| /* | |
| * - - - - - old list cache code from here on - - - - - | |
| */ | |
| #ifdef ETCH_CACHE_USES_LINKEDLIST | |
| objrec* cache_find(const unsigned int objtype) | |
| { | |
| return objrec_find(objtype); | |
| } | |
| void* cache_get (const unsigned int objtype) | |
| { | |
| return objrec_get(objtype); | |
| } | |
| void* cache_del (const unsigned int objtype) | |
| { | |
| return objrec_del(objtype); | |
| } | |
| objrec* cache_add (const unsigned int objtype, void* data) | |
| { | |
| return objrec_add(objtype, data); | |
| } | |
| int cache_clear() | |
| { | |
| return objtable_clear(); | |
| } | |
| int cache_count() | |
| { | |
| return objtable_count(); | |
| } | |
| /* | |
| * objrec_find | |
| * look up an object in the runtable | |
| */ | |
| objrec* objrec_find(const unsigned int objtype) | |
| { | |
| objrec* p = runtable; | |
| for(; p; p = p->next) | |
| if (p->obj_type == objtype) | |
| break; | |
| return p; | |
| } | |
| /* | |
| * objrec_get | |
| * look up an object in the runtable, returning its data, or null. | |
| */ | |
| void* objrec_get(const unsigned int objtype) | |
| { | |
| objrec* p = objrec_find(objtype); | |
| return p? p->obj_impl: NULL; | |
| } | |
| #pragma warning (disable:4311) // todo debug stmts | |
| /* | |
| * objrec_add | |
| * if obj rec exists, return it, otherwise add a new obj rec and return it. | |
| */ | |
| objrec* objrec_add(const unsigned int objtype, void* data) | |
| { | |
| objrec* p = objrec_find(objtype); | |
| if (p) return p; | |
| /* we do not track allocations for the tracking hashtable | |
| * since the tracking table does of course not yet exist. */ | |
| p = is_memtable_instance? | |
| malloc(sizeof(objrec)): | |
| malloc(sizeof(objrec)); /* we are currently not tracking the cache at all */ | |
| /* malloc(sizeof(objrec), ETCHTYPEA_CACHEREC); */ | |
| p->obj_type = objtype; p->obj_impl = data; p->next = NULL; | |
| if (runtable == NULL) /* first entry? */ | |
| runtable = runtable_tail = p; | |
| else /* chain entry */ | |
| { assert(runtable_tail); | |
| runtable_tail->next = p; | |
| runtable_tail = p; /* added to end - new entry is new tail */ | |
| } | |
| /* printf("cache: add %08x\n",(unsigned)p); */ | |
| return p; | |
| } | |
| /* | |
| * objrec_del | |
| * Remove specified object record from list and free its bucket memory. | |
| * Return pointer to item's data entry, which is not freed here. | |
| */ | |
| void* objrec_del(const unsigned int objtype) | |
| { | |
| void* data = NULL; | |
| objrec* p = runtable, *prior = NULL; | |
| for(; p; p = p->next) | |
| { | |
| if (p->obj_type != objtype) | |
| { prior = p; /* not found yet */ | |
| continue; | |
| } | |
| if (prior) /* found: chain around this entry */ | |
| prior->next = p->next; | |
| /* if last entry, adjust tail ptr */ | |
| if (runtable_tail == p) | |
| runtable_tail = prior; | |
| /* if first entry, adjust head ptr */ | |
| if (runtable == p) | |
| runtable = prior; | |
| /* save off data, free the bucket */ | |
| data = p->obj_impl; | |
| free(p); | |
| /* printf("cache: del %08x\n",(unsigned)p); */ | |
| break; | |
| } | |
| return data; | |
| } | |
| /* | |
| * objtable_count | |
| * returns the current count of cache buckets. | |
| */ | |
| int objtable_count() | |
| { | |
| objrec *p = NULL; | |
| int count = 0; | |
| for(p = runtable; p; p = p->next, count++); | |
| return count; | |
| } | |
| /* | |
| * objtable_clear | |
| * remove all objects from the runtime list, freeing bucket memory and | |
| * data memory for each. Returns the count of buckets so cleared. | |
| */ | |
| int objtable_clear() | |
| { | |
| objrec *p = NULL, *nextp = NULL; | |
| int count = 0; | |
| for(p = runtable; p; p = nextp, count++) | |
| { | |
| /* note that we must be careful to not clear the cache before we | |
| are done accessing object vtables */ | |
| free(p->obj_impl); /* free data memory */ | |
| nextp = p->next; | |
| /* printf("cache: del %08x\n",(unsigned)p); */ | |
| free(p); /* free bucket memory */ | |
| } | |
| /* printf("cache: %d items cleared\n",count); */ | |
| runtable = runtable_tail = NULL; | |
| return count; | |
| } | |
| #endif // #ifdef ETCH_CACHE_USES_LINKEDLIST | |