cdc3a486d6
The original idea was weighted B-trees composed out of weighted rbyds, with the two weight systems being independent. Descent down the B-tree uses the same technique in the current metadata data-structure of searching for which branch to take during fetch, basically getting the search for free (well, on top of the already required O(m) fetch operation). But this is fundamentally flawed. While file names provide an absolute reference for finding matches, weights are relative references. So we don't have enough information to do weight-based lookup during fetch. This smells just like the relative-vs-absolute key issues that led to rbyd vs rbd trees in the first place... One option is to do rbyd traversals at each B-tree node to build the necessary information to figure out the weights. But with rbyd traversals taking O(m log m), this makes B-tree lookups O(log n * m log m), and B-tree traversals a messy O(n log n * m log m), which is acceptable, but disapointing for what will likely be the most common operation in the filesystem. But the rbyd trees _are_ already weighted. A better solution might be to go back and rethink the seperation of B-tree weights and rbyd ids. Unfortunately, with only 16-bits available for rbyd ids, this would likely require a rewrite of how rbyd tags are encoded...
4871 lines
168 KiB
TOML
4871 lines
168 KiB
TOML
|
|
# Test this inner rbyd data-structure
|
|
|
|
# test with a number of different erase values
|
|
defines.ERASE_VALUE = [0xff, 0x00, 0x1b, -1]
|
|
|
|
[cases.test_rbyd_commit]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_commit]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
'''
|
|
|
|
[cases.test_rbyd_commit_fetch_commit]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// commit with the second attribute
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
'''
|
|
|
|
# [cases.test_rbyd_fetchmatch]
|
|
# [cases.test_rbyd_multi_fetchmatch]
|
|
|
|
# TODO we really need to test dense keys...
|
|
|
|
[cases.test_rbyd_lookup]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_lookup]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_get]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_get]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try an empty commit
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), buffer, 4)
|
|
=> 4;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_traverse]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
lfsr_stag_t tag;
|
|
|
|
// traverse requires correct biasing of the weights in the rbyd tree
|
|
// so that lookups return strictly the tag greater than or equal to
|
|
// the tag requested
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 1, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 2, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFS_ERR_NOENT);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 1, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 2, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFS_ERR_NOENT);
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_traverse]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
lfsr_stag_t tag;
|
|
|
|
// traverse requires correct biasing of the weights in the rbyd tree
|
|
// so that lookups return strictly the tag greater than or equal to
|
|
// the tag requested
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 1, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 2, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFS_ERR_NOENT);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 1, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFSR_TAG2(UATTR, 2, -1));
|
|
tag = lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag), &off, &size);
|
|
assert(tag == LFS_ERR_NOENT);
|
|
'''
|
|
|
|
[cases.test_rbyd_bifoliate]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// create a split in the leaves
|
|
// <b
|
|
// => .-'|
|
|
// 1 1 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
|
|
// split the other direction
|
|
// >b
|
|
// => .-'|
|
|
// 2 2 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_bflips]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// ignore a black edge
|
|
// <b <b
|
|
// .-'| => .----'|
|
|
// 1 2 1 2 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
|
|
// flip a black edge
|
|
// <b >b
|
|
// .-'| => .-'|
|
|
// 1 2 1 2 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_trifoliate]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// ignore a black edge
|
|
// <r
|
|
// .----'|
|
|
// <b => | <b
|
|
// .-'| | .-'|
|
|
// 1 2 1 2 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
|
|
// flip a black edge
|
|
// >r
|
|
// .-'|
|
|
// <b => | >b
|
|
// .-'| .--|-'|
|
|
// 2 3 2 3 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
'''
|
|
|
|
|
|
[cases.test_rbyd_rflips]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// ignore a red edge and black edge
|
|
// <r <r
|
|
// .----'| .-------'|
|
|
// | <b => | <b
|
|
// | .-'| | .----'|
|
|
// 1 2 3 1 2 3 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
|
|
// ignore a red edge, flip a black edge
|
|
// <r <r
|
|
// .----'| .-------'|
|
|
// | <b => | >b
|
|
// | .-'| | .-'|
|
|
// 1 2 3 1 2 3 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
|
|
// flip a red edge and black edge
|
|
// <r >r
|
|
// .----'| .-'|
|
|
// | <b => | >b
|
|
// | .-'| .--|-'|
|
|
// 1 2 3 1 2 3 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
|
|
// flip a red edge, ignore a black edge
|
|
// <r >r
|
|
// .-'| .-------'|
|
|
// | >b => | >b
|
|
// .--|-'| | .-'|
|
|
// 3 1 2 3 1 2 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_quadrifoliate]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// ignore a red edge and black edge
|
|
// <y
|
|
// .-------'|
|
|
// <r | <r
|
|
// .----'| => | .----'|
|
|
// | <b | | <b
|
|
// | .-'| | | .-'|
|
|
// 1 2 3 1 2 3 4
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// ignore a red edge, flip a black edge
|
|
// <y >y
|
|
// .-------'| .-'|
|
|
// <r | >r | >r
|
|
// .----'| => | .-'| => .--|-'|
|
|
// | <b | | >b | | <b
|
|
// | .-'| | .--|-'| .--|--|-'|
|
|
// 1 3 4 1 3 4 2 1 3 4 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// flip a red edge and black edge
|
|
// >y
|
|
// .-'|
|
|
// <r | >b
|
|
// .----'| => .--|-'|
|
|
// | <b | | >b
|
|
// | .-'| .--|--|-'|
|
|
// 2 3 4 2 3 4 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// flip a red edge, ignore a black edge
|
|
// >y
|
|
// .-------'|
|
|
// <r | >r
|
|
// .-'| => | .-'|
|
|
// | >b | | <b
|
|
// .--|-'| | .--|-'|
|
|
// 4 2 3 4 2 3 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_rotations]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// all three the same
|
|
// <y
|
|
// .-------'|
|
|
// <r | <r
|
|
// .----'| => | .----'|
|
|
// | <b | | <b
|
|
// | .-'| | | .-'|
|
|
// 1 2 3 1 2 3 4
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// yellow and red alt the same
|
|
// <y
|
|
// .-------'|
|
|
// <r | <r
|
|
// .----'| => | .----'|
|
|
// | <b | | >b
|
|
// | .-'| | | .-'|
|
|
// 1 2 4 1 2 4 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// yellow and black alt the same
|
|
// <y <y
|
|
// .-------'| .-------'|
|
|
// <r | >r | <r
|
|
// .----'| => | .----'| => | .-'|
|
|
// | >b | | <b | | >b
|
|
// | .-'| | | .-'| | .--|-'|
|
|
// 1 4 2 1 4 2 3 1 4 2 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// red and black alt the same
|
|
// >y <y
|
|
// .-------'| .----'|
|
|
// >r | <r | <r
|
|
// .----'| => | .----'| => | .-'|
|
|
// | <b | | <b | | >b
|
|
// | .-'| | | .-'| .--|--|-'|
|
|
// 4 1 2 4 1 2 3 4 1 2 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_ysplits]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// split a yellow triple, not taking any alt
|
|
// <b
|
|
// .-'|
|
|
// <y <y |
|
|
// .-------'| .-------'| |
|
|
// | <r => | <r |
|
|
// | .----'| | .----' |
|
|
// | | <b | | <b
|
|
// | | .-'| | | .----'|
|
|
// 1 2 3 4 1 2 3 4 4
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// split a yellow triple, taking the black alt
|
|
// <b
|
|
// .-'|
|
|
// <y <y |
|
|
// .-------'| .-------'| |
|
|
// | <r => | <r |
|
|
// | .----'| | .----' |
|
|
// | | <b | | >b
|
|
// | | .-'| | | .-'|
|
|
// 1 2 3 4 1 2 3 4 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// split a yellow triple, taking the red alt
|
|
// <y >b
|
|
// .-------'| .-'|
|
|
// | <r | <b
|
|
// | .----'| => .--------|-'|
|
|
// | | <b | <b |
|
|
// | | .-'| | .-'| |
|
|
// 1 2 3 4 1 2 3 4 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
|
|
// split a yellow triple, taking the yellow alt
|
|
// <y >b
|
|
// .-------'| .-'|
|
|
// | <r | >b
|
|
// | .----'| => .-----|-'|
|
|
// | | <b | <b |
|
|
// | | .-'| | .-'| |
|
|
// 1 2 3 4 1 2 3 4 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_quintifoliate]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// split a yellow triple, not taking any alt
|
|
// <b
|
|
// .-'|
|
|
// <y |
|
|
// .-------'| |
|
|
// <y | <r |
|
|
// .-------'| => | .----' |
|
|
// | <r | | <r
|
|
// | .----'| | | .----'|
|
|
// | | <b | | | <b
|
|
// | | .-'| | | | .-'|
|
|
// 1 2 3 4 1 2 3 4 5
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
|
|
// split a yellow triple, taking the black alt
|
|
// <b
|
|
// .-'|
|
|
// <y |
|
|
// .-------'| |
|
|
// <y | <r |
|
|
// .-------'| => | .----' |
|
|
// | <r | | >r
|
|
// | .----'| | | .-'|
|
|
// | | <b | | | >b
|
|
// | | .-'| | | .--|-'|
|
|
// 1 2 4 5 1 2 4 5 3
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
|
|
// split a yellow triple, taking the red alt
|
|
// >b
|
|
// .-'|
|
|
// <y | <r
|
|
// .-------'| .--------|-'|
|
|
// | <r | | >b
|
|
// | .----'| => | .-----|-'|
|
|
// | | <b | | <b |
|
|
// | | .-'| | | .-'| |
|
|
// 1 3 4 5 1 3 4 5 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
|
|
// split a yellow triple, taking the yellow alt
|
|
// >b
|
|
// .-'|
|
|
// <y | >r
|
|
// .-------'| .-----|-'|
|
|
// | <r | | >b
|
|
// | .----'| => .--|-----|-'|
|
|
// | | <b | | <b |
|
|
// | | .-'| | | .-'| |
|
|
// 2 3 4 5 2 3 4 5 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_prunes]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// don't prune
|
|
// <b <b
|
|
// .-'| .----'|
|
|
// <y | <y |
|
|
// .-------'| | .-------'| |
|
|
// | <r | | <r |
|
|
// | .----' | => | .----' |
|
|
// | | <r | | <r
|
|
// | | .----'| | | .-------'|
|
|
// | | | <b | | | <b
|
|
// | | | .-'| | | | .----'|
|
|
// 1 2 3 4 5 1 2 3 4 5 5
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
|
|
// prune by taking a red alt
|
|
// <b
|
|
// .-'|
|
|
// <y | >b
|
|
// .-------'| | .-'|
|
|
// | <r | | <b
|
|
// | .----' | => .-----------|-'|
|
|
// | | <r | <r |
|
|
// | | .----'| | .----'| |
|
|
// | | | <b | | <b |
|
|
// | | | .-'| | | .-'| |
|
|
// 1 2 3 4 5 1 2 3 4 5 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
|
|
// prune by taking a yellow alt (this needs to prune during the rflip)
|
|
// <b
|
|
// .-'|
|
|
// <y | >b
|
|
// .-------'| | .-'|
|
|
// | <r | | >b
|
|
// | .----' | => .--------|-'|
|
|
// | | <r | <r |
|
|
// | | .----'| | .----'| |
|
|
// | | | <b | | <b |
|
|
// | | | .-'| | | .-'| |
|
|
// 1 2 3 4 5 1 2 3 4 5 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_sextifoliate]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// don't prune
|
|
// <b
|
|
// .----'|
|
|
// <b <y |
|
|
// .-'| .-------'| |
|
|
// <y | | <r |
|
|
// .-------'| | | .----' |
|
|
// | <r | | | <y
|
|
// | .----' | => | | .-------'|
|
|
// | | <r | | | <r
|
|
// | | .----'| | | | .----'|
|
|
// | | | <b | | | | <b
|
|
// | | | .-'| | | | | .-'|
|
|
// 1 2 3 4 5 1 2 3 4 5 6
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 6, -1, "\xff\xff\xff\xff", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 6, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 6, -1);
|
|
|
|
// prune by taking a red alt
|
|
// <b >b
|
|
// .-'| .-'|
|
|
// <y | | <r
|
|
// .-------'| | .-----------|-'|
|
|
// | <r | | | >b
|
|
// | .----' | => | .--------|-'|
|
|
// | | <r | | <r |
|
|
// | | .----'| | | .----'| |
|
|
// | | | <b | | | <b |
|
|
// | | | .-'| | | | .-'| |
|
|
// 1 3 4 5 6 1 3 4 5 6 2
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 6, -1, "\xff\xff\xff\xff", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 6, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 6, -1);
|
|
|
|
// prune by taking a yellow alt (this needs to prune during the rflip)
|
|
// <b >b
|
|
// .-'| .-'|
|
|
// <y | | >r
|
|
// .-------'| | .--------|-'|
|
|
// | <r | | | >b
|
|
// | .----' | => .--|--------|-'|
|
|
// | | <r | | <r |
|
|
// | | .----'| | | .----'| |
|
|
// | | | <b | | | <b |
|
|
// | | | .-'| | | | .-'| |
|
|
// 2 3 4 5 6 2 3 4 5 6 1
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
LFSR_ATTR2(UATTR, 6, -1, "\xff\xff\xff\xff", 4,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL))))))) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 3, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 5, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 6, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 6, -1);
|
|
'''
|
|
|
|
[cases.test_rbyd_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// build the attribute list for the current permutation
|
|
struct lfsr_attr attrs[N];
|
|
for (unsigned j = 0; j < N; j++) {
|
|
attrs[j] = *LFSR_ATTR2(
|
|
UATTR, perm[j]+1, -1,
|
|
"\xaa\xaa\xaa\xaa", 4,
|
|
(j+1 < N) ? &attrs[j+1] : NULL);
|
|
}
|
|
|
|
// test the given permutation
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, j+1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, j+1, -1);
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// test the given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, perm[j]+1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
NULL)) => 0;
|
|
}
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, j+1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, j+1, -1);
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_large]
|
|
in = 'lfs.c'
|
|
# ORDER:
|
|
# 0 = in-order
|
|
# 1 = reverse-order
|
|
# 2 = random-order
|
|
defines.ORDER = [0, 1, 2]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// create the rbyd tree
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
// keep appending tags until we run out of space
|
|
//
|
|
// note, this will likely repeat tags, but that's ok
|
|
//
|
|
lfs_size_t count = 0;
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0;; i++) {
|
|
uint8_t x
|
|
= (ORDER == 0) ? (uint8_t)i
|
|
: (ORDER == 1) ? (uint8_t)(((lfs_size_t)-1) - i)
|
|
: (uint8_t)TEST_PRNG(&prng);
|
|
int err = lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, x, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
NULL));
|
|
// if we can't fit an fcrc, erased is set to false, but if we can,
|
|
// lfsr_rbyd_commit may error later with LFS_ERR_RANGE
|
|
if (!rbyd.erased || err == LFS_ERR_RANGE) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
|
|
count = i;
|
|
}
|
|
|
|
// check that we can still lookup all the tags
|
|
prng = 42;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
for (lfs_size_t i = 0; i < count; i++) {
|
|
uint8_t x
|
|
= (ORDER == 0) ? (uint8_t)i
|
|
: (ORDER == 1) ? (uint8_t)(((lfs_size_t)-1) - i)
|
|
: (uint8_t)TEST_PRNG(&prng);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, x, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, x, -1);
|
|
}
|
|
'''
|
|
|
|
|
|
### Removal testing ###
|
|
|
|
[cases.test_rbyd_remove]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// add and remove one attribute
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes, remove the first one
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes, remove the second one
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N+1'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, perm[j]+1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
NULL)) => 0;
|
|
}
|
|
|
|
// copy block so we can reset after each remove
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try removing each tag
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// print what we are removing to help debugging
|
|
printf("--- remove: %d ---\n", j+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, j+1, -1, NULL, 0, NULL)) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N; k++) {
|
|
lfsr_stag_t tag = lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, k+1, -1), &off, &size);
|
|
if (k == j) {
|
|
if (j == N-1) {
|
|
assert(tag == LFS_ERR_NOENT);
|
|
} else {
|
|
assert(tag == LFSR_TAG2(UATTR, j+1+1, -1));
|
|
}
|
|
} else {
|
|
assert(tag == LFSR_TAG2(UATTR, k+1, -1));
|
|
}
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_missing]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 4'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// create a tree two attributes
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
NULL))) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to remove tags that aren't there, this should do nothing
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// one last fetch to make sure nothing was broken
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_again]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 8'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// create a tree
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 3, -1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 4, -1, "\xdd\xdd\xdd\xdd", 4,
|
|
LFSR_ATTR2(UATTR, 5, -1, "\xee\xee\xee\xee", 4,
|
|
NULL)))))) => 0;
|
|
// remove several attributes
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0,
|
|
LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0,
|
|
LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)))) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to remove tags that aren't there, this should do nothing
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to remove the tags again, just to make sure (keep in mind
|
|
// these removes still commit to the rbyd)
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// one last fetch to make sure nothing was broken
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 2, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 3, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 4, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 4, -1);
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 5, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_all]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// commit with one attribute, remove it
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes, remove both
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0,
|
|
LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0, NULL))) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// commit with two attributes, remove both in the other order
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 2, -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0,
|
|
LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL))) => 0;
|
|
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG2(UATTR, 2, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_all_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2*N+1'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// create one consistent block
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, j+1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
NULL)) => 0;
|
|
}
|
|
|
|
// copy block so we can reset after each remove
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// restore backup
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// remove each tag in permutation order
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, perm[j]+1, -1, NULL, 0, NULL)) => 0;
|
|
}
|
|
|
|
// check that all tags are now removed
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, j+1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
}
|
|
|
|
// try resuming from all tags being removed
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, 1, -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6,
|
|
NULL)) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, 1, -1), &off, &size)
|
|
=> LFSR_TAG2(UATTR, 1, -1);
|
|
for (unsigned j = 1; j < N; j++) {
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, j+1, -1), &off, &size)
|
|
=> LFS_ERR_NOENT;
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N + N + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_remove_append_permutations]
|
|
defines.N = 'range(1, 6)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N+2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, perm[j]+1, -1, "\xaa\xaa\xaa\xaa", 4,
|
|
NULL)) => 0;
|
|
}
|
|
|
|
// copy block so we can reset after each remove
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try removing each tag
|
|
for (unsigned j = 0; j < N; j++) {
|
|
for (unsigned l = 0; l < N; l++) {
|
|
// print what we are removing to help debugging
|
|
printf("--- remove: %d, append: %d ---\n", j+1, l+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// remove
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(RMUATTR, j+1, -1, NULL, 0, NULL)) => 0;
|
|
|
|
// try appending each tag to make sure the rbyd tree
|
|
// is still usable
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, l+1, -1,
|
|
"\xaa\xaa\xaa\xaa\xaa\xaa", 6,
|
|
NULL)) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N; k++) {
|
|
lfsr_stag_t tag = lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, k+1, -1), &off, &size);
|
|
if (k == l) {
|
|
assert(tag == LFSR_TAG2(UATTR, l+1, -1));
|
|
assert(size == 6);
|
|
} else if (k == j) {
|
|
if (j == N-1) {
|
|
assert(tag == LFS_ERR_NOENT);
|
|
} else {
|
|
assert(tag == LFSR_TAG2(UATTR, j+1+1, -1));
|
|
}
|
|
} else {
|
|
assert(tag == LFSR_TAG2(UATTR, k+1, -1));
|
|
assert(size == 4);
|
|
}
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N + 1 + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
|
|
### Insertion testing ###
|
|
|
|
[cases.test_rbyd_create]
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try to create one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
|
|
// try to create two ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
// try to create two in the other direction
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
// create a third to the right
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
// create a third to the left
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
// create a third in the middle
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_create]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 3'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try to create one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
|
|
// try to create two ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
// try to create two in the other direction
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
|
|
// create a third to the right
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
// create a third to the left
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
// create a third in the middle
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 3);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4) => 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
'''
|
|
|
|
[cases.test_rbyd_create_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// build the attribute list for the current permutation
|
|
struct lfsr_attr attrs[N];
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
attrs[j] = *LFSR_ATTR(
|
|
MKREG, id,
|
|
names[perm[j] % 6], 4,
|
|
(j+1 < N) ? &attrs[j+1] : NULL);
|
|
}
|
|
|
|
// test the given permutation
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == N);
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, j), buffer, 4) => 4;
|
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_create_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// test the given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4,
|
|
NULL)) => 0;
|
|
}
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == N);
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, j), buffer, 4) => 4;
|
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_create_large]
|
|
in = 'lfs.c'
|
|
# ORDER:
|
|
# 0 = in-order
|
|
# 1 = reverse-order
|
|
# 2 = random-order
|
|
defines.ORDER = [0, 1, 2]
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
|
|
// create the rbyd tree
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
// keep inserting tags until we run out of space
|
|
//
|
|
// note, the ids we create this way are both sparse and sometimes
|
|
// repeated, so we need to mod our current rbyd size to avoid invalid
|
|
// insertions
|
|
//
|
|
uint32_t prng = 42;
|
|
for (lfs_size_t i = 0;; i++) {
|
|
uint16_t x
|
|
= (ORDER == 0) ? (uint16_t)i
|
|
: (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i)
|
|
: (uint16_t)TEST_PRNG(&prng);
|
|
x = x % (rbyd.count+1);
|
|
|
|
int err = lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, x, names[x % 6], 4, NULL));
|
|
// if we can't fit an fcrc, erased is set to false, but if we can,
|
|
// lfsr_rbyd_commit may error later with LFS_ERR_RANGE
|
|
if (!rbyd.erased || err == LFS_ERR_RANGE) {
|
|
break;
|
|
}
|
|
assert(err == 0);
|
|
}
|
|
|
|
// check that we can at least lookup all the tags
|
|
lfs_off_t off;
|
|
lfs_size_t size;
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
for (uint16_t x = 0; x < rbyd.count; x++) {
|
|
lfsr_rbyd_lookup(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, x), &off, &size)
|
|
=> LFSR_TAG(MKREG, x);
|
|
}
|
|
'''
|
|
|
|
|
|
### Deletion testing ###
|
|
|
|
[cases.test_rbyd_delete]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try to delete one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the other id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the largest of three
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the smallest of three
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the middle
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_range]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// try to delete one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2, NULL))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the other id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2, NULL))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the largest of three
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2,
|
|
LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, 2, "\xcc\xcc", 2, NULL))))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the smallest of three
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2,
|
|
LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, 2, "\xcc\xcc", 2, NULL))))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try to delete the middle
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2,
|
|
LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, 2, "\xcc\xcc", 2, NULL))))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 2);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 1), buffer, 4)
|
|
=> 4;
|
|
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4)
|
|
=> 2;
|
|
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N+1'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4,
|
|
NULL)) => 0;
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try deleting each id
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// print what we are deleting to help debugging
|
|
printf("--- delete: %d ---\n", j+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0;
|
|
assert(rbyd.count == N-1);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N-1; k++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, k), buffer, 4) => 4;
|
|
if (k >= j) {
|
|
assert(memcmp(buffer, names[(k+1) % 6], 4) == 0);
|
|
} else {
|
|
assert(memcmp(buffer, names[k % 6], 4) == 0);
|
|
}
|
|
}
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, N-1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_range_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.M = 'range(1, 4)'
|
|
in = 'lfs.c'
|
|
if = '''
|
|
BLOCK_SIZE/PROG_SIZE >= N+N*M+1
|
|
&& BLOCK_SIZE >= 4096
|
|
'''
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4,
|
|
NULL)) => 0;
|
|
// note uattrs have a smaller size to help debugging
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, u+1, id, names[perm[j] % 6], 2,
|
|
NULL)) => 0;
|
|
}
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try deleting each id
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// print what we are deleting to help debugging
|
|
printf("--- delete: %d ---\n", j+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0;
|
|
assert(rbyd.count == N-1);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N-1; k++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, k), buffer, 4) => 4;
|
|
if (k >= j) {
|
|
assert(memcmp(buffer, names[(k+1) % 6], 4) == 0);
|
|
} else {
|
|
assert(memcmp(buffer, names[k % 6], 4) == 0);
|
|
}
|
|
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, u+1, k), buffer, 4) => 2;
|
|
if (k >= j) {
|
|
assert(memcmp(buffer, names[(k+1) % 6], 2) == 0);
|
|
} else {
|
|
assert(memcmp(buffer, names[k % 6], 2) == 0);
|
|
}
|
|
}
|
|
}
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, N-1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, 1, N-1), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N+N*M + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_all]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// create and delete one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete two ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete two ids in the other order
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete three ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete three ids in the other order
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 2, NULL, 0,
|
|
LFSR_ATTR(RM, 1, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_all_range]
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= 2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
uint8_t buffer[4];
|
|
|
|
// create and delete one id
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2, NULL))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete two ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2, NULL))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete two ids in the other order
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2, NULL))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 1, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete three ids
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2,
|
|
LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, 2, "\xcc\xcc", 2, NULL))))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// create and delete three ids in the other order
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4,
|
|
LFSR_ATTR2(UATTR, 1, 0, "\xaa\xaa", 2,
|
|
LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
|
|
LFSR_ATTR2(UATTR, 1, 1, "\xbb\xbb", 2,
|
|
LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
|
|
LFSR_ATTR2(UATTR, 1, 2, "\xcc\xcc", 2, NULL))))))) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, 2, NULL, 0,
|
|
LFSR_ATTR(RM, 1, NULL, 0,
|
|
LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0;
|
|
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_all_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
in = 'lfs.c'
|
|
if = '''
|
|
BLOCK_SIZE/PROG_SIZE >= 2*N+1
|
|
&& BLOCK_SIZE >= 1024
|
|
'''
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[6];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// create one consistent block
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, j, names[j % 6], 4,
|
|
NULL)) => 0;
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// restore backup
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// delete each id in permutation order
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on previous deletions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = 0; k < j; k++) {
|
|
if (perm[k] < perm[j]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
uint16_t rbyd_count_before = rbyd.count;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0;
|
|
assert(rbyd.count == rbyd_count_before-1);
|
|
}
|
|
|
|
// check that all tags are now removed
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try resuming from all tags being removed
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6,
|
|
NULL)) => 0;
|
|
assert(rbyd.count == 1);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 0), buffer, 6)
|
|
=> 6;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 1), buffer, 6)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + 2*N + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_all_range_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.M = 'range(1, 4)'
|
|
in = 'lfs.c'
|
|
if = '''
|
|
BLOCK_SIZE/PROG_SIZE >= N+N*M + N + 1+M
|
|
&& BLOCK_SIZE >= 4096
|
|
'''
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[6];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// create one consistent block
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, j, names[j % 6], 4,
|
|
NULL)) => 0;
|
|
// note uattrs have a smaller size to help debugging
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, u+1, j, names[j % 6], 2,
|
|
NULL)) => 0;
|
|
}
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// restore backup
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// delete each id in permutation order
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on previous deletions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = 0; k < j; k++) {
|
|
if (perm[k] < perm[j]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
uint16_t rbyd_count_before = rbyd.count;
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0;
|
|
assert(rbyd.count == rbyd_count_before-1);
|
|
}
|
|
|
|
// check that all tags are now removed
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 0);
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 0), buffer, 4)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// try resuming from all tags being removed
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6,
|
|
NULL)) => 0;
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, u+1, 0, "\xaa\xaa\xaa", 3,
|
|
NULL)) => 0;
|
|
}
|
|
assert(rbyd.count == 1);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.count == 1);
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 0), buffer, 6)
|
|
=> 6;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0);
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, u+1, 0), buffer, 6)
|
|
=> 3;
|
|
assert(memcmp(buffer, "\xaa\xaa\xaa", 3) == 0);
|
|
}
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, 1), buffer, 6)
|
|
=> LFS_ERR_NOENT;
|
|
lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, 1, 1), buffer, 6)
|
|
=> LFS_ERR_NOENT;
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N+N*M + N + 1+M;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_create_permutations]
|
|
defines.N = 'range(1, 6)'
|
|
in = 'lfs.c'
|
|
if = 'BLOCK_SIZE/PROG_SIZE >= N+2'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][6] = {
|
|
"\xaa\xaa\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[6];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4,
|
|
NULL)) => 0;
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try deleting each id
|
|
for (unsigned j = 0; j < N; j++) {
|
|
for (unsigned l = 0; l < N; l++) {
|
|
// print what we are deleting to help debugging
|
|
printf("--- delete: %d, create: %d ---\n", j+1, l+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// delete
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, j, NULL, 0,
|
|
NULL)) => 0;
|
|
assert(rbyd.count == N-1);
|
|
|
|
// try creating each tag to make sure the rbyd tree
|
|
// is still usable
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, l, names[l % 6], 6,
|
|
NULL)) => 0;
|
|
assert(rbyd.count == N);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N; k++) {
|
|
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, k), buffer, 6);
|
|
if (k == l) {
|
|
assert(size == 6);
|
|
assert(memcmp(buffer, names[l % 6], 6) == 0);
|
|
} else {
|
|
uint16_t expected = k;
|
|
if (expected > l) {
|
|
expected -= 1;
|
|
}
|
|
if (expected >= j) {
|
|
expected += 1;
|
|
}
|
|
assert(size == 4);
|
|
assert(memcmp(buffer, names[expected % 6], 4) == 0);
|
|
}
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N + 1 + 1;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_delete_create_range_permutations]
|
|
defines.N = 'range(1, 6)'
|
|
defines.M = 'range(1, 4)'
|
|
in = 'lfs.c'
|
|
if = '''
|
|
BLOCK_SIZE/PROG_SIZE >= N+N*M + 1 + 1+M
|
|
&& BLOCK_SIZE >= 4096
|
|
'''
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.trunk = 0,
|
|
.off = 0,
|
|
.rev = 1,
|
|
.crc = 0,
|
|
.count = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][6] = {
|
|
"\xaa\xaa\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[6];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]+1);
|
|
}
|
|
printf("] ---\n");
|
|
|
|
// create given permutation with multiple commits
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
for (unsigned j = 0; j < N; j++) {
|
|
// adjust id based on future insertions
|
|
uint16_t id = perm[j];
|
|
for (unsigned k = j+1; k < N; k++) {
|
|
if (perm[j] > perm[k]) {
|
|
id -= 1;
|
|
}
|
|
}
|
|
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4,
|
|
NULL)) => 0;
|
|
// note uattrs have a smaller size to help debugging
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, u+1, id, names[perm[j] % 6], 2,
|
|
NULL)) => 0;
|
|
}
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
// copy block so we can reset after each delete
|
|
lfsr_rbyd_t backup_rbyd = rbyd;
|
|
uint8_t backup_block[BLOCK_SIZE];
|
|
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
|
|
// try deleting each id
|
|
for (unsigned j = 0; j < N; j++) {
|
|
for (unsigned l = 0; l < N; l++) {
|
|
// print what we are deleting to help debugging
|
|
printf("--- delete: %d, create: %d ---\n", j+1, l+1);
|
|
|
|
rbyd = backup_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
|
|
rbyd.block, 0, backup_block, rbyd.off) => 0;
|
|
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
|
|
|
|
// delete
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RM, j, NULL, 0,
|
|
NULL)) => 0;
|
|
assert(rbyd.count == N-1);
|
|
|
|
// try creating each tag to make sure the rbyd tree
|
|
// is still usable
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(MKREG, l, names[l % 6], 6,
|
|
NULL)) => 0;
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR2(UATTR, u+1, l, names[l % 6], 3,
|
|
NULL)) => 0;
|
|
}
|
|
assert(rbyd.count == N);
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
|
|
cfg->block_size, NULL) => 0;
|
|
for (unsigned k = 0; k < N; k++) {
|
|
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG(MKREG, k), buffer, 6);
|
|
if (k == l) {
|
|
assert(size == 6);
|
|
assert(memcmp(buffer, names[l % 6], 6) == 0);
|
|
} else {
|
|
uint16_t expected = k;
|
|
if (expected > l) {
|
|
expected -= 1;
|
|
}
|
|
if (expected >= j) {
|
|
expected += 1;
|
|
}
|
|
assert(size == 4);
|
|
assert(memcmp(buffer, names[expected % 6], 4) == 0);
|
|
}
|
|
|
|
for (unsigned u = 0; u < M; u++) {
|
|
size = lfsr_rbyd_get(&lfs, &rbyd,
|
|
LFSR_TAG2(UATTR, u+1, k), buffer, 6);
|
|
if (k == l) {
|
|
assert(size == 3);
|
|
assert(memcmp(buffer, names[l % 6], 3) == 0);
|
|
} else {
|
|
uint16_t expected = k;
|
|
if (expected > l) {
|
|
expected -= 1;
|
|
}
|
|
if (expected >= j) {
|
|
expected += 1;
|
|
}
|
|
assert(size == 2);
|
|
assert(memcmp(buffer, names[expected % 6], 2) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// keep track of the worst size
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are roughly ~8 bytes
|
|
lfs_size_t n = 1 + N+N*M + 1 + 1+M;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 8*n*(2*lfs_nlog2(n)+1));
|
|
printf("avg height: %u B (N=%u, estimate=%u)\n",
|
|
worst_size / n, n, 8*(2*lfs_nlog2(n)+1));
|
|
// note this only holds true with byte-level progs
|
|
if (PROG_SIZE == 1) {
|
|
assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1));
|
|
}
|
|
'''
|