Selaa lähdekoodia

structure: add CHash, a generic hash table implemented with the help of the C preprocessor

ambrop7 13 vuotta sitten
vanhempi
sitoutus
7e67cc16e2
5 muutettua tiedostoa jossa 468 lisäystä ja 0 poistoa
  1. 39 0
      structure/CHash.h
  2. 59 0
      structure/CHash_decl.h
  3. 67 0
      structure/CHash_footer.h
  4. 67 0
      structure/CHash_header.h
  5. 236 0
      structure/CHash_impl.h

+ 39 - 0
structure/CHash.h

@@ -0,0 +1,39 @@
+/**
+ * @file CHash.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BADVPN_CHASH_H
+#define BADVPN_CHASH_H
+
+#include <stdlib.h>
+
+#include <misc/debug.h>
+#include <misc/merge.h>
+#include <misc/balloc.h>
+
+#endif

+ 59 - 0
structure/CHash_decl.h

@@ -0,0 +1,59 @@
+/**
+ * @file CHash_decl.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CHash_header.h"
+
+typedef struct {
+    CHashLink *buckets;
+    size_t numBuckets;
+    size_t numEntries;
+} CHash;
+
+typedef struct {
+    CHashEntry *ptr;
+    CHashLink link;
+} CHashRef;
+
+static CHashLink CHashNullLink ();
+static CHashRef CHashNullRef ();
+
+static int CHash_Init (CHash *o, size_t numBuckets);
+static void CHash_Free (CHash *o);
+static CHashRef CHash_Deref (CHashArg arg, CHashLink link);
+static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing);
+static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry);
+static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry);
+static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key);
+static CHashRef CHash_GetFirst (const CHash *o, CHashArg arg);
+static CHashRef CHash_GetNext (const CHash *o, CHashArg arg, CHashRef entry);
+static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry);
+static size_t CHash_NumEntries (const CHash *o);
+static int CHash_IsEmpty (const CHash *o);
+
+#include "CHash_footer.h"

+ 67 - 0
structure/CHash_footer.h

@@ -0,0 +1,67 @@
+/**
+ * @file CHash_footer.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Preprocessor inputs:
+#undef CHASH_PARAM_NAME
+#undef CHASH_PARAM_ENTRY
+#undef CHASH_PARAM_LINK
+#undef CHASH_PARAM_KEY
+#undef CHASH_PARAM_ARG
+#undef CHASH_PARAM_NULL
+#undef CHASH_PARAM_DEREF
+#undef CHASH_PARAM_HASHFUN
+#undef CHASH_PARAM_KEYSEQUAL
+#undef CHASH_PARAM_ENTRY_KEY
+#undef CHASH_PARAM_ENTRY_NEXT
+
+// types
+#undef CHash
+#undef CHashEntry
+#undef CHashLink
+#undef CHashRef
+#undef CHashArg
+#undef CHashKey
+
+// static values
+#undef CHashNullLink
+#undef CHashNullRef
+
+// public functions
+#undef CHash_Init
+#undef CHash_Free
+#undef CHash_Deref
+#undef CHash_Insert
+#undef CHash_InsertMulti
+#undef CHash_Remove
+#undef CHash_Lookup
+#undef CHash_GetFirst
+#undef CHash_GetNext
+#undef CHash_GetNextEqual
+#undef CHash_NumEntries
+#undef CHash_IsEmpty

+ 67 - 0
structure/CHash_header.h

@@ -0,0 +1,67 @@
+/**
+ * @file CHash_header.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Preprocessor inputs:
+// CHASH_PARAM_NAME - name of this data structure
+// CHASH_PARAM_ENTRY - type of entry
+// CHASH_PARAM_LINK - type of entry link (usually a pointer or index to an array)
+// CHASH_PARAM_KEY - type of key
+// CHASH_PARAM_ARG - type of argument pass through to comparisons
+// CHASH_PARAM_NULL - invalid link
+// CHASH_PARAM_DEREF(arg, link) - dereference a non-null link
+// CHASH_PARAM_HASHFUN(arg, key) - hash function, return size_t
+// CHASH_PARAM_KEYSEQUAL(arg, key1, key2) - compares equality of two keys
+// CHASH_PARAM_ENTRY_KEY - key member in entry
+// CHASH_PARAM_ENTRY_NEXT - next member in entry
+
+// types
+#define CHash CHASH_PARAM_NAME
+#define CHashEntry CHASH_PARAM_ENTRY
+#define CHashLink CHASH_PARAM_LINK
+#define CHashRef MERGE(CHASH_PARAM_NAME, Ref)
+#define CHashArg CHASH_PARAM_ARG
+#define CHashKey CHASH_PARAM_KEY
+
+// static values
+#define CHashNullLink MERGE(CHash, NullLink)
+#define CHashNullRef MERGE(CHash, NullRef)
+
+// public functions
+#define CHash_Init MERGE(CHash, _Init)
+#define CHash_Free MERGE(CHash, _Free)
+#define CHash_Deref MERGE(CHash, _Deref)
+#define CHash_Insert MERGE(CHash, _Insert)
+#define CHash_InsertMulti MERGE(CHash, _InsertMulti)
+#define CHash_Remove MERGE(CHash, _Remove)
+#define CHash_Lookup MERGE(CHash, _Lookup)
+#define CHash_GetFirst MERGE(CHash, _GetFirst)
+#define CHash_GetNext MERGE(CHash, _GetNext)
+#define CHash_GetNextEqual MERGE(CHash, _GetNextEqual)
+#define CHash_NumEntries MERGE(CHash, _NumEntries)
+#define CHash_IsEmpty MERGE(CHash, _IsEmpty)

+ 236 - 0
structure/CHash_impl.h

@@ -0,0 +1,236 @@
+/**
+ * @file CHash_impl.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CHash_header.h"
+
+static CHashLink CHashNullLink ()
+{
+    return CHASH_PARAM_NULL;
+}
+
+static CHashRef CHashNullRef ()
+{
+    CHashRef entry = {NULL, CHASH_PARAM_NULL};
+    return entry;
+}
+
+static int CHash_Init (CHash *o, size_t numBuckets)
+{
+    if (numBuckets == 0) {
+        numBuckets = 1;
+    }
+    
+    o->numBuckets = numBuckets;
+    o->numEntries = 0;
+    
+    o->buckets = (CHashLink *)BAllocArray(o->numBuckets, sizeof(o->buckets[0])); 
+    if (!o->buckets) {
+        return 0;
+    }
+    
+    for (size_t i = 0; i < o->numBuckets; i++) {
+        o->buckets[i] = CHashNullLink();
+    }
+    
+    return 1;
+}
+
+static void CHash_Free (CHash *o)
+{
+    BFree(o->buckets);
+}
+
+static CHashRef CHash_Deref (CHashArg arg, CHashLink link)
+{
+    if (link == CHashNullLink()) {
+        return CHashNullRef();
+    }
+    
+    CHashRef entry;
+    entry.ptr = CHASH_PARAM_DEREF(arg, link);
+    entry.link = link;
+    
+    ASSERT(entry.ptr)
+    
+    return entry;
+}
+
+static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing)
+{
+    CHashKey key = entry.ptr->CHASH_PARAM_ENTRY_KEY;
+    size_t index = CHASH_PARAM_HASHFUN(arg, key) % o->numBuckets;
+    
+    CHashRef e = CHash_Deref(arg, o->buckets[index]);
+    while (e.link != CHashNullLink()) {
+        if (CHASH_PARAM_KEYSEQUAL(arg, key, e.ptr->CHASH_PARAM_ENTRY_KEY)) {
+            if (out_existing) {
+                *out_existing = e;
+            }
+            return 0;
+        }
+        e = CHash_Deref(arg, e.ptr->CHASH_PARAM_ENTRY_NEXT);
+    }
+    
+    entry.ptr->CHASH_PARAM_ENTRY_NEXT = o->buckets[index];
+    o->buckets[index] = entry.link;
+    
+    o->numEntries++;
+    
+    if (out_existing) {
+        *out_existing = entry;
+    }
+    return 1;
+}
+
+static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry)
+{
+    CHashKey key = entry.ptr->CHASH_PARAM_ENTRY_KEY;
+    size_t index = CHASH_PARAM_HASHFUN(arg, key) % o->numBuckets;
+    
+    CHashRef prev = CHashNullRef();
+    CHashRef cur = CHash_Deref(arg, o->buckets[index]);
+    while (cur.link != CHashNullLink()) {
+        if (CHASH_PARAM_KEYSEQUAL(arg, cur.ptr->CHASH_PARAM_ENTRY_KEY, key)) {
+            break;
+        }
+        prev = cur;
+        cur = CHash_Deref(arg, cur.ptr->CHASH_PARAM_ENTRY_NEXT);
+    }
+    
+    if (cur.link == CHashNullLink() || prev.link == CHashNullLink()) {
+        entry.ptr->CHASH_PARAM_ENTRY_NEXT = o->buckets[index];
+        o->buckets[index] = entry.link;
+    } else {
+        entry.ptr->CHASH_PARAM_ENTRY_NEXT = cur.link;
+        prev.ptr->CHASH_PARAM_ENTRY_NEXT = entry.link;
+    }
+    
+    o->numEntries++;
+}
+
+static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry)
+{
+    CHashKey key = entry.ptr->CHASH_PARAM_ENTRY_KEY;
+    size_t index = CHASH_PARAM_HASHFUN(arg, key) % o->numBuckets;
+    
+    CHashRef prev = CHashNullRef();
+    CHashRef cur = CHash_Deref(arg, o->buckets[index]);
+    while (cur.link != entry.link) {
+        prev = cur;
+        cur = CHash_Deref(arg, cur.ptr->CHASH_PARAM_ENTRY_NEXT);
+        ASSERT(cur.link != CHashNullLink());
+    }
+    
+    if (prev.link == CHashNullLink()) {
+        o->buckets[index] = entry.ptr->CHASH_PARAM_ENTRY_NEXT;
+    } else {
+        prev.ptr->CHASH_PARAM_ENTRY_NEXT = entry.ptr->CHASH_PARAM_ENTRY_NEXT;
+    }
+    
+    o->numEntries--;
+}
+
+static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key) 
+{
+    size_t index = CHASH_PARAM_HASHFUN(arg, key) % o->numBuckets;
+    
+    CHashRef e = CHash_Deref(arg, o->buckets[index]);
+    while (e.link != CHashNullLink()) {
+        if (CHASH_PARAM_KEYSEQUAL(arg, e.ptr->CHASH_PARAM_ENTRY_KEY, key)) {
+            return e;
+        }
+        e = CHash_Deref(arg, e.ptr->CHASH_PARAM_ENTRY_NEXT);
+    }
+    
+    return CHashNullRef();
+}
+
+static CHashRef CHash_GetFirst (const CHash *o, CHashArg arg)
+{
+    size_t i = 0;
+    while (i < o->numBuckets && o->buckets[i] == CHashNullLink()) {
+        i++;
+    }
+    
+    if (i == o->numBuckets) {
+        return CHashNullRef();
+    }
+    
+    return CHash_Deref(arg, o->buckets[i]);
+}
+
+static CHashRef CHash_GetNext (const CHash *o, CHashArg arg, CHashRef entry)
+{
+    CHashLink next = entry.ptr->CHASH_PARAM_ENTRY_NEXT;
+    if (next != CHashNullLink()) {
+        return CHash_Deref(arg, next);
+    }
+    
+    CHashKey key = entry.ptr->CHASH_PARAM_ENTRY_KEY;
+    size_t i = CHASH_PARAM_HASHFUN(arg, key) % o->numBuckets;
+    i++;
+    
+    while (i < o->numBuckets && o->buckets[i] == CHashNullLink()) {
+        i++;
+    }
+    
+    if (i == o->numBuckets) {
+        return CHashNullRef();
+    }
+    
+    return CHash_Deref(arg, o->buckets[i]);
+}
+
+static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry)
+{
+    CHashLink next = entry.ptr->CHASH_PARAM_ENTRY_NEXT;
+    
+    if (next == CHashNullLink()) {
+        return CHashNullRef();
+    }
+    
+    CHashRef next_ref = CHash_Deref(arg, next);
+    if (!CHASH_PARAM_KEYSEQUAL(arg, next_ref.ptr->CHASH_PARAM_ENTRY_KEY, entry.ptr->CHASH_PARAM_ENTRY_KEY)) {
+        return CHashNullRef();
+    }
+    
+    return next_ref;
+}
+
+static size_t CHash_NumEntries (const CHash *o)
+{
+    return o->numEntries;
+}
+
+static int CHash_IsEmpty (const CHash *o)
+{
+    return o->numEntries == 0;
+}
+
+#include "CHash_footer.h"