c0ee405cf2
1. Search backwards through our tags to find the most recent, best matching id. 2. Replay tags after the found id to adjust for any pending changes. In theory this should work in controlled cases, but there are a lot of corner cases around grows and shrinks. Tests are written, and failing, but I think it may be simpler and more efficient to implement this in a single pass, with tighter assumptions about what grow/shrinks are allowed.
12223 lines
413 KiB
TOML
12223 lines
413 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)'
|
|
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;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
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
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_traverse_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
# 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
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
# 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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// cleanup
|
|
free(backup_block);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N + 2;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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_random_append_removes]
|
|
defines.N = 'range(1, 33)'
|
|
defines.ITER = 1000
|
|
# 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 = 1; seed < ITER+1; 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 permutation
|
|
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("--- worst ---\n");
|
|
printf("worst_seed: %d\n", worst_seed);
|
|
printf("worst_size: %d\n", worst_size);
|
|
printf("worst_perm: [");
|
|
uint32_t prng = worst_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 rbyd block
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
prng = worst_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 rbyd
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1,
|
|
NULL)) => 0;
|
|
} else {
|
|
// update our rbyd
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RMUATTR(attr), -1, NULL, 0,
|
|
NULL)) => 0;
|
|
}
|
|
}
|
|
|
|
// 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)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.rev = 1,
|
|
.off = 0,
|
|
.crc = 0,
|
|
.trunk = 0,
|
|
.weight = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_create_traverse_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[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)'
|
|
in = 'lfs.c'
|
|
code = '''
|
|
lfs_t lfs;
|
|
lfs_init(&lfs, cfg) => 0;
|
|
|
|
lfsr_rbyd_t init_rbyd = {
|
|
.block = 0,
|
|
.rev = 1,
|
|
.off = 0,
|
|
.crc = 0,
|
|
.trunk = 0,
|
|
.weight = 0,
|
|
.erased = true,
|
|
};
|
|
lfsr_rbyd_t rbyd;
|
|
const uint8_t names[6][4] = {
|
|
"\xaa\xaa\xaa\xaa",
|
|
"\xbb\xbb\xbb\xbb",
|
|
"\xcc\xcc\xcc\xcc",
|
|
"\xdd\xdd\xdd\xdd",
|
|
"\xee\xee\xee\xee",
|
|
"\xff\xff\xff\xff",
|
|
};
|
|
uint8_t buffer[4];
|
|
|
|
// keep track of the worst case log size
|
|
lfs_size_t worst_size = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N + N*M;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N + N*M;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_multi_mixed_traverse_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.M = 'range(1, 4)'
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
# 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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint8_t perm[N*M];
|
|
unsigned stack[N*M];
|
|
for (uint8_t i = 0; i < N*M; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N*M) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint8_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint8_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// cleanup
|
|
free(backup_block);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N+N*M + 2;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint8_t perm[N*M];
|
|
unsigned stack[N*M];
|
|
for (uint8_t i = 0; i < N*M; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N*M) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint8_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint8_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// cleanup
|
|
free(backup_block);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N + 2;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
}
|
|
|
|
// cleanup
|
|
free(backup_block);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// test that tree is self-balancing, we should be strictly bounded
|
|
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
|
|
lfs_size_t n = 1 + N+N*M + 1 + 1+M;
|
|
printf("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
# 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)'
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
# 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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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)'
|
|
# 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;
|
|
|
|
// 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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
|
|
worst_size = lfs_max(worst_size, rbyd.off);
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
// 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("worst size: %u B (N=%u, estimate=%u)\n",
|
|
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
|
|
printf("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_random_create_deletes]
|
|
defines.N = 'range(1, 33)'
|
|
defines.ITER = 1000
|
|
# 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 = 1; seed < ITER+1; 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 permutation
|
|
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("--- worst ---\n");
|
|
printf("worst_seed: %d\n", worst_seed);
|
|
printf("worst_size: %d\n", worst_size);
|
|
printf("worst_perm: [");
|
|
uint32_t prng = worst_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 rbyd block
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
|
|
prng = worst_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)) {
|
|
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 {
|
|
count -= 1;
|
|
// update our rbyd
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(SHRINK, id, NULL, 1,
|
|
NULL)) => 0;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
'''
|
|
|
|
|
|
# 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
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_grow_traverse_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
# 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
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_mixed_grow_traverse_permutations]
|
|
defines.N = 'range(1, 8)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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;
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_sparse_grow_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_sparse_shrink_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_sparse_delete_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_sparse_attr_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.W = 5
|
|
# 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
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
|
|
## Test unwritten attributes
|
|
|
|
[cases.test_rbyd_unwritten_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
# 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_ = 0;
|
|
lfs_ssize_t id_ = -1;
|
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
|
lfs_size_t weight_ = 0;
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
printf("], written: %d/%jd ---\n", w, N);
|
|
|
|
// build the attribute lists 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 && j+1 != w) ? &attrs[j+1] : NULL);
|
|
}
|
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
|
struct lfsr_attr *unwritten = w < N ? &attrs[w] : NULL;
|
|
|
|
// create rbyd with written attr
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
|
rbyd.block, cfg->block_size, NULL) => 0;
|
|
|
|
// test lookup both written/unwritten
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
LFSR_TAG_UATTR(j+1), -1,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_UATTR(j+1));
|
|
assert(id_ == -1);
|
|
assert(lfsr_data_len(data_) == 4);
|
|
assert(weight_ == 0);
|
|
}
|
|
|
|
// test traverse both written/unwritten
|
|
tag_ = 0;
|
|
id_ = -1;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_UATTR(j+1));
|
|
assert(id_ == -1);
|
|
assert(lfsr_data_len(data_) == 4);
|
|
assert(weight_ == 0);
|
|
}
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_unwritten_random]
|
|
defines.N = 'range(1, 13)'
|
|
defines.ITER = 1000
|
|
# 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];
|
|
|
|
// iterate through seeds so we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N);
|
|
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;
|
|
|
|
// set up our unwritten attr list
|
|
struct lfsr_attr *attrs = malloc(N*sizeof(struct lfsr_attr));
|
|
unsigned j = 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
|
|
if (i < w) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1,
|
|
NULL)) => 0;
|
|
// append to our unwritten attrs
|
|
} else {
|
|
if (j > 0) {
|
|
attrs[j-1].next = &attrs[j];
|
|
}
|
|
attrs[j] = *LFSR_ATTR(
|
|
UATTR(attr), -1, &alpha[i % 26], 1,
|
|
NULL);
|
|
j += 1;
|
|
}
|
|
} else {
|
|
// update our sim
|
|
sim[attr] = '\0';
|
|
// update our rbyd
|
|
if (i < w) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(RMUATTR(attr), -1, NULL, 0,
|
|
NULL)) => 0;
|
|
// append to our unwritten attrs
|
|
} else {
|
|
if (j > 0) {
|
|
attrs[j-1].next = &attrs[j];
|
|
}
|
|
attrs[j] = *LFSR_ATTR(
|
|
RMUATTR(attr), -1, NULL, 0,
|
|
NULL);
|
|
j += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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_pendingget(&lfs, &rbyd, attrs,
|
|
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_pendingget(&lfs, &rbyd, attrs,
|
|
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);
|
|
}
|
|
}
|
|
|
|
// cleanup
|
|
free(sim);
|
|
free(attrs);
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_unwritten_create_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
# 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_ = 0;
|
|
lfs_ssize_t id_ = -1;
|
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
|
lfs_size_t weight_ = 0;
|
|
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];
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
printf("], written: %d/%jd ---\n", w, N);
|
|
|
|
// build the attribute lists 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,
|
|
&attrs[2*j+1]);
|
|
attrs[2*j+1] = *LFSR_ATTR(
|
|
MKREG, id, names[perm[j] % 6], 4,
|
|
(j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL);
|
|
}
|
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
|
struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL;
|
|
|
|
// create rbyd with written attr
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
|
rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.weight == w);
|
|
|
|
// test lookup both written/unwritten
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
|
LFSR_TAG_MKREG, j, buffer, 4) => 4;
|
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
|
}
|
|
|
|
// test traverse both written/unwritten
|
|
tag_ = 0;
|
|
id_ = -1;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_MKREG);
|
|
assert(id_ == j);
|
|
assert(lfsr_data_len(data_) == 4);
|
|
assert(weight_ == 1);
|
|
}
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_unwritten_create_random]
|
|
defines.N = 'range(1, 13)'
|
|
defines.ITER = 1000
|
|
# 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];
|
|
|
|
// iterate through seeds so we can reproduce easily
|
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N);
|
|
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;
|
|
|
|
// set up attr list
|
|
struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr));
|
|
unsigned j = 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
|
|
if (i < w) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(GROW, id, NULL, 1,
|
|
LFSR_ATTR(MKREG, id, &alpha[i % 26], 1,
|
|
NULL))) => 0;
|
|
} else {
|
|
if (j > 0) {
|
|
attrs[j-1].next = &attrs[j];
|
|
}
|
|
attrs[j] = *LFSR_ATTR(
|
|
GROW, id, NULL, 1,
|
|
&attrs[j+1]);
|
|
attrs[j+1] = *LFSR_ATTR(
|
|
MKREG, id, &alpha[i % 26], 1,
|
|
NULL);
|
|
j += 2;
|
|
}
|
|
} else {
|
|
// update our sim
|
|
memmove(sim+id, sim+id+1, count-id-1);
|
|
count -= 1;
|
|
// update our rbyd
|
|
if (i < w) {
|
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
|
LFSR_ATTR(SHRINK, id, NULL, 1,
|
|
NULL)) => 0;
|
|
} else {
|
|
if (j > 0) {
|
|
attrs[j-1].next = &attrs[j];
|
|
}
|
|
attrs[j] = *LFSR_ATTR(
|
|
SHRINK, id, NULL, 1,
|
|
NULL);
|
|
j += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)count; id++) {
|
|
lfs_ssize_t size = lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
|
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");
|
|
|
|
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
|
|
lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
|
LFSR_TAG_MKREG, id, buffer, 4) => 1;
|
|
assert(memcmp(&sim[id], buffer, 1) == 0);
|
|
}
|
|
|
|
// cleanup
|
|
free(sim);
|
|
free(attrs);
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_unwritten_mixed_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.M = 'range(1, 4)'
|
|
# 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_ = 0;
|
|
lfs_ssize_t id_ = -1;
|
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
|
lfs_size_t weight_ = 0;
|
|
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];
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
printf("], written: %d/%jd ---\n", w, N);
|
|
|
|
// build the attribute lists 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,
|
|
&attrs[(2+M)*j+1]);
|
|
attrs[(2+M)*j+1] = *LFSR_ATTR(
|
|
MKREG, id, names[perm[j] % 6], 4,
|
|
&attrs[(2+M)*j+2]);
|
|
for (unsigned u = 0; u < M; u++) {
|
|
attrs[(2+M)*j+2+u] = *LFSR_ATTR(
|
|
UATTR(u+1), id, names[perm[j] % 6], 2,
|
|
&attrs[(2+M)*j+2+u+1]);
|
|
}
|
|
}
|
|
if (w > 0) {
|
|
attrs[(2+M)*w-1].next = NULL;
|
|
}
|
|
attrs[(2+M)*N-1].next = NULL;
|
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
|
struct lfsr_attr *unwritten = w < N ? &attrs[(2+M)*w] : NULL;
|
|
|
|
// create rbyd with written attr
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
|
rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.weight == w);
|
|
|
|
// test lookup both written/unwritten
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
|
LFSR_TAG_MKREG, j, buffer, 4) => 4;
|
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
|
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
|
LFSR_TAG_UATTR(u+1), j, buffer, 4) => 2;
|
|
assert(memcmp(buffer, names[j % 6], 2) == 0);
|
|
}
|
|
}
|
|
|
|
// test traverse both written/unwritten
|
|
tag_ = 0;
|
|
id_ = -1;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_MKREG);
|
|
assert(id_ == j);
|
|
assert(lfsr_data_len(data_) == 4);
|
|
assert(weight_ == 1);
|
|
|
|
for (unsigned u = 0; u < M; u++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_UATTR(u+1));
|
|
assert(id_ == j);
|
|
assert(lfsr_data_len(data_) == 2);
|
|
assert(weight_ == 1);
|
|
}
|
|
}
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|
|
|
|
[cases.test_rbyd_unwritten_sparse_permutations]
|
|
defines.N = 'range(1, 7)'
|
|
defines.W = 5
|
|
# 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_ = 0;
|
|
lfs_ssize_t id_ = -1;
|
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
|
lfs_size_t weight_ = 0;
|
|
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];
|
|
|
|
// test all permutations of a given size
|
|
uint16_t perm[N];
|
|
unsigned stack[N];
|
|
for (uint16_t i = 0; i < N; i++) {
|
|
perm[i] = i;
|
|
stack[i] = 0;
|
|
}
|
|
|
|
unsigned i = 1;
|
|
while (i < N) {
|
|
// test each number of written/unwritten tags, this gives us a quick
|
|
// way to test several unwritten situations
|
|
for (unsigned w = 0; w <= N; w++) {
|
|
// print permutation to help debugging
|
|
printf("--- permutation: [");
|
|
for (unsigned j = 0; j < N; j++) {
|
|
if (j > 0) {
|
|
printf(", ");
|
|
}
|
|
printf("%d", perm[j]);
|
|
}
|
|
printf("], written: %d/%jd ---\n", w, N);
|
|
|
|
// build the attribute lists 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*W, NULL, W,
|
|
&attrs[2*j+1]);
|
|
attrs[2*j+1] = *LFSR_ATTR(
|
|
MKREG, id*W+W-1, names[perm[j] % 6], 4,
|
|
(j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL);
|
|
}
|
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
|
struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL;
|
|
|
|
// create rbyd with written attr
|
|
rbyd = init_rbyd;
|
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
|
|
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
|
rbyd.block, cfg->block_size, NULL) => 0;
|
|
assert(rbyd.weight == w*W);
|
|
|
|
// test lookup both written/unwritten
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
|
LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4;
|
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
|
}
|
|
|
|
// test traverse both written/unwritten
|
|
tag_ = 0;
|
|
id_ = -1;
|
|
for (unsigned j = 0; j < N; j++) {
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, &weight_) => 0;
|
|
assert(tag_ == LFSR_TAG_MKREG);
|
|
assert(id_ == j*W+W-1);
|
|
assert(lfsr_data_len(data_) == 4);
|
|
assert(weight_ == W);
|
|
}
|
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
|
lfsr_tag_next(tag_), id_,
|
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
|
}
|
|
|
|
// next permutation using Heap's algorithm
|
|
if (stack[i] < i) {
|
|
if (i % 2 == 0) {
|
|
uint16_t t = perm[0];
|
|
perm[0] = perm[i];
|
|
perm[i] = t;
|
|
} else {
|
|
uint16_t t = perm[stack[i]];
|
|
perm[stack[i]] = perm[i];
|
|
perm[i] = t;
|
|
}
|
|
stack[i] += 1;
|
|
i = 1;
|
|
} else {
|
|
stack[i] = 0;
|
|
i += 1;
|
|
}
|
|
}
|
|
'''
|