diff --git a/src/Makefile.am b/src/Makefile.am index 2a10221..126f1d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,3 +45,8 @@ TESTS += parser.test check_PROGRAMS += parser.test parser_test_SOURCES = parser_test.c test.h parser_test_LDADD = libapfl.a + +TESTS += resizable.test +check_PROGRAMS += resizable.test +resizable_test_SOURCES = resizable_test.c test.h resizable.h +resizable_test_LDADD = libapfl.a diff --git a/src/resizable.c b/src/resizable.c index 9e00733..d688978 100644 --- a/src/resizable.c +++ b/src/resizable.c @@ -59,15 +59,65 @@ apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t return apfl_resizable_ensure_cap(elem_size, mem, cap, len + more_elements); // TODO: What if len + more_elements overflows? } -bool -apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len) -{ - if (!apfl_resizable_ensure_cap_for_more_elements(elem_size, mem, *len, cap, other_len)) { +bool apfl_resizable_splice( + size_t elem_size, + void **mem, + size_t *len, + size_t *cap, + size_t cut_start, + size_t cut_len, + const void *other_mem, + size_t other_len +) { + if (cut_start > *len || cut_start + cut_len > *len) { return false; } - memcpy(*((char**)mem) + (elem_size * *len), other_mem, other_len * elem_size); - *len += other_len; + if (other_len > cut_len) { + if (!apfl_resizable_ensure_cap_for_more_elements( + elem_size, + mem, + *len, + cap, + other_len - cut_len + )) { + return false; + } + } + + size_t src_off = cut_start + cut_len; + size_t dst_off = cut_start + other_len; + + memmove( + ((char *)(*mem)) + (dst_off * elem_size), + ((char *)(*mem)) + (src_off * elem_size), + (*len - cut_start - cut_len) * elem_size + ); + + if (other_len > 0 && other_mem != NULL) { + memcpy( + ((char *)(*mem)) + cut_start * elem_size, + other_mem, + other_len * elem_size + ); + } + + *len += other_len - cut_len; return true; } + +bool +apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len) +{ + return apfl_resizable_splice( + elem_size, + mem, + len, + cap, + *len, + 0, + other_mem, + other_len + ); +} diff --git a/src/resizable.h b/src/resizable.h index fe7d12c..a7c8338 100644 --- a/src/resizable.h +++ b/src/resizable.h @@ -18,7 +18,18 @@ bool apfl_resizable_resize(size_t elem_size, void **mem, size_t *len, size_t *ca bool apfl_resizable_ensure_cap(size_t elem_size, void **mem, size_t *cap, size_t want_cap); bool apfl_resizable_ensure_cap_for_more_elements(size_t elem_size, void **mem, size_t len, size_t *cap, size_t more_elements); +bool apfl_resizable_splice( + size_t elem_size, + void **mem, + size_t *len, + size_t *cap, + size_t start, + size_t cut_len, + const void *other_mem, + size_t other_len +); bool apfl_resizable_append(size_t elem_size, void **mem, size_t *len, size_t *cap, const void *other_mem, size_t other_len); + #endif diff --git a/src/resizable_test.c b/src/resizable_test.c new file mode 100644 index 0000000..f8d6115 --- /dev/null +++ b/src/resizable_test.c @@ -0,0 +1,325 @@ +#include + +#include "test.h" + +#include "resizable.h" + +struct splice_test_data { + const int *old; + size_t cut_start; + size_t cut_len; + const int *other; + const int *want; +}; + +static size_t +getlen(const int *data) +{ + size_t len = 0; + for (; *data != 0; data++) { + len++; + } + return len; +} + +static void +run_splice_test(testctx t, struct splice_test_data st) +{ + size_t len = getlen(st.old); + int *mem = must_alloc(t, sizeof(int) * len); + memcpy(mem, st.old, sizeof(int) * len); + size_t cap = len; + + size_t other_len = getlen(st.other); + + if (!apfl_resizable_splice( + sizeof(int), + (void **)&mem, + &len, + &cap, + st.cut_start, + st.cut_len, + st.other, + other_len + )) { + test_fatalf(t, "splice failed!"); + } + + size_t want_len = getlen(st.want); + + if (len != want_len) { + test_fatalf(t, "Expected len=%d, got %d", (int)want_len, (int)len); + } + if (cap < want_len) { + test_fatalf(t, "Expected cap>=%d, got %d", (int)want_len, (int)cap); + } + + for (size_t i = 0; i < len; i++) { + int have = mem[i]; + int want = st.want[i]; + + if (have != want) { + test_failf(t, "have(%d) != want(%d) at i=%d", have, want, (int)i); + } + } + + free(mem); +} + +TEST(splice_empty, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {0}, + .cut_start = 0, + .cut_len = 0, + .other = (int[]) {0}, + .want = (int[]) {0}, + }); +} + +TEST(splice_remove_single_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 0, + .cut_len = 1, + .other = (int[]) {0}, + .want = (int[]) {2, 3, 0}, + }); +} + +TEST(splice_remove_multiple_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 0, + .cut_len = 2, + .other = (int[]) {0}, + .want = (int[]) {3, 0}, + }); +} + +TEST(splice_remove_single_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 2, + .cut_len = 1, + .other = (int[]) {0}, + .want = (int[]) {1, 2, 0}, + }); +} + +TEST(splice_remove_multiple_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 1, + .cut_len = 2, + .other = (int[]) {0}, + .want = (int[]) {1, 0}, + }); +} + +TEST(splice_remove_single_mid, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 1, + .cut_len = 1, + .other = (int[]) {0}, + .want = (int[]) {1, 3, 0}, + }); +} + +TEST(splice_replace_same_size_single_mid, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 1, + .cut_len = 1, + .other = (int[]) {42, 0}, + .want = (int[]) {1, 42, 3, 0}, + }); +} + +TEST(splice_replace_larger_size_single_mid, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 1, + .cut_len = 1, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {1, 10, 20, 30, 3, 0}, + }); +} + +TEST(splice_replace_smaller_size_single_mid, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 1, + .cut_len = 2, + .other = (int[]) {10, 0}, + .want = (int[]) {1, 10, 4, 0}, + }); +} + +TEST(splice_replace_same_size_single_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 0, + .cut_len = 1, + .other = (int[]) {42, 0}, + .want = (int[]) {42, 2, 3, 0}, + }); +} + +TEST(splice_replace_larger_size_single_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 0, + .cut_len = 1, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {10, 20, 30, 2, 3, 0}, + }); +} + +TEST(splice_replace_smaller_size_single_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 2, + .other = (int[]) {10, 0}, + .want = (int[]) {10, 3, 4, 0}, + }); +} + +TEST(splice_replace_same_size_single_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 2, + .cut_len = 1, + .other = (int[]) {42, 0}, + .want = (int[]) {1, 2, 42, 0}, + }); +} + +TEST(splice_replace_larger_size_single_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 0}, + .cut_start = 2, + .cut_len = 1, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {1, 2, 10, 20, 30, 0}, + }); +} + +TEST(splice_replace_smaller_size_single_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 2, + .cut_len = 2, + .other = (int[]) {10, 0}, + .want = (int[]) {1, 2, 10, 0}, + }); +} + +TEST(splice_remove_all, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 4, + .other = (int[]) {0}, + .want = (int[]) {0}, + }); +} + +TEST(splice_replace_all_same_size, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 4, + .other = (int[]) {10, 20, 30, 40, 0}, + .want = (int[]) {10, 20, 30, 40, 0}, + }); +} + +TEST(splice_replace_all_smaller_size, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 4, + .other = (int[]) {10, 0}, + .want = (int[]) {10, 0}, + }); +} + +TEST(splice_replace_all_larger_size, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 4, + .other = (int[]) {10, 20, 30, 40, 50, 60, 0}, + .want = (int[]) {10, 20, 30, 40, 50, 60, 0}, + }); +} + +TEST(splice_noop, t) { + for (size_t i = 0; i < 4; i++) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = i, + .cut_len = 0, + .other = (int[]) {0}, + .want = (int[]) {1, 2, 3, 4, 0}, + }); + } +} + +TEST(splice_insert_begin, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 0, + .cut_len = 0, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {10, 20, 30, 1, 2, 3, 4, 0}, + }); +} + +TEST(splice_insert_mid, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 1, + .cut_len = 0, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {1, 10, 20, 30, 2, 3, 4, 0}, + }); +} + +TEST(splice_insert_end, t) { + run_splice_test(t, (struct splice_test_data) { + .old = (int[]) {1, 2, 3, 4, 0}, + .cut_start = 4, + .cut_len = 0, + .other = (int[]) {10, 20, 30, 0}, + .want = (int[]) {1, 2, 3, 4, 10, 20, 30, 0}, + }); +} + +TESTS_BEGIN + ADDTEST(splice_empty), + ADDTEST(splice_remove_single_begin), + ADDTEST(splice_remove_multiple_begin), + ADDTEST(splice_remove_single_end), + ADDTEST(splice_remove_multiple_end), + ADDTEST(splice_remove_single_mid), + ADDTEST(splice_replace_same_size_single_mid), + ADDTEST(splice_replace_larger_size_single_mid), + ADDTEST(splice_replace_smaller_size_single_mid), + ADDTEST(splice_replace_same_size_single_begin), + ADDTEST(splice_replace_larger_size_single_begin), + ADDTEST(splice_replace_smaller_size_single_begin), + ADDTEST(splice_replace_same_size_single_end), + ADDTEST(splice_replace_larger_size_single_end), + ADDTEST(splice_replace_smaller_size_single_end), + ADDTEST(splice_remove_all), + ADDTEST(splice_replace_all_same_size), + ADDTEST(splice_replace_all_smaller_size), + ADDTEST(splice_replace_all_larger_size), + ADDTEST(splice_noop), + ADDTEST(splice_insert_begin), + ADDTEST(splice_insert_mid), + ADDTEST(splice_insert_end), +TESTS_END