resizable: Implement splicing

This commit is contained in:
Laria 2022-01-20 21:33:04 +01:00
parent d81bef9184
commit cc79bab69f
4 changed files with 397 additions and 6 deletions

View file

@ -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

View file

@ -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
);
}

View file

@ -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

325
src/resizable_test.c Normal file
View file

@ -0,0 +1,325 @@
#include <string.h>
#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