From 5765b8c3099baabb354a025d8a6cd39f8fb4c5e1 Mon Sep 17 00:00:00 2001 From: Matthew Zito Date: Mon, 4 Nov 2024 21:21:55 -0800 Subject: [PATCH] feat(buffer): add buffer_slice util fn --- include/libutil.h | 19 ++++++++++++++++-- src/array.c | 31 +++++++++++++++++++++++++++++ src/buffer.c | 22 +++++++++++++++++++++ t/array_test.c | 14 +++++++++++++ t/buffer_test.c | 50 +++++++++++++++++++++++++++++++++++++++++++++-- t/io_test.c | 4 ++-- t/main.c | 2 +- 7 files changed, 135 insertions(+), 7 deletions(-) diff --git a/include/libutil.h b/include/libutil.h index 2133ad4..023e27b 100644 --- a/include/libutil.h +++ b/include/libutil.h @@ -177,10 +177,13 @@ void array_foreach(array_t *array, callback_t *callback); */ array_t *array_concat(array_t *arr1, array_t *arr2); +bool array_insert(array_t *array, unsigned int index, void *el, + free_fn *free_old_el); + /** * array_free frees the array and its internal state container. Safe to use - * with an array of primitives. Accepts an optional function pointer if you want - * all values to be freed. + * with an array of primitives. Accepts an optional function pointer if you + * want all values to be freed. */ void array_free(array_t *array, free_fn *free_fnptr); @@ -229,6 +232,18 @@ bool buffer_append_with(buffer_t *buf, const char *s, unsigned int len); */ buffer_t *buffer_concat(buffer_t *buf_a, buffer_t *buf_b); +/** + * Retrieve a slice of the buffer state. + * + * @param self the buffer instance + * @param start start index of the slice + * @param end_inclusive inclusive end index of the slice + * @param dest a buffer into which the slice will be stored + * @return int -1 if an error occurred, else 0 + */ +int buffer_slice(buffer_t *self, unsigned int start, unsigned int end_inclusive, + char *dest); + /** * buffer_free deallocates the dynamic memory used by a given buffer_t*. */ diff --git a/src/array.c b/src/array.c index b051b28..89b0712 100644 --- a/src/array.c +++ b/src/array.c @@ -112,6 +112,37 @@ bool array_push(array_t *array, void *el) { return true; } +bool array_insert(array_t *array, unsigned int index, void *el, + free_fn *free_old_el) { + __array_t *internal = (__array_t *)array; + + if (internal->capacity < index) { + void **next_state = realloc(internal->state, (index) * sizeof(void *)); + if (!next_state) { + free(next_state); + errno = ENOMEM; + + return false; + } + + internal->state = next_state; + internal->capacity = index; + + internal->state[internal->size++] = el; + + return true; + } + + void *old_el = internal->state[internal->size++]; + if (old_el && free_old_el) { + free_old_el(old_el); + } + + internal->state[internal->size] = el; + + return true; +} + void *array_pop(array_t *array) { __array_t *internal = (__array_t *)array; diff --git a/src/buffer.c b/src/buffer.c index aeb4dc2..abe1aaa 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -91,3 +91,25 @@ void buffer_free(buffer_t *buf) { free(internal); } + +// TODO: error enums +// TODO: rename all to `self` +// TODO: remove inclusivity flags - user can just pass the end index they want +int buffer_slice(buffer_t *self, unsigned int start, unsigned int end_inclusive, + char *dest) { + __buffer_t *internal = (__buffer_t *)self; + + if (internal->len < end_inclusive || internal->state == NULL) { + return -1; + } + + unsigned int x = 0; + unsigned int i = start; + for (; i <= end_inclusive; i++, x++) { + memcpy(&dest[x], &internal->state[i], 1); + } + + dest[x] = '\0'; + + return 0; +} diff --git a/t/array_test.c b/t/array_test.c index 8060f0f..5644c70 100644 --- a/t/array_test.c +++ b/t/array_test.c @@ -325,11 +325,15 @@ void test_array_concat(void) { array_t *arr2 = array_collect(v[3], v[4], v[5]); array_t *concatenated = array_concat(arr1, arr2); + array_free(arr1, NULL); + array_free(arr2, NULL); ok(array_size(concatenated) == 6, "has the expected size"); foreach (concatenated, i) { is(array_get(concatenated, i), v[i], "contains %s at index %d", v[i], i); } + + array_free(concatenated, NULL); } void test_array_realloc_sanity(void) { @@ -339,6 +343,7 @@ void test_array_realloc_sanity(void) { for (int i = 0; i < 20; i++) { array_push(arr, v); } + array_free(arr, NULL); } // bug fix @@ -354,6 +359,15 @@ void test_array_get_negative_idx(void) { is(array_get(arr, -1), "y", "retrieves the new last element given index -1"); is(array_get(arr, -2), "x", "retrieves the penultimate element given index -2"); + + array_free(arr, NULL); +} + +void test_array_insert(void) { + array_t *arr = array_init(); + + array_insert(arr, 100, "xxx", NULL); + array_free(arr, NULL); } void run_array_tests(void) { diff --git a/t/buffer_test.c b/t/buffer_test.c index dcdf60b..8f8bfad 100644 --- a/t/buffer_test.c +++ b/t/buffer_test.c @@ -126,15 +126,61 @@ void test_buffer_free_nonnull(void) { lives({ buffer_free(buf); }, "frees the buffer's memory"); } +void test_buffer_slice_ok(void) { + buffer_t *buf = buffer_init("test world test"); + + char slice[32]; + int ret = buffer_slice(buf, 5, 9, slice); + ok(ret == 0, "retval is zero indicating no errors"); + is(slice, "world", "returns the expected slice"); +} + +void test_buffer_slice_empty_buffer(void) { + buffer_t *buf = buffer_init(""); + + char slice[32]; + int ret = buffer_slice(buf, 0, 0, slice); + ok(ret == 0, "retval is zero indicating no errors"); + is(slice, "", "returns the expected slice"); +} + +void test_buffer_slice_bad_range(void) { + buffer_t *buf = buffer_init("test world test"); + + char slice[32]; + int ret = buffer_slice( + buf, 5, 32, + NULL); // No segfault from this proves the function fast-fails + ok(ret == -1, "retval is -1 indicating a bad range"); +} + +void test_buffer_slice_null_buffer(void) { + buffer_t *buf = buffer_init(NULL); + + char slice[32]; + int ret = buffer_slice(buf, 0, 0, NULL); + ok(ret == -1, "retval is -1 indicating a NULL internal state"); +} + void run_buffer_tests(void) { + test_buffer_init(); + test_buffer_init_with_initial(); + test_buffer_free(); test_buffer_free_nonnull(); - test_buffer_init(); + test_buffer_size(); + test_buffer_state(); - test_buffer_init_with_initial(); + test_buffer_append(); test_buffer_append_with(); + test_buffer_concat(); test_buffer_concat_on_null(); + + test_buffer_slice_ok(); + test_buffer_slice_empty_buffer(); + test_buffer_slice_bad_range(); + test_buffer_slice_null_buffer(); } diff --git a/t/io_test.c b/t/io_test.c index c2438cc..68dadfd 100644 --- a/t/io_test.c +++ b/t/io_test.c @@ -18,8 +18,8 @@ void test_read_all_full_file() { size_t sz = sizeof(data); read_all_result ret = read_all(fd, &data, &sz); - ok(READ_ALL_OK == ret, "returns READ_ALL_OK on successful read\n"); - ok(sz == 68241, "reads the correct number of bytes\n"); + ok(READ_ALL_OK == ret, "returns READ_ALL_OK on successful read"); + ok(sz == 68241, "reads the correct number of bytes"); is("The Colour Out of Space\n", s_substr(data, 0, 23, true), "first chars match"); is("troubling my sleep.\n", s_substr(data, 68221, 68241, true), diff --git a/t/main.c b/t/main.c index 2f3ed31..14d9d7b 100644 --- a/t/main.c +++ b/t/main.c @@ -2,7 +2,7 @@ #include "tests.h" int main() { - plan(171); + plan(177); run_array_tests(); run_buffer_tests();