From 5e0418029d10ec3ce08718895aa6e94bdf9ba8f3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 24 Jan 2023 15:42:00 -0600 Subject: [PATCH] Added mixed attr+id testing, tweaks to 3-leb tag utility functions --- lfs.c | 104 +- tests/test_rbyd.toml | 2369 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 2298 insertions(+), 175 deletions(-) diff --git a/lfs.c b/lfs.c index b336e93b..7968e7a7 100644 --- a/lfs.c +++ b/lfs.c @@ -626,6 +626,14 @@ static inline bool lfsr_tag_isalt(lfsr_tag_t tag) { return tag & 0x8; } +static inline bool lfsr_tag_iscrc(lfsr_tag_t tag) { + return (tag & ~0x0010) == LFSR_TAG_CRC; +} + +static inline bool lfsr_tag_ismk(lfsr_tag_t tag) { + return (tag & ~0x03f0) == LFSR_TAG_MK; +} + //static inline bool lfsr_tag_iscrc(lfsr_tag_t tag) { // return (tag & 0xe) == 0x6; //} @@ -754,18 +762,16 @@ static inline bool lfsr_tag_follow(lfsr_tag_t alt, lfs_size_t weight, lfsr_tag_t tag, lfsr_sid_t id) { // TODO do we actually need lfsr_tag_key? // TODO hmm, are key and weight entirely exclusive? - if (!lfsr_tag_key(alt)) { - if (lfsr_tag_isgt(alt)) { - return id >= upper - (lfs_ssize_t)weight; - } else { - return id < lower + (lfs_ssize_t)weight; - } + if (lfsr_tag_isgt(alt)) { + return id >= upper - (lfs_ssize_t)weight + || (/*lfsr_tag_key(alt) + &&*/ id == (upper - (lfs_ssize_t)weight)-1 + && lfsr_tag_key(tag) > lfsr_tag_key(alt)); } else { - if (lfsr_tag_isgt(alt)) { - return lfsr_tag_key(tag) > lfsr_tag_key(alt); - } else { - return lfsr_tag_key(tag) <= lfsr_tag_key(alt); - } + return id < lower + (lfs_ssize_t)weight + || (/*lfsr_tag_key(alt) + &&*/ id == lower + (lfs_ssize_t)weight + && lfsr_tag_key(tag) <= lfsr_tag_key(alt)); } } @@ -775,32 +781,24 @@ static inline lfsr_tag_t lfsr_tag_flipalt(lfsr_tag_t alt) { static inline lfs_size_t lfsr_tag_flipweight(lfsr_tag_t alt, lfs_size_t weight, lfsr_sid_t lower, lfsr_sid_t upper) { - if (!lfsr_tag_key(alt)) { - return (upper-lower) - weight; - } else { - return weight; - } + return (upper-lower) - weight - 1; } static inline void lfsr_tag_trimweight(lfsr_tag_t alt, lfs_size_t weight, lfsr_sid_t *lower, lfsr_sid_t *upper) { - if (!lfsr_tag_key(alt)) { - if (lfsr_tag_isgt(alt)) { - *upper -= weight; - } else { - *lower += weight; - } + if (lfsr_tag_isgt(alt)) { + *upper -= weight; + } else { + *lower += weight; } } static inline void lfsr_tag_untrimweight(lfsr_tag_t alt, lfs_size_t weight, lfsr_sid_t *lower, lfsr_sid_t *upper) { - if (!lfsr_tag_key(alt)) { - if (lfsr_tag_isgt(alt)) { - *upper += weight; - } else { - *lower -= weight; - } + if (lfsr_tag_isgt(alt)) { + *upper += weight; + } else { + *lower -= weight; } } @@ -808,7 +806,7 @@ static inline void lfsr_tag_trimtag(lfsr_tag_t alt, lfsr_sid_t lower_id, lfsr_sid_t upper_id, lfsr_tag_t *lower_tag, lfsr_tag_t *upper_tag, lfsr_sid_t id) { - if (lfsr_tag_key(alt)) { + //if (lfsr_tag_key(alt)) { // TODO test if we fit id??? if (lfsr_tag_isgt(alt)) { if (id == upper_id-1) { @@ -819,7 +817,7 @@ static inline void lfsr_tag_trimtag(lfsr_tag_t alt, *lower_tag = alt; } } - } + //} } @@ -1437,7 +1435,7 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd, } // not an end-of-commit crc - if ((tag & ~0x10) != LFSR_TAG_CRC) { + if (!lfsr_tag_iscrc(tag)) { // fcrc is only valid if the last tag was a crc hasfcrc = false; @@ -1453,7 +1451,7 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd, } // found a mk? update id count - if ((tag & ~0x3f0) == LFSR_TAG_MK) { + if (lfsr_tag_ismk(tag)) { // NOTE we can't check for overflow/underflow here because we // may be overeagerly parsing an invalid commit, it's ok for // this to overflow/underflow as long as we throw it out later @@ -1943,7 +1941,7 @@ static void lfsr_rbyd_p_red( static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lfsr_tag_t tag, lfsr_sid_t id, const void *buffer, lfs_size_t size) { // tags must be in a valid id range at this point - if ((tag & ~0x3f0) == LFSR_TAG_MK) { + if (lfsr_tag_ismk(tag)) { LFS_ASSERT(rbyd_->weight < 0xffff); LFS_ASSERT(id <= rbyd_->weight); } else { @@ -1965,7 +1963,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lfsr_sid_t lower_id_; lfsr_tag_t upper_tag_; lfsr_sid_t upper_id_; - if ((tag & ~0x3f0) == LFSR_TAG_MK) { + if (lfsr_tag_ismk(tag)) { lower_tag_ = 0; lower_id_ = id; upper_tag_ = lower_tag_; @@ -2308,28 +2306,20 @@ stem:; if (lfsr_tag_isrm(lower_tag_)) { // no split needed, prune the removed tag - } else if (lower_id_ < id) { - // split less than id - // - // note this is consistent for all appends and only happens when - // appending to the end of the tree - alt = LFSR_TAG_ALT(B, LE, 0); - weight = lower_id_+1 - lower_lower_id; - jump = lower_branch; - - } else if (lower_id_ == id - && lfsr_tag_key(lower_tag_) < lfsr_tag_key(tag)) { - // split less than tag + } else if (lower_id_ < id + || (lower_id_ == id + && lfsr_tag_key(lower_tag_) < lfsr_tag_key(tag))) { + // split less than // // note this is consistent for all appends and only happens when // appending to the end of the tree alt = LFSR_TAG_ALT(B, LE, lower_tag_); - weight = 0; + weight = lower_id_ - lower_lower_id; jump = lower_branch; - } else if ((tag & ~0x3f0) == LFSR_TAG_MK) { + } else if (lfsr_tag_ismk(tag)) { if (upper_id_ >= id) { - alt = LFSR_TAG_ALT(B, GT, 0); + alt = LFSR_TAG_ALT(B, GT, tag); weight = (upper_upper_id-1 - id) + 1; jump = upper_branch; } @@ -2351,16 +2341,12 @@ stem:; // jump = upper_branch; // } // - } else if (upper_id_ > id) { - // split greater than id - alt = LFSR_TAG_ALT(B, GT, 0); - weight = upper_upper_id-1 - id; - jump = upper_branch; - - } else if (lfsr_tag_key(upper_tag_) > lfsr_tag_key(tag)) { - // split greater than tag + } else if (upper_id_ > id + || (upper_id_ == id + && lfsr_tag_key(upper_tag_) > lfsr_tag_key(tag))) { + // split greater than alt = LFSR_TAG_ALT(B, GT, tag); - weight = 0; + weight = upper_upper_id-1 - id; jump = upper_branch; } @@ -2405,7 +2391,7 @@ leaf:; // note we do this here since it is possible to insert into an // empty tree // - if ((tag & ~0x3f0) == LFSR_TAG_MK) { + if (lfsr_tag_ismk(tag)) { rbyd_->weight += 1; } diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index d7ac810d..bb745694 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -209,6 +209,40 @@ code = ''' assert(size_ == 4); lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // commit with two attributes, in the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_lookup] @@ -302,6 +336,41 @@ code = ''' assert(size_ == 4); lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // commit with two attributes, in the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_get] @@ -374,6 +443,27 @@ code = ''' => 4; lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) => LFS_ERR_NOENT; + + // commit with two attributes, in the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) + => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_get] @@ -448,127 +538,28 @@ code = ''' => 4; lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) => LFS_ERR_NOENT; -''' -# TODO this but for creates? -# TODO and for tags that cross id boundaries? - -[cases.test_rbyd_traverse] -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_; - lfsr_sid_t id_; - lfs_off_t off_; - lfs_size_t size_; - - // traverse requires correct biasing of the weights in the rbyd tree - // so that lookups return strictly the tag greater than or equal to - // the tag requested + // commit with two attributes, in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, - LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; - - lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(1)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(2)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; - - lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; - lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(1)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(2)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; -''' - -[cases.test_rbyd_multi_traverse] -in = 'lfs.c' -if = 'BLOCK_SIZE/PROG_SIZE >= 2' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_; - lfsr_sid_t id_; - lfs_off_t off_; - lfs_size_t size_; - - // traverse requires correct biasing of the weights in the rbyd tree - // so that lookups return strictly the tag greater than or equal to - // the tag requested - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; - - lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(1)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(2)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) + => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; - lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(1)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => 0; - assert(tag_ == LFSR_TAG_UATTR(2)); - assert(id_ == -1); - assert(size_ == 4); - lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, - &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, buffer, 4) + => 4; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, buffer, 4) + => LFS_ERR_NOENT; ''' [cases.test_rbyd_bifoliate] @@ -2180,7 +2171,6 @@ code = ''' // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.block) => 0; - for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, @@ -2231,6 +2221,369 @@ code = ''' } ''' +[cases.test_rbyd_traverse] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_multi_traverse] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_traverse_permutations] +defines.N = 'range(1, 8)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // build the attribute list for the current permutation + struct lfsr_attr attrs[N]; + for (unsigned j = 0; j < N; j++) { + attrs[j] = *LFSR_ATTR( + UATTR(perm[j]+1), -1, + "\xaa\xaa\xaa\xaa", 4, + (j+1 < N) ? &attrs[j+1] : NULL); + } + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(j+1)); + assert(id_ == -1); + assert(size_ == 4); + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + +[cases.test_rbyd_multi_traverse_permutations] +defines.N = 'range(1, 8)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, + NULL)) => 0; + } + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(j+1)); + assert(id_ == -1); + assert(size_ == 4); + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + [cases.test_rbyd_large] in = 'lfs.c' # ORDER: @@ -3585,7 +3938,6 @@ code = ''' // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.block) => 0; - for (unsigned j = 0; j < N; j++) { // adjust id based on future insertions uint16_t id = perm[j]; @@ -3643,6 +3995,444 @@ code = ''' } ''' +[cases.test_rbyd_create_traverse] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_multi_create_traverse] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_create_traverse_permutations] +defines.N = 'range(1, 8)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // build the attribute list for the current permutation + struct lfsr_attr attrs[N]; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + attrs[j] = *LFSR_ATTR( + MKREG, id, + names[perm[j] % 6], 4, + (j+1 < N) ? &attrs[j+1] : NULL); + } + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == j); + assert(size_ == 4); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + +[cases.test_rbyd_multi_create_traverse_permutations] +defines.N = 'range(1, 8)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // test the given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + } + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == j); + assert(size_ == 4); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + [cases.test_rbyd_create_large] in = 'lfs.c' # ORDER: @@ -3717,6 +4507,1353 @@ code = ''' ''' +### Mixed create and attr testing ### + +[cases.test_rbyd_mixed] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + uint8_t buffer[4]; + + // try to create one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + + // try to create two ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + // try to create two in the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))))) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + // create a third to the right + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + // create a third to the left + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))))))) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + // create a third in the middle + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))))) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); +''' + +[cases.test_rbyd_multi_mixed] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + uint8_t buffer[4]; + + // try to create one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + + // try to create two ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + // try to create two in the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + + // create a third to the right + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL)) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + // create a third to the left + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + // create a third in the middle + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xcc\xcc\xcc\xcc", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xcc\xcc", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0; + + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 3); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); +''' + +[cases.test_rbyd_mixed_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + uint8_t buffer[4]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // build the attribute list for the current permutation + struct lfsr_attr attrs[N+N*M]; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + attrs[j*(M+1)] = *LFSR_ATTR( + MKREG, id, + names[perm[j] % 6], 4, + (j*(M+1)+1 < N+N*M) ? &attrs[j*(M+1)+1] : NULL); + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + attrs[j*(M+1)+u+1] = *LFSR_ATTR( + UATTR(u+1), id, + names[perm[j] % 6], 2, + (j*(M+1)+u+1+1 < N+N*M) ? &attrs[j*(M+1)+u+1+1] : NULL); + } + } + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4) + => 2; + assert(memcmp(buffer, names[j % 6], 2) == 0); + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + lfs_size_t n = 1 + N + N*M; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_multi_mixed_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + uint8_t buffer[4]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2, + NULL)) => 0; + } + } + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4) + => 2; + assert(memcmp(buffer, names[j % 6], 2) == 0); + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + lfs_size_t n = 1 + N + N*M; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_mixed_traverse] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_multi_mixed_traverse] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // traverse requires correct biasing of the weights in the rbyd tree + // so that lookups return strictly the tag greater than or equal to + // the tag requested + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // also try the other direction + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xbb\xbb\xbb\xbb", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xbb\xbb", 2, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, 0, -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 0); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 0); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == 1); + assert(size_ == 4); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == 1); + assert(size_ == 2); + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_mixed_traverse_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // build the attribute list for the current permutation + struct lfsr_attr attrs[N+N*M]; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + attrs[j*(M+1)] = *LFSR_ATTR( + MKREG, id, + names[perm[j] % 6], 4, + (j*(M+1)+1 < N+N*M) ? &attrs[j*(M+1)+1] : NULL); + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + attrs[j*(M+1)+u+1] = *LFSR_ATTR( + UATTR(u+1), id, + names[perm[j] % 6], 2, + (j*(M+1)+u+1+1 < N+N*M) ? &attrs[j*(M+1)+u+1+1] : NULL); + } + } + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, attrs) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == j); + assert(size_ == 4); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == j); + assert(size_ == 2); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, names[j % 6], 2) == 0); + } + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + +[cases.test_rbyd_multi_mixed_traverse_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][4] = { + "\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee", + "\xff\xff\xff\xff", + }; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // test all permutations of a given size + uint16_t perm[N]; + unsigned stack[N]; + for (uint16_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // test the given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2, + NULL)) => 0; + } + } + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + + // try traversing all tags + tag_ = 0; + id_ = -1; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == j); + assert(size_ == 4); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == j); + assert(size_ == 2); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, names[j % 6], 2) == 0); + } + } + lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } +''' + + #### Deletion testing ### # #[cases.test_rbyd_delete]