Files
littlefs/tests/test_rbyd.toml
T
Christopher Haster 08f5d9ddf4 Middle of a rewrite for 3-leb encoding, but rbyd appends and creates both work
If we combine rbyd ids and B-tree weights, we need 32-bit ids since this
will eventually need to cover the full range of a file. This simply
doesn't fit into a single word anymore, unless littlefs uses 64-bit tags.
Generally not a great idea for a filesystem targeting even 8-bit
microcontrollers.

So here is a tag encoding that uses 3 leb128 words. This will likely
have more code cost and slightly more disk usage (we can no longer fit
tags into 2 bytes), though with most tags being alt pointers (O(m log m)
vs O(m)), this may not be that significant.

Note that we try to keep tags limited to 14-bits to avoid an extra leb128 byte,
which would likely affect all alt pointers. To pull this off we do away
with the subtype/suptype distinction, limiting in-tree tag types to
10-bits encoded on a per-suptype basis:

  in-tree tags:
                       ttttttt ttt00rv
                                 ^--^^- 10-bit type
                                    '|- removed bit
                                     '- valid bit
  iiii iiiiiii iiiiiii iiiiiii iiiiiii
                                     ^- n-bit id
       lllllll lllllll lllllll lllllll
                                     ^- m-bit length

  out-of-tree tags:
                       ttttttt ttt010v
                                 ^---^- 10-bit type
                                     '- valid bit
                               0000000
       lllllll lllllll lllllll lllllll
                                     ^- m-bit length

  alt tags:
                       kkkkkkk kkk1dcv
                                 ^-^^^- 10-bit key
                                   '||- direction bit
                                    '|- color bit
                                     '- valid bit
  wwww wwwwwww wwwwwww wwwwwww wwwwwww
                                     ^- n-bit weight
       jjjjjjj jjjjjjj jjjjjjj jjjjjjj
                                     ^- m-bit jump

The real pain is that with separate integers for id and tag, it no
longer makes sense to combine these into one big weight field. This
requires a significant rewrite.
2023-02-12 17:14:44 -06:00

5353 lines
182 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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_ATTR(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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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_ATTR(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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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_ATTR(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_ATTR(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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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_TAG_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_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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_TAG_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_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
'''
# TODO this but for creates?
# TODO and for tags that cross id boundaries?
[cases.test_rbyd_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
lfs_off_t off_;
lfs_size_t size_;
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
lfs_off_t off_;
lfs_size_t size_;
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &off_, &size_) => 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
// split the other direction
// >b
// => .-'|
// 2 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_bflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_trifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_rflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_quadrifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_rotations]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_ysplits]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_quintifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_prunes]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_sextifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(6), -1, "\xff\xff\xff\xff", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
// 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_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(6), -1, "\xff\xff\xff\xff", 4,
LFSR_ATTR(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_TAG_UATTR(1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
'''
[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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(
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_TAG_UATTR(j+1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -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;
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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(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_TAG_UATTR(j+1), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -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;
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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfsr_sid_t id_;
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_ATTR(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_TAG_UATTR(x), -1,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(x));
assert(id_ == -1);
assert(size_ == 4);
}
'''
#### 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(1), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(1), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(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_TAG_UATTR(k+1), -1, &off, &size);
# if (k == j) {
# if (j == N-1) {
# assert(tag == LFS_ERR_NOENT);
# } else {
# assert(tag == LFSR_TAG_UATTR(j+1+1), -1);
# }
# } else {
# assert(tag == LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
# NULL))) => 0;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size)
# => LFSR_TAG_UATTR(2), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size)
# => LFSR_TAG_UATTR(4), -1;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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_TAG_UATTR(1), -1, &off, &size)
# => LFS_ERR_NOENT;
# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(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_TAG_UATTR(j+1), -1, &off, &size)
# => LFS_ERR_NOENT;
# }
#
# // try resuming from all tags being removed
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(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_TAG_UATTR(1), -1, &off, &size)
# => LFSR_TAG_UATTR(1), -1;
# for (unsigned j = 1; j < N; j++) {
# lfsr_rbyd_lookup(&lfs, &rbyd,
# LFSR_TAG_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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(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_ATTR(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_TAG_UATTR(k+1), -1, &off, &size);
# if (k == l) {
# assert(tag == LFSR_TAG_UATTR(l+1), -1);
# assert(size == 6);
# } else if (k == j) {
# if (j == N-1) {
# assert(tag == LFS_ERR_NOENT);
# } else {
# assert(tag == LFSR_TAG_UATTR(j+1+1), -1);
# }
# } else {
# assert(tag == LFSR_TAG_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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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.weight == 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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.weight == 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,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 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.weight+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
lfsr_tag_t tag_;
lfsr_sid_t id_;
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.weight; x++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, x,
&tag_, &id_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == x);
assert(size_ == 4);
}
'''
#### 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2,
# NULL)) => 0;
# }
# }
# assert(rbyd.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))) => 0;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0;
#
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(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.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(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.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(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.weight == 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.weight == 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_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
# LFSR_ATTR(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.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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.weight == 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.weight_before = rbyd.weight;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0;
# assert(rbyd.weight == rbyd.weight_before-1);
# }
#
# // check that all tags are now removed
# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
# assert(rbyd.weight == 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.weight == 1);
#
# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
# assert(rbyd.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(u+1), j, names[j % 6], 2,
# NULL)) => 0;
# }
# }
# assert(rbyd.weight == 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.weight_before = rbyd.weight;
# lfsr_rbyd_commit(&lfs, &rbyd,
# LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0;
# assert(rbyd.weight == rbyd.weight_before-1);
# }
#
# // check that all tags are now removed
# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
# assert(rbyd.weight == 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_ATTR(UATTR(u+1), 0, "\xaa\xaa\xaa", 3,
# NULL)) => 0;
# }
# assert(rbyd.weight == 1);
#
# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
# assert(rbyd.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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.weight == 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.weight == 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.weight == 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,
# .rev = 1,
# .off = 0,
# .crc = 0,
# .trunk = 0,
# .weight = 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_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2,
# NULL)) => 0;
# }
# }
# assert(rbyd.weight == 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.weight == 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_ATTR(UATTR(u+1), l, names[l % 6], 3,
# NULL)) => 0;
# }
# assert(rbyd.weight == 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));
# }
#'''