a897b875d3
This was a rather simple exercise. lfsr_btree_commit does most of the work already, so all this needed was setting up the pending attributes correctly. Also: - Tweaked dbgrbyd.py's tree rendering to match dbgbtree.py's. - Added a print to each B-tree test to help find the resulting B-tree when debugging.
1218 lines
36 KiB
TOML
1218 lines
36 KiB
TOML
|
|
# maximize lookahead buffer, we don't actually gc so we only get one pass
|
|
# of the disk for these tests
|
|
defines.LOOKAHEAD_SIZE = 'BLOCK_COUNT / 8'
|
|
|
|
|
|
# test an empty tree
|
|
[cases.test_btree_zero]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create an empty tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
# test an inlined tree
|
|
[cases.test_btree_one]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a single-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
# test a single-rbyd tree
|
|
[cases.test_btree_two]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_two_backwards]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
# still a single-rbyd tree, just making sure it works
|
|
[cases.test_btree_three]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "c", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 2);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "c", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_three_backwards]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "c", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 2);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "c", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
# try larger trees, when exactly a tree splits depends on the disk geometry, so
|
|
# we don't really have a better way of testing multi-rbyd trees
|
|
[cases.test_btree_push]
|
|
defines.N = [4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_push_backwards]
|
|
defines.N = [4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1,
|
|
&alphas[(N-1-i) % 26], 1) => 0;
|
|
}
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_push_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.ITER = 10
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
// iterate through severals seeds that we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// create lfs here since we need to reset each iteration, we're
|
|
// space constrained and we can't expect gc to work at this point
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// set up a simulation to compare against
|
|
//
|
|
// fun fact this is slower than our actual tree! unfun fact this is
|
|
// starting to be a problem...
|
|
char *sim = malloc(N);
|
|
lfs_size_t sim_size = 0;
|
|
memset(sim, 0, N);
|
|
|
|
uint32_t prng = seed;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random id
|
|
lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
lfsr_btree_push(&lfs, &btree, id, LFSR_TAG_INLINED, 1,
|
|
&alphas[i % 26], 1) => 0;
|
|
|
|
// add to sim
|
|
memcpy(&sim[id+1], &sim[id], sim_size-id);
|
|
sim[id] = alphas[i % 26];
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
assert(btree.weight == N);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
}
|
|
'''
|
|
|
|
[cases.test_btree_sparse]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i*W+W-1);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, N*W,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
id_ = -1;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i*W+W-1);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
defines.ITER = 10
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
// iterate through severals seeds that we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// create lfs here since we need to reset each iteration, we're
|
|
// space constrained and we can't expect gc to work at this point
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// set up a simulation to compare against
|
|
//
|
|
// fun fact this is slower than our actual tree! unfun fact this is
|
|
// starting to be a problem...
|
|
char *sim = malloc(N);
|
|
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 0;
|
|
memset(sim, 0, N);
|
|
memset(sim_weights, 0, N*sizeof(lfs_size_t));
|
|
|
|
uint32_t prng = seed;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random id
|
|
lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1);
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < id; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
// add to btree
|
|
lfsr_btree_push(&lfs, &btree, weighted_id, LFSR_TAG_INLINED, weight,
|
|
&alphas[i % 26], 1) => 0;
|
|
|
|
// add to sim
|
|
memcpy(&sim[id+1], &sim[id], sim_size-id);
|
|
memcpy(&sim_weights[id+1], &sim_weights[id],
|
|
(sim_size-id)*sizeof(lfs_size_t));
|
|
sim[id] = alphas[i % 26];
|
|
sim_weights[id] = weight;
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_id+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(btree.weight == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == weighted_id+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
id_ = -1;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == weighted_id+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
}
|
|
'''
|
|
|
|
|
|
# test btree updates
|
|
|
|
# try some small trees for easy corner cases first
|
|
[cases.test_btree_update_one]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a single-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
// update the tree
|
|
lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_two]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
// update the tree
|
|
lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0;
|
|
lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "B", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "B", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_three]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a two-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "c", 1) => 0;
|
|
// update the tree
|
|
lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0;
|
|
lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "B", 1) => 0;
|
|
lfsr_btree_update(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "C", 1) => 0;
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 0);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 1);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "B", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == 2);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "C", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update]
|
|
defines.N = [4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
// update the tree
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_update(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
|
&uppers[i % 26], 1) => 0;
|
|
}
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &uppers[i % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.ITER = 10
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
// iterate through severals seeds that we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// create lfs here since we need to reset each iteration, we're
|
|
// space constrained and we can't expect gc to work at this point
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
|
|
// set up a simulation to compare against
|
|
//
|
|
// fun fact this is slower than our actual tree! unfun fact this is
|
|
// starting to be a problem...
|
|
char *sim = malloc(N);
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
sim[i] = alphas[i % 26];
|
|
}
|
|
|
|
uint32_t prng = seed;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random id
|
|
lfs_size_t id = TEST_PRNG(&prng) % N;
|
|
|
|
// update btree
|
|
lfsr_btree_update(&lfs, &btree, id, LFSR_TAG_INLINED, 1,
|
|
&uppers[i % 26], 1) => 0;
|
|
|
|
// update sim
|
|
sim[id] = uppers[i % 26];
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
assert(btree.weight == N);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
}
|
|
'''
|
|
|
|
[cases.test_btree_update_sparse]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
// update the tree
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_update(&lfs, &btree, i*W+W-1, LFSR_TAG_INLINED, W,
|
|
&uppers[i % 26], 1) => 0;
|
|
}
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i*W+W-1);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, &uppers[i % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, N*W,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
id_ = -1;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == i*W+W-1);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, &uppers[i % 26], 1) == 0);
|
|
}
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
defines.ITER = 10
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
// iterate through severals seeds that we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// create lfs here since we need to reset each iteration, we're
|
|
// space constrained and we can't expect gc to work at this point
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
// create free lookahead
|
|
memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size);
|
|
lfs.free.off = 0;
|
|
lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size,
|
|
lfs.cfg->block_count);
|
|
lfs.free.i = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W,
|
|
&alphas[i % 26], 1) => 0;
|
|
}
|
|
|
|
// set up a simulation to compare against
|
|
//
|
|
// fun fact this is slower than our actual tree! unfun fact this is
|
|
// starting to be a problem...
|
|
char *sim = malloc(N);
|
|
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
sim[i] = alphas[i % 26];
|
|
sim_weights[i] = W;
|
|
}
|
|
|
|
uint32_t prng = seed;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random id
|
|
lfs_size_t id = TEST_PRNG(&prng) % N;
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < id; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
// update btree
|
|
lfsr_btree_update(&lfs, &btree,
|
|
weighted_id+sim_weights[id]-1, LFSR_TAG_INLINED, weight,
|
|
&uppers[i % 26], 1) => 0;
|
|
|
|
// update sim
|
|
sim[id] = uppers[i % 26];
|
|
sim_weights[id] = weight;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_id+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: 0x%x.%x w%d\n",
|
|
btree.u.trunk.block,
|
|
btree.u.trunk.limit,
|
|
btree.weight);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(btree.weight == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t id_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == weighted_id+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
id_ = -1;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual id in btree space
|
|
lfs_size_t weighted_id = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_id += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_INLINED);
|
|
assert(id_ == weighted_id+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_get(&lfs, &btree, id_+1,
|
|
&tag_, &id_, &weight_,
|
|
buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
}
|
|
'''
|
|
|
|
# [cases.test_btree_pop]
|
|
# [cases.test_btree_pop_fuzz]
|
|
|
|
# [cases.test_btree_split]
|
|
# [cases.test_btree_split_fuzz]
|
|
|
|
# [cases.test_btree_general_fuzz]
|
|
|
|
# TODO also test copy-on-write!
|