update ucx

Sun, 21 May 2023 16:34:58 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 21 May 2023 16:34:58 +0200
changeset 750
4d7a2238c5ac
parent 749
bbadf84cfc2d
child 751
9598ff469033

update ucx

dav/Makefile file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
libidav/Makefile file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
ucx/array_list.c file | annotate | diff | comparison | revisions
ucx/cx/iterator.h file | annotate | diff | comparison | revisions
ucx/cx/list.h file | annotate | diff | comparison | revisions
ucx/cx/map.h file | annotate | diff | comparison | revisions
ucx/hash_map.c file | annotate | diff | comparison | revisions
ucx/linked_list.c file | annotate | diff | comparison | revisions
ucx/list.c file | annotate | diff | comparison | revisions
ucx/map.c file | annotate | diff | comparison | revisions
--- a/dav/Makefile	Sun May 21 11:52:06 2023 +0200
+++ b/dav/Makefile	Sun May 21 16:34:58 2023 +0200
@@ -81,9 +81,9 @@
 	$(LD) -o $(XATTRTOOL_BIN) $(XATTR_OBJ) $(LDFLAGS)
 
 ../build/tool/%$(OBJ_EXT): %.c 
-	$(CC) $(CFLAGS) $(DAV_CFLAGS) -I../ -I../ucx -c -o $@ $<
+	$(CC) -I../ucx -I../ $(CFLAGS) $(DAV_CFLAGS) -c -o $@ $<
 
 
 cppcheck: $(DAV_SRC) $(SYNC_SRC)
-	$(CPPCHECK) $(CPPCHECK_CONFIG) $(CPPCHECK_FLAGS) -I../ucx -I../ $+ 2>> ../$(CPPCHECK_LOG)
+	$(CPPCHECK) $(CPPCHECK_CONFIG) -I../ucx  $(CPPCHECK_FLAGS) $+ 2>> ../$(CPPCHECK_LOG)
 
--- a/dav/sync.c	Sun May 21 11:52:06 2023 +0200
+++ b/dav/sync.c	Sun May 21 16:34:58 2023 +0200
@@ -36,8 +36,10 @@
 #include <utime.h>
 #include <libxml/xmlerror.h>
 #include <sys/types.h>
+#include <cx/map.h>
 #include <cx/string.h>
 #include <cx/utils.h>
+#include <cx/list.h>
 #include <cx/hash_map.h>
 #include <cx/printf.h>
 #include <dirent.h>
@@ -115,45 +117,12 @@
 
 
 
-
-static bool nulliter_valid(void const *d) {
-    return false;
-}
-
-static void * nulliter_current(void const *d) {
-    return NULL;
-}
-
-void * nulliter_current_impl(void const *d) {
-    return NULL;
-}
-
-void nulliter_next(void *d) {
-    // noop
-}
-
-bool nulliter_flag_removal(void *d) {
-    return false;
-}
-
-
-static CxIterator nullIterator(void) {
-    CxIterator iter;
-    memset(&iter, 0, sizeof(CxIterator));
-    iter.base.valid = nulliter_valid;
-    iter.base.current = nulliter_current;
-    iter.base.current_impl = nulliter_current_impl;
-    iter.base.next = nulliter_next;
-    iter.base.flag_removal = nulliter_flag_removal;
-    return iter;
-}
-
 static CxIterator mapIteratorValues(CxMap *map) {
-    return map ? cxMapIteratorValues(map) : nullIterator();
+    return cxMapIteratorValues(map ? map : cxEmptyMap);
 }
 
 static CxIterator listIterator(CxList *list) {
-    return list ? cxListIterator(list) : nullIterator();
+    return cxListIterator(list ? list : cxEmptyList);
 }
 
 typedef void*(*clonefunc)(void *elm, void *userdata);
--- a/libidav/Makefile	Sun May 21 11:52:06 2023 +0200
+++ b/libidav/Makefile	Sun May 21 16:34:58 2023 +0200
@@ -50,8 +50,8 @@
 	$(AR) $(ARFLAGS) $(AOFLAGS)$@ $(OBJ)
 
 ../build/libidav/%$(OBJ_EXT): %.c
-	$(CC) $(CFLAGS) $(DAV_CFLAGS) -I.. -I../ucx -c -o $@ $<
+	$(CC) -I../ucx $(CFLAGS) $(DAV_CFLAGS) -c -o $@ $<
 
 cppcheck: $(SRC)
-	$(CPPCHECK) $(CPPCHECK_CONFIG) $(CPPCHECK_FLAGS) -I../ucx $+ 2>> ../$(CPPCHECK_LOG)
+	$(CPPCHECK) $(CPPCHECK_CONFIG) -I../ucx $(CPPCHECK_FLAGS) $+ 2>> ../$(CPPCHECK_LOG)
 
--- a/ucx/Makefile	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/Makefile	Sun May 21 16:34:58 2023 +0200
@@ -39,6 +39,7 @@
 SRC += hash_map.c
 SRC += linked_list.c
 SRC += list.c
+SRC += map.c
 SRC += printf.c
 SRC += string.c
 SRC += utils.c
--- a/ucx/array_list.c	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/array_list.c	Sun May 21 16:34:58 2023 +0200
@@ -169,7 +169,24 @@
 
 static void cx_arl_destructor(struct cx_list_s *list) {
     cx_array_list *arl = (cx_array_list *) list;
+
+    char *ptr = arl->data;
+
+    if (list->simple_destructor) {
+        for (size_t i = 0; i < list->size; i++) {
+            cx_invoke_simple_destructor(list, ptr);
+            ptr += list->item_size;
+        }
+    }
+    if (list->advanced_destructor) {
+        for (size_t i = 0; i < list->size; i++) {
+            cx_invoke_advanced_destructor(list, ptr);
+            ptr += list->item_size;
+        }
+    }
+
     cxFree(list->allocator, arl->data);
+    cxFree(list->allocator, list);
 }
 
 static size_t cx_arl_insert_array(
--- a/ucx/cx/iterator.h	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/cx/iterator.h	Sun May 21 16:34:58 2023 +0200
@@ -51,6 +51,8 @@
 
     /**
      * Returns a pointer to the current element.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     void *(*current)(void const *);
@@ -63,12 +65,16 @@
 
     /**
      * Advances the iterator.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     void (*next)(void *);
 
     /**
      * Flag current element for removal, if possible.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     bool (*flag_removal)(void *);
--- a/ucx/cx/list.h	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/cx/list.h	Sun May 21 16:34:58 2023 +0200
@@ -70,6 +70,9 @@
 struct cx_list_class_s {
     /**
      * Destructor function.
+     *
+     * Implementations SHALL invoke the content destructor functions if provided
+     * and SHALL deallocate the list memory, if an allocator is provided.
      */
     void (*destructor)(struct cx_list_s *list);
 
@@ -632,6 +635,14 @@
 __attribute__((__nonnull__))
 void cxListDestroy(CxList *list);
 
+/**
+ * A shared instance of an empty list.
+ *
+ * Writing to that list is undefined.
+ */
+extern CxList * const cxEmptyList;
+
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
--- a/ucx/cx/map.h	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/cx/map.h	Sun May 21 16:34:58 2023 +0200
@@ -63,6 +63,24 @@
 };
 
 /**
+ * The type of iterator for a map.
+ */
+enum cx_map_iterator_type {
+    /**
+     * Iterates over key/value pairs.
+     */
+    CX_MAP_ITERATOR_PAIRS,
+    /**
+     * Iterates over keys only.
+     */
+    CX_MAP_ITERATOR_KEYS,
+    /**
+     * Iterates over values only.
+     */
+    CX_MAP_ITERATOR_VALUES
+};
+
+/**
  * The class definition for arbitrary maps.
  */
 struct cx_map_class_s {
@@ -108,40 +126,10 @@
     );
 
     /**
-     * Iterator over the key/value pairs.
-     */
-    __attribute__((__nonnull__, __warn_unused_result__))
-    CxIterator (*iterator)(CxMap const *map);
-
-    /**
-     * Iterator over the keys.
-     */
-    __attribute__((__nonnull__, __warn_unused_result__))
-    CxIterator (*iterator_keys)(CxMap const *map);
-
-    /**
-     * Iterator over the values.
+     * Creates an iterator for this map.
      */
     __attribute__((__nonnull__, __warn_unused_result__))
-    CxIterator (*iterator_values)(CxMap const *map);
-
-    /**
-     * Mutating iterator over the key/value pairs.
-     */
-    __attribute__((__nonnull__, __warn_unused_result__))
-    CxMutIterator (*mut_iterator)(CxMap *map);
-
-    /**
-     * Mutating iterator over the keys.
-     */
-    __attribute__((__nonnull__, __warn_unused_result__))
-    CxMutIterator (*mut_iterator_keys)(CxMap *map);
-
-    /**
-     * Mutating iterator over the values.
-     */
-    __attribute__((__nonnull__, __warn_unused_result__))
-    CxMutIterator (*mut_iterator_values)(CxMap *map);
+    CxIterator (*iterator)(CxMap const *map, enum cx_map_iterator_type type);
 };
 
 /**
@@ -158,6 +146,8 @@
     void *value;
 };
 
+extern CxMap *const cxEmptyMap;
+
 /**
  * Advises the map to store copies of the objects (default mode of operation).
  *
@@ -225,8 +215,8 @@
  * @return an iterator for the currently stored values
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIteratorValues(CxMap *map) {
-    return map->cl->iterator_values(map);
+static inline CxIterator cxMapIteratorValues(CxMap const *map) {
+    return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
 }
 
 /**
@@ -241,8 +231,8 @@
  * @return an iterator for the currently stored keys
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIteratorKeys(CxMap *map) {
-    return map->cl->iterator_keys(map);
+static inline CxIterator cxMapIteratorKeys(CxMap const *map) {
+    return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
 }
 
 /**
@@ -259,8 +249,8 @@
  * @see cxMapIteratorValues()
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxIterator cxMapIterator(CxMap *map) {
-    return map->cl->iterator(map);
+static inline CxIterator cxMapIterator(CxMap const *map) {
+    return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
 }
 
 
@@ -274,9 +264,7 @@
  * @return an iterator for the currently stored values
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIteratorValues(CxMap *map) {
-    return map->cl->mut_iterator_values(map);
-}
+CxMutIterator cxMapMutIteratorValues(CxMap *map);
 
 /**
  * Creates a mutating iterator over the keys of a map.
@@ -290,9 +278,7 @@
  * @return an iterator for the currently stored keys
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIteratorKeys(CxMap *map) {
-    return map->cl->mut_iterator_keys(map);
-}
+CxMutIterator cxMapMutIteratorKeys(CxMap *map);
 
 /**
  * Creates a mutating iterator for a map.
@@ -308,9 +294,7 @@
  * @see cxMapMutIteratorValues()
  */
 __attribute__((__nonnull__, __warn_unused_result__))
-static inline CxMutIterator cxMapMutIterator(CxMap *map) {
-    return map->cl->mut_iterator(map);
-}
+CxMutIterator cxMapMutIterator(CxMap *map);
 
 #ifdef __cplusplus
 } // end the extern "C" block here, because we want to start overloading
--- a/ucx/hash_map.c	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/hash_map.c	Sun May 21 16:34:58 2023 +0200
@@ -26,10 +26,12 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <string.h>
 #include "cx/hash_map.h"
 #include "cx/utils.h"
 
+#include <string.h>
+#include <assert.h>
+
 struct cx_hash_map_element_s {
     /** A pointer to the next element in the current bucket. */
     struct cx_hash_map_element_s *next;
@@ -334,13 +336,30 @@
     }
 }
 
-static CxIterator cx_hash_map_iterator(CxMap const *map) {
+static CxIterator cx_hash_map_iterator(
+        CxMap const *map,
+        enum cx_map_iterator_type type
+) {
     CxIterator iter;
 
     iter.src_handle = map;
     iter.base.valid = cx_hash_map_iter_valid;
     iter.base.next = cx_hash_map_iter_next;
-    iter.base.current = cx_hash_map_iter_current_entry;
+
+    switch (type) {
+        case CX_MAP_ITERATOR_PAIRS:
+            iter.base.current = cx_hash_map_iter_current_entry;
+            break;
+        case CX_MAP_ITERATOR_KEYS:
+            iter.base.current = cx_hash_map_iter_current_key;
+            break;
+        case CX_MAP_ITERATOR_VALUES:
+            iter.base.current = cx_hash_map_iter_current_value;
+            break;
+        default:
+            assert(false);
+    }
+
     iter.base.flag_removal = cx_hash_map_iter_flag_rm;
     iter.base.remove = false;
     iter.base.mutating = false;
@@ -370,40 +389,6 @@
     return iter;
 }
 
-static CxIterator cx_hash_map_iterator_keys(CxMap const *map) {
-    CxIterator iter = cx_hash_map_iterator(map);
-    iter.base.current = cx_hash_map_iter_current_key;
-    return iter;
-}
-
-static CxIterator cx_hash_map_iterator_values(CxMap const *map) {
-    CxIterator iter = cx_hash_map_iterator(map);
-    iter.base.current = cx_hash_map_iter_current_value;
-    return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator(CxMap *map) {
-    CxIterator it = cx_hash_map_iterator(map);
-    it.base.mutating = true;
-
-    // we know the iterators share the same memory layout
-    CxMutIterator iter;
-    memcpy(&iter, &it, sizeof(CxMutIterator));
-    return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator_keys(CxMap *map) {
-    CxMutIterator iter = cx_hash_map_mut_iterator(map);
-    iter.base.current = cx_hash_map_iter_current_key;
-    return iter;
-}
-
-static CxMutIterator cx_hash_map_mut_iterator_values(CxMap *map) {
-    CxMutIterator iter = cx_hash_map_mut_iterator(map);
-    iter.base.current = cx_hash_map_iter_current_value;
-    return iter;
-}
-
 static cx_map_class cx_hash_map_class = {
         cx_hash_map_destructor,
         cx_hash_map_clear,
@@ -411,11 +396,6 @@
         cx_hash_map_get,
         cx_hash_map_remove,
         cx_hash_map_iterator,
-        cx_hash_map_iterator_keys,
-        cx_hash_map_iterator_values,
-        cx_hash_map_mut_iterator,
-        cx_hash_map_mut_iterator_keys,
-        cx_hash_map_mut_iterator_values,
 };
 
 CxMap *cxHashMapCreate(
--- a/ucx/linked_list.c	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/linked_list.c	Sun May 21 16:34:58 2023 +0200
@@ -283,7 +283,7 @@
 #define CX_LINKED_LIST_SORT_SBO_SIZE 1024
 #endif
 
-static void *cx_linked_list_sort_merge(
+static void cx_linked_list_sort_merge(
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next,
         ptrdiff_t loc_data,
@@ -291,7 +291,9 @@
         void *ls,
         void *le,
         void *re,
-        cx_compare_func cmp_func
+        cx_compare_func cmp_func,
+        void **begin,
+        void **end
 ) {
     void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
     void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
@@ -330,11 +332,11 @@
     }
     ll_next(sorted[length - 1]) = NULL;
 
-    void *ret = sorted[0];
+    *begin = sorted[0];
+    *end = sorted[length-1];
     if (sorted != sbo) {
         free(sorted);
     }
-    return ret;
 }
 
 void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
@@ -380,8 +382,10 @@
         re = ll_next(rc);
 
         // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
-        void *sorted = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
-                                                 ln + rn, ls, le, re, cmp_func);
+        void *sorted_begin, *sorted_end;
+        cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+                                  ln + rn, ls, le, re, cmp_func,
+                                  &sorted_begin, &sorted_end);
 
         // Something left? Sort it!
         size_t remainder_length = cx_linked_list_size(re, loc_next);
@@ -390,14 +394,13 @@
             cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
 
             // merge sorted list with (also sorted) remainder
-            *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
-                                               ln + rn + remainder_length,
-                                               sorted, remainder, NULL, cmp_func);
-        } else {
-            // no remainder - we've got our sorted list
-            *begin = sorted;
+            cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+                                      ln + rn + remainder_length,
+                                      sorted_begin, remainder, NULL, cmp_func,
+                                      &sorted_begin, &sorted_end);
         }
-        if (end) *end = cx_linked_list_last(sorted, loc_next);
+        *begin = sorted_begin;
+        if (end) *end = sorted_end;
     }
 }
 
@@ -873,11 +876,13 @@
 
     cx_linked_list_node *node = ll->begin;
     while (node) {
+        cx_invoke_destructor(list, node->payload);
         void *next = node->next;
         cxFree(list->allocator, node);
         node = next;
     }
-    // do not free the list pointer, this is just a destructor!
+
+    cxFree(list->allocator, list);
 }
 
 static cx_list_class cx_linked_list_class = {
--- a/ucx/list.c	Sun May 21 11:52:06 2023 +0200
+++ b/ucx/list.c	Sun May 21 16:34:58 2023 +0200
@@ -195,34 +195,102 @@
 
 // </editor-fold>
 
+// <editor-fold desc="empty list implementation">
+
+static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) {
+    // this is a noop, but MUST be implemented
+}
+
+static void *cx_emptyl_at(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        __attribute__((__unused__)) size_t index
+) {
+    return NULL;
+}
+
+static ssize_t cx_emptyl_find(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        __attribute__((__unused__)) void const *elem
+) {
+    return -1;
+}
+
+static int cx_emptyl_compare(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    if (other->size == 0) return 0;
+    return -1;
+}
+
+static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) {
+    return false;
+}
+
+static CxIterator cx_emptyl_iterator(
+        struct cx_list_s const *list,
+        size_t index,
+        __attribute__((__unused__)) bool backwards
+) {
+    CxIterator iter = {0};
+    iter.src_handle = list;
+    iter.index = index;
+    iter.base.valid = cx_emptyl_iter_valid;
+    return iter;
+}
+
+static cx_list_class cx_empty_list_class = {
+        cx_emptyl_noop,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        cx_emptyl_noop,
+        NULL,
+        cx_emptyl_at,
+        cx_emptyl_find,
+        cx_emptyl_noop,
+        cx_emptyl_compare,
+        cx_emptyl_noop,
+        cx_emptyl_iterator,
+};
+
+CxList cx_empty_list = {
+        NULL,
+        NULL,
+        0,
+        0,
+        NULL,
+        NULL,
+        NULL,
+        false,
+        &cx_empty_list_class,
+        NULL
+};
+
+CxList *const cxEmptyList = &cx_empty_list;
+
+// </editor-fold>
+
 void cxListDestroy(CxList *list) {
-    if (list->simple_destructor) {
-        CxIterator iter = cxListIterator(list);
-        cx_foreach(void*, elem, iter) {
-            // already correctly resolved pointer - immediately invoke dtor
-            list->simple_destructor(elem);
-        }
-    }
-    if (list->advanced_destructor) {
-        CxIterator iter = cxListIterator(list);
-        cx_foreach(void*, elem, iter) {
-            // already correctly resolved pointer - immediately invoke dtor
-            list->advanced_destructor(list->destructor_data, elem);
-        }
-    }
-
     list->cl->destructor(list);
-    cxFree(list->allocator, list);
 }
 
 int cxListCompare(
         CxList const *list,
         CxList const *other
 ) {
-    if ((list->store_pointer ^ other->store_pointer) ||
-        ((list->climpl == NULL) ^ (other->climpl != NULL)) ||
+    if (
+        // if one is storing pointers but the other is not
+        (list->store_pointer ^ other->store_pointer) ||
+
+        // if one class is wrapped but the other is not
+        ((list->climpl == NULL) ^ (other->climpl == NULL)) ||
+
+        // if the resolved compare functions are not the same
         ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) !=
-         (other->climpl != NULL ? other->climpl->compare : other->cl->compare))) {
+         (other->climpl != NULL ? other->climpl->compare : other->cl->compare))
+    ) {
         // lists are definitely different - cannot use internal compare function
         if (list->size == other->size) {
             CxIterator left = cxListIterator(list);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/map.c	Sun May 21 16:34:58 2023 +0200
@@ -0,0 +1,112 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * 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.
+ *
+ * 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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "cx/map.h"
+#include <string.h>
+
+// <editor-fold desc="empty map implementation">
+
+static void cx_empty_map_noop(__attribute__((__unused__)) CxMap *map) {
+    // this is a noop, but MUST be implemented
+}
+
+static void *cx_empty_map_get(
+        __attribute__((__unused__)) CxMap const *map,
+        __attribute__((__unused__)) CxHashKey key
+) {
+    return NULL;
+}
+
+static bool cx_empty_map_iter_valid(__attribute__((__unused__)) void const *iter) {
+    return false;
+}
+
+static CxIterator cx_empty_map_iterator(
+        struct cx_map_s const *map,
+        __attribute__((__unused__)) enum cx_map_iterator_type type
+) {
+    CxIterator iter = {0};
+    iter.src_handle = map;
+    iter.base.valid = cx_empty_map_iter_valid;
+    return iter;
+}
+
+static struct cx_map_class_s cx_empty_map_class = {
+        cx_empty_map_noop,
+        cx_empty_map_noop,
+        NULL,
+        cx_empty_map_get,
+        NULL,
+        cx_empty_map_iterator
+};
+
+CxMap cx_empty_map = {
+        NULL,
+        NULL,
+        0,
+        0,
+        NULL,
+        NULL,
+        NULL,
+        false,
+        &cx_empty_map_class
+};
+
+CxMap *const cxEmptyMap = &cx_empty_map;
+
+// </editor-fold>
+
+CxMutIterator cxMapMutIteratorValues(CxMap *map) {
+    CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
+}
+
+CxMutIterator cxMapMutIteratorKeys(CxMap *map) {
+    CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
+}
+
+CxMutIterator cxMapMutIterator(CxMap *map) {
+    CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
+}

mercurial