Skip to content

Commit

Permalink
feat(buffer): add buffer_slice util fn
Browse files Browse the repository at this point in the history
  • Loading branch information
exbotanical committed Nov 5, 2024
1 parent bcbe7a3 commit bbbdce5
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 8 deletions.
2 changes: 1 addition & 1 deletion clib.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "libutil",
"version": "1.0.1",
"version": "1.0.2",
"author": "Matthew Zito",
"repo": "exbotanical/libutil",
"license": "MIT",
Expand Down
19 changes: 17 additions & 2 deletions include/libutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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*.
*/
Expand Down
31 changes: 31 additions & 0 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
22 changes: 22 additions & 0 deletions src/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
14 changes: 14 additions & 0 deletions t/array_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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) {
Expand Down
50 changes: 48 additions & 2 deletions t/buffer_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
4 changes: 2 additions & 2 deletions t/io_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion t/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include "tests.h"

int main() {
plan(171);
plan(177);

run_array_tests();
run_buffer_tests();
Expand Down

0 comments on commit bbbdce5

Please sign in to comment.