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.
This commit is contained in:
Peter Zhu 2023-07-24 10:42:04 -04:00
parent 7002d44c10
commit 633243958c
Notes: git 2023-08-25 13:01:45 +00:00

114
darray.h
View File

@ -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_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)
#define rb_darray_data_ptr(ary) ((ary)->data)
// 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)
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;