Files
littlefs/tests/test_rbyd.toml
T
Christopher Haster 55b072e761 Opened up rbyd testing for all geometries, and fixed related bugs
- Caching is still presenting issues with the new requirements for
  rbyd trees, in this case the default bd, with 64 byte progs, revealed
  and issue where rcache could become outdated when reading from disk
  while ignoring what's in the pcache.

  It assumes the pcache will always override the rcache, but this is not
  true after pcache is flushed.

  This didn't happen before as the rcache and pcache don't
  interact while writing in the previous implementation. Because of
  these new requirements the caching system probably deserves a
  rework...

- The quick tests for sublinear space utilization don't work when
  prog_size is > a byte, fortunately we should always have NOR-like
  geometry under test, so we can limit these asserts to NOR-like
  geometry.

- Lots of problems fitting these tests into 512-byte block_size
  geometries, which is a bit concerning. This may be a larger change
  from the previous implementation than expected. This may deserve more
  scrutiny at small block sizes to see how things fit, since the
  sublinear space utilization doesn't really kick in at this scale...

  On the other hand it may just be that these tests are too aggressive
  for 512-byte block sizes, since they don't yet do compaction, which
  should help with padding/crc overhead...
2023-02-12 17:14:12 -06:00

4865 lines
166 KiB
TOML

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