Files
littlefs/tests/test_rbyd.toml
T
Christopher Haster ca710b5a29 Initial, very, very rough implementation of rbyd range deletion
Tree deletion is such a pain. It always seems like an easy addition to
the core algorithm but always comes with problems.

The initial plan for deletes was to iterate through all tags, tombstone,
and then adjust weights as needed. This accomplishes deletes with little
change to the rbyd algorithm, but adds a complex traversal inside the
commit logic. Doable in one commit, but complex. It also risks weird
unintuitive corner cases since the cost of deletion grows with the number
of tags being deleted (O(m log n)).

But this rbyd data structure is a tree, so in theory it's possible to
delete a whole range of tags in a single O(log n) operation.

---

This is a proof-of-concept range deletion algorithm for rbyd trees.

Note, this does not preserve rbyd's balancing properties! But it is no
worse than tombstoning. This is acceptable for littlefs as any
unbalanced trees will be rebalanced during compaction.

The idea is to follow the same underlying dhara algorithm, where we
follow a search path and save any alt pointers not taken, but we follow
both search paths that form the outside of the range, and only keep
outside edges.

For example, a tree:

        .-------o-------.
        |               |
    .---o---.       .---o---.
    |       |       |       |
  .-o-.   .-o-.   .-o-.   .-o-.
  |   |   |   |   |   |   |   |
  a   b   c   d   e   f   g   h

To delete the range d-e, we would search for d, and search for e:

        ********o********
        *               *
    .---*****       *****---.
    |       *       *       |
  .-o-.   .-***   ***-.   .-o-.
  |   |   |   *   *   |   |   |
  a   b   c   d   e   f   g   h

And keep the outside edges:

    .---                 ---.
    |                       |
  .-o-.   .-         -.   .-o-.
  |   |   |           |   |   |
  a   b   c           f   g   h

But how do we combine the outside edges? The simpler option is to do
both searches seperately, one after the other. This would end up with a
tree like this:

    .---------o
    |         |
  .-o-.   .---o
  |   |   |   |
  a   b   c   o---------.
              |         |
              o---.   .-o-.
              |   |   |   |
              _   f   g   h

But this horribly throws off the balance of our tree! It's worse than
tombstoning, and gets worse with more tags.

An alternative strategy, which is used here, is to alternate edges as we
descend down the tree. This unfortunately is more complex, and requires
~2x the RAM, but better preserves the balance of our tree. It isn't
perfect, because we lose color information, but we can leave that up to
compaction:

  .---------o
  |         |
.-o-.       o---------.
|   |       |         |
a   b   .---o       .-o-.
        |   |       |   |
        c   o---.   g   h
            |   |
            _   f

I also hope this can be merged into lfs_rbyd_append, deduplicating the
entire core rbyd append algorithm.
2023-02-12 13:29:06 -06:00

3123 lines
109 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_fetch]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
'''
[cases.test_rbyd_multi_fetch]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
lfs_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;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_lookup]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_get]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
assert(memcmp(buffer, &(uint32_t){0xaaaaaaaa}, 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
assert(memcmp(buffer, &(uint32_t){0xaaaaaaaa}, 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), buffer, 4)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_get]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try an empty commit
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> LFS_ERR_NOENT;
// commit with one attribute
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
assert(memcmp(buffer, &(uint32_t){0xaaaaaaaa}, 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
assert(memcmp(buffer, &(uint32_t){0xaaaaaaaa}, 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> LFS_ERR_NOENT;
// commit with two attributes
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), buffer, 4)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), buffer, 4)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
lfs_srtag_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;
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
tag = lfs_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 1, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 2, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_ERR_NOENT);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
tag = lfs_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 1, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 2, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_ERR_NOENT);
'''
[cases.test_rbyd_multi_traverse]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
lfs_srtag_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;
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
tag = lfs_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 1, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 2, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_ERR_NOENT);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
tag = lfs_rbyd_lookup(&lfs, &rbyd, 0, &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 1, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_MKRTAG(UATTR, 2, 0));
tag = lfs_rbyd_lookup(&lfs, &rbyd, lfs_rtag_inc(tag), &off, &size);
assert(tag == LFS_ERR_NOENT);
'''
[cases.test_rbyd_bifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
// split the other direction
// >b
// => .-'|
// 2 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
'''
[cases.test_rbyd_bflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
// flip a black edge
// <b >b
// .-'| => .-'|
// 1 2 1 2 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
'''
[cases.test_rbyd_trifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL)))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
// flip a black edge
// >r
// .-'|
// <b => | >b
// .-'| .--|-'|
// 2 3 2 3 1
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
'''
[cases.test_rbyd_rflips]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
'''
[cases.test_rbyd_quadrifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
'''
[cases.test_rbyd_rotations]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
'''
[cases.test_rbyd_ysplits]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
'''
[cases.test_rbyd_quintifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
'''
[cases.test_rbyd_prunes]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
'''
[cases.test_rbyd_sextifoliate]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 6, 0, &(uint32_t){0xffffffff}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 6, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 6, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 6, 0, &(uint32_t){0xffffffff}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 6, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 6, 0);
// 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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4,
LFS_MKRATTR(UATTR, 3, 0, &(uint32_t){0xcccccccc}, 4,
LFS_MKRATTR(UATTR, 4, 0, &(uint32_t){0xdddddddd}, 4,
LFS_MKRATTR(UATTR, 5, 0, &(uint32_t){0xeeeeeeee}, 4,
LFS_MKRATTR(UATTR, 6, 0, &(uint32_t){0xffffffff}, 4,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL))))))) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 3, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 4, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 4, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 5, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 5, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 6, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 6, 0);
'''
[cases.test_rbyd_permutations]
defines.N = 'range(1, 8)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 lfs_rattr attrs[N];
for (int j = 0; j < N; j++) {
attrs[j] = *LFS_MKRATTR(
UATTR, perm[j]+1, 0,
&(uint32_t){0xaaaaaaaa}, 4,
(j+1 < N) ? &attrs[j+1] : NULL);
}
// test the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, attrs) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
for (int j = 0; j < N; j++) {
lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(UATTR, j+1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, j+1, 0);
}
// next permutation using Heap's algorithm
if (stack[i] < i) {
if (i % 2 == 0) {
uint8_t t = perm[0];
perm[0] = perm[i];
perm[i] = t;
} else {
uint8_t t = perm[stack[i]];
perm[stack[i]] = perm[i];
perm[i] = t;
}
stack[i] += 1;
i = 1;
} else {
stack[i] = 0;
i += 1;
}
}
'''
[cases.test_rbyd_multi_permutations]
defines.N = 'range(1, 8)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 (int j = 0; j < N; j++) {
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, perm[j]+1, 0, &(uint32_t){0xaaaaaaaa}, 4,
NULL)) => 0;
}
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
for (int j = 0; j < N; j++) {
lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(UATTR, j+1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, j+1, 0);
}
// next permutation using Heap's algorithm
if (stack[i] < i) {
if (i % 2 == 0) {
uint8_t t = perm[0];
perm[0] = perm[i];
perm[i] = t;
} else {
uint8_t t = perm[stack[i]];
perm[stack[i]] = perm[i];
perm[i] = t;
}
stack[i] += 1;
i = 1;
} else {
stack[i] = 0;
i += 1;
}
}
'''
[cases.test_rbyd_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;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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 = lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, x, 0, &(uint32_t){0xaaaaaaaa}, 4,
NULL));
// if we can't fit an fcrc, erased is set to false, but if we can,
// lfs_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;
lfs_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);
lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(UATTR, x, 0), &off, &size)
=> LFS_MKRTAG(UATTR, x, 0);
}
'''
### Removal testing ###
[cases.test_rbyd_remove]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 1, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes, remove the first one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 1, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes, remove the second one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 2, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_multi_remove]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 1, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes, remove the first one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 1, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 2, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
// commit with two attributes, remove the second one
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 1, 0, &(uint32_t){0xaaaaaaaa}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, 2, 0, &(uint32_t){0xbbbbbbbb}, 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, 2, 0, NULL)) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 1, 0), &off, &size)
=> LFS_MKRTAG(UATTR, 1, 0);
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 2, 0), &off, &size)
=> LFS_ERR_NOENT;
lfs_rbyd_lookup(&lfs, &rbyd, LFS_MKRTAG(UATTR, 3, 0), &off, &size)
=> LFS_ERR_NOENT;
'''
[cases.test_rbyd_remove_permutations]
defines.N = 'range(1, 7)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 lfs_rattr attrs[N];
for (int j = 0; j < N; j++) {
attrs[j] = *LFS_MKRATTR(
UATTR, perm[j]+1, 0,
&(uint32_t){0xaaaaaaaa}, 4,
(j+1 < N) ? &attrs[j+1] : NULL);
}
// create the given permutation
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd, attrs) => 0;
// copy block so we can reset after each remove
lfs_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 (int 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_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, j+1, 0, NULL)) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
for (int k = 0; k < N; k++) {
lfs_srtag_t tag = lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(UATTR, k+1, 0), &off, &size);
if (k == j) {
if (j == N-1) {
assert(tag == LFS_ERR_NOENT);
} else {
assert(tag == LFS_MKRTAG(UATTR, j+1+1, 0));
}
} else {
assert(tag == LFS_MKRTAG(UATTR, k+1, 0));
}
}
}
// next permutation using Heap's algorithm
if (stack[i] < i) {
if (i % 2 == 0) {
uint8_t t = perm[0];
perm[0] = perm[i];
perm[i] = t;
} else {
uint8_t t = perm[stack[i]];
perm[stack[i]] = perm[i];
perm[i] = t;
}
stack[i] += 1;
i = 1;
} else {
stack[i] = 0;
i += 1;
}
}
'''
[cases.test_rbyd_multi_remove_permutations]
defines.N = 'range(1, 7)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
lfs_off_t off;
lfs_size_t size;
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 (int j = 0; j < N; j++) {
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(UATTR, perm[j]+1, 0, &(uint32_t){0xaaaaaaaa}, 4,
NULL)) => 0;
}
// copy block so we can reset after each remove
lfs_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 (int 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_rbyd_commit(&lfs, &rbyd,
LFS_MKRRMATTR(UATTR, j+1, 0, NULL)) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
for (int k = 0; k < N; k++) {
lfs_srtag_t tag = lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(UATTR, k+1, 0), &off, &size);
if (k == j) {
if (j == N-1) {
assert(tag == LFS_ERR_NOENT);
} else {
assert(tag == LFS_MKRTAG(UATTR, j+1+1, 0));
}
} else {
assert(tag == LFS_MKRTAG(UATTR, k+1, 0));
}
}
}
// 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;
}
}
'''
### Insertion testing ###
[cases.test_rbyd_insert]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try to insert one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// try to insert two ids
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// try to insert two in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// insert a third to the right
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// insert a third to the left
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)))) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// insert a third in the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xcc\xcc\xcc\xcc", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL)))) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
'''
[cases.test_rbyd_multi_insert]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try to insert one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// try to insert two ids
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// try to insert two in the other direction
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// insert a third to the right
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// insert a third to the left
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// insert a third in the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 3);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4) => 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4) => 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4) => 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
'''
[cases.test_rbyd_insert_permutations]
defines.N = 'range(1, 8)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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];
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 lfs_rattr attrs[N];
for (int j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (int k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
attrs[j] = *LFS_MKRATTR(
CREATEREG, 0, id+1,
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;
lfs_rbyd_commit(&lfs, &rbyd, attrs) => 0;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == N);
for (int j = 0; j < N; j++) {
lfs_rbyd_get(&lfs, &rbyd,
LFS_MKRTAG(CREATEREG, 0, j+1), buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
// next permutation using Heap's algorithm
if (stack[i] < i) {
if (i % 2 == 0) {
uint8_t t = perm[0];
perm[0] = perm[i];
perm[i] = t;
} else {
uint8_t t = perm[stack[i]];
perm[stack[i]] = perm[i];
perm[i] = t;
}
stack[i] += 1;
i = 1;
} else {
stack[i] = 0;
i += 1;
}
}
'''
[cases.test_rbyd_multi_insert_permutations]
defines.N = 'range(1, 8)'
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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];
// test all permutations of a given size
uint8_t perm[N];
uint8_t stack[N];
for (uint8_t i = 0; i < N; i++) {
perm[i] = i;
stack[i] = 0;
}
uint8_t i = 1;
while (i < N) {
// print permutation to help debugging
printf("--- permutation: [");
for (int 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 (int j = 0; j < N; j++) {
// adjust id based on future insertions
uint16_t id = perm[j];
for (int k = j+1; k < N; k++) {
if (perm[j] > perm[k]) {
id -= 1;
}
}
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, id+1, names[perm[j] % 6], 4,
NULL)) => 0;
}
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == N);
for (int j = 0; j < N; j++) {
lfs_rbyd_get(&lfs, &rbyd,
LFS_MKRTAG(CREATEREG, 0, j+1), buffer, 4) => 4;
assert(memcmp(buffer, names[j % 6], 4) == 0);
}
// next permutation using Heap's algorithm
if (stack[i] < i) {
if (i % 2 == 0) {
uint8_t t = perm[0];
perm[0] = perm[i];
perm[i] = t;
} else {
uint8_t t = perm[stack[i]];
perm[stack[i]] = perm[i];
perm[i] = t;
}
stack[i] += 1;
i = 1;
} else {
stack[i] = 0;
i += 1;
}
}
'''
[cases.test_rbyd_insert_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;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_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 = lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, x+1, names[x % 6], 4, NULL));
// if we can't fit an fcrc, erased is set to false, but if we can,
// lfs_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;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
for (uint16_t x = 0; x < rbyd.count; x++) {
lfs_rbyd_lookup(&lfs, &rbyd,
LFS_MKRTAG(CREATEREG, 0, x+1), &off, &size)
=> LFS_MKRTAG(CREATEREG, 0, x+1);
}
'''
### Deletion testing ###
[cases.test_rbyd_delete]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try to delete one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 2, NULL, 0, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the other id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 1, NULL, 0, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the largest of three
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 3, NULL, 0, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the smallest of three
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 1, NULL, 0, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 2, NULL, 0, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
'''
[cases.test_rbyd_delete_range]
in = 'lfs.c'
code = '''
lfs_t lfs;
lfs_init(&lfs, cfg) => 0;
lfs_rbyd_t init_rbyd = {
.block = 0,
.trunk = 0,
.off = 0,
.rev = 1,
.crc = 0,
.count = 0,
.erased = true,
};
lfs_rbyd_t rbyd;
uint8_t buffer[4];
// try to delete one id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(UATTR, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(UATTR, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 2, NULL, 0, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the other id
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(UATTR, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(UATTR, 0, 2, "\xbb\xbb\xbb\xbb", 4, NULL))))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 1, NULL, 0, NULL)) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 1);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => LFS_ERR_NOENT;
// try to delete the largest of three
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(UATTR, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(UATTR, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4,
LFS_MKRATTR(UATTR, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 3, NULL, 0, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
// // try to delete the smallest of three
// rbyd = init_rbyd;
// lfs_bd_erase(&lfs, rbyd.block) => 0;
// lfs_rbyd_commit(&lfs, &rbyd,
// LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
// LFS_MKRATTR(UATTR, 0, 1, "\xaa\xaa\xaa\xaa", 4,
// LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
// LFS_MKRATTR(UATTR, 0, 2, "\xbb\xbb\xbb\xbb", 4,
// LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4,
// LFS_MKRATTR(UATTR, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
// lfs_rbyd_commit(&lfs, &rbyd,
// LFS_MKRATTR(DELETE, 0, 1, NULL, 0, NULL)) => 0;
//
// assert(rbyd.count == 2);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
//// TODO
//// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
//// => LFS_ERR_NOENT;
//
// lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
// assert(rbyd.count == 2);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
// => 4;
// assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
//// TODO
//// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
//// => LFS_ERR_NOENT;
// try to delete the middle
rbyd = init_rbyd;
lfs_bd_erase(&lfs, rbyd.block) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(CREATEREG, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(UATTR, 0, 1, "\xaa\xaa\xaa\xaa", 4,
LFS_MKRATTR(CREATEREG, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(UATTR, 0, 2, "\xbb\xbb\xbb\xbb", 4,
LFS_MKRATTR(CREATEREG, 0, 3, "\xcc\xcc\xcc\xcc", 4,
LFS_MKRATTR(UATTR, 0, 3, "\xcc\xcc\xcc\xcc", 4, NULL))))))) => 0;
lfs_rbyd_commit(&lfs, &rbyd,
LFS_MKRATTR(DELETE, 0, 2, NULL, 0, NULL)) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
lfs_rbyd_fetch(&lfs, &rbyd, rbyd.block, NULL) => 0;
assert(rbyd.count == 2);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 1), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(UATTR, 0, 2), buffer, 4)
=> 4;
assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0);
// TODO
// lfs_rbyd_get(&lfs, &rbyd, LFS_MKRTAG(CREATEREG, 0, 3), buffer, 4)
// => LFS_ERR_NOENT;
'''