]> granicus.if.org Git - graphviz/commitdiff
outline 'boxes_t' implementation into a generic list
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Sat, 3 Dec 2022 18:27:48 +0000 (10:27 -0800)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Sun, 4 Dec 2022 18:02:33 +0000 (10:02 -0800)
This will allow us to avoid reimplementing variants of the same list data
structure repeatedly. It extends the boxes functions with some other useful
additions and slightly modifies the growth strategy. On first addition, a single
element is now allocated instead of 128. This seems more appropriate for a data
structure that we intend to use more pervasively.

This also replaces common/memory.h allocation that was used in boxes.h with
cgraph/alloc.h calls.

lib/cgraph/CMakeLists.txt
lib/cgraph/Makefile.am
lib/cgraph/cgraph.vcxproj
lib/cgraph/cgraph.vcxproj.filters
lib/cgraph/list.h [new file with mode: 0644]
lib/common/boxes.h

index 7255b88a7677242ab92f4ceb9ed041d5448d79fd..a9daada00e0b24e498ecb791e24cbee585881cd8 100644 (file)
@@ -19,6 +19,7 @@ add_library(cgraph SHARED
   exit.h
   itos.h
   likely.h
+  list.h
   prisize_t.h
   stack.h
   startswith.h
index ef8b92b91ce7c42fbfac016b108378ec72c01104..3ea4a8d6cf794f1defde4fb115483a1e6095563f 100644 (file)
@@ -10,7 +10,7 @@ endif
 
 pkginclude_HEADERS = cgraph.h
 noinst_HEADERS = agxbuf.h alloc.h bitarray.h cghdr.h exit.h itos.h likely.h \
-       prisize_t.h stack.h startswith.h strcasecmp.h strview.h tokenize.h \
+       list.h prisize_t.h stack.h startswith.h strcasecmp.h strview.h tokenize.h \
        unreachable.h unused.h
 noinst_LTLIBRARIES = libcgraph_C.la
 lib_LTLIBRARIES = libcgraph.la
index ee95eeb2af038b1441571cdedf4b111b7150e6c7..158b1def6f4871c2723bb45b15262025a2afef1f 100644 (file)
@@ -105,6 +105,7 @@ win_flex -oscan.c scan.l</Command>
     <ClInclude Include="exit.h" />
     <ClInclude Include="itos.h" />
     <ClInclude Include="likely.h" />
+    <ClInclude Include="list.h" />
     <ClInclude Include="prisize_t.h" />
     <ClInclude Include="stack.h" />
     <ClInclude Include="startswith.h" />
index ee2d35f65d619d42180e690692ac2e7b9f1ff70c..56c361ee9cedff2de3356cadaa640adc0bee9fc6 100644 (file)
@@ -39,6 +39,9 @@
     <ClInclude Include="likely.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="list.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="prisize_t.h">
       <Filter>Header Files</Filter>
     </ClInclude>
diff --git a/lib/cgraph/list.h b/lib/cgraph/list.h
new file mode 100644 (file)
index 0000000..a9eafe5
--- /dev/null
@@ -0,0 +1,172 @@
+#pragma once
+
+#include <assert.h>
+#include <cgraph/alloc.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/** create a new list type and its associated member functions
+ *
+ * \param name Type name to give the list container
+ * \param type Type of the elements the list will store
+ */
+#define DEFINE_LIST(name, type)                                                \
+                                                                               \
+  /** list container                                                           \
+   *                                                                           \
+   * All members of this type are considered private. They should only be      \
+   * accessed through the functions below.                                     \
+   */                                                                          \
+  typedef struct {                                                             \
+    type *data;      /* backing storage */                                     \
+    size_t size;     /* number of elements in the list */                      \
+    size_t capacity; /* available storage slots */                             \
+  } name##_t;                                                                  \
+                                                                               \
+  /** create a new list                                                        \
+   *                                                                           \
+   * This function is provided for convenience, but it the equivalent of zero  \
+   * initialization, `list_t list = {0}`. In general, the latter should be     \
+   * preferred as it can be understood locally.                                \
+   *                                                                           \
+   * \return A new empty list                                                  \
+   */                                                                          \
+  static inline name##_t name##_new(void) { return (name##_t){0}; }            \
+                                                                               \
+  /** get the number of elements in a list */                                  \
+  static size_t name##_size(const name##_t *list) {                            \
+    assert(list != NULL);                                                      \
+    return list->size;                                                         \
+  }                                                                            \
+                                                                               \
+  /** does this list contain no elements? */                                   \
+  static inline bool name##_is_empty(const name##_t *list) {                   \
+    assert(list != NULL);                                                      \
+    return name##_size(list) == 0;                                             \
+  }                                                                            \
+                                                                               \
+  static inline void name##_append(name##_t *list, type item) {                \
+    assert(list != NULL);                                                      \
+                                                                               \
+    /* do we need to expand the backing storage? */                            \
+    if (list->size == list->capacity) {                                        \
+      size_t c = list->capacity == 0 ? 1 : (list->capacity * 2);               \
+      list->data = gv_recalloc(list->data, list->capacity, c, sizeof(type));   \
+      list->capacity = c;                                                      \
+    }                                                                          \
+                                                                               \
+    list->data[list->size] = item;                                             \
+    ++list->size;                                                              \
+  }                                                                            \
+                                                                               \
+  /** retrieve an element from a list                                          \
+   *                                                                           \
+   * \param list List to operate on                                            \
+   * \param index Element index to get                                         \
+   * \return Element at the given index                                        \
+   */                                                                          \
+  static inline type name##_get(const name##_t *list, size_t index) {          \
+    assert(list != NULL);                                                      \
+    assert(index < list->size && "index out of bounds");                       \
+    return list->data[index];                                                  \
+  }                                                                            \
+                                                                               \
+  /** assign to an element in a list                                           \
+   *                                                                           \
+   * \param list List to operate on                                            \
+   * \param index Element to assign to                                         \
+   * \param item Value to assign                                               \
+   */                                                                          \
+  static inline void name##_set(name##_t *list, size_t index, type item) {     \
+    assert(list != NULL);                                                      \
+    assert(index < list->size && "index out of bounds");                       \
+    list->data[index] = item;                                                  \
+  }                                                                            \
+                                                                               \
+  /** access an element in a list for the purpose of modification              \
+   *                                                                           \
+   * Because this acquires an internal pointer into the list structure, `get`  \
+   * and `set` should be preferred over this function. `get` and `set` are     \
+   * easier to reason about. In particular, the pointer returned by this       \
+   * function is invalidated by any list operation that may reallocate the     \
+   * backing storage (e.g. `shrink_to_fit`).                                   \
+   *                                                                           \
+   * \param list List to operate on                                            \
+   * \param index Element to get a pointer to                                  \
+   * \return Pointer to the requested element                                  \
+   */                                                                          \
+  static inline type *name##_at(name##_t *list, size_t index) {                \
+    assert(list != NULL);                                                      \
+    assert(index < list->size && "index out of bounds");                       \
+    return &list->data[index];                                                 \
+  }                                                                            \
+                                                                               \
+  /** remove all elements from a list */                                       \
+  static inline void name##_clear(name##_t *list) {                            \
+    assert(list != NULL);                                                      \
+    list->size = 0;                                                            \
+  }                                                                            \
+                                                                               \
+  /** shrink or grow the list to the given size                                \
+   *                                                                           \
+   * \param list List to operate on                                            \
+   * \param size New size of the list                                          \
+   * \param value Default to assign to any new elements                        \
+   */                                                                          \
+  static inline void name##_resize(name##_t *list, size_t size, type value) {  \
+    assert(list != NULL);                                                      \
+                                                                               \
+    while (list->size < size) {                                                \
+      name##_append(list, value);                                              \
+    }                                                                          \
+    list->size = size;                                                         \
+  }                                                                            \
+                                                                               \
+  /** deallocate unused backing storage, shrinking capacity to size */         \
+  static inline void name##_shrink_to_fit(name##_t *list) {                    \
+    assert(list != NULL);                                                      \
+                                                                               \
+    if (list->size > list->capacity) {                                         \
+      list->data =                                                             \
+          gv_recalloc(list->data, list->capacity, list->size, sizeof(type));   \
+      list->capacity = list->size;                                             \
+    }                                                                          \
+  }                                                                            \
+                                                                               \
+  /** free resources associated with a list */                                 \
+  static inline void name##_free(name##_t *list) {                             \
+    assert(list != NULL);                                                      \
+    free(list->data);                                                          \
+    *list = (name##_t){0};                                                     \
+  }                                                                            \
+                                                                               \
+  /** create a new list from a bare array and element count                    \
+   *                                                                           \
+   * This can be useful when receiving data from a caller who does not use     \
+   * this API, but the callee wants to. Note that the backing data for the     \
+   * array must have been heap-allocated.                                      \
+   *                                                                           \
+   * \param data Array of existing elements                                    \
+   * \param size Number of elements pointed to by `data`                       \
+   * \return A managed list containing the provided elements                   \
+   */                                                                          \
+  static inline name##_t name##_attach(type *data, size_t size) {              \
+    assert(data != NULL || size == 0);                                         \
+    return (name##_t){.data = data, .size = size, .capacity = size};           \
+  }                                                                            \
+                                                                               \
+  /** transform a managed list into a bare array                               \
+   *                                                                           \
+   * This can be useful when needing to pass data to a callee who does not     \
+   * use this API. The managed list is emptied and left in a state where it    \
+   * can be reused for other purposes.                                         \
+   *                                                                           \
+   * \param list List to operate on                                            \
+   * \return A pointer to an array of the `list->size` elements                \
+   */                                                                          \
+  static inline type *name##_detach(name##_t *list) {                          \
+    assert(list != NULL);                                                      \
+    type *data = list->data;                                                   \
+    *list = (name##_t){0};                                                     \
+    return data;                                                               \
+  }
index c8885a81c82568874436fda335426d1f64ff0d87..ebb9b67fd310c5937da2e7ac2bd52ef050e870da 100644 (file)
@@ -1,59 +1,6 @@
 #pragma once
 
-#include <assert.h>
+#include <cgraph/list.h>
 #include <common/geom.h>
-#include <common/memory.h>
-#include <stdlib.h>
-#include <string.h>
 
-// a dynamically expanding array of boxes
-typedef struct {
-  boxf *data;
-  size_t size;
-  size_t capacity;
-} boxes_t;
-
-/** Add an entry to the end of a boxes array.
- *
- * This may expand the array if it is not already large enough to contain the
- * new element.
- *
- * \param boxes Array to append to.
- * \param item Element to append.
- */
-static inline void boxes_append(boxes_t *boxes, boxf item) {
-
-  assert(boxes != NULL);
-
-  // do we need to expand the array?
-  if (boxes->size == boxes->capacity) {
-    size_t c = boxes->capacity == 0 ? 128 : (boxes->capacity * 2);
-    boxes->data = grealloc(boxes->data, c * sizeof(boxes->data[0]));
-    boxes->capacity = c;
-  }
-
-  boxes->data[boxes->size] = item;
-  ++boxes->size;
-}
-
-/** Remove all entries from a boxes array.
- *
- * \param boxes Array to clear.
- */
-static inline void boxes_clear(boxes_t *boxes) {
-  assert(boxes != NULL);
-  boxes->size = 0;
-}
-
-/** Deallocate memory associated with a boxes array.
- *
- * Following a call to this function, the array is reusable as if it had just
- * been initialized.
- *
- * \param boxes Array to deallocate.
- */
-static inline void boxes_free(boxes_t *boxes) {
-  assert(boxes != NULL);
-  free(boxes->data);
-  memset(boxes, 0, sizeof(*boxes));
-}
+DEFINE_LIST(boxes, boxf)