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