Files
littlefs/tests/test_rbyd.toml
T
Christopher Haster 546fff77fb Adopted full le16 tags instead of 14-bit leb128 tags
The main motivation for this was issues fitting a good tag encoding into
14-bits. The extra 2-bits (though really only 1 bit was needed) from
making this not a leb encoding opens up the space from 3 suptypes to
15 suptypes, which is nothing to shake a stick at.

The main downsides:
1. We can't rely on leb encoding for effectively-infinite extensions.
2. We can't shorten small tags (crcs, grows, shrinks) to one byte.

For 1., extending the leb encoding beyond 14-bits is already
unpalatable, because it would increase RAM costs in the tag
encoder/decoder,` which must assume a worst-case tag size, and would likely
add storage cost to every alt pointer, more on this in the next section.

The current encoding is quite generous, so I think it is unlikely we
will exceed the 16-bit encoding space. But even if we do, it's possible
to use a spare bit for an "extended" set of tags in the future.

As for 2., the lack of compression is a downside, but I've realized the
only tags that really matter storage-wise are the alt pointers. In any
rbyds there will be roughly O(m log m) alt pointers, but at most O(m) of
any other tags. What this means is that the encoding of any other tag is
in the noise of the encoding of our alt pointers.

Our alt pointers are already pretty densely packed. But because the
sparse key part of alt-pointers are stored as-is, the worst-case
encoding of in-tree tags likely ends up as the encoding of our
alt-pointers. So going up to 3-byte tags adds a surprisingly large
storage cost.

As a minor plus, le16s should be slightly cheaper to encode/decode. It
should also be slightly easier to debug tags on-disk.

  tag encoding:
                     TTTTtttt ttttTTTv
                        ^--------^--^^- 4+3-bit suptype
                                 '---|- 8-bit subtype
                                     '- valid bit
  iiii iiiiiii iiiiiii iiiiiii iiiiiii
                                     ^- m-bit id/weight
  llll lllllll lllllll lllllll lllllll
                                     ^- m-bit length/jump

Also renamed the "mk" tags, since they no longer have special behavior
outside of providing names for entries:
- LFSR_TAG_MK       => LFSR_TAG_NAME
- LFSR_TAG_MKBRANCH => LFSR_TAG_BNAME
- LFSR_TAG_MKREG    => LFSR_TAG_REG
- LFSR_TAG_MKDIR    => LFSR_TAG_DIR
2023-03-25 14:36:29 -05:00

11387 lines
394 KiB
TOML

# Test this inner rbyd data-structure
# test with a number of different erase values
defines.ERASE_VALUE = [0xff, 0x00, 0x1b]
# set block_size to the full size of disk so we can test arbitrarily
# large rbyd trees, we don't really care about block sizes at this
# abstraction level
#
# ok not quite full disk size (we do use the full disk size in bench_rbyd),
# but a bit less since erasing the full disk takes time and we don't want to
# waste time when testing
defines.BLOCK_SIZE = 32768
[cases.test_rbyd_commit]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
'''
[cases.test_rbyd_multi_commit]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
'''
[cases.test_rbyd_commit_fetch_commit]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// commit with the second attribute
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
'''
# [cases.test_rbyd_fetchmatch]
# [cases.test_rbyd_multi_fetchmatch]
# TODO we really need to test dense keys...
[cases.test_rbyd_lookup]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_lookup]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_get]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes, in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_get]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes, in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4)
=> 4;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_bifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// create a split in the leaves
// <b
// => .-'|
// 1 1 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
// split the other direction
// >b
// => .-'|
// 2 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_bflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// ignore a black edge
// <b <b
// .-'| => .----'|
// 1 2 1 2 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
// flip a black edge
// <b >b
// .-'| => .-'|
// 1 2 1 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_trifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// ignore a black edge
// <r
// .----'|
// <b => | <b
// .-'| | .-'|
// 1 2 1 2 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// flip a black edge
// >r
// .-'|
// <b => | >b
// .-'| .--|-'|
// 2 3 2 3 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_rflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// ignore a red edge and black edge
// <r <r
// .----'| .-------'|
// | <b => | <b
// | .-'| | .----'|
// 1 2 3 1 2 3 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// ignore a red edge, flip a black edge
// <r <r
// .----'| .-------'|
// | <b => | >b
// | .-'| | .-'|
// 1 2 3 1 2 3 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// flip a red edge and black edge
// <r >r
// .----'| .-'|
// | <b => | >b
// | .-'| .--|-'|
// 1 2 3 1 2 3 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
// flip a red edge, ignore a black edge
// <r >r
// .-'| .-------'|
// | >b => | >b
// .--|-'| | .-'|
// 3 1 2 3 1 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_quadrifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// ignore a red edge and black edge
// <y
// .-------'|
// <r | <r
// .----'| => | .----'|
// | <b | | <b
// | .-'| | | .-'|
// 1 2 3 1 2 3 4
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// ignore a red edge, flip a black edge
// <y >y
// .-------'| .-'|
// <r | >r | >r
// .----'| => | .-'| => .--|-'|
// | <b | | >b | | <b
// | .-'| | .--|-'| .--|--|-'|
// 1 3 4 1 3 4 2 1 3 4 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// flip a red edge and black edge
// >y
// .-'|
// <r | >b
// .----'| => .--|-'|
// | <b | | >b
// | .-'| .--|--|-'|
// 2 3 4 2 3 4 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// flip a red edge, ignore a black edge
// >y
// .-------'|
// <r | >r
// .-'| => | .-'|
// | >b | | <b
// .--|-'| | .--|-'|
// 4 2 3 4 2 3 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_rotations]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// all three the same
// <y
// .-------'|
// <r | <r
// .----'| => | .----'|
// | <b | | <b
// | .-'| | | .-'|
// 1 2 3 1 2 3 4
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// yellow and red alt the same
// <y
// .-------'|
// <r | <r
// .----'| => | .----'|
// | <b | | >b
// | .-'| | | .-'|
// 1 2 4 1 2 4 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// yellow and black alt the same
// <y <y
// .-------'| .-------'|
// <r | >r | <r
// .----'| => | .----'| => | .-'|
// | >b | | <b | | >b
// | .-'| | | .-'| | .--|-'|
// 1 4 2 1 4 2 3 1 4 2 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// red and black alt the same
// >y <y
// .-------'| .----'|
// >r | <r | <r
// .----'| => | .----'| => | .-'|
// | <b | | <b | | >b
// | .-'| | | .-'| .--|--|-'|
// 4 1 2 4 1 2 3 4 1 2 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_ysplits]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// split a yellow triple, not taking any alt
// <b
// .-'|
// <y <y |
// .-------'| .-------'| |
// | <r => | <r |
// | .----'| | .----' |
// | | <b | | <b
// | | .-'| | | .----'|
// 1 2 3 4 1 2 3 4 4
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the black alt
// <b
// .-'|
// <y <y |
// .-------'| .-------'| |
// | <r => | <r |
// | .----'| | .----' |
// | | <b | | >b
// | | .-'| | | .-'|
// 1 2 3 4 1 2 3 4 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the red alt
// <y >b
// .-------'| .-'|
// | <r | <b
// | .----'| => .--------|-'|
// | | <b | <b |
// | | .-'| | .-'| |
// 1 2 3 4 1 2 3 4 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the yellow alt
// <y >b
// .-------'| .-'|
// | <r | >b
// | .----'| => .-----|-'|
// | | <b | <b |
// | | .-'| | .-'| |
// 1 2 3 4 1 2 3 4 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_quintifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// split a yellow triple, not taking any alt
// <b
// .-'|
// <y |
// .-------'| |
// <y | <r |
// .-------'| => | .----' |
// | <r | | <r
// | .----'| | | .----'|
// | | <b | | | <b
// | | .-'| | | | .-'|
// 1 2 3 4 1 2 3 4 5
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the black alt
// <b
// .-'|
// <y |
// .-------'| |
// <y | <r |
// .-------'| => | .----' |
// | <r | | >r
// | .----'| | | .-'|
// | | <b | | | >b
// | | .-'| | | .--|-'|
// 1 2 4 5 1 2 4 5 3
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the red alt
// >b
// .-'|
// <y | <r
// .-------'| .--------|-'|
// | <r | | >b
// | .----'| => | .-----|-'|
// | | <b | | <b |
// | | .-'| | | .-'| |
// 1 3 4 5 1 3 4 5 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// split a yellow triple, taking the yellow alt
// >b
// .-'|
// <y | >r
// .-------'| .-----|-'|
// | <r | | >b
// | .----'| => .--|-----|-'|
// | | <b | | <b |
// | | .-'| | | .-'| |
// 2 3 4 5 2 3 4 5 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_prunes]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// don't prune
// <b <b
// .-'| .----'|
// <y | <y |
// .-------'| | .-------'| |
// | <r | | <r |
// | .----' | => | .----' |
// | | <r | | <r
// | | .----'| | | .-------'|
// | | | <b | | | <b
// | | | .-'| | | | .----'|
// 1 2 3 4 5 1 2 3 4 5 5
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// prune by taking a red alt
// <b
// .-'|
// <y | >b
// .-------'| | .-'|
// | <r | | <b
// | .----' | => .-----------|-'|
// | | <r | <r |
// | | .----'| | .----'| |
// | | | <b | | <b |
// | | | .-'| | | .-'| |
// 1 2 3 4 5 1 2 3 4 5 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
// prune by taking a yellow alt (this needs to prune during the rflip)
// <b
// .-'|
// <y | >b
// .-------'| | .-'|
// | <r | | >b
// | .----' | => .--------|-'|
// | | <r | <r |
// | | .----'| | .----'| |
// | | | <b | | <b |
// | | | .-'| | | .-'| |
// 1 2 3 4 5 1 2 3 4 5 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_sextifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// don't prune
// <b
// .----'|
// <b <y |
// .-'| .-------'| |
// <y | | <r |
// .-------'| | | .----' |
// | <r | | | <y
// | .----' | => | | .-------'|
// | | <r | | | <r
// | | .----'| | | | .----'|
// | | | <b | | | | <b
// | | | .-'| | | | | .-'|
// 1 2 3 4 5 1 2 3 4 5 6
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(6), -1, "\xff\xff\xff\xff", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
// prune by taking a red alt
// <b >b
// .-'| .-'|
// <y | | <r
// .-------'| | .-----------|-'|
// | <r | | | >b
// | .----' | => | .--------|-'|
// | | <r | | <r |
// | | .----'| | | .----'| |
// | | | <b | | | <b |
// | | | .-'| | | | .-'| |
// 1 3 4 5 6 1 3 4 5 6 2
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(6), -1, "\xff\xff\xff\xff", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
// prune by taking a yellow alt (this needs to prune during the rflip)
// <b >b
// .-'| .-'|
// <y | | >r
// .-------'| | .--------|-'|
// | <r | | | >b
// | .----' | => .--|--------|-'|
// | | <r | | <r |
// | | .----'| | | .----'| |
// | | | <b | | | <b |
// | | | .-'| | | | .-'| |
// 2 3 4 5 6 2 3 4 5 6 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(6), -1, "\xff\xff\xff\xff", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))))))) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(3));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(5));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(6), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(6));
assert(id_ == -1);
assert(size_ == 4);
'''
[cases.test_rbyd_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[N];
for (unsigned j = 0; j < N; j++) {
attrs[j] = *LFSR_ATTR(
UATTR(perm[j]+1), -1,
"\xaa\xaa\xaa\xaa", 4,
(j+1 < N) ? &attrs[j+1] : NULL);
}
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// traverse requires correct biasing of the weights in the rbyd tree
// so that lookups return strictly the tag greater than or equal to
// the tag requested
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// traverse requires correct biasing of the weights in the rbyd tree
// so that lookups return strictly the tag greater than or equal to
// the tag requested
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// also try the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(0), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[N];
for (unsigned j = 0; j < N; j++) {
attrs[j] = *LFSR_ATTR(
UATTR(perm[j]+1), -1,
"\xaa\xaa\xaa\xaa", 4,
(j+1 < N) ? &attrs[j+1] : NULL);
}
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
// try traversing all tags
tag_ = 0;
id_ = -1;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 4);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_update_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// update each tag in permutation order
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1,
"\xaa\xaa\xaa\xaa\xaa\xaa", 6,
NULL)) => 0;
}
// check that all tags have been updated
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(j+1));
assert(id_ == -1);
assert(size_ == 6);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_large]
in = 'lfs.c'
# ORDER:
# 0 = in-order
# 1 = reverse-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// create the rbyd tree
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
// keep appending tags until we run out of space
//
// note, this will likely repeat tags, but that's ok
//
lfs_size_t count = 0;
uint32_t prng = 42;
for (lfs_size_t i = 0;; i++) {
uint8_t x
= (ORDER == 0) ? (uint8_t)i
: (ORDER == 1) ? (uint8_t)(((lfs_size_t)-1) - i)
: (uint8_t)TEST_PRNG(&prng);
int err = lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(x), -1, "\xaa\xaa\xaa\xaa", 4,
NULL));
if (err == LFS_ERR_RANGE) {
break;
}
assert(err == 0);
count = i;
}
// check that we can still lookup all the tags
prng = 42;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (lfs_size_t i = 0; i < count; i++) {
uint8_t x
= (ORDER == 0) ? (uint8_t)i
: (ORDER == 1) ? (uint8_t)(((lfs_size_t)-1) - i)
: (uint8_t)TEST_PRNG(&prng);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(x), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(x));
assert(id_ == -1);
assert(size_ == 4);
}
'''
### Removal testing ###
[cases.test_rbyd_remove]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// add and remove one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, remove the first one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, remove the second one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(2), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
// print what we are removing to help debugging
printf("--- remove: %d ---\n", j);
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
for (unsigned k = 0; k < N; k++) {
int err = lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(k+1), -1,
&tag_, &id_, NULL, &off_, &size_);
assert(!err || err == LFS_ERR_NOENT);
if (k == j) {
if (j == N-1) {
assert(err == LFS_ERR_NOENT);
} else {
assert(!err);
assert(tag_ == LFSR_TAG_UATTR(j+1+1));
assert(id_ == -1);
assert(size_ == 4);
}
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
// try appending the tag back to make sure things still work
printf("--- append: %d ---\n", j);
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6,
NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
for (unsigned k = 0; k < N; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(k+1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k == j) {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 6);
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + 2;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_traverse_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
// print what we are removing to help debugging
printf("--- remove: %d ---\n", j);
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0;
// try traversing over the tags
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block,
cfg->block_size, NULL) => 0;
tag_ = 0;
id_ = -1;
for (unsigned k = 0; k < N-1; k++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k >= j) {
assert(tag_ == LFSR_TAG_UATTR(k+1+1));
assert(id_ == -1);
assert(size_ == 4);
} else {
assert(tag_ == LFSR_TAG_UATTR(k+1));
assert(id_ == -1);
assert(size_ == 4);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
[cases.test_rbyd_remove_missing]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// create a tree two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// try to remove tags that aren't there, this should do nothing
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// one last fetch to make sure nothing was broken
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_remove_again]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// create a tree
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4,
NULL)))))) => 0;
// remove several attributes
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// try to remove tags that aren't there, this should do nothing
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// try to remove the tags again, just to make sure (keep in mind
// these removes still commit to the rbyd)
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// one last fetch to make sure nothing was broken
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(4));
assert(id_ == -1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_remove_all]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// commit with one attribute, remove it
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, remove both
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0,
LFSR_ATTR(RMUATTR(2), -1, NULL, 0, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
// commit with two attributes, remove both in the other order
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(2), -1, NULL, 0,
LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_remove_all_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa", 4,
NULL)) => 0;
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// remove each tag in permutation order
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(perm[j]+1), -1, NULL, 0, NULL)) => 0;
}
// check that all tags are now removed
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// try resuming from all tags being removed
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6,
NULL)) => 0;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == -1);
assert(size_ == 6);
for (unsigned j = 1; j < N; j++) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N + 1;
printf("--- summary --\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# the main purpose of this test is to try to fuzz for failures in the
# balancing algorithm
[cases.test_rbyd_fuzz_append_removes]
defines.N = 'range(1, 33)'
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
for (unsigned i = 0; i < N; i++) {
// choose an attr
uint8_t attr = TEST_PRNG(&prng) % N;
// choose append or remove
if (TEST_PRNG(&prng) & 1) {
printf("a0x%02x=%c", attr, alpha[i % 26]);
} else {
printf("r0x%02x", attr);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against
char *sim = malloc(N);
memset(sim, 0, N);
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
for (unsigned i = 0; i < N; i++) {
// choose an attr
uint8_t attr = TEST_PRNG(&prng) % N;
// choose append or remove
if (TEST_PRNG(&prng) & 1) {
// update our sim
sim[attr] = alpha[i % 26];
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1,
NULL)) => 0;
} else {
// update our sim
sim[attr] = '\0';
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(attr), -1, NULL, 0,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
bool first = true;
for (unsigned attr = 0; attr < N; attr++) {
if (sim[attr]) {
if (!first) {
printf(", ");
}
first = false;
printf("0x%02x=%c", attr, sim[attr]);
}
}
printf("]\n");
printf("rbyd: [");
first = true;
for (unsigned attr = 0; attr < N; attr++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(attr), -1, buffer, 4);
if (size >= 0) {
if (!first) {
printf(", ");
}
first = false;
printf("0x%02x=%.*s", attr, size, buffer);
}
}
printf("]\n");
for (unsigned attr = 0; attr < N; attr++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(attr), -1, buffer, 4);
if (sim[attr]) {
assert(size == 1);
assert(memcmp(&sim[attr], buffer, 1) == 0);
} else {
assert(size == LFS_ERR_NOENT);
}
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size
printf("--- summary ---\n");
printf("worst seed: %d\n", worst_seed);
printf("worst size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= N*12*(2*lfs_nlog2(N)+1)+1);
}
'''
### Insertion testing ###
[cases.test_rbyd_create]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// try to create one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
assert(rbyd.weight == 1);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
assert(rbyd.weight == 2);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))))) => 0;
assert(rbyd.weight == 2);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))))))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))))))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
assert(rbyd.weight == 1);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
assert(rbyd.weight == 2);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
assert(rbyd.weight == 2);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 2, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
'''
[cases.test_rbyd_create_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[2*N];
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
attrs[2*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
(2*j+1 < 2*N) ? &attrs[2*j+1] : NULL);
attrs[2*j+1] = *LFSR_ATTR(
REG, 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_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_create_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_create_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// traverse requires correct biasing of the weights in the rbyd tree
// so that lookups return strictly the tag greater than or equal to
// the tag requested
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 0, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_create_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[2*N];
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
attrs[2*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
(2*j+1 < 2*N) ? &attrs[2*j+1] : NULL);
attrs[2*j+1] = *LFSR_ATTR(
REG, 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_REG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_create_traverse_permutations]
defines.N = 'range(1, 8)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_create_large]
in = 'lfs.c'
# ORDER:
# 0 = in-order
# 1 = reverse-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
// create the rbyd tree
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
// keep inserting tags until we run out of space
//
// note, the ids we create this way are both sparse and sometimes
// repeated, so we need to mod our current rbyd size to avoid invalid
// insertions
//
uint32_t prng = 42;
for (lfs_size_t i = 0;; i++) {
uint16_t x
= (ORDER == 0) ? (uint16_t)i
: (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i)
: (uint16_t)TEST_PRNG(&prng);
x = x % (rbyd.weight+1);
int err = lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, x, NULL, 1,
LFSR_ATTR(REG, 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_REG, x,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xcc\xcc\xcc\xcc", 4,
LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 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_REG, 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_REG, 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(REG, 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(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 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(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 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(REG, 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(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 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(REG, 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(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 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(REG, 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(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xcc\xcc", 2) == 0);
'''
[cases.test_rbyd_mixed_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[(2+M)*N];
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
attrs[(2+M)*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
((2+M)*j+1 < (2+M)*N) ? &attrs[(2+M)*j+1] : NULL);
attrs[(2+M)*j+1] = *LFSR_ATTR(
REG, 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_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_multi_mixed_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_mixed_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// traverse requires correct biasing of the weights in the rbyd tree
// so that lookups return strictly the tag greater than or equal to
// the tag requested
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 0, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 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(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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(REG, 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(REG, 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_REG);
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_REG);
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_REG);
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_REG);
assert(id_ == 1);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 1);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, "\xbb\xbb", 2) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_mixed_traverse_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// build the attribute list for the current permutation
struct lfsr_attr attrs[N*(2+M)];
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
attrs[(2+M)*j+0] = *LFSR_ATTR(
GROW, id, NULL, 1,
((2+M)*j+1 < N*(2+M)) ? &attrs[(2+M)*j+1] : NULL);
attrs[(2+M)*j+1] = *LFSR_ATTR(
REG, 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_REG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == j);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_multi_mixed_traverse_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG);
assert(id_ == j);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == j);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[j % 6], 2) == 0);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_update_permutations]
defines.N = 'range(1, 4)'
defines.M = 'range(1, 3)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(REG, j, names[j % 6], 4,
NULL))) => 0;
// note uattrs have a smaller size to help debugging
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2,
NULL)) => 0;
}
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N*M];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N*M);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N*M; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// update each tag in permutation order
for (unsigned j = 0; j < N*M; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(perm[j]%M+1), perm[j]/M,
names[(perm[j]/M) % 6], 3,
NULL)) => 0;
}
// check that all tags have been updated
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> 3;
assert(memcmp(buffer, names[j % 6], 3) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_remove_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG);
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_REG, k, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k,
&tag_, &id_, NULL, &off_, &size_) => 0;
if (k == j/M && u == j%M) {
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 3);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 3;
assert(memcmp(buffer, names[k % 6], 3) == 0);
} else {
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + 2;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_mixed_remove_all_permutations]
defines.N = 'range(1, 4)'
defines.M = 'range(1, 3)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(REG, j, names[j % 6], 4,
NULL))) => 0;
// note uattrs have a smaller size to help debugging
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2,
NULL)) => 0;
}
}
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N*M];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N*M);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N*M; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// remove each tag in permutation order
for (unsigned j = 0; j < N*M; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(perm[j]%M+1), perm[j]/M, NULL, 0,
NULL)) => 0;
}
// check that all tags have been removed
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, j, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4)
=> LFS_ERR_NOENT;
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + N*M + N*M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
[cases.test_rbyd_mixed_large]
in = 'lfs.c'
# ORDER:
# 0 = in-order
# 1 = reverse-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.M = 'range(1, 4)'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
// create the rbyd tree
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
// keep inserting tags until we run out of space
//
// note, the ids we create this way are both sparse and sometimes
// repeated, so we need to mod our current rbyd size to avoid invalid
// insertions
//
uint32_t prng = 42;
for (lfs_size_t i = 0;; i++) {
uint16_t x
= (ORDER == 0) ? (uint16_t)i
: (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i)
: (uint16_t)TEST_PRNG(&prng);
x = x % (rbyd.weight+1);
// build a single attribute list with all attributes, if this fails
// it should fail atomically
struct lfsr_attr attrs[2+M];
attrs[0] = *LFSR_ATTR(
GROW, x, NULL, 1,
&attrs[1]);
attrs[1] = *LFSR_ATTR(
REG, 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_REG, x,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => LFS_ERR_NOENT;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => LFS_ERR_NOENT;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => LFS_ERR_NOENT;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 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_REG, 2, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4)
=> LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][6] = {
"\xaa\xaa\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee\xee\xee",
"\xff\xff\xff\xff\xff\xff",
};
uint8_t buffer[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 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_REG, k, buffer, 6)
=> 6;
assert(memcmp(buffer, names[k % 6], 6) == 0);
} else {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, k, buffer, 6)
=> 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N + 2;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][6] = {
"\xaa\xaa\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee\xee\xee",
"\xff\xff\xff\xff\xff\xff",
};
uint8_t buffer[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 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_REG, 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_REG, k, buffer, 6)
=> 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(u+1), k, buffer, 6)
=> 2;
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
}
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + 1 + 1+M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_traverse_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG);
assert(id_ == k);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
if (k >= j) {
assert(memcmp(buffer, names[(k+1) % 6], 4) == 0);
} else {
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_traverse_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG);
assert(id_ == k);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
if (k >= j) {
assert(memcmp(buffer, names[(k+1) % 6], 4) == 0);
} else {
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(u+1));
assert(id_ == k);
assert(size_ == 2);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2;
if (k >= j) {
assert(memcmp(buffer, names[(k+1) % 6], 2) == 0);
} else {
assert(memcmp(buffer, names[k % 6], 2) == 0);
}
}
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT;
}
// cleanup
free(backup_block);
}
'''
# Note, "delete_all" is a weird state for rbyd trees to be in, since they
# don't really have a trunk at this point
[cases.test_rbyd_delete_all]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
// create and delete one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, 0, buffer, 4)
=> LFS_ERR_NOENT;
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_all_permutations]
defines.N = 'range(1, 7)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(REG, j, names[j % 6], 4,
NULL))) => 0;
}
assert(rbyd.weight == N);
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// delete each id in permutation order
for (unsigned j = 0; j < N; j++) {
// adjust id based on previous deletions
uint16_t id = perm[j];
for (unsigned k = 0; k < j; k++) {
if (perm[k] < perm[j]) {
id -= 1;
}
}
lfs_size_t rbyd_weight_before = rbyd.weight;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1, NULL)) => 0;
assert(rbyd.weight == rbyd_weight_before-1);
}
// check that all tags are now removed
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == 0);
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_REG, 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(REG, 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_REG, 0, buffer, 6)
=> 6;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0);
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_REG, 1, buffer, 6)
=> LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + 2*N + 1;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# NOTE if we separate physical/logical block sizes we may be able to
# use emubd's copy-on-write copy to speed this up significantly
[cases.test_rbyd_delete_all_range_permutations]
defines.N = 'range(1, 7)'
defines.M = 'range(1, 4)'
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
uint8_t buffer[6];
// keep track of the worst case log size
lfs_size_t worst_size = 0;
size_t worst_perm_i = 0;
// create one consistent block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, j, NULL, 1,
LFSR_ATTR(REG, j, names[j % 6], 4,
NULL))) => 0;
// note uattrs have a smaller size to help debugging
for (unsigned u = 0; u < M; u++) {
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2,
NULL)) => 0;
}
}
assert(rbyd.weight == N);
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.off);
lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off,
rbyd.block, 0, backup_block, rbyd.off) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// restore backup
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.block, 0, backup_block, rbyd.off) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
// delete each id in permutation order
for (unsigned j = 0; j < N; j++) {
// adjust id based on previous deletions
uint16_t id = perm[j];
for (unsigned k = 0; k < j; k++) {
if (perm[k] < perm[j]) {
id -= 1;
}
}
lfs_size_t rbyd_weight_before = rbyd.weight;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1, NULL)) => 0;
assert(rbyd.weight == rbyd_weight_before-1);
}
// check that all tags are now removed
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == 0);
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_REG, 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(REG, 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_REG, 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_REG, 1, buffer, 6)
=> LFS_ERR_NOENT;
lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(1), 1, buffer, 6)
=> LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_perm_i = perm_i;
}
}
// cleanup
free(backup_block);
// test that tree is self-balancing, we should be strictly bounded
// by height <= 2*log(n)+1, assume tags are strictly <=12 bytes
lfs_size_t n = 1 + N+N*M + N + 1+M;
printf("--- summary ---\n");
printf("worst permutation: %zd\n", worst_perm_i);
printf("worst size: %u B (N=%u, estimate=%u)\n",
worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4);
printf("worst avg height: %u B (N=%u, estimate=%u)\n",
worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4);
// note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4);
}
'''
# the main purpose of this test is to try to fuzz for failures in the
# balancing algorithm
[cases.test_rbyd_fuzz_create_deletes]
defines.N = 'range(1, 33)'
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose create or delete
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
printf("c%d=%c", id, alpha[i % 26]);
count += 1;
} else {
printf("d%d", id);
count -= 1;
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N);
memset(sim, 0, N);
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose create or delete
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
// update our sim
memmove(sim+id+1, sim+id, count-id);
sim[id] = alpha[i % 26];
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, 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_REG, 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_REG, id, buffer, 4) => 1;
assert(memcmp(&sim[id], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''
# this test is to catch the mistake of letting deletes unconditionally
# append an "altgt 0x0 w0"
[cases.test_rbyd_delete_end]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
uint8_t buffer[4];
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
// create three ids
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4,
LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xbb\xbb\xbb\xbb", 4,
LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 2, "\xcc\xcc\xcc\xcc", 4,
NULL))))))))) => 0;
// delete the last two
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, 1, NULL, 2, NULL)) => 0;
// create some new ids, if unconditional altgts are used this
// will end up losing tags
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 1, NULL, 1,
LFSR_ATTR(REG, 1, "\xdd\xdd\xdd\xdd", 4,
LFSR_ATTR(UATTR(1), 1, "\xdd\xdd", 2,
LFSR_ATTR(GROW, 2, NULL, 1,
LFSR_ATTR(REG, 2, "\xee\xee\xee\xee", 4,
LFSR_ATTR(UATTR(1), 2, "\xee\xee", 2,
NULL))))))) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xdd\xdd\xdd\xdd", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xdd\xdd", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xee\xee\xee\xee", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xee\xee", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 3, buffer, 4)
=> LFS_ERR_NOENT;
lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0;
assert(rbyd.weight == 3);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 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_REG, 1, buffer, 4) => 4;
assert(memcmp(buffer, "\xdd\xdd\xdd\xdd", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2;
assert(memcmp(buffer, "\xdd\xdd", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 2, buffer, 4) => 4;
assert(memcmp(buffer, "\xee\xee\xee\xee", 4) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2;
assert(memcmp(buffer, "\xee\xee", 2) == 0);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 3, buffer, 4)
=> LFS_ERR_NOENT;
'''
# Test rbyd weights
[cases.test_rbyd_grow]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
// make id0 with weight w1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 2, "\xbb\xbb\xbb\xbb", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 5, "\xcc\xcc\xcc\xcc", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 9, "\xdd\xdd\xdd\xdd", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 14, "\xee\xee\xee\xee", 4,
NULL))) => 0;
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 0);
assert(weight_ == 1);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 2);
assert(weight_ == 2);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 5);
assert(weight_ == 3);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == 9);
assert(weight_ == 4);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 0, "\xaa\xaa\xaa\xaa", 4,
// make id2 with weight w2
LFSR_ATTR(GROW, 1, NULL, 2,
LFSR_ATTR(REG, 2, "\xbb\xbb\xbb\xbb", 4,
// make id5 with weight w3
LFSR_ATTR(GROW, 3, NULL, 3,
LFSR_ATTR(REG, 5, "\xcc\xcc\xcc\xcc", 4,
// make id9 with weight w4
LFSR_ATTR(GROW, 6, NULL, 4,
LFSR_ATTR(REG, 9, "\xdd\xdd\xdd\xdd", 4,
// make id14 with weight w5
LFSR_ATTR(GROW, 10, NULL, 5,
LFSR_ATTR(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_grow_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG, j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, j*W+W-1, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
}
'''
[cases.test_rbyd_grow_traverse_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
}
'''
# Weights mixed with attributes
[cases.test_rbyd_mixed_grow]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
// make id0 with weight w1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9,
LFSR_ATTR(GROW, 0, NULL, 1,
LFSR_ATTR(REG, 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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 0,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 2,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 5,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 9,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, 14,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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(REG, 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(REG, 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(REG, 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(REG, 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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
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_REG);
assert(id_ == 14);
assert(weight_ == 5);
assert(size_ == 4);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == 14);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
'''
[cases.test_rbyd_mixed_grow_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9, NULL)) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, 1,
LFSR_ATTR(REG, 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_REG, j*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, j*W+W-1, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
}
'''
[cases.test_rbyd_mixed_grow_traverse_permutations]
defines.N = 'range(1, 8)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// test the given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(3), -1, "unrelated", 9, NULL)) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, 1,
LFSR_ATTR(REG, 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_REG);
assert(id_ == j*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(2));
assert(id_ == j*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
}
lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_,
&tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT;
}
'''
[cases.test_rbyd_sparse_grow_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG, k*W+W-1+1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1+1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1+1);
assert(weight_ == W);
assert(size_ == 4);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
}
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
'''
[cases.test_rbyd_sparse_shrink_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG, k*W+W-1-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1-1);
assert(weight_ == W);
assert(size_ == 4);
} else {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
}
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
'''
[cases.test_rbyd_sparse_delete_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][6] = {
"\xaa\xaa\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee\xee\xee",
"\xff\xff\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[6];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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(REG, 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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 6) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
}
}
}
}
'''
[cases.test_rbyd_sparse_attr_permutations]
defines.N = 'range(1, 7)'
defines.W = 5
# -1 => exhaust all permutations
# n => reproduce a specific permutation
defines.PERMUTATION = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const uint8_t names[6][4] = {
"\xaa\xaa\xaa\xaa",
"\xbb\xbb\xbb\xbb",
"\xcc\xcc\xcc\xcc",
"\xdd\xdd\xdd\xdd",
"\xee\xee\xee\xee",
"\xff\xff\xff\xff",
};
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
uint8_t buffer[4];
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
for (size_t i = 0;
i < (PERMUTATION == -1 ? perm_count : 1);
i++) {
uint32_t perm[N];
size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION;
TEST_PERMUTATION(perm_i, perm, N);
// print permutation to help debugging
printf("--- permutation: %zd [", perm_i);
for (unsigned j = 0; j < N; j++) {
if (j > 0) {
printf(", ");
}
printf("%d", perm[j]);
}
printf("] ---\n");
// create given permutation with multiple commits
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
for (unsigned j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (unsigned k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id*W, NULL, W,
LFSR_ATTR(REG, 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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
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_REG, k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_REG);
assert(id_ == k*W+W-1);
assert(weight_ == W);
assert(size_ == 4);
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4;
assert(memcmp(buffer, names[k % 6], 4) == 0);
if (k == j) {
lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), k*W+W-1,
&tag_, &id_, &weight_, &off_, &size_) => 0;
assert(tag_ == LFSR_TAG_UATTR(1));
assert(id_ == k*W+W-1);
assert(weight_ == 0);
assert(size_ == 2);
}
}
}
}
'''
# Some more fuzzish testing
[cases.test_rbyd_fuzz_mixed]
defines.N = 'range(1, 33)'
defines.M = 3
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete or attr append/remove
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose an attr
uint8_t u = TEST_PRNG(&prng) % M;
if (id == (lfs_ssize_t)count || op == 0) {
printf("c%d=%c", id, alpha[i % 26]);
count += 1;
} else if (op == 1) {
printf("d%d", id);
count -= 1;
} else if (op == 2) {
printf("a%d,%d=%c", id, u, alpha[i % 26]);
} else if (op == 3) {
printf("r%d,%d", id, u);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N*(M+1));
memset(sim, 0, N*(M+1));
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete or attr append/remove
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose an attr
uint8_t u = TEST_PRNG(&prng) % M;
if (id == (lfs_ssize_t)count || op == 0) {
// update our sim
memmove(sim+(id+1)*(M+1), sim+id*(M+1), (count-id)*(M+1));
memset(&sim[id*(M+1)], 0, M+1);
sim[id*(M+1)] = alpha[i % 26];
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, id, NULL, 1,
LFSR_ATTR(REG, id, &alpha[i % 26], 1,
NULL))) => 0;
} else if (op == 1) {
// update our sim
memmove(sim+id*(M+1), sim+(id+1)*(M+1), (count-id-1)*(M+1));
count -= 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, id, NULL, 1,
NULL)) => 0;
} else if (op == 2) {
// update our sim
sim[id*(M+1) + u+1] = alpha[i % 26];
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1,
NULL)) => 0;
} else if (op == 3) {
// update our sim
sim[id*(M+1) + u+1] = '\0';
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(RMUATTR(u), id, NULL, 0,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
printf("%c", sim[id*(M+1)]);
for (uint8_t u = 0; u < M; u++) {
if (sim[id*(M+1) + u+1]) {
printf("%c", sim[id*(M+1) + u+1]);
} else {
printf("_");
}
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
printf("rbyd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)rbyd.weight; id++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_REG, id, buffer, 4);
if (size >= 0) {
printf("%.*s", size, buffer);
} else {
printf("?");
}
for (uint8_t u = 0; u < M; u++) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
LFSR_TAG_UATTR(u), id, buffer, 4);
if (size >= 0) {
printf("%.*s", size, buffer);
} else {
printf("_");
}
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
assert(count == rbyd.weight);
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, id, buffer, 4) => 1;
assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''
[cases.test_rbyd_fuzz_sparse]
defines.N = 'range(1, 33)'
defines.W = 5
defines.SAMPLES = 1000
# -1 => all pseudo-random seeds
# n => reproduce a specific seed
defines.SEED = -1
# large progs take too long for now
if = 'PROG_SIZE < 512'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfsr_rbyd_t init_rbyd = {
.block = 0,
.rev = 1,
.off = 0,
.crc = 0,
.trunk = 0,
.weight = 0,
.erased = true,
};
lfsr_rbyd_t rbyd;
lfsr_tag_t tag_;
lfs_ssize_t id_;
lfs_size_t weight_;
lfs_off_t off_;
lfs_size_t size_;
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
uint8_t buffer[4];
// keep track of the worst case size and seed
lfs_size_t worst_size = 0;
uint32_t worst_seed = 0;
// iterate through seeds so we can reproduce easily
for (uint32_t seed = (SEED == -1 ? 1 : SEED);
(SEED == -1 ? seed < SAMPLES+1 : seed == SEED);
seed++) {
printf("--- seed: %d ---\n", seed);
printf("perm: [");
uint32_t prng = seed;
lfs_size_t count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete/grow/shrink
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose a weight
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
if (id == (lfs_ssize_t)count || op == 0) {
printf("c%dw%d=%c", id, weight, alpha[i % 26]);
count += 1;
} else if (op == 1) {
printf("d%d", id);
count -= 1;
} else if (op == 2) {
printf("g%dw%d", id, weight);
} else if (op == 3) {
printf("s%dw%d", id, weight);
}
if (i < N-1) {
printf(", ");
}
}
printf("]\n");
// set up a simulation to compare against, fun fact this performs
// worst than our actual rbyd block!
char *sim = malloc(N);
lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t));
memset(sim, 0, N);
memset(sim_weights, 0, N*sizeof(lfs_size_t));
// set up rbyd block
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
prng = seed;
count = 0;
for (unsigned i = 0; i < N; i++) {
// choose create/delete/grow/shrink
uint8_t op = TEST_PRNG(&prng) % 4;
// choose an id
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
// choose a weight
lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W);
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
if (id == (lfs_ssize_t)count || op == 0) {
// update our sim
memmove(sim+id+1, sim+id, count-id);
memmove(sim_weights+id+1, sim_weights+id,
(count-id)*sizeof(lfs_size_t));
sim[id] = alpha[i % 26];
sim_weights[id] = weight;
count += 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, weighted_id, NULL, weight,
LFSR_ATTR(REG, weighted_id+weight-1,
&alpha[i % 26], 1,
NULL))) => 0;
} else if (op == 1) {
// get the correct weight from the sim
weight = sim_weights[id];
// update our sim
memmove(sim+id, sim+id+1, count-id-1);
memmove(sim_weights+id, sim_weights+id+1,
(count-id-1)*sizeof(lfs_size_t));
count -= 1;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, weighted_id, NULL, weight,
NULL)) => 0;
} else if (op == 2) {
// update our sim
sim_weights[id] += weight;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(GROW, weighted_id, NULL, weight,
NULL)) => 0;
} else if (op == 3) {
// don't let shrink go to zero here! this is already hard enough
// to simulate
weight = lfs_min(weight, sim_weights[id]-1);
// update our sim
sim_weights[id] -= weight;
// update our rbyd
lfsr_rbyd_commit(&lfs, &rbyd,
LFSR_ATTR(SHRINK, weighted_id, NULL, weight,
NULL)) => 0;
}
}
// compare rbyd vs simulation
printf("expd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
printf("%cw%d", sim[id], sim_weights[id]);
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
printf("rbyd: [");
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
int err = lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_REG, weighted_id,
&tag_, &id_, &weight_, &off_, &size_);
if (!err) {
lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd,
tag_, id_, buffer, 4);
if (size >= 0) {
printf("%.*sw%d", size, buffer, weight_);
} else {
printf("?");
}
} else {
printf("?");
}
if (id < (lfs_ssize_t)count-1) {
printf(", ");
}
}
printf("]\n");
// calculate total weight
lfs_size_t total_weight = 0;
for (lfs_ssize_t j = 0; j < (lfs_ssize_t)count; j++) {
total_weight += sim_weights[j];
}
assert(total_weight == rbyd.weight);
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
// calculate actual id in rbyd space
lfs_ssize_t weighted_id = 0;
for (lfs_ssize_t j = 0; j < id; j++) {
weighted_id += sim_weights[j];
}
lfsr_rbyd_lookup(&lfs, &rbyd,
LFSR_TAG_REG, weighted_id,
&tag_, &id_, &weight_, &off_, &size_) => 0;
lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 1;
assert(memcmp(&sim[id], buffer, 1) == 0);
}
// keep track of the worst seed
if (rbyd.off > worst_size) {
worst_size = rbyd.off;
worst_seed = seed;
}
// cleanup
free(sim);
free(sim_weights);
}
// print the worst seed + size, and rerun it so it's left on the disk
// if used with -ddisk
printf("--- summary ---\n");
printf("worst_seed: %d\n", worst_seed);
printf("worst_size: %d\n", worst_size);
// our tree should be strictly <= 2*log(n)+1, assume tags are strictly
// <=12 bytes, note this only holds true with byte-level progs
if (PROG_SIZE == 1) {
assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1);
}
'''