resizable: Implement splicing
This commit is contained in:
parent
d81bef9184
commit
cc79bab69f
4 changed files with 397 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
325
src/resizable_test.c
Normal 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
|
||||
Loading…
Reference in a new issue