You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by jd...@apache.org on 2009/04/22 19:25:51 UTC
svn commit: r767594 [18/43] - in /incubator/etch/trunk/binding-c/runtime/c:
./ ext/ ext/hashtab/ ext/lib/ inc/ lib/ project/ project/$etchstop/
project/bin/ project/etch/ project/logcli/ project/logsrv/ project/notes/
project/test/ project/test/logcli/...
Added: incubator/etch/trunk/binding-c/runtime/c/src/common/etchhash.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/common/etchhash.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/common/etchhash.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/common/etchhash.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,1008 @@
+/* $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.
+ */
+
+/*
+ hashtable.c -- implementation of an underlying hashtable.
+ provided herein are implementations for the jenkins hashtable APIs.
+*/
+
+#include "etchhash.h"
+#include "etchmem.h"
+#include "etchrun.h"
+#include "etchmutex.h"
+#include "jenkhtab.h"
+#include "jenklook.h"
+
+
+/**
+ * implementation of etch_hashtable.insert() for the jenkins hashtable.
+ * key and data are pointers to memory owned by the caller. The hashtable does
+ * not make copies of this data. The caller is responsible for freeing said
+ * memory, however note that etch_hashtable.clear() frees this memory.
+ * key cannot be null but data can be null.
+ * datalen parameter is ignored for this implementation.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the inserted item.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_insert(void* realtable, void* key, const int keylen,
+ void* data, const int datalen, void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !key || !keylen) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == hadd((htab*)realtable, key, keylen, data)) /* jenkhash.lib */
+ result = -1; /* key already exists most likely */
+ else
+ if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.inserth() for the jenkins hashtable.
+ * key and data are pointers to memory owned by the caller. the hashtable does
+ * not make copies of this data. the caller is responsible for freeing said
+ * memory, however note that etch_hashtable.clear() frees this memory.
+ * key cannot be null but data can be null.
+ * key object is expected to contain its hashkey value in its first 4 bytes.
+ * jenkins will use this value, rather than compute a hash value.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the inserted item.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_inserth(void* realtable, void* key, void* data, void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !key) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == haddx((htab*)realtable, key, data)) /* jenkhash.lib */
+ result = -1; /* key already exists most likely */
+ else
+ if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.find() for the jenkins hashtable.
+ * moves the current position on the underlying table to that of supplied key.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the located item's key and value.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_find(void* realtable, void* key, const int keylen,
+ void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !key || !keylen) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == hfind((htab*)realtable, (unsigned char*)key, keylen))
+ result = -1;
+ else
+ if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.findh() for the jenkins hashtable.
+ * Implements a find by hashed key, otherwise see comments for find().
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_findh(void* realtable, const unsigned int hashed,
+ void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !hashed) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == hfindx((htab*)realtable, hashed))
+ result = -1;
+ else
+ if (out)
+ { char* tkey = hkey ((htab*)realtable);
+ void* data = hstuff((htab*)realtable);
+ etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.first() for the jenkins hashtable.
+ * moves the current position on the underlying table to that of the first item.
+ * If out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the located item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating bad params or an empty table.
+ * @note this method is NOT SYNCHRONIZED, since in most or all cases it is invoked
+ * only during iteration of the map, and in that case the map is locked explicitly
+ * by the caller (hashtable_setlock()), and the mutex may not support nesting.
+ * if there is a need to synch it otherwise, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, map->synclock);
+ * map->first(...);
+ * map->synchook(ETCH_SYNC_REL_, map->synclock);
+ */
+int jenkins_first(void* realtable, void* in, void** out)
+{
+ int result = 0;
+
+ if (FALSE == hfirst((htab*)realtable))
+ result = -1; /* table is empty most likely */
+ else
+ if (out)
+ { char* tkey = hkey ((htab*)realtable);
+ void* data = hstuff((htab*)realtable);
+ etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.next() for the jenkins hashtable.
+ * Moves the current position on the underlying table to that of the next item
+ * in the table. If out is non_null, *out is assumed to point at a etch_hashitem,
+ * and this struct is populated with pointers to the located item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating either bad params, or that
+ * there are no more entries, in which case the current position will have wrapped
+ * to the first item, if any entries in fact still remain.
+ * @note this method is NOT SYNCHRONIZED, since in most or all cases it is invoked
+ * only during iteration of the map, and in that case the map is locked explicitly
+ * by the caller (hashtable_setlock()), and the mutex may not support nesting.
+ * if there is a need to synch it otherwise, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, map->synclock);
+ * map->next(...);
+ * map->synchook(ETCH_SYNC_REL_, map->synclock);
+ */
+int jenkins_next(void* realtable, void* in, void** out)
+{
+ int is_next_found = 0, is_table_empty = 0;
+ char* tkey = NULL;
+ void* data = NULL;
+ if (!realtable) return -1;
+
+ /* hnext returns 1 if there is a next entry, or 0 if there is no next entry
+ * and the position has wrapped to the beginning of the table. However if
+ * the table is now empty, there is no current position, and so we test for
+ * that condition before attempting to reference the current position.
+ */
+ is_next_found = hnext((htab*)realtable); /* jenkhash.h */
+ is_table_empty = NULL == ((htab*)realtable)->ipos;
+
+ if (out) /* return data at current position if requested */
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+
+ if (!is_table_empty)
+ { tkey = hkey ((htab*)realtable);
+ data = hstuff((htab*)realtable);
+ }
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ return is_next_found? 0: -1;
+}
+
+
+/**
+ * implementation of etch_hashtable.current() for the jenkins hashtable.
+ * retrieves data for the entry at the current hashtable position.
+ * *out is assumed to point at a etch_hashitem struct; this struct is populated
+ * with pointers to the current item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating bad params or an empty table.
+ * @note this method is NOT SYNCHRONIZED, since it is not meaningful for a shared
+ * hashtable (current position will change with every operation).
+ * if there is a need to synch it, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, map->synclock);
+ * map->current(...);
+ * map->synchook(ETCH_SYNC_REL_, map->synclock);
+ */
+int jenkins_current(void* realtable, void* in, void** out)
+{
+ unsigned hash = 0;
+ char* tkey = NULL;
+ void* data = NULL;
+ etch_hashitem* outentry = NULL;
+ if (!realtable || !out || !((htab*)realtable)->count) return -1;
+
+ tkey = hkey ((htab*)realtable);
+ data = hstuff((htab*)realtable);
+ hash =((htab*)realtable)->ipos->hval;
+
+ outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ outentry->hash = hash;
+
+ return tkey? 0: -1;
+}
+
+
+/**
+ * Implementation of etch_hashtable.remove() for the jenkins hashtable.
+ * Frees the entry for the key supplied , but neither the key nor the value,
+ * are freed, recall that neither of these is a copy but are instead pointers.
+ * To actually free memory for these items, pass etch_hashitem** &out),
+ * and you can then free(out->key), and free(out->value), at your leisure.
+ * Result is zero if OK, otherwise -1;
+ */
+int jenkins_remove(void* realtable, void* key, const int keylen,
+ void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !key || !keylen) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == hfind((htab*)realtable, key, keylen)) /* locate entry */
+ result = -1; /* key nonexistent most likely */
+ else
+ { if (out) /* save off the entry contents if requested */
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ hdel((htab*)realtable); /* delete entry at current position */
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+/**
+ * jenkins_removeh()
+ */
+int jenkins_removeh(void* realtable, const unsigned key, void* in, void** out)
+{
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable || !key) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (FALSE == hfindx((htab*)realtable, key)) /* locate entry */
+ result = -1;
+ else
+ { if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ }
+
+ hdel((htab*)realtable); /* delete entry at current position */
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return result;
+}
+
+
+
+/**
+ * implementation of etch_hashtable.clear() for the jenkins hashtable.
+ * empties the table and, if requested, frees memory for keys/and/or values.
+ * out parameter is ignored for this implementation.
+ * Result is count of table entries freed, or -1 if error.
+ * Use the freeuser parameter with care. recall that the hashtable stores
+ * pointers to keys and data, plus key length. If user did not allocate each
+ * key separately then setting freeuser would cause a crash. for example,
+ * if I used a vector of keys and a vector of key lengths; setting freeuser
+ * would ask to free (key length) bytes at some vector offset, obviously
+ * an error. also, currently freeuser does not check the memtable, so if
+ * allocations are being tracked, freeuser should not be used. we could
+ * change this code to query the memtable first, or alternatively, change
+ * etch_free to not complain about allegedly missing memtable entries.
+ */
+int jenkins_clear (void* realtable, const int freekey, const int freeval,
+ void* in, void** out)
+{
+ int freecount = 0, currcount = 0, freehandled = 0;
+ int is_static_keys = 0, is_static_values = 0, is_etch_free = 0;
+ mapcallback callback = NULL;
+ char* key, *value;
+ htab* jenktable;
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!(jenktable = (htab*)realtable)) return -1;
+
+ if (sync) /* acquire lock */
+ sync(ETCH_SYNC_SET_, ht->synclock);
+
+ if (ht)
+ { is_static_keys = ht->is_readonly_keys;
+ is_static_values = ht->is_readonly_values;
+ is_etch_free = ht->is_tracked_memory;
+ callback = ht->freehook;
+ }
+
+ while (0 < (currcount = hcount(jenktable)))
+ {
+ key = hkey(jenktable); /* free entry's key if asked */
+ value = hstuff(jenktable);
+ /* optional callback to handle free */
+ freehandled = callback? callback(key, value): FALSE;
+
+ if (freekey && !is_static_keys && !freehandled)
+ if (is_etch_free)
+ etch_free(key);
+ else free(key);
+
+ if (freeval && !is_static_values && !freehandled)
+ if (is_etch_free)
+ etch_free(value);
+ else free(value);
+
+ hdel(jenktable); /* free current table slot */
+ freecount++;
+ }
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return freecount; /* return count of items freed */
+}
+
+
+/**
+ * implementation of etch_hashtable.count() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current number of table entries, or -1 if error.
+ */
+int jenkins_count(void* realtable, void* in, void** out)
+{
+ const int count = realtable? ((htab*)realtable)->count: -1;
+ return count;
+}
+
+
+/**
+ * implementation of etch_hashtable.size() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current maximum number of table entries, or -1 if error.
+ */
+int jenkins_size(void* realtable, void* in, void** out)
+{
+ const int count = realtable? 1 << ((htab*)realtable)->logsize: -1;
+ return count;
+}
+
+
+/**
+ * implementation of etch_hashtable.stats() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current maximum number of table entries, or -1 if error.
+ */
+int jenkins_stats(void* realtable, void* in, void** out)
+{
+ if (realtable) hstat((htab*)realtable);
+ return realtable? 0: -1;
+}
+
+
+/**
+ * jenkins_hash
+ * implementation of etch_hashtable.hash() for the jenkins hashtable.
+ * priorhash is result of the previous operation, or any arbitrary value.
+ * in and out parameters are ignored for this implementation. result is a
+ * hash of the supplied key, as used by the jenkins hashtable, or zero
+ * if parameters were in error.
+ * author's comments: If you need less than 32 bits, use a bitmask.
+ * for example, if you need only 10 bits, do h = (h & hashmask(10)),
+ * in which case, the hash table should have hashsize(10) elements.
+ * if you are hashing n strings (unsigned char**)k, do it like this:
+ * for (i=0, h=0; i < n; ++i) h = lookup(k[i], len[i], h);
+ */
+int jenkins_hash(void* realtable, char* key, const int keylen,
+ const int priorhash, void* in, void** out)
+{
+ if (!realtable || !key || keylen < 1 || keylen > MAX_ETCHHASHKEY) return 0;
+ return lookup(key, keylen, priorhash);
+}
+
+
+/**
+ * jenkins_hashx
+ * see comments at jenkins_hash
+ */
+int jenkins_hashx(char* key, const int keylen, const int priorhash)
+{
+ if (!key || keylen < 1 || keylen > MAX_ETCHHASHKEY) return 0;
+ return lookup(key, keylen, priorhash);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * constructors, destructors
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * constructor for a hashtable implementation. implements iterable.
+ * creates the underlying hashtable and returns a populated etch_hashtable
+ * interface to it. initialsize is the number of items the hashtable can hold
+ * before it reallocates itself. note that this value may be altered by the
+ * implementation, e.g. if it is out of range or if it is not a power of 2.
+ */
+etch_hashtable* new_hashtable(const int initialsize)
+{
+ etch_hashtable* hashtable = ctor_jenkins_hashtable(initialsize);
+
+ new_iterable(&hashtable->iterable, NULL, hashtable_iterable_first,
+ hashtable_iterable_next, hashtable_iterable_has_next);
+
+ hashtable->is_readonly_keys = HASHTABLE_DEFAULT_READONLY_KEYS;
+ hashtable->is_readonly_values = HASHTABLE_DEFAULT_READONLY_VALUES;
+ hashtable->is_tracked_memory = HASHTABLE_DEFAULT_TRACKED_MEMORY;
+ hashtable->content_type = HASHTABLE_DEFAULT_CONTENT_TYPE;
+
+ return hashtable;
+}
+
+
+/**
+ * constructor for etch_set, which is a hashtable containing object keys
+ * and null values
+ */
+etch_set* new_set (const int initialsize)
+{
+ etch_set* newset = new_hashtable(initialsize);
+ newset->content_type = ETCHHASHTABLE_CONTENT_OBJECT_NONE;
+ newset->is_readonly_values = TRUE;
+ return newset;
+}
+
+
+/**
+ * constructor for the etch_hashtable interface. constructs and initializes
+ * the interface shell, but not the underlying hashtable.
+ */
+etch_hashtable* new_etch_hashtable()
+{
+ etch_hashtable* newmap = 0;
+ /* we do not track the allocation of the tracking hashtable
+ * since the tracking table of course does not yet exist.
+ */
+ if (is_memtable_instance)
+ newmap = malloc(sizeof(etch_hashtable));
+ else newmap = etch_malloc(sizeof(etch_hashtable), ETCHTYPEB_HASHTABLE);
+
+ memset(newmap, 0, sizeof(etch_hashtable));
+ newmap->obj_type = ETCHTYPEB_HASHTABLE;
+ newmap->class_id = CLASSID_HASHTABLE;
+ newmap->get_hashkey = defgethashkey;
+ newmap->get_hashkey((objmask*)newmap);
+ return newmap;
+}
+
+
+/**
+ * destructor for a hashtable implementation.
+ * is_free_keys parameter asks that memory pointed to by hashbucket keys be freed.
+ * Use this with care, since this obviously requires that you have malloc'ed keys
+ * individually, and did not, for example, hash your keys out of a memory vector,
+ * static variables, or the like. is_free_values likewise for the table content.
+ * The readonly flags set on the hashtable take precedence over either.
+ * Use of either of course means your pointers to content will now be dangling.
+ */
+int destroy_hashtable(etch_hashtable* map, const int is_free_keys, const int is_free_values)
+{
+ int is_free_keymem = 0, is_free_valmem = 0;
+ if (NULL == map) return -1;
+
+ if (map->refcount > 0) /* if object is refcounted */
+ if (--map->refcount > 0) /* destroy only if last ref */
+ return -1;
+
+ if (map->synchook)
+ if (0 != map->synchook(ETCH_SYNC_TRY_, map->synclock))
+ return -1; /* disallow multiple destroy */
+
+ if (!is_etchobj_static_content(map))
+ {
+ is_free_keymem = !map->is_readonly_keys && is_free_keys;
+ is_free_valmem = !map->is_readonly_values && is_free_values;
+
+ /* free all buckets, and also contents only if is_free_contents is set */
+ if (map->realtable && map->vtab && !is_etchobj_static_content(map))
+ {
+ map->vtab->clear(map->realtable, is_free_keymem, is_free_valmem, map, 0);
+ map->vtab->hdestroy(map->realtable, map, 0);
+ }
+
+ if (map->synchook) /* release lock */
+ {
+ map->synchook(ETCH_SYNC_REL_, map->synclock);
+ if (!is_etchobj_static_content(map))
+ map->synchook(ETCH_SYNC_DEL_, map->synclock);
+ }
+ }
+
+ if (!is_etchobj_static_shell(map))
+ if (is_memtable_instance) /* free the etch_hashtable object */
+ free(map);
+ else etch_free(map);
+
+ return 0;
+}
+
+
+/**
+ * implementation of etch_hashtable.destroy() for the jenkins hashtable.
+ * destroys the table, but not the memory allocated for the individual item
+ * keys and values. use clear(), not destroy(), for that purpose.
+ * out parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_destroy(void* realtable, void* in, void** out)
+{
+ etch_hashtable* ht = (etch_hashtable*) in;
+ mapcallback sync = ht && ht->synchook && ht->synclock? ht->synchook: NULL;
+ if (!realtable) return -1;
+
+ if (sync)
+ if (0 != sync(ETCH_SYNC_TRY_, ht->synclock))
+ return -1; /* disallow multiple destroy */
+
+ if (((htab*)realtable)->table)
+ hdestroy((htab*) realtable); /* jenkhash.lib */
+
+ if (sync) /* release lock */
+ sync(ETCH_SYNC_REL_, ht->synclock);
+
+ return 0;
+}
+
+
+/**
+ * implementation of etch_hashtable.create() for the jenkins hashtable.
+ * this is the constructor for the underlying hashtable.
+ * we receive initial size in items and convert this to bit width.
+ * If initial size supplied is not a power of 2 we make it so.
+ * jenkins takes a log2 value as size, e.g. size 6 means size is 6 bits wide = 64.
+ * returns in *out, a pointer to a jenkins htab struct describing the underlying table.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_create(const int initialsize_items, void* in, void** out)
+{
+ htab* hashtable = NULL;
+ int initialsize_bits_plus1 = 0, initialsize, divby2;
+ if (out == NULL) return -1;
+
+ if (initialsize_items <= 0)
+ initialsize = ETCH_DEFAULT_HASHTABLE_SIZE;
+ else
+ if (initialsize_items < MIN_INITIAL_HASHTABLE_SIZE)
+ initialsize = MIN_INITIAL_HASHTABLE_SIZE;
+ else initialsize = initialsize_items;
+
+ if (initialsize > MAX_INITIAL_HASHTABLE_SIZE)
+ initialsize = MAX_INITIAL_HASHTABLE_SIZE;
+
+ for (divby2 = initialsize; divby2; divby2 >>= 1)
+ initialsize_bits_plus1++;
+
+ hashtable = hcreate(initialsize_bits_plus1 - 1); /* jenkhash.lib */
+
+ *out = hashtable;
+ return hashtable? 0: -1;
+}
+
+
+/**
+ * destroy_hashtable_default()
+ * default destructor for jenkins hashtable
+ */
+int destroy_hashtable_default(etch_hashtable* map)
+{
+ destroy_hashtable(map, !map->is_readonly_keys, !map->is_readonly_values);
+ return 0;
+}
+
+
+/**
+ * clone_hashtable_default()
+ * default copy constructor for jenkins hashtable.
+ * we won't do an implementation at this level, since we would need to also clone
+ * content, and only the instantiator knows key/value sizes
+ */
+etch_hashtable* clone_hashtable_default(etch_hashtable* map)
+{
+ return NULL;
+}
+
+
+/**
+ * ctor_jenkins_hashtable()
+ * constructor for jenkins hashtable interface.
+ * populates interface implementation methods and creates the underlying hashtable.
+ * returns pointer to etch_hashtable, or NULL if table could not be created.
+ */
+etch_hashtable* ctor_jenkins_hashtable(const int initialsize_items)
+{
+ htab* jenkins_hashtable = NULL;
+ etch_hashtable* hashtable = NULL;
+ i_etch_hashtable* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_HASHTABLE_VTAB;
+ int result = 0, is_just_cached = FALSE;
+
+ hashtable = new_etch_hashtable();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(hashtable->vtab, sizeof(i_etch_hashtable), CLASS_ID);
+ vtab->create = jenkins_create;
+ vtab->hdestroy = jenkins_destroy;
+ vtab->insert = jenkins_insert;
+ vtab->inserth = jenkins_inserth;
+ vtab->find = jenkins_find;
+ vtab->findh = jenkins_findh;
+ vtab->first = jenkins_first;
+ vtab->next = jenkins_next;
+ vtab->current = jenkins_current;
+ vtab->remove = jenkins_remove;
+ vtab->removeh = jenkins_removeh;
+ vtab->clear = jenkins_clear;
+ vtab->count = jenkins_count;
+ vtab->size = jenkins_size;
+ vtab->stats = jenkins_stats;
+
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ is_just_cached = TRUE;
+ }
+
+ hashtable->vtab = vtab;
+
+ /* create the underlying real hashtable */
+ result = vtab->create(initialsize_items, NULL, &jenkins_hashtable);
+ hashtable->realtable = jenkins_hashtable;
+
+ hashtable->destroy = destroy_hashtable_default;
+ hashtable->clone = clone_hashtable_default;
+
+ if (result == -1)
+ { if (is_just_cached)
+ cache_del(CLASS_ID);
+ hashtable->destroy(hashtable);
+ }
+
+ return hashtable;
+}
+
+
+/*
+ * new_systemhashtable()
+ * for such a hashtable we will not use the vtab interface
+ * but rather will call the implementation methods directly.
+ */
+etch_hashtable* new_systemhashtable(const int initialsize_items)
+{
+ int result = 0;
+ htab* jenkins_hashtable = NULL;
+ etch_hashtable* hashtable = 0;
+
+ hashtable = malloc(sizeof(etch_hashtable));
+ memset(hashtable, 0, sizeof(etch_hashtable));
+
+ result = jenkins_create(initialsize_items, NULL, &jenkins_hashtable);
+ hashtable->realtable = jenkins_hashtable;
+
+ if (result == 0)
+ new_iterable(&hashtable->iterable, NULL, hashtable_iterable_first,
+ hashtable_iterable_next, hashtable_iterable_has_next);
+
+ else /* some problem creating hashtable */
+ {
+ delete_systemhashtable(hashtable);
+ hashtable = NULL;
+ }
+
+ return hashtable;
+}
+
+
+/*
+ * delete_systemhashtable()
+ * delete an untracked hashtable
+ */
+void delete_systemhashtable(etch_hashtable* hashtable)
+{
+ if (!hashtable) return;
+ jenkins_destroy(hashtable->realtable, NULL, NULL);
+ free(hashtable);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * hashtable synchronization
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * hashtable_defsynchook()
+ * hashtable synchronization hook usable for most purposes.
+ * to enable synchronization, set map.synchook to this function,
+ * and set map.synclock to an instantiated mutex.
+ */
+int hashtable_defsynchook(void* action, void* mutex)
+{
+ /* all the casting is to quash pointer cast warnings */
+ return etchmutex_hookproc((int)(((size_t) action) & 0xf), mutex);
+}
+
+
+/*
+ * hashtable_getlock()
+ * explicitly set this map's synchronization lock, waiting if unavailable.
+ * this should be used only for locking a map prior to iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_getlock (etch_hashtable* map)
+{
+ ETCH_ASSERT(map && map->synchook && map->synclock);
+ return map->synchook(ETCH_SYNC_SET_, map->synclock);
+}
+
+
+/*
+ * hashtable_trylock()
+ * explicitly set this map's synchronization lock, failing if unavailable.
+ * this should be used only for locking a map prior to iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_trylock (etch_hashtable* map)
+{
+ ETCH_ASSERT(map && map->synchook && map->synclock);
+ return map->synchook(ETCH_SYNC_TRY_, map->synclock);
+}
+
+
+/*
+ * hashtable_rellock()
+ * release explicitly set this map's synchronization lock.
+ * this should be used only for unlocking a map after iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_rellock (etch_hashtable* map)
+{
+ ETCH_ASSERT(map && map->synchook && map->synclock);
+ return map->synchook(ETCH_SYNC_REL_, map->synclock);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * i_iterable implementations
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * hashtable_iterable_first()
+ * i_iterable first() implementation
+ */
+int hashtable_iterable_first(etch_iterator* iter)
+{
+ etch_hashtable* hash = NULL;
+ etch_hashitem hashbucket, *outentry = &hashbucket;
+ if (!iter || !iter->collection) return -1;
+ iter->current_key = iter->current_value = NULL;
+ hash = iter->collection;
+
+ if (-1 == hash->vtab->first(hash->realtable, 0, &outentry))
+ return -1;
+
+ iter->current_key = outentry->key;
+ iter->current_value = outentry->value;
+ iter->ordinal++;
+ return 0;
+}
+
+
+/*
+ * hashtable_iterable_next()
+ * i_iterable next() implementation
+ * functions as first() if there is no current position.
+ */
+int hashtable_iterable_next(etch_iterator* iter)
+{
+ etch_hashtable* hash = NULL;
+ etch_hashitem hashbucket, *outentry = &hashbucket;
+ if (!iter || !iter->collection || !iter->ordinal) return -1;
+ iter->current_key = iter->current_value = NULL;
+ hash = iter->collection;
+
+ if (-1 == hash->vtab->next(hash->realtable, 0, &outentry))
+ return -1;
+
+ iter->current_key = outentry->key;
+ iter->current_value = outentry->value;
+ iter->ordinal++;
+ return 0;
+}
+
+
+/*
+ * hashtable_iterable_has_next()
+ * i_iterable has_next() implementation.
+ */
+int hashtable_iterable_has_next(etch_iterator* iter)
+{
+ return iter && iter->collection && iter->current_key && iter->ordinal;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * clear() callbacks
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * string_to_object_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a string-to-etch_object hashtable.
+ * handlers return FALSE to indicate memory management NOT handled,
+ */
+int string_to_object_clear_handler (wchar_t* key, objmask* value)
+{
+ etch_free(key); /* free string key */
+ if (value) value->destroy(value); /* free etch object value */
+ return TRUE;
+}
+
+
+/*
+ * object_to_object_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a objmask-to-objmask hashtable.
+ * handlers return FALSE to indicate memory management NOT handled.
+ */
+int object_to_object_clear_handler (objmask* key, objmask* value)
+{
+ if (key) key->destroy(key); /* free etch object key */
+ if (value) value->destroy(value); /* free etch object value */
+ return TRUE;
+}
+
+
+/*
+ * etch_noop_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a objmask-to-objmask hashtable.
+ */
+int etch_noop_clear_handler (objmask* key, objmask* value)
+{
+ return TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * etch_map, etch_set
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * new_etch_map()
+ * an etch_map is a hashtable having object keys and object values.
+ * an object key should of course have its hashkey pre-computed appropriately.
+ * if instantiator wants to use non-disposable objects as keys and/or values,
+ * is_readonly_keys and/or is_readonly_values should be set, and the freehook
+ * callback overridden. furthermore if caller chooses to use the same object
+ * as both key and value, similar steps should be taken to ensure that code
+ * does not try to destroy both key and value.
+ */
+etch_hashtable* new_etch_map(const int initialsize)
+{
+ etch_hashtable* newmap = new_hashtable(initialsize);
+ newmap->content_type = ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
+ newmap->freehook = object_to_object_clear_handler;
+ newmap->is_readonly_keys = newmap->is_readonly_values = FALSE;
+ return newmap;
+}
+
+
+/*
+ * new_etch_set()
+ * an etch_set is a hashtable having object keys and null values.
+ */
+etch_hashtable* new_etch_set(const int initialsize)
+{
+ etch_hashtable* newset = new_hashtable(initialsize);
+ newset->class_id = CLASSID_ETCH_SET; /* serializer will expect this */
+ newset->content_type = ETCHHASHTABLE_CONTENT_OBJECT_NONE;
+ newset->freehook = object_to_object_clear_handler;
+ newset->is_readonly_keys = FALSE;
+ newset->is_readonly_values = TRUE;
+ return newset;
+}
+
+
Added: incubator/etch/trunk/binding-c/runtime/c/src/common/etchhashfunc.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/common/etchhashfunc.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/common/etchhashfunc.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/common/etchhashfunc.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,48 @@
+/* $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.
+ */
+
+/*
+ * hashfunc.c -- implementation of a hash function.
+ * we use the hash function from the jenkins hashtable.
+ */
+
+#include "etch.h"
+#include "jenklook.h"
+
+
+/**
+ * etchhash -- global method to hash an arbitrary byte string to 32 bits.
+ * note that keylen is the key byte length, not string length, as these of
+ * course differ for unicode.
+ * priorhash is result of the previous operation, or any arbitrary value --
+ * see jenkin's comments below for an example.
+ * returns hash of the supplied key, as used by the jenkins hashtable,
+ * or zero if parameters were in error.
+ *
+ * jenkins' comments: if you need less than 32 bits, use a bitmask.
+ * for example, if you need only 10 bits, do h = (h & hashmask(10)),
+ * in which case, the hash table should have hashsize(10) elements.
+ * if you are hashing n strings (unsigned char**)k, do it like this:
+ * for (i=0, h=0; i < n; ++i) h = lookup(k[i], len[i], h);
+*/
+unsigned etchhash(const void* pkey, const int keylen, const unsigned priorhash)
+{ /* jenkhash.lib */
+ if (!pkey || keylen < 1 || keylen > MAX_ETCHHASHKEY) return 0;
+ return lookup((ub1*)pkey, keylen, priorhash);
+}
+
Added: incubator/etch/trunk/binding-c/runtime/c/src/common/etchlog.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/common/etchlog.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/common/etchlog.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/common/etchlog.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,498 @@
+/* $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.
+ */
+
+/*
+ * etchlog.c
+ * etch c binding logger
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <time.h>
+#include <direct.h>
+#include <sys/stat.h>
+
+#if IS_WINDOWS_ETCH
+#pragma warning(disable:4996) /* potentially unsafe function warning */
+#endif
+
+#include "etchthread.h" /* for mutex */
+#include "etchlog.h"
+#include "etchutl.h"
+
+void etchlog_timestamp (char* buf, const char* component, etchlog_level lvl);
+void etchlogw_timestamp(wchar_t* buf, const wchar_t* component, etchlog_level lvl);
+int etchlog_copy_logfile(const int);
+#define ETCHLOG_STACKBUFSIZE 256
+#define ETCHLOG_PREAMBLESIZE 19
+#define ETCHLOG_STAMPLEN 12
+#define ETCHLOG_CBUFLEN 4
+char* ETCHLOGX = "LOGX";
+
+char* etchlog_level_str[] =
+{
+ "X","D","I","W","E"
+};
+
+wchar_t* etchlogw_level_str[] =
+{
+ L"X",L"D",L"I",L"W",L"E"
+};
+
+char etchlogbuf[ETCHLOG_STACKBUFSIZE];
+int etch_loglines, etch_logfiles;
+int is_etch_logfile_open, is_etchlog_shutdown, is_etchlog_suspended;
+FILE* flog;
+char* logpath;
+#define ETCHCLIENTLOGDIR "../logcli"
+#define ETCHCLIENTLOGPATH "../logcli/etchclient.log"
+#define ETCHSERVERLOGDIR "../logsrv"
+#define ETCHSERVERLOGPATH "../logsrv/etchserver.log"
+#define IS_ETCH_LOG_FILE TRUE
+#define IS_ETCH_LOG_CONSOLE TRUE
+
+
+/**
+ * etchlog_open()
+ * log file open.
+ */
+int etchlog_open (const int is_client)
+{
+ return is_client? etchlog_open_client(): etchlog_open_server();
+}
+
+
+/**
+ * etchlog_open_server()
+ * log file open.
+ */
+int etchlog_open_server()
+{
+ config.calculated_is_client = FALSE;
+ return etchlog_openx(ETCHSERVERLOGPATH);
+}
+
+
+/**
+ * etchlog_open_client()
+ * client log file lazy open.
+ */
+int etchlog_open_client()
+{
+ config.calculated_is_client = TRUE;
+ return etchlog_openx(ETCHCLIENTLOGPATH);
+}
+
+
+/**
+ * etchlog_openx()
+ * log file lazy open.
+ * note that the etch .exe directory will be different when the etch is started
+ * in the debugger, than when it is started from the command line, and so the log
+ * directories, being relative to the .exe directory, will change accordingly.
+ */
+int etchlog_openx (char* filepath)
+{
+ int result = 0;
+
+ if (is_etch_logfile_open);
+ else
+ if (is_etchlog_shutdown)
+ result = -1;
+ else
+ { const char* dirpath = config.calculated_is_client? ETCHCLIENTLOGDIR: ETCHSERVERLOGDIR;
+ mkdir (dirpath); /* create the log directory if it does not already exist */
+ logpath = filepath;
+
+ #if(0) /* code to identify effective directory */
+ { const char* t = "it works!", *fn = "etchtest.txt";
+ FILE* f = fopen(fn,"w"); size_t n = fwrite(t,1,strlen(t),f); fclose(f);
+ printf("*** etchlog_openx wrote %d to %s\n", n, fn);
+ }
+ #endif
+
+ etchlog_copy_logfile (FALSE); /* back up etchlog.log */
+
+ if (flog = fopen(filepath, "w"))
+ { is_etch_logfile_open = TRUE;
+ etch_loglines = 0;
+ }
+ else
+ { printf("could not open log file %s\n", filepath);
+ is_etchlog_shutdown = TRUE;
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * etchlog_close()
+ * currently can't be reopened.
+ */
+int etchlog_close()
+{
+ if (is_etch_logfile_open);
+ fclose(flog);
+ is_etch_logfile_open = FALSE;
+ is_etchlog_shutdown = TRUE;
+ return 0;
+}
+
+
+/**
+ * etchlog_write()
+ * write specified ansi string to log.
+ * @return count of bytes written.
+ */
+int etchlog_write(char* buf)
+{
+ int bytecount = 0, bytesout = 0, result = 0;
+ if (is_etchlog_suspended) return 0;
+
+ if (buf && (0 > (bytecount = (int) strlen(buf))))
+ if (config.is_log_to_file && is_etch_logfile_open && !is_etchlog_suspended)
+ if (0 < (bytesout = (int) fwrite(buf, 1, bytecount, flog)))
+ { fflush(flog); /* linecount assumes each line has a line feed */
+ if (++etch_loglines >= config.max_log_lines)
+ { result = etchlog_copy_logfile(TRUE); /* copy and reopen */
+ assert(0 == result);
+ }
+ }
+
+ return bytesout;
+}
+
+
+/**
+ * etchlog()
+ * write ansi string to log.
+ */
+void etchlog (char *comp, etchlog_level level, const char *fmt, ...)
+{
+ char *buf;
+ va_list args;
+ int result = 0, arglen, totallen, charcount;
+
+ if (level < config.loglevel) return;
+
+ loglock->acquire(loglock); /* todo use a queue instead of serializing? */
+
+ do {
+
+ va_start(args, fmt);
+ if (0 > (arglen = _vscprintf (fmt, args) + 1)) break;
+
+ if ((totallen = arglen + ETCHLOG_PREAMBLESIZE) > ETCHLOG_STACKBUFSIZE)
+ buf = malloc(totallen); /* eschew etch_malloc since immediately freed */
+ else buf = etchlogbuf;
+ charcount = totallen - 1;
+ memset(buf,' ', charcount); buf[charcount] = '\0';
+
+ if (strlen(comp) > 4) comp[4] = '\0'; /* module id exactly 4 characters */
+
+ etchlog_timestamp(buf, comp, level);
+
+ vsprintf(buf + ETCHLOG_PREAMBLESIZE, fmt, args);
+ va_end(fmt);
+
+ #if IS_ETCH_LOG_CONSOLE /* log to console, unless configured otherwise */
+ if (config.is_log_to_console)
+ printf(buf);
+ #endif /* IS_ETCH_LOG_CONSOLE */
+
+ #if IS_ETCH_LOG_FILE /* log to file, unless configured otherwise */
+ if (config.is_log_to_file && is_etch_logfile_open && !is_etchlog_suspended)
+ if (fwrite(buf, 1, charcount, flog) > 0)
+ { fflush(flog); /* linecount assumes each line has a line feed */
+ if (++etch_loglines >= config.max_log_lines)
+ { result = etchlog_copy_logfile(TRUE); /* copy and reopen */
+ if (-1 == result) printf("*** LOGFILE COPY FAILED\n");
+ assert(0 == result);
+ }
+ }
+ #endif /* IS_ETCH_LOG_FILE */
+
+ if (totallen > ETCHLOG_STACKBUFSIZE)
+ free(buf); /* see etch_malloc comment above */
+
+ } while(0);
+
+ loglock->release(loglock); /* todo use a queue instead of serializing */
+}
+
+
+/**
+ * etchlogw()
+ * write unicode string to log.
+ */
+void etchlogw (wchar_t *comp, etchlog_level level, const wchar_t *fmt, ...)
+{
+ wchar_t *buf;
+ va_list args;
+ int argcharlen, totalcharlen, bytelen;
+
+ if (level < config.loglevel) return;
+
+ loglock->acquire(loglock); /* todo use a queue instead of serializing? */
+
+ do {
+
+ va_start(args, fmt);
+ if (0 > (argcharlen = _vscwprintf(fmt, args) + 1)) break;
+
+ totalcharlen = argcharlen + ETCHLOG_PREAMBLESIZE;
+ bytelen = totalcharlen * sizeof(wchar_t);
+
+ if (bytelen > ETCHLOG_STACKBUFSIZE)
+ buf = malloc(bytelen); /* eschew etch_malloc since immediately freed */
+ else buf = (wchar_t*) etchlogbuf;
+
+ memset (buf, 0, bytelen);
+ wmemset(buf, L' ', totalcharlen);
+
+ if (wcslen(comp) > 4) comp[4] = L'\0'; /* module id exactly 4 characters */
+
+ etchlogw_timestamp(buf, comp, level);
+
+ /* note that offsets are specified in characters not bytes */
+ vswprintf(buf + ETCHLOG_PREAMBLESIZE, totalcharlen, fmt, args);
+ va_end((void*) fmt);
+
+
+ #if IS_ETCH_LOG_CONSOLE /* log to console, unless configured otherwise */
+ if (config.is_log_to_console)
+ wprintf(buf);
+ #endif /* IS_ETCH_LOG_CONSOLE */
+
+
+ #if IS_ETCH_LOG_FILE /* log to file, unless configured otherwise */
+ if (config.is_log_to_file)
+ if (0 == etchlog_open_server())
+ if (fwrite(buf, 1, bytelen, flog) > 0)
+ { fflush(flog);
+ if (++etch_loglines >= config.max_log_lines)
+ { const int result = etchlog_copy_logfile(TRUE);
+ assert(0 == result);
+ }
+ }
+ #endif /* IS_ETCH_LOG_FILE */
+
+ if (bytelen > ETCHLOG_STACKBUFSIZE)
+ free(buf); /* see etch_malloc comment above */
+
+ } while(0);
+
+ loglock->release(loglock); /* todo use a queue instead of serializing */
+}
+
+
+/**
+ * etchlog_timestamp()
+ * sets preamble to supplied buffer which must be at least 20 bytes.
+ * string length of preamble is always 19.
+ */
+void etchlog_timestamp(char* buf, const char* component, etchlog_level lvl)
+{ /* 012345678901234567 */
+ /* MMDD HHMMSS CCCC L */
+ static const char* TIMESTAMPMASK = "%02d%02d %02d%02d%02d ";
+ char cbuf[ETCHLOG_CBUFLEN+1];
+ struct tm* t;
+ time_t l;
+
+ time(&l);
+ t = localtime(&l);
+ memset (cbuf,' ',ETCHLOG_CBUFLEN+1);
+ memcpy (cbuf, component, strlen(component));
+
+ sprintf(buf, TIMESTAMPMASK, /* t->tm_year+1900, */
+ t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+
+ memcpy(buf+12, cbuf, ETCHLOG_CBUFLEN);
+ *(buf+17) = *etchlog_level_str[lvl];
+ *(buf+19) = '\0'; /* caller will overwrite term */
+}
+
+
+/**
+ * etchlogw_timestamp()
+ */
+void etchlogw_timestamp(wchar_t* buf, const wchar_t* component, etchlog_level lvl)
+{ /* 012345678901234567 */
+ /* MMDD HHMMSS CCCC L */
+ static const wchar_t* TIMESTAMPMASKW = L"%02d%02d %02d%02d%02d ";
+ wchar_t cbuf[ETCHLOG_CBUFLEN+1];
+ struct tm* t;
+ time_t l;
+
+ time(&l);
+ t = localtime(&l);
+ wmemset (cbuf, L' ', ETCHLOG_CBUFLEN+1);
+ wmemcpy (cbuf, component, wcslen(component));
+
+ swprintf(buf, ETCHLOG_STAMPLEN+1, TIMESTAMPMASKW, /* t->tm_year+1900, */
+ t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+
+ wmemcpy(buf+12, cbuf, ETCHLOG_CBUFLEN); /* note that numeric constants */
+ *(buf+17) = *etchlog_level_str[lvl]; /* specify characters not bytes */
+ *(buf+19) = '\0'; /* caller will overwrite term */
+}
+
+
+/**
+ * etchlog_get_dirpath()
+ * get the relative path to the log file directory.
+ * @return a path string or null.
+ */
+char* etchlog_get_dirpath()
+{
+ return config.calculated_is_client? ETCHCLIENTLOGDIR: ETCHSERVERLOGDIR;
+}
+
+
+/**
+ * etchlog_get_logfile_count()
+ * @return the current running count of log files.
+ */
+int etchlog_get_logfile_count()
+{
+ return etch_logfiles;
+}
+
+
+/**
+ * etchlog_set_logfile_count()
+ * set the initial count of log files.
+ */
+void etchlog_set_logfile_count(const int count)
+{
+ etch_logfiles = count;
+}
+
+
+/**
+ * etchlog_get_bkuppath()
+ * get a backup log file name/relative path.
+ * @param outbuf buffer to receive the file name/path
+ * @param outbufsize size of the out buffer, must be a/l 23 + characters plus
+ * the size of the relative path. if relative directory strings are "../logcli/"
+ * and "../logsrv/", the return string will be 32 characters including null term.
+ * @param seqno the start value of the timestamp sequencer.
+ * @return current sequencer on success, -1 failure. file name and relative path in outbuf.
+ */
+int etchlog_get_bkuppath (char* outbuf, const int outbufsize, int seqno)
+{
+ /* 0 1 2 3
+ /* 012345678901234567890123456789012 */
+ /* ../logcli/YYYYMMDDHHMMSSNNNN.log */
+ struct tm* t;
+ char buf[48], *p = 0;
+ const char *BKMASK = "%04d%02d%02d%02d%02d%02d%04d%s", *DOTLOG = ".log";
+ time_t l; time(&l);
+ t = localtime(&l);
+ strcpy (buf, config.calculated_is_client? ETCHCLIENTLOGDIR: ETCHSERVERLOGDIR);
+ strcat (buf, "/");
+ p = buf + (int) strlen(buf);
+
+ while(1)
+ { sprintf (p, BKMASK, t->tm_year+1900, t->tm_mon+1,
+ t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, seqno, DOTLOG);
+ if (!etch_existsfile(buf)) break;
+ if (++seqno > 9999) return -1;
+ }
+
+ if (outbufsize < ((int) strlen(buf) + 1)) return -1;
+ strcpy(outbuf, buf);
+ return seqno + 1;
+}
+
+
+/**
+ * etchlog_purge_logfiles()
+ * purge files from current log directory.
+ * @return count of files purged.
+ */
+int etchlog_purge_logfiles ()
+{
+ int purgedcount = 0;
+
+ if (etch_logfiles >= config.max_log_files + ETCHCONFIGDEF_LOGCOUNT_GRACE)
+ {
+ char* dirpath = etchlog_get_dirpath();
+ const int purgecount = etch_logfiles - config.max_log_files;
+ etchlog (ETCHLOGX, ETCHLOG_XDEBUG, "purging %d log files from %s ...\n",
+ purgecount, dirpath);
+ purgedcount = etchlog_purgefiles (dirpath, purgecount, 0);
+ etchlog (ETCHLOGX, ETCHLOG_DEBUG, "purged %d log files\n", purgedcount);
+ etch_logfiles -= purgedcount;
+ }
+
+ return purgedcount;
+}
+
+
+/**
+ * etchlog_copy_logfile()
+ * copy primary log file to a backup file.
+ * @param is_purge_logfiles if true, and count of log files exceeds configured
+ * maximum plus a grace overage, a number of log files is deleted such that the
+ * log file count is brought back down to the configured maximum.
+ * @remarks we may at some point want to move the log file purge to a work thread.
+ * @return 0 success, -1 failure.
+ */
+int etchlog_copy_logfile (const int is_purge_logfiles)
+{
+ char newpath[48];
+ int result = -1, attempts = 0, seqno = 0;
+ if (NULL == logpath) return -1;
+
+ is_etchlog_suspended = TRUE;
+ if (is_etch_logfile_open)
+ fclose(flog);
+
+ while (attempts++ < 10)
+ {
+ if (-1 == (seqno = etchlog_get_bkuppath (newpath, sizeof(newpath), seqno)))
+ break;
+
+ if (0 != rename (logpath, newpath))
+ continue;
+
+ etch_logfiles++;
+ etch_loglines = 0;
+
+ if (is_etch_logfile_open)
+ if (NULL == (flog = fopen(logpath, "w")))
+ break;
+
+ is_etchlog_suspended = FALSE;
+
+ if (is_purge_logfiles)
+ etchlog_purge_logfiles();
+
+ result = 0;
+ break;
+ }
+
+ return result;
+}
+
Added: incubator/etch/trunk/binding-c/runtime/c/src/common/etchmap.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/common/etchmap.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/common/etchmap.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/common/etchmap.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,486 @@
+/* $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.
+ */
+
+/*
+ * etchmap.c -- generic int or string to object map.
+ * note that this code does not implement an etch_map type, that type was added
+ * to etch after we had already defined this particular API.
+ * todo: convert global cache to use this code.
+ */
+
+#include "etchmap.h"
+#include "etch_global.h"
+#pragma warning (disable:4996)
+
+
+/*
+ * make_etchmap_key()
+ * given 32-bit key constructs alpha key for the map returning key byte length
+ */
+int make_etchmap_key(const unsigned int key, char* buf, const int buflen)
+{
+ _itoa_s(key, buf, buflen, 10);
+ return (int)strlen(buf);
+}
+
+
+/*
+ * etchmap_populate_out()
+ * populate caller's out struct
+ */
+void etchmap_populate_out(etch_hashitem* useritem, etch_hashitem* curritem)
+{
+ useritem->key = curritem->key;
+ useritem->value = curritem->value;
+ useritem->hash = curritem->hash;
+}
+
+
+/*
+ * etchmap_findxl()
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_findxl(etch_hashtable* map, char* key, unsigned keylen, void** out)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_find(map->realtable, key, keylen, 0, &founditem);
+
+ #if ETCHMAP_DEBUG
+ #pragma warning(disable:4313)
+ if (result == 0)
+ printf("etchmap_found key %s len %d addr %08x\n",
+ key, keylen, founditem->value);
+ else printf("etchmap_notfound key %s len %d\n", key, keylen);
+ #endif
+
+ if (result == -1) return NULL;
+
+ if (out)
+ etchmap_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_find()
+ * locate object with specified key, returning it or NULL.
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_find(etch_hashtable* map, const unsigned int objkey, void** out)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+
+ unsigned keylen = make_etchmap_key(objkey, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_findxl(map, ckey, keylen, out);
+}
+
+
+/*
+ * etchmap_findx()
+ * locate object with specified ansi char key, returning it or NULL
+ */
+void* etchmap_findx(etch_hashtable* map, char* key, void** out)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_findxl(map, key, keylen, out);
+}
+
+
+/*
+ * etchmap_findxw()
+ * locate object with specified unicode key, returning it or NULL
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_findxw(etch_hashtable* map, wchar_t* key, void** out)
+{
+ unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_findxl(map, (char*) key, keylen, out);
+}
+
+
+/*
+ * etchmap_find_by_hash()
+ * locate object with specified hashkey, returning it or NULL.
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_find_by_hash(etch_hashtable* map, const unsigned hash, void** out)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_findh(map->realtable, hash, map, &founditem);
+ if (result == -1) return NULL;
+
+ if (out)
+ etchmap_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_current()
+ * return object at current position
+ */
+etch_hashitem* etchmap_current(etch_hashtable* map)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_current(map->realtable, 0, &founditem);
+ return (result == -1)? NULL: founditem;
+}
+
+
+/*
+ * etchmap_delxl()
+ * remove specified object from map given ansi char key and length.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delxl (etch_hashtable* map, char* ckey, const unsigned keylen)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_remove(map->realtable, ckey, keylen, 0, &founditem);
+ if (result == -1) return NULL;
+
+ free(founditem->key); /* free 4-byte key allocated in etchmap_add() */
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_del()
+ * remove specified object from map given integer key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_del (etch_hashtable* map, const unsigned int objkey)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+
+ unsigned keylen = make_etchmap_key(objkey, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_delxl(map, ckey, keylen);
+}
+
+
+/*
+ * etchmap_delx()
+ * remove specified object from map given ansi char key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delx (etch_hashtable* map, char* key)
+{
+ const unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_delxl(map, key, keylen);
+}
+
+
+/*
+ * etchmap_delxw()
+ * remove specified object from map given unicode key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delxw (etch_hashtable* map, wchar_t* key)
+{
+ const unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_delxl(map, (char*) key, keylen);
+}
+
+
+/*
+ * etchmap_insertxl()
+ * add specified object to map given ansi char key and char length,
+ * with preexistence test optional.
+ * @param key a string for which caller retains ownership, map makes a copy.
+ * @param data an object owned by map or not depending on map attributes.
+ * @return hash of supplied key, or zero if insertion error.
+ */
+int etchmap_insertxl (etch_hashtable* map,
+ 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 = etchmap_findxl(map, 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 ETCHMAP_DEBUG
+ #pragma warning(disable:4313)
+ printf("etchmap_insertxl key %s len %d addr %08x\n", pkey, keylen, data);
+ #endif
+
+ result = jenkins_insert
+ (map->realtable, pkey, keylen, data, 0, 0, &inserteditem);
+
+ /* etchmap_dump(); */
+
+ return inserteditem->hash;
+}
+
+
+/*
+ * etchmap_insertxlw()
+ * add specified object to map given unicode key and char length,
+ * with preexistence test optional.
+ * @param key a string for which caller retains ownership, map makes a copy.
+ * @param data an object owned by map or not depending on map attributes.
+ * @return hash of supplied key, or zero if insertion error.
+ */
+int etchmap_insertxlw (etch_hashtable* map,
+ wchar_t* key, const unsigned keylen, void* data, const int is_check)
+{
+ int result = 0, keylent = 0;
+ wchar_t* pkey = NULL;
+ void* pval = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* inserteditem = &hashbucket;
+ memset(&hashbucket, 0, sizeof(etch_hashitem));
+
+ if (is_check)
+ { pval = etchmap_findxl(map, (char*) key, keylen, &inserteditem);
+ if (pval) return inserteditem->hash; /* entry exists */
+ }
+
+ keylent = keylen + sizeof(wchar_t); /* add new entry */
+ /* watch this spot. issue observed here where etch_malloc reports that it
+ * "could not add x to heap tracking store". my guess is that a heap item
+ * at address x had been etch_freed, but key x was for some reason not
+ * removed from the tracking table. memory address x was subsequently
+ * re-issued by malloc, and when etch_malloc attempts to add it to the
+ * tracking table, the address already exists there (or there is a hash
+ * collision?). regardless, this is a tracking error, is probably not a
+ * leak (because etch_free frees the memory regardless of this error),
+ * however the error messages are a problem so we need to address this.
+ */
+ pkey = etch_malloc(keylent, 0);
+ wcscpy(pkey, key);
+
+ result = jenkins_insert
+ (map->realtable, pkey, keylen, data, 0, 0, &inserteditem);
+
+ return inserteditem->hash;
+}
+
+
+/*
+ * etchmap_insert()
+ * add specified object to map with preexistence test optional.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insert (etch_hashtable* map,
+ const unsigned int key, void* data, const int is_check)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+ unsigned keylen = make_etchmap_key(key, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_insertxl(map, ckey, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_insertx()
+ * add specified object to map with preexistence test optional.
+ * param key a string for which caller retains ownership, map makes a copy.
+ * @param data the value of the key/value pair, owned by map or not depending
+ * on map attributes.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insertx (etch_hashtable* map, char* key, void* data, const int is_check)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_insertxl(map, key, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_insertxw()
+ * add specified object to map with preexistence test optional.
+ * param key a string for which caller retains ownership, map makes a copy.
+ * @param data the value of the key/value pair, owned by map or not depending
+ * on map attributes.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insertxw (etch_hashtable* map, wchar_t* key, void* data, const int is_check)
+{
+ unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_insertxlw(map, key, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_add()
+ * add specified object to map given integer key.
+ * return 0 or -1.
+ */
+int etchmap_add (etch_hashtable* map, const unsigned int objkey, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insert (map, objkey, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_addx()
+ * add specified object to map.
+ * return 0 or -1.
+ */
+int etchmap_addx(etch_hashtable* map, char* key, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insertx (map, key, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_addxw()
+ * add specified object to map.
+ * return 0 or -1.
+ */
+int etchmap_addxw(etch_hashtable* map, wchar_t* key, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insertxw (map, key, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_count()
+ * return count of items in map.
+ */
+int etchmap_count(etch_hashtable* map)
+{
+ return map? map->vtab->count(map->realtable, 0, 0): 0;
+}
+
+
+/*
+ * string_to_etchobject_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of
+ * a string to object type map, where the objects are etch_objects.
+ * handlers return FALSE to indicate memory free NOT handled.
+ */
+int string_to_etchobject_clear_handler (char* key, objmask* value)
+{
+ etch_free(key);
+ if (value) value->destroy(value);
+ return TRUE;
+}
+
+
+/*
+ * string_to_genericobject_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of
+ * a string to object type map, where the objects are not etch_objects.
+ * handlers return FALSE to indicate memory free NOT handled.
+ */
+int string_to_genericobject_clear_handler (char* key, void* value)
+{
+ etch_free(key);
+ etch_free(value);
+ return TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - - -
+ * etch_map (object to object map)
+ * - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchmap_is_object_key
+ * @return boolean value indicating whether hashtable key is an etch object
+ */
+int etchmap_is_object_key(etch_hashtable* map)
+{
+ int result = FALSE;
+ if (map) switch(map->content_type)
+ { case ETCHHASHTABLE_CONTENT_OBJECT_OBJECT:
+ case ETCHHASHTABLE_CONTENT_OBJECT_NONE:
+ result = TRUE;
+ }
+ return result;
+}
+
+
+/**
+ * etchmap_map_add
+ * inserts item to object/object hashtable
+ * it is assumed that hashkey has been pre-computed on the key object.
+ */
+int etchmap_map_add (etch_hashtable* map, objmask* key, objmask* value)
+{
+ const int result = map->vtab->inserth(map->realtable, key, value, map, 0);
+ return result;
+}
+
+
+/**
+ * etchmap_map_find()
+ * finds object with specified object key .
+ * it is assumed that hashkey has been pre-computed on the key object.
+ * @return 0 or -1
+ * found item is returned via out parameter, if supplied.
+ */
+int etchmap_map_find(etch_hashtable* map, objmask* key, etch_hashitem** out)
+{
+ const int result = map->vtab->findh(map->realtable, key->hashkey, map, out);
+ return result;
+}
+
+
+/**
+ * etchmap_set_add
+ * inserts item to object/null hashtable
+ * it is assumed that hashkey has been pre-computed on the key object.
+ */
+int etchmap_set_add (etch_hashtable* set, objmask* key)
+{
+ const int result = set->vtab->inserth(set->realtable, key, NULL, set, 0);
+ return result;
+}
+
+
+
+
+
Added: incubator/etch/trunk/binding-c/runtime/c/src/common/etchmem.c
URL: http://svn.apache.org/viewvc/incubator/etch/trunk/binding-c/runtime/c/src/common/etchmem.c?rev=767594&view=auto
==============================================================================
--- incubator/etch/trunk/binding-c/runtime/c/src/common/etchmem.c (added)
+++ incubator/etch/trunk/binding-c/runtime/c/src/common/etchmem.c Wed Apr 22 17:25:43 2009
@@ -0,0 +1,448 @@
+/* $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.
+ */
+
+/*
+ * etchmem.c -- heap memory allocate and free.
+ * the c binding wraps the heap allocator in order to track allocations.
+ * we supply the etch_malloc macro which, when ETCH_DEBUGALLOC is defined,
+ * will accept module name and code line number, along with object type and
+ * allocation byte length, in order to track allocations and frees, and thus
+ * enable identification of memory leaks.
+ */
+
+#include "etch_global.h"
+#include "etchlog.h"
+char* ETCHMEMX = "MEMX";
+
+#ifdef ETCH_DEBUGALLOC /* defined, or not, in etchmem.h */
+
+#pragma warning (disable:4313) /* display of ptr as %08x warning */
+
+memrec* debug_find_memrec(void* p, char* file, const short line);
+
+
+/*
+ * find_module_name()
+ * private method which looks up a code module name and if not present,
+ * adds it to the filepath cache. a hash of the code file name is returned,
+ * or zero if error.
+ */
+unsigned find_module_name(char* name) /* __FILE__ is not unicode */
+{
+ unsigned hash = 0;
+ char* namefound = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+ memset(thisitem, 0, sizeof(etch_hashitem));
+
+ /* look up name in cache. NULL is returned because the value is the key */
+ cache_findx(name, &thisitem);
+ if (thisitem->key) return thisitem->hash;
+
+ /* insert name into cache. no value needed, name key is value. */
+ hash = cache_insertx (name, NULL, FALSE);
+ return hash;
+}
+
+
+/*
+ * instantiate_memtable()
+ * create the singleton memory allocation tracking hashtable.
+ */
+void instantiate_memtable()
+{
+ is_memtable_instance = TRUE; /* flag to not debug_malloc() this allocation */
+ memtable = new_hashtable(DEFETCHHEAPTABLESIZE);
+ is_memtable_instance = FALSE;
+
+ memtable->is_readonly_keys = memtable->is_readonly_values = FALSE;
+ memtable->is_tracked_memory = FALSE;
+}
+
+
+/*
+ * showentry()
+ * display contents of a memory allocation table entry.
+ * free the table entry if requested.
+ */
+void showentry(void** pkey, memrec* memtblentry,
+ const int is_freeitem, const int is_console)
+{
+ char* filepath = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+ memset(thisitem, 0, sizeof(etch_hashitem));
+
+ /* look up key by its hash value. value of key is a source file path */
+ cache_find_by_hash(memtblentry->filehash, &thisitem);
+ filepath = thisitem->key;
+
+ if (NULL == filepath)
+ if (is_console)
+ etchlog (ETCHMEMX, ETCHLOG_ERROR,
+ "etch cache error - no path with hashkey %u cached\n",
+ memtblentry->filehash);
+ /* printf("\nMEMX etch cache error - no path with hashkey %u cached\n",
+ memtblentry->filehash);
+ */
+ else;
+ else
+ if (is_console)
+ #if IS_USING_ALLOC_ID
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "%s line %d serial %d size %d at %08x\n", filepath,
+ memtblentry->line, memtblentry->id, memtblentry->size, *pkey);
+ /* printf("\n%s line %d serial %d size %d at %08x\n", filepath,
+ memtblentry->line, memtblentry->id, memtblentry->size, *pkey);
+ */
+ #else /* IS_USING_ALLOC_ID */
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "%s line %d obj %d size %d at %08x\n", filepath,
+ memtblentry->line, memtblentry->objtype, memtblentry->size, *pkey);
+ /* printf("\n%s line %d obj %d size %d at %08x\n", filepath,
+ memtblentry->line, memtblentry->objtype, memtblentry->size, *pkey);
+ */
+ #endif /* IS_USING_ALLOC_ID */
+
+ /* use is_freeitem with care, what we are saying here is show me what remains
+ * allocated, and if there are allocations remaining, clean them up, i.e. they
+ * are memory leaks. both the tracked etch_malloc allocation, and its memtable
+ * bucket, are freed when this is the case.
+ */
+ if (is_freeitem)
+ {
+ etch_free(*pkey);
+ }
+}
+
+
+/*
+ * debug_showmem()
+ * show debug_malloc()'ed heap allocations outstanding.
+ * returns number of bytes currently allocated. is_freeitem specifies that we
+ * should free each allocation remaining. is_console asks for stdout display
+ * of statistics including source file and line number of each allocation.
+ */
+int debug_showmem(const int is_freeitem, const int is_console)
+{
+ int leaks = 0, count = 0;
+ size_t allocated_bytes;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisentry = &hashbucket;
+ if (!memtable) return 0;
+ allocated_bytes = etchheap_currbytes;
+
+ if (is_console)
+ {
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "etch heap hiwater count %u bytes %u\n", etchheap_hiwater, etchheap_maxbytes);
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "%d bytes remain allocated on etch heap\n", allocated_bytes);
+ }
+
+ count = memtable->vtab->count(memtable->realtable, 0, 0);
+ if (0 == count) return 0;
+
+ if (0 == memtable->vtab->first(memtable->realtable, NULL, &thisentry))
+ {
+ showentry((void**)thisentry->key, (memrec*)thisentry->value,
+ is_freeitem, is_console);
+ leaks = 1;
+ }
+ else
+ if (is_console)
+ etchlog (ETCHMEMX, ETCHLOG_ERROR, "etch hashtable.first() failed\n");
+
+ while(0 == memtable->vtab->next(memtable->realtable, NULL, &thisentry))
+ {
+ showentry((void**)thisentry->key, (memrec*)thisentry->value,
+ is_freeitem, is_console);
+ leaks++;
+ }
+
+ if (is_console)
+ {
+ if (is_freeitem)
+ etchlog (ETCHMEMX, ETCHLOG_ERROR,
+ "%d etch heap allocations leaked %d bytes of which %d remain\n",
+ leaks, allocated_bytes, etchheap_currbytes);
+ else etchlog (ETCHMEMX, ETCHLOG_ERROR,
+ "%d etch heap allocations totaling %d bytes remain\n",
+ leaks, allocated_bytes);
+ }
+
+ return (int)etchheap_currbytes;
+}
+
+
+/*
+ * debug_dumpmem()
+ * list contents of tracking table
+ */
+void debug_dumpmem()
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+ if (!memtable) return;
+ #pragma warning (disable:4311)
+
+ if (0 == memtable->vtab->first(memtable->realtable, NULL, &myentry))
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "%x: %x\n", *(unsigned*)myentry->key, (unsigned)myentry->value);
+ else etchlog (ETCHMEMX, ETCHLOG_ERROR,"etch memtable.first() failed\n");
+
+ /*
+ if (0 == memtable->vtab->first(memtable->realtable, NULL, &myentry))
+ printf("\n%x: %x\n", *(unsigned*)myentry->key, (unsigned)myentry->value);
+ else printf("\nMEMX etch memtable.first() failed\n");
+ */
+
+ while(0 == memtable->vtab->next(memtable->realtable, NULL, &myentry))
+ etchlog (ETCHMEMX, ETCHLOG_DEBUG,
+ "%x: %x\n", *(unsigned*)myentry->key, (unsigned)myentry->value);
+ /* printf("%x: %x\n", *(unsigned*)myentry->key, (unsigned)myentry->value); */
+}
+
+
+/*
+ * next_alloc_id: return memory allocation serial number
+ */
+unsigned next_alloc_id() { return ++curr_alloc_id; }
+
+
+
+/**
+ * debug_malloc()
+ * tracks mallocs by entering the malloc'ed object info, code file, and code line,
+ * into a tracking table. This is a private method -- it should not be invoked
+ * directly, but rather via the etch_malloc macro, when ETCH_DEBUGALLOC is defined.
+ */
+void* debug_malloc(size_t nbytes, const short objtype, char* file, const short line)
+{
+ char *pkey = 0, breakpoint = 0; /* __FILE__ is not unicode */
+ int hashresult = 0;
+ unsigned pathhash = 0;
+ memrec* rec = 0;
+ void* p = NULL;
+ if (nbytes <= 0) return NULL;
+ p = malloc(nbytes); /* complete user's malloc request at least */
+
+ pathhash = find_module_name(file); /* cache source file name */
+ if (pathhash == 0) return p;
+
+ if (!memtable) /* lazy load the tracking table */
+ instantiate_memtable();
+
+ rec = malloc(sizeof(memrec)); /* don't track tracking object allocations */
+ rec->size = nbytes;
+ rec->objtype = objtype;
+ rec->line = line;
+ rec->filehash = pathhash;
+
+ /* we can watch for a specific allocation sequence number and break into the
+ * debugger here when it occurs. after an execution we would call debug_showmem()
+ * to display leaked allocations and their sequence numbers. all the etch unit
+ * tests do so after each test, see etch_showmem(). on a subsequent execution,
+ * we would compile in a set of the global etch_watch_id to that sequence number,
+ * ( preferably in main(), for example, etch_watch_id = 127; ) and rerun.
+ * we can set a breakpoint below, or compile in IS_USING_DEBUGBREAK, to break
+ * into the debugger at the desired memory allocation. we can then examine the
+ * call stack to determine the source of that particular and presumably leaked
+ * memory allocation.
+ */
+ #if IS_USING_ALLOC_ID
+ rec->id = next_alloc_id();
+ #if IS_TRACKING_ETCHHEAP
+ if (etch_watch_id == rec->id) /* * * * * watch breakpoint * * * * */
+ #if IS_USING_DEBUGBREAK
+ __debugbreak();
+ #else
+ breakpoint++; /* set breakpoint here when watching an allocation number */
+ #endif /* IS_USING_DEBUGBREAK */
+ #endif /* #if IS_TRACKING_ETCHHEAP */
+ #endif /* #if IS_USING_ALLOC_ID */
+
+ /* the key for our tracking record key/value pair, is the pointer to heap
+ * memory returned by malloc; i.e. we build a map keyed on memory addresses.
+ * the map therefore stores a void**, a pointer to the pointer to the original
+ * malloc. debug_malloc() also allocates 4 bytes for the hashkey, which is
+ * eventually freed by debug_free().
+ */
+ pkey = malloc(sizeof(void*)); /* allocate memory for the hashkey */
+ memcpy(pkey, &p, sizeof(void*)); /* copy user's mem* into that key */
+
+ #if IS_TRACKING_ETCHHEAP
+ if (etch_watch_addr == (size_t) pkey)
+ #if IS_USING_DEBUGBREAK
+ __debugbreak();
+ #else
+ breakpoint++; /* set breakpoint here when watching for a memory address */
+ #endif /* IS_USING_DEBUGBREAK */
+ #endif /* #if IS_TRACKING_ETCHHEAP */
+
+ /* insert this allocation record into the tracking map */
+ hashresult = memtable->vtab->insert(memtable->realtable,
+ pkey, sizeof(void*), rec, sizeof(memrec), 0, 0);
+
+ if (hashresult == -1)
+ etchlog(ETCHMEMX, ETCHLOG_ERROR,
+ "error inserting entry to heap tracking store\n");
+ #if(0)
+ else etchlog(ETCHMEMX, ETCHLOG_XDEBUG,"memtbl inserted key '%08x' obj %d size %d\n",
+ (unsigned)p, objtype, nbytes);
+ /*
+ else printf("\nMEMX memtbl inserted key '%08x' obj %d size %d\n",
+ (unsigned)p, objtype, nbytes);
+ */
+ #endif
+
+ etchheap_currbytes += nbytes;
+ if (etchheap_currbytes > etchheap_maxbytes) etchheap_maxbytes = etchheap_currbytes;
+ if (++etchheap_count > etchheap_hiwater) etchheap_hiwater = etchheap_count;
+ return p;
+}
+
+
+/**
+ * debug_realloc()
+ * tracks mallocs by entering the malloc'ed object info, code file, and code line,
+ * into a tracking table. This is a private method -- it should not be invoked
+ * directly, but rather via the etch_malloc macro, when ETCH_DEBUGALLOC is defined.
+ */
+void* debug_realloc(void* p, size_t nbytes, const short objtype,
+ char* file, const short line)
+{
+ void *new_ptr = NULL;
+ memrec *oldrec_ptr = NULL;
+
+ if (p == NULL) /* acts as malloc */
+ return debug_malloc(nbytes, objtype, file, line);
+
+ if (nbytes == 0) /* acts as free */
+ {
+ debug_free(p, file, line);
+ return NULL;
+ }
+
+ oldrec_ptr = debug_find_memrec(p, file, line);
+ ETCH_ASSERT(oldrec_ptr != NULL);
+ if (oldrec_ptr == NULL) return NULL;
+
+ new_ptr = debug_malloc(nbytes, objtype, file, line);
+ if (new_ptr == NULL) return NULL; /* per realloc spec */
+
+ memcpy(new_ptr, p, min(nbytes, oldrec_ptr->size));
+
+ debug_free(p, file, line);
+
+ return new_ptr;
+}
+
+
+/**
+ * debug_free()
+ * tracks free()s by looking up the associated malloc() record and removing it
+ * from the tracking table. this is a private method -- it should not be invoked
+ * directly, but rather via the etch_free macro, when ETCH_DEBUGALLOC is defined.
+ */
+int debug_free(void* p, char* file, const short line) /* __FILE__ not unicode */
+{
+ int hashresult = 0, buckresult = 0, breakpoint = 0;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisentry = &hashbucket;
+ void* this_key = 0;
+ memrec* mrec = 0;
+ if (!p) return -1;
+
+ do
+ { if (!memtable) break;
+
+ #if IS_TRACKING_ETCHHEAP
+ if (etch_watch_addr == (size_t) p)
+ #if IS_USING_DEBUGBREAK
+ __debugbreak();
+ #else
+ breakpoint++; /* set breakpoint here when watching for a memory address */
+ #endif /* IS_USING_DEBUGBREAK */
+ #endif /* #if IS_TRACKING_ETCHHEAP */
+
+ /* first free the memory tracking table entry. recall that the key to the
+ * tracking record is the value of the heap address returned by malloc.
+ */
+ hashresult = memtable->vtab->find /* retrieve the map entry */
+ (memtable->realtable, &p, sizeof(void*), NULL, &thisentry);
+
+ if (hashresult < 0)
+ { etchlog(ETCHMEMX, ETCHLOG_ERROR,
+ "etch heap tracking store missing key '%08x'\n", (unsigned)p);
+ break;
+ }
+
+ this_key = thisentry->key;
+ mrec = (memrec*) thisentry->value;
+
+ #if IS_TRACKING_ETCHHEAP
+ if (etch_watch_id == mrec->id)
+ breakpoint++; /* set breakpoint here when watching an alloc number */
+ #endif /* #if IS_TRACKING_ETCHHEAP */
+
+ #if(0)
+ printf("\nDFRE memtbl freeing key '%08x' obj %d size %d line %d file %s\n",
+ (unsigned)p, mrec->objtype, mrec->size, mrec->line, mrec->file);
+ #endif
+
+ buckresult = memtable->vtab->remove /* free the map bucket */
+ (memtable->realtable, &p, sizeof(void*), NULL, &thisentry);
+
+ etchheap_count--;
+ etchheap_currbytes -= mrec->size;
+ free(mrec); /* free memory for the tracking record */
+ free(this_key); /* free memory for the hashkey */
+
+ } while(0);
+
+ free(p); /* finally free the actual object */
+ return hashresult;
+}
+
+
+/**
+ * debug_find_memrec()
+ * looks up the memory record in the map for specified address p.
+ * returns NULL if not found.
+ */
+memrec* debug_find_memrec(void* p, char* file, const short line)
+{
+ etch_hashitem hashbucket, *thisentry = &hashbucket;
+ memrec* this_memrec = NULL;
+
+ if (!p || !memtable);
+ else
+ if (0 == memtable->vtab->find(memtable->realtable, &p,
+ sizeof(void*), NULL, &thisentry))
+
+ this_memrec = (memrec*) thisentry->value;
+
+ else etchlog(ETCHMEMX, ETCHLOG_ERROR,
+ "etch heap tracking store missing key '%08x'\n", (unsigned)p);
+
+ return this_memrec;
+}
+
+
+#endif /* #ifdef ETCH_DEBUGALLOC */
\ No newline at end of file