6dcdf1ed61
It's probably better to have a separate names for a tag category and any specific name, but I can't think of a better name for this tag, and I hadn't noticed that I was already ignoring the C prefix for CCKSUM tags in many places. NAME/CKSUM now mean both the specific tag and tag category, which is a bit of a hack since both happen to be the 0th-subtype of their categories.
4475 lines
138 KiB
TOML
4475 lines
138 KiB
TOML
# Test the mid-level B-trees
|
|
after = 'test_rbyd'
|
|
|
|
# maximize lookahead buffer, we don't actually gc so we only get one pass
|
|
# of the disk for these tests
|
|
defines.LOOKAHEAD_SIZE = 'lfs_alignup(BLOCK_COUNT / 8, 8)'
|
|
|
|
# helper functions
|
|
in = 'lfs.c'
|
|
code = '''
|
|
static int lfsr_btree_get(lfs_t *lfs,
|
|
const lfsr_btree_t *btree, lfs_size_t bid,
|
|
lfsr_tag_t *tag_, lfs_size_t *weight_,
|
|
void *buffer, lfs_size_t size) {
|
|
lfsr_data_t data;
|
|
int err = lfsr_btree_lookup(lfs, btree, bid,
|
|
tag_, weight_, &data);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return lfsr_data_read(lfs, &data, buffer, size);
|
|
}
|
|
|
|
static int lfsr_btree_push(lfs_t *lfs, lfsr_btree_t *btree,
|
|
lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight,
|
|
lfsr_data_t data) {
|
|
LFS_ASSERT(bid <= lfsr_btree_weight(btree));
|
|
return lfsr_btree_commit(lfs, btree, LFSR_ATTRS(
|
|
LFSR_ATTR(bid, TAG(tag), +weight, DATA(data))));
|
|
}
|
|
|
|
static int lfsr_btree_set(lfs_t *lfs, lfsr_btree_t *btree,
|
|
lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight,
|
|
lfsr_data_t data) {
|
|
LFS_ASSERT(bid < lfsr_btree_weight(btree));
|
|
LFS_ASSERT(lfsr_btree_weight(btree) > 0);
|
|
|
|
// lookup weight to compute deltas
|
|
lfs_size_t weight_;
|
|
int err = lfsr_btree_lookupnext(lfs, btree, bid,
|
|
NULL, NULL, &weight_, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
// note we need a second tag here in case our entry has a
|
|
// name attributes, the name attribute holds the weight not
|
|
// the struct tag
|
|
return lfsr_btree_commit(lfs, btree, LFSR_ATTRS(
|
|
LFSR_ATTR(bid, WIDE(TAG(tag)), 0, DATA(data)),
|
|
LFSR_ATTR(bid, GROW, weight - weight_, NULL)));
|
|
}
|
|
|
|
static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid) {
|
|
LFS_ASSERT(bid < lfsr_btree_weight(btree));
|
|
LFS_ASSERT(lfsr_btree_weight(btree) > 0);
|
|
|
|
// lookup weight to compute deltas
|
|
lfs_size_t weight_;
|
|
int err = lfsr_btree_lookupnext(lfs, btree, bid,
|
|
NULL, NULL, &weight_, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return lfsr_btree_commit(lfs, btree, LFSR_ATTRS(
|
|
LFSR_ATTR(bid, RM, -weight_, NULL)));
|
|
}
|
|
|
|
static int lfsr_btree_split(lfs_t *lfs, lfsr_btree_t *btree,
|
|
lfs_size_t bid, lfsr_data_t name,
|
|
lfsr_tag_t tag1, lfs_size_t weight1, lfsr_data_t data1,
|
|
lfsr_tag_t tag2, lfs_size_t weight2, lfsr_data_t data2) {
|
|
LFS_ASSERT(bid < lfsr_btree_weight(btree));
|
|
LFS_ASSERT(lfsr_btree_weight(btree) > 0);
|
|
|
|
// lookup weight to compute deltas
|
|
lfs_size_t weight_;
|
|
int err = lfsr_btree_lookupnext(lfs, btree, bid,
|
|
NULL, NULL, &weight_, NULL);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return lfsr_btree_commit(lfs, btree, LFSR_ATTRS(
|
|
LFSR_ATTR(bid, GROW, +weight1-weight_, NULL),
|
|
LFSR_ATTR(bid-(weight_-1)+weight1-1, TAG(tag1), 0, DATA(data1)),
|
|
(lfsr_data_size(&name) > 0
|
|
? LFSR_ATTR(bid-(weight_-1)+weight1,
|
|
NAME, +weight2, DATA(name))
|
|
: LFSR_ATTR_NOOP),
|
|
(lfsr_data_size(&name) > 0
|
|
? LFSR_ATTR(bid-(weight_-1)+weight1+weight2-1,
|
|
TAG(tag2), 0, DATA(data2))
|
|
: LFSR_ATTR(bid-(weight_-1)+weight1,
|
|
TAG(tag2), +weight2, DATA(data2)))));
|
|
}
|
|
'''
|
|
|
|
|
|
# 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create an empty tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 0);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("c", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "c", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("c", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "c", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &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 = [1, 2, 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_size_t n = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_push_backwards]
|
|
defines.N = [1, 2, 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_size_t n = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[(N-1-i) % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, n-1-i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[(N-1-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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_push_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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 bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
int err = lfsr_btree_push(&lfs, &btree, bid, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = alphas[i % 26];
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_btree_push_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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_size_t n = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n*W);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == i*W+W-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_push_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.W = 5
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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 bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// add to btree
|
|
int err = lfsr_btree_push(&lfs, &btree,
|
|
weighted_bid, LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_weights[bid+1], &sim_weights[bid],
|
|
(sim_size-bid)*sizeof(lfs_size_t));
|
|
sim[bid] = alphas[i % 26];
|
|
sim_weights[bid] = weight;
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
// update the tree
|
|
lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("A", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
// update the tree
|
|
lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("A", 1)) => 0;
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("B", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "B", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("c", 1)) => 0;
|
|
// update the tree
|
|
lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("A", 1)) => 0;
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("B", 1)) => 0;
|
|
lfsr_btree_set(&lfs, &btree, 2, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("C", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "B", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "C", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update]
|
|
defines.N = [1, 2, 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
// update the tree
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_set(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == N);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.SAMPLES = 10
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 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 bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % N;
|
|
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree, bid, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = 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: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == N);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, N,
|
|
&tag_, &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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
// update the tree
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_set(&lfs, &btree, i*W+W-1, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == N*W);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == i*W+W-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &uppers[i % 26], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_update_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.W = 5
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 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 bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % N;
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1, LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = uppers[i % 26];
|
|
sim_weights[bid] = weight;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < N; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
|
|
# test btree pops
|
|
|
|
# try some corner cases first, these are actually pretty tricky since we
|
|
# need to recognize when to collapse back into an inlined tree
|
|
[cases.test_btree_pop_one]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
// pop!
|
|
lfsr_btree_pop(&lfs, &btree, 0) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 0);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try to putting it back to see if things still work
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("A", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try looking up tags
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_two]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
// pop!
|
|
lfsr_btree_pop(&lfs, &btree, 1) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try to putting it back to see if things still work
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("B", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "B", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_two_other]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
// pop!
|
|
lfsr_btree_pop(&lfs, &btree, 0) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try to putting it back to see if things still work
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("A", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "A", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_three]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("c", 1)) => 0;
|
|
// pop!
|
|
lfsr_btree_pop(&lfs, &btree, 2) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try looking up tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try to putting it back to see if things still work
|
|
lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("C", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try looking up tags
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "a", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "b", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 2,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "C", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, 3,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.REMAINING = [64, 2, 1, 0]
|
|
if = 'N > REMAINING'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
// drain the tree
|
|
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
|
|
int err = lfsr_btree_pop(&lfs, &btree, N-1-i);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == REMAINING);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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, REMAINING,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try recovering
|
|
lfsr_btree_push(&lfs, &btree, REMAINING, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("R", 1)) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, REMAINING,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "R", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, REMAINING+1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_backwards]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.REMAINING = [64, 2, 1, 0]
|
|
if = 'N > REMAINING'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
// drain the tree
|
|
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
|
|
int err = lfsr_btree_pop(&lfs, &btree, 0);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == REMAINING);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[(i+(N-REMAINING)) % 26], 1) == 0);
|
|
}
|
|
|
|
// and check that we can't lookup elements that aren't in the tree
|
|
lfsr_btree_get(&lfs, &btree, REMAINING,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try recovering
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("R", 1)) => 0;
|
|
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, "R", 1) == 0);
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i+1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &alphas[(i+(N-REMAINING)) % 26], 1) == 0);
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, REMAINING+1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.REMAINING = [64, 2, 1, 0]
|
|
defines.SEED = 'range(10)'
|
|
if = 'N > REMAINING'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 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_size = 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-REMAINING); i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % sim_size;
|
|
|
|
// remove from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree, bid);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// remove from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
sim_size -= 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
[cases.test_btree_pop_sparse]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
defines.REMAINING = [64, 2, 1, 0]
|
|
if = 'N > REMAINING'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
// drain the tree
|
|
for (lfs_size_t i = 0; i < N-REMAINING; i++) {
|
|
int err = lfsr_btree_pop(&lfs, &btree, (N-1-i)*W+W-1);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == REMAINING*W);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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, REMAINING*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// try recovering
|
|
lfsr_btree_push(&lfs, &btree, REMAINING*W, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF("R", 1)) => 0;
|
|
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, REMAINING*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
assert(memcmp(buffer, "R", 1) == 0);
|
|
|
|
lfsr_btree_get(&lfs, &btree, (REMAINING+1)*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < REMAINING; i++) {
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == i*W+W-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == REMAINING*W+W-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == W);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "R", 1) == 0);
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_pop_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.W = 5
|
|
defines.REMAINING = [64, 2, 1, 0]
|
|
defines.SEED = 'range(10)'
|
|
if = 'N > REMAINING'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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;
|
|
|
|
// set up simulation and btree with pseudo-random weights
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
int err = lfsr_btree_push(&lfs, &btree,
|
|
weighted_bid, LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
return;
|
|
}
|
|
assert(err == 0);
|
|
|
|
sim[i] = alphas[i % 26];
|
|
sim_weights[i] = weight;
|
|
sim_size += 1;
|
|
}
|
|
|
|
for (lfs_size_t i = 0; i < (N-REMAINING); i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % sim_size;
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// remove from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// remove from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
memmove(&sim_weights[bid], &sim_weights[bid+1],
|
|
(sim_size-(bid+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
|
|
# test btree splits
|
|
[cases.test_btree_split]
|
|
defines.N = [1, 2, 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[0 % 26], 1)) => 0;
|
|
lfs_size_t n = 1;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
int err = lfsr_btree_split(&lfs, &btree, i-1, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&alphas[(i-1) % 26], 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&alphas[(i-0) % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_split_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 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_size = 1;
|
|
memset(sim, 0, N);
|
|
sim[0] = '_';
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % sim_size;
|
|
|
|
// split btree
|
|
int err = lfsr_btree_split(&lfs, &btree, bid, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&alphas[i % 26], 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// split sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid+0] = alphas[i % 26];
|
|
sim[bid+1] = uppers[i % 26];
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_btree_split_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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&alphas[0 % 26], 1)) => 0;
|
|
lfs_size_t n = 1;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
int err = lfsr_btree_split(&lfs, &btree, (i-1)*W+W-1, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, W, LFSR_DATA_BUF(&alphas[(i-1) % 26], 1),
|
|
LFSR_TAG_DATA, W, LFSR_DATA_BUF(&alphas[(i-0) % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n*W);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i*W+W-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_split_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.W = 5
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF("_", 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));
|
|
lfs_size_t sim_size = 1;
|
|
memset(sim, 0, N);
|
|
memset(sim_weights, 0, N*sizeof(lfs_size_t));
|
|
sim[0] = '_';
|
|
sim_weights[0] = W;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % sim_size;
|
|
// choose pseudo-random weights
|
|
lfs_size_t weight1 = 1 + (TEST_PRNG(&prng) % W);
|
|
lfs_size_t weight2 = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// split btree
|
|
int err = lfsr_btree_split(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, weight1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1),
|
|
LFSR_TAG_DATA, weight2,
|
|
LFSR_DATA_BUF(&uppers[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_weights[bid+1], &sim_weights[bid],
|
|
(sim_size-bid)*sizeof(lfs_size_t));
|
|
sim[bid+0] = alphas[i % 26];
|
|
sim[bid+1] = uppers[i % 26];
|
|
sim_weights[bid+0] = weight1;
|
|
sim_weights[bid+1] = weight2;
|
|
sim_size += 1;
|
|
|
|
// TODO rm
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
|
|
# Some specific corner cases
|
|
[cases.test_btree_drop]
|
|
# this should large enough so only one entry can fit in a block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
defines.SIBLING = [0, 1]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// force it to split
|
|
|
|
// the extra push here avoids trying to inline the big entry
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 1)) => 0;
|
|
uint8_t buf1[SIZE];
|
|
memset(buf1, 'a', SIZE);
|
|
uint8_t buf2[SIZE];
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf1, SIZE),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
// force compaction
|
|
btree.u.rbyd.eoff = -1;
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// now remove one entry, since this brings the rbyd down to zero,
|
|
// this should force one of the blocks to drop
|
|
lfsr_btree_pop(&lfs, &btree, SIBLING) => 0;
|
|
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// check that our other entry is fine
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buf1, SIZE) => SIZE;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buf1, (SIBLING ? "a" : "b"), 1) == 0);
|
|
|
|
// and check that our pop worked
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buf1, SIZE) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_drop_compact]
|
|
# this should large enough so only one entry can fit in a block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
defines.SIBLING = [0, 1]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// force it to split
|
|
|
|
// the extra push here avoids trying to inline the big entry
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 1)) => 0;
|
|
uint8_t buf1[SIZE];
|
|
memset(buf1, 'a', SIZE);
|
|
uint8_t buf2[SIZE];
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf1, SIZE),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
// force compaction
|
|
btree.u.rbyd.eoff = -1;
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// now remove one entry, since this brings the rbyd down this zero,
|
|
// this should force one of the blocks to drop
|
|
//
|
|
// do this while forcing a compaction
|
|
btree.u.rbyd.eoff = -1;
|
|
lfsr_btree_pop(&lfs, &btree, SIBLING) => 0;
|
|
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// check that our other entry is fine
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buf1, SIZE) => SIZE;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buf1, (SIBLING ? "a" : "b"), 1) == 0);
|
|
|
|
// and check that our pop worked
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buf1, SIZE) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_drop_split]
|
|
# this should large enough so only one entry can fit in a block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
defines.SIBLING = [0, 1]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// force it to split
|
|
|
|
// the extra push here avoids trying to inline the big entry
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 1)) => 0;
|
|
uint8_t buf1[SIZE];
|
|
memset(buf1, 'a', SIZE);
|
|
uint8_t buf2[SIZE];
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf1, SIZE),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
|
|
// force compaction, causing a split, but while we're splitting,
|
|
// also remove an entry, bringing the split rbyd down to zero mid split
|
|
//
|
|
// messy, isn't it? this is why we need an explicit test
|
|
//
|
|
btree.u.rbyd.eoff = -1;
|
|
lfsr_btree_pop(&lfs, &btree, SIBLING) => 0;
|
|
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// check that our other entry is fine
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buf1, SIZE) => SIZE;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buf1, (SIBLING ? "a" : "b"), 1) == 0);
|
|
|
|
// and check that our pop worked
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buf1, SIZE) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_drop_merge]
|
|
# this should large enough so only one entry can fit in a block
|
|
defines.SIZE = 'BLOCK_SIZE / 4'
|
|
defines.SIBLING = [0, 1]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
|
|
// force it to split
|
|
|
|
// the extra push here avoids trying to inline the big entry
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 1)) => 0;
|
|
uint8_t buf1[SIZE];
|
|
memset(buf1, 'a', SIZE);
|
|
uint8_t buf2[SIZE];
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_NULL,
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf1, SIZE),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
// force compaction
|
|
btree.u.rbyd.eoff = -1;
|
|
memset(buf2, 'b', SIZE);
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(buf2, SIZE)) => 0;
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// now make both entries small so they should be merged if either compacts
|
|
lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("a", 1)) => 0;
|
|
lfsr_btree_set(&lfs, &btree, 1, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("b", 1)) => 0;
|
|
|
|
// force compaction, while removing one entry, this drops the rbyd
|
|
// down to zero while also triggering a merge
|
|
btree.u.rbyd.eoff = -1;
|
|
lfsr_btree_pop(&lfs, &btree, SIBLING) => 0;
|
|
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// check that our other entry is fine
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
lfsr_btree_get(&lfs, &btree, 0,
|
|
&tag_, &weight_, buf1, SIZE) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buf1, (SIBLING ? "a" : "b"), 1) == 0);
|
|
|
|
// and check that our pop worked
|
|
lfsr_btree_get(&lfs, &btree, 1,
|
|
&tag_, &weight_, buf1, SIZE) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
|
|
# Some more general fuzz testing
|
|
[cases.test_btree_general_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.SEED = 'range(100)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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 op
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
if (op == 0 || bid == sim_size) {
|
|
// push to btree
|
|
int err = lfsr_btree_push(&lfs, &btree, bid,
|
|
LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// push to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = alphas[i % 26];
|
|
sim_size += 1;
|
|
|
|
} else if (op == 1) {
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree, bid,
|
|
LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = alphas[i % 26];
|
|
|
|
} else {
|
|
// pop from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree, bid);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// pop from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
sim_size -= 1;
|
|
}
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
[cases.test_btree_general_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
defines.SEED = 'range(100)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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 op
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (op == 0 || bid == sim_size) {
|
|
// push to btree
|
|
int err = lfsr_btree_push(&lfs, &btree, weighted_bid,
|
|
LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// push to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_weights[bid+1], &sim_weights[bid],
|
|
(sim_size-bid)*sizeof(lfs_size_t));
|
|
sim[bid] = alphas[i % 26];
|
|
sim_weights[bid] = weight;
|
|
sim_size += 1;
|
|
|
|
} else if (op == 1) {
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1, LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = alphas[i % 26];
|
|
sim_weights[bid] = weight;
|
|
|
|
} else {
|
|
// remove from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// remove from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
memmove(&sim_weights[bid], &sim_weights[bid+1],
|
|
(sim_size-(bid+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
}
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%dw%d=%c", weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_get(&lfs, &btree, weighted_bid+sim_weights[i]-1,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, total_weight,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// also test that we can traverse the tree without prior knowledge
|
|
lfs_size_t bid_ = -1;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == sim_weights[i]);
|
|
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
lfsr_btree_lookupnext(&lfs, &btree, bid_+1,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
'''
|
|
|
|
|
|
# test key-value btrees
|
|
[cases.test_btree_find_zero]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a zero-entry tree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 0);
|
|
|
|
// try to find tags
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, "aaa", 3,
|
|
&bid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_find_one]
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("0", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 1);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0*DID, "aaa", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "0", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 1*DID, "aab", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "0", 1) == 0);
|
|
'''
|
|
|
|
[cases.test_btree_find_two]
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("0", 1)) => 0;
|
|
lfsr_btree_split(&lfs, &btree, 0,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(0), LFSR_DATA_BUF("aab", 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("0", 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("1", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 2);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, "aaa", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "0", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, "aab", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 1);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "1", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, "aac", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 1);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "1", 1) == 0);
|
|
'''
|
|
|
|
[cases.test_btree_find_three]
|
|
in = 'lfs.c'
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("0", 1)) => 0;
|
|
lfsr_btree_split(&lfs, &btree, 0,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(1*DID), LFSR_DATA_BUF("aab", 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("0", 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("1", 1)) => 0;
|
|
lfsr_btree_split(&lfs, &btree, 1,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(2*DID), LFSR_DATA_BUF("aac", 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("1", 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("2", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0*DID, "aaa", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "0", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 1*DID, "aab", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 1);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "1", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 2*DID, "aac", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 2);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "2", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 3*DID, "aad", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 2);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "2", 1) == 0);
|
|
'''
|
|
|
|
[cases.test_btree_find_three_backwards]
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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_DATA, 1,
|
|
LFSR_DATA_BUF("0", 1)) => 0;
|
|
lfsr_btree_split(&lfs, &btree, 0,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(2*DID), LFSR_DATA_BUF("aac", 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("1", 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("2", 1)) => 0;
|
|
lfsr_btree_split(&lfs, &btree, 0,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(1*DID), LFSR_DATA_BUF("aab", 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("0", 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF("1", 1)) => 0;
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == 3);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0*DID, "aaa", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 0);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "0", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 1*DID, "aab", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 1);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "1", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 2*DID, "aac", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 2);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "2", 1) == 0);
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 3*DID, "aad", 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == 2);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, "2", 1) == 0);
|
|
'''
|
|
|
|
[cases.test_btree_find]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&nums[0 % 10], 1)) => 0;
|
|
lfs_size_t n = 1;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
char name[3] = {
|
|
alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26]
|
|
};
|
|
int err = lfsr_btree_split(&lfs, &btree, i-1,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(i*DID), LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[(i-1) % 10], 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[(i-0) % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
char name[3] = {
|
|
alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26]
|
|
};
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, i*DID, name, 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == i);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &nums[i % 10], 1) == 0);
|
|
}
|
|
'''
|
|
|
|
[cases.test_btree_find_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 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);
|
|
char (*sim_names)[3] = malloc(N*3);
|
|
lfs_size_t sim_size = 1;
|
|
memset(sim, 0, N);
|
|
memset(sim_names, 0, N*3);
|
|
sim[0] = '_';
|
|
memcpy(&sim_names[0], "___", 3);
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
// choose a pseudo-random name
|
|
lfs_size_t x = TEST_PRNG(&prng) % (26*26*26);
|
|
char name[3] = {
|
|
alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26]
|
|
};
|
|
|
|
// find where to split
|
|
lfs_size_t bid = 0;
|
|
while (bid+1 < sim_size && memcmp(sim_names[bid+1], name, 3) <= 0) {
|
|
bid += 1;
|
|
}
|
|
// just skip exact matches for now
|
|
if (memcmp(sim_names[bid], name, 3) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// split btree
|
|
int err = lfsr_btree_split(&lfs, &btree, bid,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(0), LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[i % 10], 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// split sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_names[bid+1], &sim_names[bid], (sim_size-bid)*3);
|
|
sim[bid+0] = nums[i % 10];
|
|
sim[bid+1] = nums[i % 10];
|
|
memcpy(&sim_names[bid+1], name, 3);
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%.3s=%c", sim_names[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, sim_names[i], 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == i);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
free(sim_names);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
[cases.test_btree_find_sparse]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
# true or false for if we should use dids vs names
|
|
defines.DID = [false, true]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF(&nums[0 % 10], 1)) => 0;
|
|
lfs_size_t n = 1;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
char name[3] = {
|
|
alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26]
|
|
};
|
|
int err = lfsr_btree_split(&lfs, &btree, (i-1)*W+W-1,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(i*DID), LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, W, LFSR_DATA_BUF(&nums[(i-1) % 10], 1),
|
|
LFSR_TAG_DATA, W, LFSR_DATA_BUF(&nums[(i-0) % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n*W);
|
|
|
|
// try to find tags
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
char name[3] = {
|
|
alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26]
|
|
};
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, i*DID, name, 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == i*W+W-1);
|
|
assert(weight_ == W);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &nums[i % 10], 1) == 0);
|
|
}
|
|
'''
|
|
|
|
[cases.test_btree_find_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.W = 5
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF("_", 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);
|
|
char (*sim_names)[3] = malloc(N*3);
|
|
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 1;
|
|
memset(sim, 0, N);
|
|
memset(sim_names, 0, N*3);
|
|
memset(sim_weights, 0, N*sizeof(lfs_size_t));
|
|
sim[0] = '_';
|
|
memcpy(&sim_names[0], "___", 3);
|
|
sim_weights[0] = W;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 1; i < N; i++) {
|
|
// choose a pseudo-random name
|
|
lfs_size_t x = TEST_PRNG(&prng) % (26*26*26);
|
|
char name[3] = {
|
|
alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26]
|
|
};
|
|
// choose pseudo-random weights
|
|
lfs_size_t weight1 = 1 + (TEST_PRNG(&prng) % W);
|
|
lfs_size_t weight2 = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// find where to split
|
|
lfs_size_t bid = 0;
|
|
while (bid+1 < sim_size && memcmp(sim_names[bid+1], name, 3) <= 0) {
|
|
bid += 1;
|
|
}
|
|
// just skip exact matches for now
|
|
if (memcmp(sim_names[bid], name, 3) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// split btree
|
|
int err = lfsr_btree_split(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1,
|
|
LFSR_DATA_CAT(LFSR_DATA_LEB128(0), LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, weight1, LFSR_DATA_BUF(&nums[i % 10], 1),
|
|
LFSR_TAG_DATA, weight2, LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// split sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_names[bid+1], &sim_names[bid], (sim_size-bid)*3);
|
|
memmove(&sim_weights[bid+1], &sim_weights[bid],
|
|
(sim_size-bid)*sizeof(lfs_size_t));
|
|
sim[bid+0] = nums[i % 10];
|
|
sim[bid+1] = nums[i % 10];
|
|
memcpy(&sim_names[bid+1], name, 3);
|
|
sim_weights[bid+0] = weight1;
|
|
sim_weights[bid+1] = weight2;
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%.3sid%dw%d=%c",
|
|
sim_names[i],
|
|
weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i],
|
|
sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, sim_names[i], 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
free(sim_names);
|
|
free(sim_weights);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|
|
# make sure we test finds with other operations
|
|
[cases.test_btree_find_general_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.SEED = 'range(100)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF("_", 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);
|
|
char (*sim_names)[3] = malloc(N*3);
|
|
lfs_size_t sim_size = 1;
|
|
memset(sim, 0, N);
|
|
memset(sim_names, 0, N*3);
|
|
sim[0] = '_';
|
|
memcpy(&sim_names[0], "___", 3);
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random op
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size == 0 ? 1 : sim_size);
|
|
// choose a pseudo-random name
|
|
lfs_size_t x = TEST_PRNG(&prng) % (26*26*26);
|
|
char name[3] = {
|
|
alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26]
|
|
};
|
|
|
|
// don't let sim drop below one element
|
|
if (op == 0 || sim_size <= 1) {
|
|
// find where to split
|
|
lfs_size_t bid = 0;
|
|
while (bid+1 < sim_size
|
|
&& memcmp(sim_names[bid+1], name, 3) <= 0) {
|
|
bid += 1;
|
|
}
|
|
// just skip exact matches for now
|
|
if (memcmp(sim_names[bid], name, 3) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// split btree
|
|
lfs_size_t split_bid;
|
|
lfsr_data_t split_data;
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, name, 3,
|
|
&split_bid, NULL, NULL, &split_data) => 0;
|
|
uint8_t split_buf[4];
|
|
lfsr_data_read(&lfs, &split_data, split_buf, 4) => 1;
|
|
if (split_bid > bid) {
|
|
int err = lfsr_btree_split(&lfs, &btree,
|
|
split_bid,
|
|
LFSR_DATA_CAT(
|
|
LFSR_DATA_LEB128(0),
|
|
LFSR_DATA_BUF(sim_names[bid+1], 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[i % 10], 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(split_buf, 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
} else {
|
|
int err = lfsr_btree_split(&lfs, &btree,
|
|
split_bid, LFSR_DATA_CAT(
|
|
LFSR_DATA_LEB128(0),
|
|
LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(split_buf, 1),
|
|
LFSR_TAG_DATA, 1, LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
|
|
// split sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_names[bid+1], &sim_names[bid], (sim_size-bid)*3);
|
|
sim[bid+1] = nums[i % 10];
|
|
memcpy(&sim_names[bid+1], name, 3);
|
|
sim_size += 1;
|
|
|
|
} else if (op == 1) {
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree, bid,
|
|
LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = nums[i % 10];
|
|
|
|
} else {
|
|
// pop from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree, bid);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// pop from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
memmove(&sim_names[bid], &sim_names[bid+1], (sim_size-(bid+1))*3);
|
|
sim_size -= 1;
|
|
|
|
// our B-tree doesn't actually track the name of id0, so we need
|
|
// mirror this in our sim
|
|
if (bid == 0) {
|
|
memcpy(&sim_names[0], "___", 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%.3s=%c", sim_names[i], sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, sim_names[i], 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == i);
|
|
assert(weight_ == 1);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
free(sim_names);
|
|
'''
|
|
|
|
[cases.test_btree_find_general_sparse_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
defines.W = 5
|
|
defines.SEED = 'range(100)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
const char *nums = "0123456789";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a btree
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_DATA, W,
|
|
LFSR_DATA_BUF("_", 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);
|
|
char (*sim_names)[3] = malloc(N*3);
|
|
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
|
|
lfs_size_t sim_size = 1;
|
|
memset(sim, 0, N);
|
|
memset(sim_names, 0, N*3);
|
|
memset(sim_weights, 0, N*sizeof(lfs_size_t));
|
|
sim[0] = '_';
|
|
memcpy(&sim_names[0], "___", 3);
|
|
sim_weights[0] = W;
|
|
|
|
uint32_t prng = SEED;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
// choose a pseudo-random op
|
|
uint8_t op = TEST_PRNG(&prng) % 3;
|
|
// choose a pseudo-random bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size == 0 ? 1 : sim_size);
|
|
// choose a pseudo-random name
|
|
lfs_size_t x = TEST_PRNG(&prng) % (26*26*26);
|
|
char name[3] = {
|
|
alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26]
|
|
};
|
|
// choose a pseudo-random weight
|
|
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// don't let sim drop below one element
|
|
if (op == 0 || sim_size <= 1) {
|
|
// find where to split
|
|
lfs_size_t bid = 0;
|
|
while (bid+1 < sim_size
|
|
&& memcmp(sim_names[bid+1], name, 3) <= 0) {
|
|
bid += 1;
|
|
}
|
|
// just skip exact matches for now
|
|
if (memcmp(sim_names[bid], name, 3) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < bid; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
// split btree
|
|
lfs_size_t split_bid;
|
|
lfs_size_t split_weight;
|
|
lfsr_data_t split_data;
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, name, 3,
|
|
&split_bid, NULL, &split_weight,
|
|
&split_data) => 0;
|
|
uint8_t split_buf[4];
|
|
lfsr_data_read(&lfs, &split_data, split_buf, 4) => 1;
|
|
if (split_bid > weighted_bid+sim_weights[bid]-1) {
|
|
int err = lfsr_btree_split(&lfs, &btree, split_bid,
|
|
LFSR_DATA_CAT(
|
|
LFSR_DATA_LEB128(0),
|
|
LFSR_DATA_BUF(sim_names[bid+1], 3)),
|
|
LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&nums[i % 10], 1),
|
|
LFSR_TAG_DATA, split_weight,
|
|
LFSR_DATA_BUF(split_buf, 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
} else {
|
|
int err = lfsr_btree_split(&lfs, &btree, split_bid,
|
|
LFSR_DATA_CAT(
|
|
LFSR_DATA_LEB128(0),
|
|
LFSR_DATA_BUF(name, 3)),
|
|
LFSR_TAG_DATA, split_weight,
|
|
LFSR_DATA_BUF(split_buf, 1),
|
|
LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
|
|
// split sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
memmove(&sim_names[bid+1], &sim_names[bid], (sim_size-bid)*3);
|
|
memmove(&sim_weights[bid+1], &sim_weights[bid],
|
|
(sim_size-bid)*sizeof(lfs_size_t));
|
|
sim[bid+1] = nums[i % 10];
|
|
memcpy(&sim_names[bid+1], name, 3);
|
|
sim_weights[bid+1] = weight;
|
|
sim_size += 1;
|
|
|
|
} else if (op == 1) {
|
|
// update btree
|
|
int err = lfsr_btree_set(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1, LFSR_TAG_DATA, weight,
|
|
LFSR_DATA_BUF(&nums[i % 10], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// update sim
|
|
sim[bid] = nums[i % 10];
|
|
sim_weights[bid] = weight;
|
|
|
|
} else {
|
|
// pop from btree
|
|
int err = lfsr_btree_pop(&lfs, &btree,
|
|
weighted_bid+sim_weights[bid]-1);
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// pop from sim
|
|
memmove(&sim[bid], &sim[bid+1], sim_size-(bid+1));
|
|
memmove(&sim_names[bid], &sim_names[bid+1], (sim_size-(bid+1))*3);
|
|
memmove(&sim_weights[bid], &sim_weights[bid+1],
|
|
(sim_size-(bid+1))*sizeof(lfs_size_t));
|
|
sim_size -= 1;
|
|
|
|
// our B-tree doesn't actually track the name of id0, so we need
|
|
// mirror this in our sim
|
|
if (bid == 0) {
|
|
memcpy(&sim_names[0], "___", 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%.3sid%dw%d=%c",
|
|
sim_names[i],
|
|
weighted_bid+sim_weights[i]-1,
|
|
sim_weights[i],
|
|
sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
|
|
lfs_size_t total_weight = 0;
|
|
for (lfs_size_t j = 0; j < sim_size; j++) {
|
|
total_weight += sim_weights[j];
|
|
}
|
|
assert(lfsr_btree_weight(&btree) == total_weight);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t bid_;
|
|
lfs_size_t weight_;
|
|
lfsr_data_t data_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
// calculate actual bid in btree space
|
|
lfs_size_t weighted_bid = 0;
|
|
for (lfs_size_t j = 0; j < i; j++) {
|
|
weighted_bid += sim_weights[j];
|
|
}
|
|
|
|
lfsr_btree_namelookup(&lfs, &btree, 0, sim_names[i], 3,
|
|
&bid_, &tag_, &weight_, &data_) => 0;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(bid_ == weighted_bid+sim_weights[i]-1);
|
|
assert(weight_ == sim_weights[i]);
|
|
lfsr_data_read(&lfs, &data_, buffer, 4) => 1;
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
free(sim_names);
|
|
free(sim_weights);
|
|
'''
|
|
|
|
|
|
## B-tree traversal tests ##
|
|
|
|
# some simple btree traversals
|
|
[cases.test_btree_traversal]
|
|
defines.N = [1, 2, 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.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 0;
|
|
lfs_alloc_ack(&lfs);
|
|
|
|
// create a tree with N elements
|
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
lfs_size_t n = 0;
|
|
for (lfs_size_t i = 0; i < N; i++) {
|
|
int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
n += 1;
|
|
}
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == n);
|
|
|
|
// check that the elements are in the tree
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// test that we can traverse the tree, keeping track of all blocks we see
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_btraversal_t traversal = LFSR_BTRAVERSAL();
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*N);
|
|
|
|
lfsr_binfo_t binfo;
|
|
int err = lfsr_btraversal_read(&lfs, &btree, &traversal, &binfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
if (binfo.tag == LFSR_TAG_BRANCH) {
|
|
printf("traversal: %d 0x%x w%d btree 0x%x.%x\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight,
|
|
binfo.u.rbyd.block, binfo.u.rbyd.trunk);
|
|
|
|
// keep track of seen blocks
|
|
seen[binfo.u.rbyd.block / 8] |= 1 << (binfo.u.rbyd.block % 8);
|
|
|
|
} else if (binfo.tag == LFSR_TAG_DATA) {
|
|
printf("traversal: %d 0x%x w%d data %d\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight,
|
|
lfsr_data_size(&binfo.u.data));
|
|
|
|
} else {
|
|
// well this shouldn't happen
|
|
printf("traversal: %d 0x%x w%d\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight);
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// if traversal worked, we should be able to clobber all other blocks
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// and the tree should still work
|
|
|
|
// check that the elements are in the tree
|
|
for (lfs_size_t i = 0; i < n; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
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_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_btree_traversal_fuzz]
|
|
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
|
|
defines.SEED = 'range(10)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, CFG) => 0;
|
|
// create free lookahead
|
|
memset(lfs.lookahead.buffer, 0, CFG->lookahead_size);
|
|
lfs.lookahead.start = 0;
|
|
lfs.lookahead.size = lfs_min(8*CFG->lookahead_size,
|
|
CFG->block_count);
|
|
lfs.lookahead.next = 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 bid
|
|
lfs_size_t bid = TEST_PRNG(&prng) % (sim_size+1);
|
|
|
|
// add to btree
|
|
int err = lfsr_btree_push(&lfs, &btree, bid, LFSR_TAG_DATA, 1,
|
|
LFSR_DATA_BUF(&alphas[i % 26], 1));
|
|
// ignore space issues
|
|
if (err == LFS_ERR_NOSPC) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
// add to sim
|
|
memmove(&sim[bid+1], &sim[bid], sim_size-bid);
|
|
sim[bid] = alphas[i % 26];
|
|
sim_size += 1;
|
|
}
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
bool first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
uint8_t buffer[4];
|
|
lfsr_tag_t tag_;
|
|
lfs_size_t weight_;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// test that we can traverse the tree, keeping track of all blocks
|
|
// we see
|
|
uint8_t *seen = malloc((BLOCK_COUNT+7)/8);
|
|
memset(seen, 0, (BLOCK_COUNT+7)/8);
|
|
|
|
lfsr_btraversal_t traversal = LFSR_BTRAVERSAL();
|
|
for (lfs_block_t i = 0;; i++) {
|
|
// a bit hacky, but this catches infinite loops
|
|
assert(i < 2*N);
|
|
|
|
lfsr_binfo_t binfo;
|
|
int err = lfsr_btraversal_read(&lfs, &btree, &traversal, &binfo);
|
|
assert(!err || err == LFS_ERR_NOENT);
|
|
if (err == LFS_ERR_NOENT) {
|
|
break;
|
|
}
|
|
|
|
if (binfo.tag == LFSR_TAG_BRANCH) {
|
|
printf("traversal: %d 0x%x w%d btree 0x%x.%x\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight,
|
|
binfo.u.rbyd.block, binfo.u.rbyd.trunk);
|
|
|
|
// keep track of seen blocks
|
|
seen[binfo.u.rbyd.block / 8] |= 1 << (binfo.u.rbyd.block % 8);
|
|
|
|
} else if (binfo.tag == LFSR_TAG_DATA) {
|
|
printf("traversal: %d 0x%x w%d data %d\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight,
|
|
lfsr_data_size(&binfo.u.data));
|
|
|
|
} else {
|
|
// well this shouldn't happen
|
|
printf("traversal: %d 0x%x w%d\n",
|
|
binfo.bid,
|
|
binfo.tag,
|
|
binfo.weight);
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// if traversal worked, we should be able to clobber all other blocks
|
|
uint8_t clobber_buf[BLOCK_SIZE];
|
|
memset(clobber_buf, 0xcc, BLOCK_SIZE);
|
|
for (lfs_block_t block = 0; block < BLOCK_COUNT; block++) {
|
|
if (!(seen[block / 8] & (1 << (block % 8)))) {
|
|
CFG->erase(CFG, block) => 0;
|
|
CFG->prog(CFG, block, 0, clobber_buf, BLOCK_SIZE) => 0;
|
|
}
|
|
}
|
|
free(seen);
|
|
|
|
// and the tree should still work
|
|
|
|
// check that btree matches sim
|
|
printf("expd: [");
|
|
first = true;
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
if (!first) {
|
|
printf(", ");
|
|
}
|
|
first = false;
|
|
printf("%c", sim[i]);
|
|
}
|
|
printf("]\n");
|
|
printf("btree: w%d 0x%x.%x\n",
|
|
btree.u.rbyd.weight,
|
|
btree.u.rbyd.block,
|
|
btree.u.rbyd.trunk);
|
|
assert(lfsr_btree_weight(&btree) == sim_size);
|
|
|
|
for (lfs_size_t i = 0; i < sim_size; i++) {
|
|
lfsr_btree_get(&lfs, &btree, i,
|
|
&tag_, &weight_, buffer, 4) => 1;
|
|
assert(tag_ == LFSR_TAG_DATA);
|
|
assert(weight_ == 1);
|
|
assert(memcmp(buffer, &sim[i], 1) == 0);
|
|
}
|
|
|
|
// and no extra elements
|
|
lfsr_btree_get(&lfs, &btree, sim_size,
|
|
&tag_, &weight_, buffer, 4) => LFS_ERR_NOENT;
|
|
|
|
// clean up sim
|
|
free(sim);
|
|
lfs_deinit(&lfs) => 0;
|
|
'''
|
|
|