Files
littlefs/tests/test_rbyd.toml
T
Christopher Haster 0756c0acf2 Cleanup of code that is no longer going to be used
- lfsr_rbyd_predictedlookup, the new B-tree approach means we hopefully
  won't need this anymore. Worst case this remove can be reverted.

- LFSR_TAG_FROM - this will likely come back, but needs to be
  rewrittern.
2023-03-19 01:21:31 -05:00

11387 lines
396 KiB
TOML

# Test this inner rbyd data-structure
# test with a number of different erase values
defines.ERASE_VALUE = [0xff, 0x00, 0x1b]
# set block_size to the full size of disk so we can test arbitrarily
# large rbyd trees, we don't really care about block sizes at this
# abstraction level
#
# ok not quite full disk size (we do use the full disk size in bench_rbyd),
# but a bit less since erasing the full disk takes time and we don't want to
# waste time when testing
defines.BLOCK_SIZE = 32768
[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'
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'
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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, in the other direction
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_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, in the other direction
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, NULL)) => 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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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;
// commit with two attributes, in the other direction
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_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'
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;
// commit with two attributes, in the other direction
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, NULL)) => 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;
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_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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_;
lfs_ssize_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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[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_;
lfs_ssize_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, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
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_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_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_;
lfs_ssize_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, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
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, NULL)) => 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_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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;
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
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;
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_update_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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 = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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;
// update each tag in permutation order
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1,
"\xaa\xaa\xaa\xaa\xaa\xaa", 6,
NULL)) => 0;
}
// check that all tags have been updated
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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 6);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[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_;
lfs_ssize_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 (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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(x));
assert(id_ == -1);
assert(size_ == 4);
}
'''
### Removal testing ###
[cases.test_rbyd_remove]
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_;
lfs_ssize_t id_;
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_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &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_ATTR(RMUATTR(2), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &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_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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 = malloc(rbyd.off);
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);
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(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++) {
int err = lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(k+1), -1,
&tag_, &id_, NULL, &off_, &size_);
assert(!err || err == LFS_ERR_NOENT);
if (k == j) {
if (j == N-1) {
assert(err == LFS_ERR_NOENT);
} else {
assert(!err);
assert(tag_ == LFSR_TAG_UATTR(j+1+1));
assert(id_ == -1);
assert(size_ == 4);
}
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
// try appending the tag back to make sure things still work
printf("--- append: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(j+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_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(k+1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k == j) {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 6);
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + 2;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_traverse_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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 = malloc(rbyd.off);
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);
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(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0;
// try traversing over the tags
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
tag_ = 0;
id_ = -1;
for (unsigned k = 0; k < N-1; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k >= j) {
assert(tag_ == LFSR_TAG_UATTR(k+1+1));
assert(id_ == -1);
assert(size_ == 4);
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
[cases.test_rbyd_remove_missing]
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_;
lfs_ssize_t id_;
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,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// try to remove tags that aren't there, this should do nothing
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &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,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_remove_again]
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_;
lfs_ssize_t id_;
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_ATTR(RMUATTR(1), -1, NULL, 0,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// try to remove tags that aren't there, this should do nothing
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &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_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &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,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_remove_all]
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_;
lfs_ssize_t id_;
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_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_ATTR(RMUATTR(1), -1, NULL, 0,
LFSR_ATTR(RMUATTR(2), -1, NULL, 0, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_ATTR(RMUATTR(2), -1, NULL, 0,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &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_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_all_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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 = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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_ATTR(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,
&tag_, &id_, NULL, &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,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 6);
for (unsigned j = 1; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N + 1;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# the main purpose of this test is to try to fuzz for failures in the
# balancing algorithm
[cases.test_rbyd_fuzz_append_removes]
defines.N = 'range(1, 33)'
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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 char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
for (unsigned i = 0; i < N; i++) {
// choose an attr
uint8_t attr = TEST_PRNG(&prng) % N;
// choose append or remove
if (TEST_PRNG(&prng) & 1) {
printf("a0x%02x=%c", attr, alpha[i % 26]);
} else {
printf("r0x%02x", attr);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against
char *sim = malloc(N);
memset(sim, 0, N);
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
for (unsigned i = 0; i < N; i++) {
// choose an attr
uint8_t attr = TEST_PRNG(&prng) % N;
// choose append or remove
if (TEST_PRNG(&prng) & 1) {
// update our sim
sim[attr] = alpha[i % 26];
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1,
NULL)) => 0;
} else {
// update our sim
sim[attr] = '\0';
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(attr), -1, NULL, 0,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
bool first = true;
for (unsigned attr = 0; attr < N; attr++) {
if (sim[attr]) {
if (!first) {
printf(", ");
}
first = false;
printf("0x%02x=%c", attr, sim[attr]);
}
}
printf("]\n");
printf("rbyd: [");
first = true;
for (unsigned attr = 0; attr < N; attr++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(attr), -1, buffer, 4);
if (size >= 0) {
if (!first) {
printf(", ");
}
first = false;
printf("0x%02x=%.*s", attr, size, buffer);
}
}
printf("]\n");
for (unsigned attr = 0; attr < N; attr++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(attr), -1, buffer, 4);
if (sim[attr]) {
assert(size == 1);
assert(memcmp(&sim[attr], buffer, 1) == 0);
} else {
assert(size == LFS_ERR_NOENT);
}
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size
printf("--- summary ---\n");
printf("worst seed: %d\n", worst_seed);
printf("worst size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= N*12*(2*lfs_nlog2(N)+1)+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(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
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'
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(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 2, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
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)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[2*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[2*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
(2*j+1 < 2*N) ? &attrs[2*j+1] : NULL);
attrs[2*j+1] = *LFSR_ATTR(
MKREG, id, names[perm[j] % 6], 4,
(2*j+2 < 2*N) ? &attrs[2*j+2] : 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
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_create_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_create_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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_create_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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_create_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[2*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[2*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
(2*j+1 < 2*N) ? &attrs[2*j+1] : NULL);
attrs[2*j+1] = *LFSR_ATTR(
MKREG, id, names[perm[j] % 6], 4,
(2*j+2 < 2*N) ? &attrs[2*j+2] : 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);
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_create_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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);
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[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(GROW, x, NULL, 1,
LFSR_ATTR(MKREG, x, names[x % 6], 4, NULL)));
if (err == LFS_ERR_RANGE) {
break;
}
assert(err == 0);
}
// check that we can at least lookup all the tags
//
// note with random order we can't check that stored values reliably
lfsr_tag_t tag_;
lfs_ssize_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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == x);
assert(size_ == 4);
}
'''
### Mixed create and attr testing ###
[cases.test_rbyd_mixed]
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_UATTR(1), 0, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 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);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
// try to create two ids
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
// create a third to the right
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, 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);
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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
// create a third to the left
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
// create a third in the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
'''
[cases.test_rbyd_multi_mixed]
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_UATTR(1), 0, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 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);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
// try to create two ids
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
// create a third to the right
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, 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);
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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
// create a third to the left
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
// create a third in the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 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_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_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
'''
[cases.test_rbyd_mixed_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[(2+M)*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[(2+M)*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
((2+M)*j+1 < (2+M)*N) ? &attrs[(2+M)*j+1] : NULL);
attrs[(2+M)*j+1] = *LFSR_ATTR(
MKREG, id, names[perm[j] % 6], 4,
((2+M)*j+2 < (2+M)*N) ? &attrs[(2+M)*j+2] : NULL);
// note uattrs have a smaller size to help debugging
for (unsigned u = 0; u < M; u++) {
attrs[(2+M)*j+2+u] = *LFSR_ATTR(
UATTR(u+1), id, names[perm[j] % 6], 2,
((2+M)*j+2+u+1 < (2+M)*N)
? &attrs[(2+M)*j+2+u+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);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_mixed_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
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(GROW, id, NULL, 1,
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;
}
}
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);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_mixed_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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_mixed_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_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xaa\xaa", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_mixed_traverse_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[N*(2+M)];
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[(2+M)*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
((2+M)*j+1 < N*(2+M)) ? &attrs[(2+M)*j+1] : NULL);
attrs[(2+M)*j+1] = *LFSR_ATTR(
MKREG, id, names[perm[j] % 6], 4,
((2+M)*j+2 < N*(2+M)) ? &attrs[(2+M)*j+2] : NULL);
// note uattrs have a smaller size to help debugging
for (unsigned u = 0; u < M; u++) {
attrs[(2+M)*j+2+u] = *LFSR_ATTR(
UATTR(u+1), id, names[perm[j] % 6], 2,
((2+M)*j+2+u+1 < N*(2+M))
? &attrs[(2+M)*j+2+u+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);
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == j);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_mixed_traverse_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
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(GROW, id, NULL, 1,
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;
}
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == N);
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == j);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_update_permutations]
defines.N = 'range(1, 4)'
defines.M = 'range(1, 3)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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,
};
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",
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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(GROW, j, NULL, 1,
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;
}
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N*M];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N*M);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N*M; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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;
// update each tag in permutation order
for (unsigned j = 0; j < N*M; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]%M+1), perm[j]/M,
names[(perm[j]/M) % 6], 3,
NULL)) => 0;
}
// check that all tags have been updated
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
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);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 3;
assert(memcmp(buffer, names[j % 6], 3) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_remove_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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,
};
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",
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation
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(GROW, id, NULL, 1,
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;
}
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
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*M; j++) {
// print what we are removing to help debugging
printf("--- remove: id%jd, %jd ---\n", j/M, (j%M)+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(RMUATTR((j%M)+1), j/M, NULL, 0, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
int err = lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k,
&tag_, &id_, NULL, &off_, &size_);
assert(!err || err == LFS_ERR_NOENT);
if (k == j/M && u == j%M) {
if (u == M-1 && k == N-1) {
assert(err == LFS_ERR_NOENT);
} else if (u == M-1) {
assert(!err);
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k+1);
assert(size_ == 4);
} else {
assert(!err);
assert(tag_ == LFSR_TAG_UATTR(u+1+1));
assert(id_ == k);
assert(size_ == 2);
}
} else {
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
// try append the tag back to make sure things still work
printf("--- append: id%jd, %jd ---\n", j/M, (j%M)+1);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR((j%M)+1), j/M, names[(j/M)%6], 3,
NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k == j/M && u == j%M) {
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 3);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 3;
assert(memcmp(buffer, names[k % 6], 3) == 0);
} else {
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + 2;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_remove_all_permutations]
defines.N = 'range(1, 4)'
defines.M = 'range(1, 3)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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,
};
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",
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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(GROW, j, NULL, 1,
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;
}
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N*M];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N*M);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N*M; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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*M; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(perm[j]%M+1), perm[j]/M, NULL, 0,
NULL)) => 0;
}
// check that all tags have been removed
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
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);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> LFS_ERR_NOENT;
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_mixed_large]
in = 'lfs.c'
# ORDER:
# 0 = in-order
# 1 = reverse-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.M = 'range(1, 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;
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);
// build a single attribute list with all attributes, if this fails
// it should fail atomically
struct lfsr_attr attrs[2+M];
attrs[0] = *LFSR_ATTR(
GROW, x, NULL, 1,
&attrs[1]);
attrs[1] = *LFSR_ATTR(
MKREG, x, names[x % 6], 4,
M > 0 ? &attrs[2] : NULL);
for (unsigned u = 0; u < M; u++) {
attrs[2+u] = *LFSR_ATTR(
UATTR(u+1), x, names[x % 6], 2,
(2+u+1 < 2+M) ? &attrs[2+u+1] : NULL);
}
int err = lfsr_rbyd_commit(&lfs, &rbyd, attrs);
if (err == LFS_ERR_RANGE) {
break;
}
assert(err == 0);
}
// check that we can at least lookup all the tags
//
// note with random order we can't check that stored values reliably
lfsr_tag_t tag_;
lfs_ssize_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_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == x);
assert(size_ == 4);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), x,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == x);
assert(size_ == 2);
}
}
'''
### Deletion testing ###
[cases.test_rbyd_delete]
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 delete one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 2, NULL, 1, 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_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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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_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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1, 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_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'
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1, 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_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_TAG_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_TAG_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_TAG_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_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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_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_TAG_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_TAG_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_TAG_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_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)))))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 2, NULL, 1, 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_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_TAG_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_TAG_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_TAG_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_TAG_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_TAG_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_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)))))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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_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_TAG_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_TAG_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_TAG_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_TAG_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_TAG_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_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)))))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1, 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_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_TAG_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_TAG_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_TAG_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_TAG_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_TAG_UATTR(1), 2, buffer, 4)
=> LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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][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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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 = malloc(rbyd.off);
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);
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(SHRINK, j, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N-1);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N-1);
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;
// try recreating the id to make sure things still work
printf("--- create: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(MKREG, j, names[j % 6], 6, NULL))) => 0;
assert(rbyd.weight == N);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N);
for (unsigned k = 0; k < N; k++) {
if (k == j) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6)
=> 6;
assert(memcmp(buffer, names[k % 6], 6) == 0);
} else {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6)
=> 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + 2;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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][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;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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 = malloc(rbyd.off);
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);
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(SHRINK, j, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N-1);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N-1);
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_TAG_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_TAG_UATTR(1), N-1, buffer, 4)
=> LFS_ERR_NOENT;
// try recreating the id to make sure things still work
printf("--- create: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(MKREG, j, names[j % 6], 6, NULL))) => 0;
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u+1), j, names[j % 6], 3, NULL)) => 0;
}
assert(rbyd.weight == N);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N);
for (unsigned k = 0; k < N; k++) {
if (k == j) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6)
=> 6;
assert(memcmp(buffer, names[k % 6], 6) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k, buffer, 6)
=> 3;
assert(memcmp(buffer, names[k % 6], 3) == 0);
}
} else {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6)
=> 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k, buffer, 6)
=> 2;
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + 1 + 1+M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_traverse_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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 = malloc(rbyd.off);
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);
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(SHRINK, j, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N-1);
// try traversing over the tags
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N-1);
tag_ = 0;
id_ = -1;
for (unsigned k = 0; k < N-1; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, 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_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_traverse_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id, NULL, 1,
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 = malloc(rbyd.off);
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);
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(SHRINK, j, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N-1);
// try traversing over the tags
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N-1);
tag_ = 0;
id_ = -1;
for (unsigned k = 0; k < N-1; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, 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_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, 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_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
# Note, "delete_all" is a weird state for rbyd trees to be in, since they
# don't really have a trunk at this point
[cases.test_rbyd_delete_all]
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];
// create and delete one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 2, NULL, 1,
LFSR_ATTR(SHRINK, 1, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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'
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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)))))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)))))))))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 2, NULL, 1,
LFSR_ATTR(SHRINK, 1, NULL, 1,
LFSR_ATTR(SHRINK, 0, NULL, 1, 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;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_all_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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(GROW, j, NULL, 1,
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 = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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;
}
}
lfs_size_t rbyd_weight_before = rbyd.weight;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1, 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(GROW, 0, NULL, 1,
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
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + 2*N + 1;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_all_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 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(GROW, j, NULL, 1,
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 = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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;
}
}
lfs_size_t rbyd_weight_before = rbyd.weight;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1, 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(GROW, 0, NULL, 1,
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_TAG_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_TAG_UATTR(1), 1, buffer, 6)
=> LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + N + 1+M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# the main purpose of this test is to try to fuzz for failures in the
# balancing algorithm
[cases.test_rbyd_fuzz_create_deletes]
defines.N = 'range(1, 33)'
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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 char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose create or delete
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
printf("c%d=%c", id, alpha[i % 26]);
count += 1;
} else {
printf("d%d", id);
count -= 1;
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N);
memset(sim, 0, N);
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose create or delete
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
// update our sim
memmove(sim+id+1, sim+id, count-id);
sim[id] = alpha[i % 26];
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(MKREG, id, &alpha[i % 26], 1,
NULL))) => 0;
} else {
// update our sim
memmove(sim+id, sim+id+1, count-id-1);
count -= 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
printf("%c", sim[id]);
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
printf("rbyd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)rbyd.weight; id++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_MKREG, id, buffer, 4);
if (size >= 0) {
printf("%.*s", size, buffer);
} else {
printf("?");
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
assert(count == rbyd.weight);
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, id, buffer, 4) => 1;
assert(memcmp(&sim[id], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''
# this test is to catch the mistake of letting deletes unconditionally
# append an "altgt 0x0 w0"
[cases.test_rbyd_delete_end]
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];
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
// create three ids
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4,
NULL))))))))) => 0;
// delete the last two
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 2, NULL)) => 0;
// create some new ids, if unconditional altgts are used this
// will end up losing tags
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), 1, "\xdd\xdd", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(MKREG, 2, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), 2, "\xee\xee", 2,
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_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, "\xdd\xdd\xdd\xdd", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xdd\xdd", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xee\xee\xee\xee", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xee\xee", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 3, buffer, 4)
=> LFS_ERR_NOENT;
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_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, "\xdd\xdd\xdd\xdd", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xdd\xdd", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xee\xee\xee\xee", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xee\xee", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 3, buffer, 4)
=> LFS_ERR_NOENT;
'''
# Test rbyd weights
[cases.test_rbyd_grow]
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_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
// make id0 with weight w1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
// make id2 with weight w2
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 2,
LFSR_ATTR(MKREG, 2, "\xbb\xbb\xbb\xbb", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
// make id5 with weight w3
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 3, NULL, 3,
LFSR_ATTR(MKREG, 5, "\xcc\xcc\xcc\xcc", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
// make id9 with weight w4
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 6, NULL, 4,
LFSR_ATTR(MKREG, 9, "\xdd\xdd\xdd\xdd", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
// make id14 with weight w5
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 10, NULL, 5,
LFSR_ATTR(MKREG, 14, "\xee\xee\xee\xee", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
'''
[cases.test_rbyd_grow_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_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
// make id0 with weight w1
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
// make id2 with weight w2
LFSR_ATTR(GROW, 1, NULL, 2,
LFSR_ATTR(MKREG, 2, "\xbb\xbb\xbb\xbb", 4,
// make id5 with weight w3
LFSR_ATTR(GROW, 3, NULL, 3,
LFSR_ATTR(MKREG, 5, "\xcc\xcc\xcc\xcc", 4,
// make id9 with weight w4
LFSR_ATTR(GROW, 6, NULL, 4,
LFSR_ATTR(MKREG, 9, "\xdd\xdd\xdd\xdd", 4,
// make id14 with weight w5
LFSR_ATTR(GROW, 10, NULL, 5,
LFSR_ATTR(MKREG, 14, "\xee\xee\xee\xee", 4,
NULL))))))))))) => 0;
// traverse, finding tags and weights
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_grow_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
}
'''
[cases.test_rbyd_grow_traverse_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# Weights mixed with attributes
[cases.test_rbyd_mixed_grow]
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_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
// make id0 with weight w1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(UATTR(2), 0, "\xaa\xaa", 2, NULL)))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
// make id2 with weight w2
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(UATTR(2), 2, "\xbb\xbb", 2,
NULL)))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
// make id5 with weight w3
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 3, NULL, 1,
LFSR_ATTR(MKREG, 3, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 3, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 3, NULL, 2,
LFSR_ATTR(UATTR(2), 5, "\xcc\xcc", 2,
NULL)))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
// make id9 with weight w4
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 6, NULL, 1,
LFSR_ATTR(MKREG, 6, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), 6, "\xdd\xdd", 2,
LFSR_ATTR(GROW, 6, NULL, 3,
LFSR_ATTR(UATTR(2), 9, "\xdd\xdd", 2,
NULL)))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
// make id14 with weight w5
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 10, NULL, 1,
LFSR_ATTR(MKREG, 10, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), 10, "\xee\xee", 2,
LFSR_ATTR(GROW, 10, NULL, 4,
LFSR_ATTR(UATTR(2), 14, "\xee\xee", 2,
NULL)))))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
'''
[cases.test_rbyd_mixed_grow_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_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9,
// make id0 with weight w1
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(UATTR(2), 0, "\xaa\xaa", 2,
// make id2 with weight w2
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(UATTR(2), 2, "\xbb\xbb", 2,
// make id5 with weight w3
LFSR_ATTR(GROW, 3, NULL, 1,
LFSR_ATTR(MKREG, 3, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 3, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 3, NULL, 2,
LFSR_ATTR(UATTR(2), 5, "\xcc\xcc", 2,
// make id9 with weight w4
LFSR_ATTR(GROW, 6, NULL, 1,
LFSR_ATTR(MKREG, 6, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), 6, "\xdd\xdd", 2,
LFSR_ATTR(GROW, 6, NULL, 3,
LFSR_ATTR(UATTR(2), 9, "\xdd\xdd", 2,
// make id14 with weight w5
LFSR_ATTR(GROW, 10, NULL, 1,
LFSR_ATTR(MKREG, 10, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), 10, "\xee\xee", 2,
LFSR_ATTR(GROW, 10, NULL, 4,
LFSR_ATTR(UATTR(2), 14, "\xee\xee", 2,
NULL)))))))))))))))))))))))))) => 0;
// traverse, finding tags and weights
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 0);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 2);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 5);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 9);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_mixed_grow_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9, NULL)) => 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(GROW, id*W, NULL, 1,
LFSR_ATTR(MKREG, id*W, names[perm[j] % 6], 4,
LFSR_ATTR(UATTR(1), id*W, names[perm[j] % 6], 2,
LFSR_ATTR(GROW, id*W, NULL, W-1,
LFSR_ATTR(UATTR(2), id*W+W-1, names[perm[j] % 6], 2,
NULL)))))) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
}
'''
[cases.test_rbyd_mixed_grow_traverse_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9, NULL)) => 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(GROW, id*W, NULL, 1,
LFSR_ATTR(MKREG, id*W, names[perm[j] % 6], 4,
LFSR_ATTR(UATTR(1), id*W, names[perm[j] % 6], 2,
LFSR_ATTR(GROW, id*W, NULL, W-1,
LFSR_ATTR(UATTR(2), id*W+W-1, names[perm[j] % 6], 2,
NULL)))))) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
// try traversing all tags
tag_ = 0;
id_ = -1;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(weight_ == 0);
assert(size_ == 9);
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_sparse_grow_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
assert(rbyd.weight == N*W);
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// try growing each id
for (unsigned j = 0; j < N; j++) {
// print what we are growing to help debugging
printf("--- growing: %d ---\n", j);
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(GROW, j*W, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N*W+1);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W+1);
for (unsigned k = 0; k < N; k++) {
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1+1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1+1);
assert(weight_ == W+1);
assert(size_ == 4);
} else if (k > j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1+1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1+1);
assert(weight_ == W);
assert(size_ == 4);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
}
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
'''
[cases.test_rbyd_sparse_shrink_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
assert(rbyd.weight == N*W);
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// try shrinking each id
for (unsigned j = 0; j < N; j++) {
// print what we are shrinking to help debugging
printf("--- shrinking: %d ---\n", j);
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(SHRINK, j*W, NULL, 1, NULL)) => 0;
assert(rbyd.weight == N*W-1);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W-1);
for (unsigned k = 0; k < N; k++) {
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1-1);
assert(weight_ == W-1);
assert(size_ == 4);
} else if (k > j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1-1);
assert(weight_ == W);
assert(size_ == 4);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
}
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
'''
[cases.test_rbyd_sparse_delete_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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][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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[6];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
assert(rbyd.weight == N*W);
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
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("--- deleting: %d ---\n", j);
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(SHRINK, j*W, NULL, W, NULL)) => 0;
assert(rbyd.weight == (N-1)*W);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == (N-1)*W);
for (unsigned k = 0; k < N-1; k++) {
if (k >= j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[(k+1) % 6], 4) == 0);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
// try recreating the id to make sure things still work
printf("--- create: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j*W, NULL, W,
LFSR_ATTR(MKREG, j*W+W-1, names[j % 6], 6,
NULL))) => 0;
assert(rbyd.weight == N*W);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
for (unsigned k = 0; k < N; k++) {
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 6);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 6) => 6;
assert(memcmp(buffer, names[k % 6], 6) == 0);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 6) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
}
'''
[cases.test_rbyd_sparse_attr_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
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(GROW, id*W, NULL, W,
LFSR_ATTR(MKREG, id*W+W-1, names[perm[j] % 6], 4,
NULL))) => 0;
}
assert(rbyd.weight == N*W);
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// try appending an attr to each id, this should not affect
// weights at all!
for (unsigned j = 0; j < N; j++) {
// print what we are appending to help debugging
printf("--- appending: %d ---\n", j);
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(UATTR(1), j*W+W-1, names[j % 6], 2, NULL)) => 0;
assert(rbyd.weight == N*W);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == k*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
}
}
// now try removing the attr
printf("--- removing: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), j*W+W-1, NULL, 0, NULL)) => 0;
assert(rbyd.weight == N*W);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
if (k == j) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), k*W+W-1,
buffer, 4) => LFS_ERR_NOENT;
}
}
// and try putting the attr back just for good measure
printf("--- appending: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), j*W+W-1, names[j % 6], 2, NULL)) => 0;
assert(rbyd.weight == N*W);
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
assert(rbyd.weight == N*W);
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_MKREG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == k*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
}
}
}
}
'''
# Some more fuzzish testing
[cases.test_rbyd_fuzz_mixed]
defines.N = 'range(1, 33)'
defines.M = 3
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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 char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete or attr append/remove
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose an attr
uint8_t u = TEST_PRNG(&prng) % M;
if (id == (lfs_ssize_t)count || op == 0) {
printf("c%d=%c", id, alpha[i % 26]);
count += 1;
} else if (op == 1) {
printf("d%d", id);
count -= 1;
} else if (op == 2) {
printf("a%d,%d=%c", id, u, alpha[i % 26]);
} else if (op == 3) {
printf("r%d,%d", id, u);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N*(M+1));
memset(sim, 0, N*(M+1));
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete or attr append/remove
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose an attr
uint8_t u = TEST_PRNG(&prng) % M;
if (id == (lfs_ssize_t)count || op == 0) {
// update our sim
memmove(sim+(id+1)*(M+1), sim+id*(M+1), (count-id)*(M+1));
memset(&sim[id*(M+1)], 0, M+1);
sim[id*(M+1)] = alpha[i % 26];
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(MKREG, id, &alpha[i % 26], 1,
NULL))) => 0;
} else if (op == 1) {
// update our sim
memmove(sim+id*(M+1), sim+(id+1)*(M+1), (count-id-1)*(M+1));
count -= 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1,
NULL)) => 0;
} else if (op == 2) {
// update our sim
sim[id*(M+1) + u+1] = alpha[i % 26];
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1,
NULL)) => 0;
} else if (op == 3) {
// update our sim
sim[id*(M+1) + u+1] = '\0';
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(u), id, NULL, 0,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
printf("%c", sim[id*(M+1)]);
for (uint8_t u = 0; u < M; u++) {
if (sim[id*(M+1) + u+1]) {
printf("%c", sim[id*(M+1) + u+1]);
} else {
printf("_");
}
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
printf("rbyd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)rbyd.weight; id++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_MKREG, id, buffer, 4);
if (size >= 0) {
printf("%.*s", size, buffer);
} else {
printf("?");
}
for (uint8_t u = 0; u < M; u++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(u), id, buffer, 4);
if (size >= 0) {
printf("%.*s", size, buffer);
} else {
printf("_");
}
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
assert(count == rbyd.weight);
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, id, buffer, 4) => 1;
assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''
[cases.test_rbyd_fuzz_sparse]
defines.N = 'range(1, 33)'
defines.W = 5
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
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_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete/grow/shrink
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose a weight
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
if (id == (lfs_ssize_t)count || op == 0) {
printf("c%dw%d=%c", id, weight, alpha[i % 26]);
count += 1;
} else if (op == 1) {
printf("d%d", id);
count -= 1;
} else if (op == 2) {
printf("g%dw%d", id, weight);
} else if (op == 3) {
printf("s%dw%d", id, weight);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N);
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
memset(sim, 0, N);
memset(sim_weights, 0, N*sizeof(lfs_size_t));
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete/grow/shrink
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose a weight
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
if (id == (lfs_ssize_t)count || op == 0) {
// update our sim
memmove(sim+id+1, sim+id, count-id);
memmove(sim_weights+id+1, sim_weights+id,
(count-id)*sizeof(lfs_size_t));
sim[id] = alpha[i % 26];
sim_weights[id] = weight;
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, weighted_id, NULL, weight,
LFSR_ATTR(MKREG, weighted_id+weight-1,
&alpha[i % 26], 1,
NULL))) => 0;
} else if (op == 1) {
// get the correct weight from the sim
weight = sim_weights[id];
// update our sim
memmove(sim+id, sim+id+1, count-id-1);
memmove(sim_weights+id, sim_weights+id+1,
(count-id-1)*sizeof(lfs_size_t));
count -= 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, weighted_id, NULL, weight,
NULL)) => 0;
} else if (op == 2) {
// update our sim
sim_weights[id] += weight;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, weighted_id, NULL, weight,
NULL)) => 0;
} else if (op == 3) {
// don't let shrink go to zero here! this is already hard enough
// to simulate
weight = lfs_min(weight, sim_weights[id]-1);
// update our sim
sim_weights[id] -= weight;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, weighted_id, NULL, weight,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
printf("%cw%d", sim[id], sim_weights[id]);
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
printf("rbyd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
int err = lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_MKREG, weighted_id,
&tag_, &id_, &weight_, &off_, &size_);
if (!err) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
tag_, id_, buffer, 4);
if (size >= 0) {
printf("%.*sw%d", size, buffer, weight_);
} else {
printf("?");
}
} else {
printf("?");
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
// calculate total weight
lfs_size_t total_weight = 0;
for (lfs_ssize_t j = 0; j < (lfs_ssize_t)count; j++) {
total_weight += sim_weights[j];
}
assert(total_weight == rbyd.weight);
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_MKREG, weighted_id,
&tag_, &id_, &weight_, &off_, &size_) => 0;
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 1;
assert(memcmp(&sim[id], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
free(sim_weights);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''