summaryrefslogtreecommitdiff
path: root/darray.h
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-07-24 10:42:04 -0400
committerPeter Zhu <peter@peterzhu.ca>2023-08-25 09:01:21 -0400
commit633243958c1b19e7d934442e23ed3d7cc3d7691b (patch)
treed40ae8c396560c38755eb4cf9b34607b526c9b29 /darray.h
parent7002d44c103fa2dfc4f4cd48ef1573fcd3a0dd73 (diff)
Add _without_gc functions to darray
These functions manipulate darray without the possibility of triggering GC, which is used for places that cannot trigger GC. These functions crash when the allocation fails.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/8113
Diffstat (limited to 'darray.h')
-rw-r--r--darray.h114
1 files changed, 85 insertions, 29 deletions
diff --git a/darray.h b/darray.h
index db95c8b60e..b7ab0ab154 100644
--- a/darray.h
+++ b/darray.h
@@ -5,6 +5,8 @@
#include <stddef.h>
#include <stdlib.h>
+#include "internal/gc.h"
+
// Type for a dynamic array. Use to declare a dynamic array.
// It is a pointer so it fits in st_table nicely. Designed
// to be fairly type-safe.
@@ -37,13 +39,21 @@
//
#define rb_darray_ref(ary, idx) (&((ary)->data[(idx)]))
-// Copy a new element into the array. ptr_to_ary is evaluated multiple times.
-//
-// void rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
-//
-#define rb_darray_append(ptr_to_ary, element) do { \
- rb_darray_ensure_space((ptr_to_ary), sizeof(**(ptr_to_ary)), \
- sizeof((*(ptr_to_ary))->data[0])); \
+/* Copy a new element into the array. ptr_to_ary is evaluated multiple times.
+ *
+ * void rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
+ */
+#define rb_darray_append(ptr_to_ary, element) \
+ rb_darray_append_impl(ptr_to_ary, element, rb_xrealloc_mul_add)
+
+#define rb_darray_append_without_gc(ptr_to_ary, element) \
+ rb_darray_append_impl(ptr_to_ary, element, rb_darray_realloc_mul_add_without_gc)
+
+#define rb_darray_append_impl(ptr_to_ary, element, realloc_func) do { \
+ rb_darray_ensure_space((ptr_to_ary), \
+ sizeof(**(ptr_to_ary)), \
+ sizeof((*(ptr_to_ary))->data[0]), \
+ realloc_func); \
rb_darray_set(*(ptr_to_ary), \
(*(ptr_to_ary))->meta.size, \
(element)); \
@@ -55,28 +65,45 @@
#define rb_darray_foreach(ary, idx_name, elem_ptr_var) \
for (size_t idx_name = 0; idx_name < rb_darray_size(ary) && ((elem_ptr_var) = rb_darray_ref(ary, idx_name)); ++idx_name)
-// Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
-//
-// Note that NULL is a valid empty dynamic array.
-//
-// void rb_darray_make(rb_darray(T) *ptr_to_ary, size_t size);
+// Iterate over valid indices in the array in a for loop
//
+#define rb_darray_for(ary, idx_name) \
+ for (size_t idx_name = 0; idx_name < rb_darray_size(ary); ++idx_name)
+
+/* Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
+ * Return 1 on success and 0 on failure.
+ *
+ * Note that NULL is a valid empty dynamic array.
+ *
+ * void rb_darray_make(rb_darray(T) *ptr_to_ary, size_t size);
+ */
#define rb_darray_make(ptr_to_ary, size) \
rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), \
- sizeof((*(ptr_to_ary))->data[0]))
+ sizeof((*(ptr_to_ary))->data[0]), rb_xcalloc_mul_add)
-#define rb_darray_data_ptr(ary) ((ary)->data)
+#define rb_darray_make_without_gc(ptr_to_ary, size) \
+ rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), \
+ sizeof((*(ptr_to_ary))->data[0]), rb_darray_calloc_mul_add_without_gc)
-// Set the size of the array to zero without freeing the backing memory.
-// Allows reusing the same array.
-//
-#define rb_darray_clear(ary) (ary->meta.size = 0)
+#define rb_darray_data_ptr(ary) ((ary)->data)
typedef struct rb_darray_meta {
size_t size;
size_t capa;
} rb_darray_meta_t;
+// Set the size of the array to zero without freeing the backing memory.
+// Allows reusing the same array.
+//
+static inline void
+rb_darray_clear(void *ary)
+{
+ rb_darray_meta_t *meta = ary;
+ if (meta) {
+ meta->size = 0;
+ }
+}
+
// Get the size of the dynamic array.
//
static inline size_t
@@ -95,20 +122,52 @@ rb_darray_capa(const void *ary)
return meta ? meta->capa : 0;
}
-// Free the dynamic array.
-//
+/* Free the dynamic array. */
static inline void
rb_darray_free(void *ary)
{
rb_darray_meta_t *meta = ary;
- ruby_sized_xfree(ary, meta->capa);
+ if (meta) ruby_sized_xfree(ary, meta->capa);
+}
+
+static inline void
+rb_darray_free_without_gc(void *ary)
+{
+ free(ary);
+}
+
+/* Internal function. Like rb_xcalloc_mul_add but does not trigger GC and does
+ * not check for overflow in arithmetic. */
+static inline void *
+rb_darray_calloc_mul_add_without_gc(size_t x, size_t y, size_t z)
+{
+ size_t size = (x * y) + z;
+
+ void *ptr = calloc(1, size);
+ if (ptr == NULL) rb_bug("rb_darray_calloc_mul_add_without_gc: failed");
+
+ return ptr;
+}
+
+/* Internal function. Like rb_xrealloc_mul_add but does not trigger GC and does
+ * not check for overflow in arithmetic. */
+static inline void *
+rb_darray_realloc_mul_add_without_gc(const void *orig_ptr, size_t x, size_t y, size_t z)
+{
+ size_t size = (x * y) + z;
+
+ void *ptr = realloc((void *)orig_ptr, size);
+ if (ptr == NULL) rb_bug("rb_darray_realloc_mul_add_without_gc: failed");
+
+ return ptr;
}
// Internal function
// Ensure there is space for one more element.
// Note: header_size can be bigger than sizeof(rb_darray_meta_t) when T is __int128_t, for example.
static inline void
-rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size)
+rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size,
+ void *(*realloc_mul_add_impl)(const void *, size_t, size_t, size_t))
{
rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
rb_darray_meta_t *meta = *ptr_to_ptr_to_meta;
@@ -118,9 +177,7 @@ rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size
// Double the capacity
size_t new_capa = current_capa == 0 ? 1 : current_capa * 2;
- rb_darray_meta_t *doubled_ary = rb_xrealloc_mul_add(meta, new_capa, element_size, header_size);
- // rb_xrealloc functions guarantee that NULL is not returned
- assert(doubled_ary != NULL);
+ rb_darray_meta_t *doubled_ary = realloc_mul_add_impl(meta, new_capa, element_size, header_size);
if (meta == NULL) {
// First allocation. Initialize size. On subsequence allocations
@@ -136,7 +193,8 @@ rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size
}
static inline void
-rb_darray_make_impl(void *ptr_to_ary, size_t array_size, size_t header_size, size_t element_size)
+rb_darray_make_impl(void *ptr_to_ary, size_t array_size, size_t header_size, size_t element_size,
+ void *(*calloc_mul_add_impl)(size_t, size_t, size_t))
{
rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
if (array_size == 0) {
@@ -144,9 +202,7 @@ rb_darray_make_impl(void *ptr_to_ary, size_t array_size, size_t header_size, siz
return;
}
- rb_darray_meta_t *meta = rb_xcalloc_mul_add(array_size, element_size, header_size);
- // rb_xcalloc functions guarantee that NULL is not returned
- assert(meta != NULL);
+ rb_darray_meta_t *meta = calloc_mul_add_impl(array_size, element_size, header_size);
meta->size = array_size;
meta->capa = array_size;