From 588a103db764f80f2802aeb02c00536414d52886 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 26 Jan 2023 10:35:13 -0600 Subject: [PATCH] Working through 3-leb range deletes, proving to be problematic The seperate interactions between ids and keys is new and confusing. This was something that the previous combined weights hid. --- lfs.c | 289 ++-- scripts/dbgrbyd.py | 121 +- tests/test_rbyd.toml | 3298 ++++++++++++++++++++++-------------------- 3 files changed, 1990 insertions(+), 1718 deletions(-) diff --git a/lfs.c b/lfs.c index 7968e7a7..42234255 100644 --- a/lfs.c +++ b/lfs.c @@ -457,13 +457,15 @@ enum lfsr_tag_type { LFSR_TAG_MKBRANCH = 0x0400, LFSR_TAG_MKREG = 0x0410, LFSR_TAG_MKDIR = 0x0420, + LFSR_TAG_RM = 0x0402, LFSR_TAG_UATTR = 0x2000, - LFSR_TAG_RMUATTR = 0x2001, + LFSR_TAG_RMUATTR = 0x2002, LFSR_TAG_BTREE = 0x0820, - LFSR_TAG_EXPAND = 0x0004, - LFSR_TAG_SHRINK = 0x0014, +// TODO these +// LFSR_TAG_EXPAND = 0x0004, +// LFSR_TAG_SHRINK = 0x0014, LFSR_TAG_ALT = 0x0008, LFSR_TAG_ALTBLE = 0x0008, @@ -491,6 +493,10 @@ enum lfsr_tag_type { (LFSR_TAG_UATTR \ | ((0xff & (lfsr_tag_t)(attr)) << 4)) +#define LFSR_TAG_RMUATTR(attr) \ + (LFSR_TAG_RMUATTR \ + | ((0xff & (lfsr_tag_t)(attr)) << 4)) + //#define LFSR_ALT_(color, dir, weight) // (LFSR_TAG_ALT // | ((0x1 & (lfsr_tag_t)(color)) << 0) @@ -779,7 +785,7 @@ static inline lfsr_tag_t lfsr_tag_flipalt(lfsr_tag_t alt) { return alt ^ 0x4; } -static inline lfs_size_t lfsr_tag_flipweight(lfsr_tag_t alt, lfs_size_t weight, +static inline lfs_size_t lfsr_tag_flipweight(lfs_size_t weight, lfsr_sid_t lower, lfsr_sid_t upper) { return (upper-lower) - weight - 1; } @@ -810,11 +816,11 @@ static inline void lfsr_tag_trimtag(lfsr_tag_t alt, // TODO test if we fit id??? if (lfsr_tag_isgt(alt)) { if (id == upper_id-1) { - *upper_tag = alt; + *upper_tag = alt + 0x10; } } else { if (id == lower_id) { - *lower_tag = alt; + *lower_tag = alt + 0x10; } } //} @@ -1458,34 +1464,42 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd, // on a bad crc weight += 1; - // found an expand/shrink? update id count - } else if (tag == LFSR_TAG_EXPAND || tag == LFSR_TAG_SHRINK) { - uint8_t buf[5]; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, limit-off, - block, off, buf, lfs_min(size, 5)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - break; - } - return err; - } - - lfs_size_t weight_; - lfs_ssize_t delta = lfs_fromleb128(&weight_, buf, 5); - if (delta < 0) { - return delta; - } - + } else if (tag == LFSR_TAG_RM) { // 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 // on a bad crc - if (tag == LFSR_TAG_EXPAND) { - weight += weight_; - } else { - weight -= weight_; - } + weight -= 1; + +// TODO this +// // found an expand/shrink? update id count +// } else if (tag == LFSR_TAG_EXPAND || tag == LFSR_TAG_SHRINK) { +// uint8_t buf[5]; +// err = lfs_bd_read(lfs, +// NULL, &lfs->rcache, limit-off, +// block, off, buf, lfs_min(size, 5)); +// if (err) { +// if (err == LFS_ERR_CORRUPT) { +// break; +// } +// return err; +// } +// +// lfs_size_t weight_; +// lfs_ssize_t delta = lfs_fromleb128(&weight_, buf, 5); +// if (delta < 0) { +// return delta; +// } +// +// // 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 +// // on a bad crc +// if (tag == LFSR_TAG_EXPAND) { +// weight += weight_; +// } else { +// weight -= weight_; +// } // found an fcrc? save for later } else if (tag == LFSR_TAG_FCRC) { @@ -1666,7 +1680,7 @@ static int lfsr_rbyd_lookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd, // TODO trimflipped? lfsr_tag_trimweight( lfsr_tag_flipalt(alt), - lfsr_tag_flipweight(alt, weight_, lower, upper), + lfsr_tag_flipweight(weight_, lower, upper), &lower, &upper); branch = branch - jump; } else { @@ -1942,12 +1956,43 @@ 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 (lfsr_tag_ismk(tag)) { - LFS_ASSERT(rbyd_->weight < 0xffff); - LFS_ASSERT(id <= rbyd_->weight); } else { LFS_ASSERT(id < rbyd_->weight); } + // figure out the range of tags to replace + lfsr_tag_t lower_tag_; + lfsr_sid_t lower_id_; + lfsr_tag_t upper_tag_; + lfsr_sid_t upper_id_; + if (lfsr_tag_ismk(tag)) { + LFS_ASSERT(rbyd_->weight < 0xffff); + LFS_ASSERT(id <= rbyd_->weight); + lower_tag_ = 0; + lower_id_ = id; + upper_tag_ = lower_tag_; + upper_id_ = lower_id_; + } else if (tag == LFSR_TAG_RM) { + LFS_ASSERT(rbyd_->weight > 0); + LFS_ASSERT(id < rbyd_->weight); + lower_tag_ = 0; + lower_id_ = id; + upper_tag_ = 0; + upper_id_ = lower_id_ + 1; + } else if (lfsr_tag_isrm(tag)) { + LFS_ASSERT(id < rbyd_->weight); + lower_tag_ = tag & ~0x2; + lower_id_ = id; + upper_tag_ = lower_tag_ + 0x10; + upper_id_ = lower_id_; + } else { + LFS_ASSERT(id < rbyd_->weight); + lower_tag_ = tag; + lower_id_ = id; + upper_tag_ = lower_tag_; + upper_id_ = lower_id_; + } + // assume we'll update our trunk lfs_off_t lower_branch = rbyd_->trunk; lfs_off_t upper_branch = lower_branch; @@ -1958,32 +2003,6 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, goto leaf; } - // figure out the range of tags to replace - lfsr_tag_t lower_tag_; - lfsr_sid_t lower_id_; - lfsr_tag_t upper_tag_; - lfsr_sid_t upper_id_; - if (lfsr_tag_ismk(tag)) { - lower_tag_ = 0; - lower_id_ = id; - upper_tag_ = lower_tag_; - upper_id_ = lower_id_; -// } else if (lfsr_tag_suptype(tag) == LFSR_TAG_RM) { -// LFS_ASSERT(rbyd_->count > 0); -// lower_tag_ = tag & ~0x7fff; -// upper_tag_ = lower_tag_ + 0x8000; -// } else if (lfsr_tag_isrm(tag)) { -// lower_tag_ = tag & ~0x2; -// lower_id_ = id; -// upper_tag_ = lower_tag_ + 0x10; -// upper_id_ = lower_id_; - } else { - lower_tag_ = tag; - lower_id_ = id; - upper_tag_ = lower_tag_; - upper_id_ = lower_id_; - } - // keep track of bounds as we descend down the tree // // this gets a bit confusing as we also may need to keep @@ -2052,7 +2071,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, upper_lower_id = lower_lower_id; upper_upper_id = lower_upper_id; upper_lower_tag = lower_lower_tag; - upper_upper_tag = lower_lower_tag; + upper_upper_tag = lower_upper_tag; } // prune? @@ -2066,16 +2085,50 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, // | | .----'| | .----'| | // 1 2 3 4 4 1 2 3 4 4 2 bool prune = false; - printf("0x%x %d (0x%x %d, 0x%x %d)\n", alt, weight_, lower_lower_tag, lower_lower_id, lower_upper_tag, lower_upper_id); - if (weight_ >= (lfs_size_t)(lower_upper_id-lower_lower_id) - // TODO does this work if id weight > 1? - || ((lfs_size_t)(lower_upper_id-lower_lower_id) == 1 + printf("%c 0x%x w%d 0x%x (0x%x %d, 0x%x %d) f=%d (0x%x %d)\n", + !diverged + ? '=' + : (lower_id_ < upper_id_ + || (lower_id_ == upper_id_ + && lower_tag_ < upper_tag_)) + ? '[' + : ']', + alt, weight_, jump, lower_lower_tag, lower_lower_id, lower_upper_tag, lower_upper_id, + lfsr_tag_follow(alt, weight_, + lower_lower_id, lower_upper_id, + lower_tag_, lower_id_), + lower_tag_, lower_id_); + LFS_ASSERT(lower_upper_id - lower_lower_id >= 1); + // TODO something better than this + lfsr_tag_t predicted_lower_tag = lower_lower_tag; + lfsr_tag_t predicted_upper_tag = lower_upper_tag; +// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) { +// // TODO need this? +// lfsr_tag_trimtag(p_alts[0], +// lower_lower_id, lower_upper_id, +// &predicted_lower_tag, &predicted_upper_tag, +// lower_id_); +// } + // TODO can this be rewritten in terms of lfsr_tag_follow? + if (weight_ > (lfs_size_t)(lower_upper_id-lower_lower_id-1) + || (weight_ == (lfs_size_t)(lower_upper_id-lower_lower_id-1) // TODO need key? - && (lfsr_tag_key(alt) - < lfsr_tag_key(lower_lower_tag) - || lfsr_tag_key(alt) - >= lfsr_tag_key(lower_upper_tag)))) { + && (lfsr_tag_isgt(alt) + ? lfsr_tag_key(predicted_lower_tag) > lfsr_tag_key(alt) + : lfsr_tag_key(predicted_upper_tag-0x10) <= lfsr_tag_key(alt)))) { +// && (lfsr_tag_key(alt) +// < lfsr_tag_key(lower_lower_tag) +// || lfsr_tag_key(alt) +// >= lfsr_tag_key(lower_upper_tag)))) { + printf("PRUUUUUUUUUUUUUUUUUUUUUUUNE\n"); + printf("%d > %d-%d-1 => %d\n", weight_, lower_upper_id, lower_lower_id, weight_ > (lfs_size_t)(lower_upper_id-lower_lower_id-1)); + printf("%d == %d-%d-1 => %d\n", weight_, lower_upper_id, lower_lower_id, weight_ == (lfs_size_t)(lower_upper_id-lower_lower_id-1)); + printf("isgt = %d\n", lfsr_tag_isgt(alt)); + printf("0x%x > 0x%x => %d\n", lfsr_tag_key(predicted_lower_tag), lfsr_tag_key(alt), lfsr_tag_key(predicted_lower_tag) > lfsr_tag_key(alt)); + printf("0x%x <= 0x%x => %d\n", lfsr_tag_key(predicted_upper_tag-0x10), lfsr_tag_key(alt), lfsr_tag_key(predicted_upper_tag-0x10) <= lfsr_tag_key(alt)); prune = true; +// lower_lower_tag = predicted_lower_tag; +// lower_upper_tag = predicted_upper_tag; // cut while following } else if (diverged && lfsr_tag_follow(alt, weight_, @@ -2086,9 +2139,15 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, == lfsr_tag_isle(alt)) { lfsr_tag_trimweight( lfsr_tag_flipalt(alt), - lfsr_tag_flipweight(alt, weight_, + lfsr_tag_flipweight(weight_, lower_lower_id, lower_upper_id), &lower_lower_id, &lower_upper_id); + lfsr_tag_trimtag( + lfsr_tag_flipalt(alt), + lower_lower_id, lower_upper_id, + &lower_lower_tag, &lower_upper_tag, + lower_id_); + printf("trim-f\n"); prune = true; // cut while not following } else if (diverged @@ -2100,12 +2159,27 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, != lfsr_tag_isle(alt)) { lfsr_tag_trimweight(alt, weight_, &lower_lower_id, &lower_upper_id); + lfsr_tag_trimtag(alt, + lower_lower_id, lower_upper_id, + &lower_lower_tag, &lower_upper_tag, + lower_id_); lfs_swap32(&jump, &branch_); + printf("trim-nf\n"); prune = true; } +// if (diverged +// && lfsr_tag_follow(alt, weight_, +// lower_lower_id, lower_upper_id, +// lower_tag_, lower_id_)) { +// printf("? %d || %d == %d\n", +// (lower_id_ < upper_id_), +// (lower_id_ == upper_id_ && lower_tag_ < upper_tag_), +// lfsr_tag_isle(alt)); +// printf("(0x%x %d) (0x%x %d)\n", lower_tag_, lower_id_, upper_tag_, upper_id_); +// } + if (prune) { - printf("PRUUUUUUUUUUUUUUUUUUUUUUUNE\n"); if (p_alts[0] && lfsr_tag_isred(p_alts[0])) { alt = lfsr_tag_mkblack(p_alts[0]); weight_ = p_weights[0]; @@ -2145,7 +2219,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lfs_swap32(&jump, &p_jumps[0]); p_alts[0] = lfsr_tag_mkblack( lfsr_tag_flipalt(p_alts[0])); - p_weights[0] = lfsr_tag_flipweight(p_alts[0], p_weights[0], + p_weights[0] = lfsr_tag_flipweight(p_weights[0], lower_lower_id, lower_upper_id); alt = lfsr_tag_mkblack(alt); @@ -2194,7 +2268,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lower_lower_id, lower_upper_id, lower_tag_, lower_id_)) { alt = lfsr_tag_flipalt(alt); - weight_ = lfsr_tag_flipweight(alt, weight_, + weight_ = lfsr_tag_flipweight(weight_, lower_lower_id, lower_upper_id); lfs_swap32(&jump, &branch_); } @@ -2213,6 +2287,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, LFS_ASSERT(lfsr_tag_isblack(alt)); lfs_swap16(&alt, &p_alts[0]); + LFS_ASSERT(weight_ >= 0); lfs_swap32(&weight_, &p_weights[0]); lfs_swap32(&jump, &p_jumps[0]); p_alts[0] = lfsr_tag_mkred(p_alts[0]); @@ -2223,13 +2298,17 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lfsr_tag_trimweight(p_alts[0], p_weights[0], &lower_lower_id, &lower_upper_id); + printf("whh? 0x%x w%d 0x%x\n", alt, weight_, jump); + printf("www? (0x%x w%d, 0x%x w%d)\n", 0, lower_lower_id, 0, upper_upper_id); alt = lfsr_tag_flipalt(alt); - weight_ = lfsr_tag_flipweight(alt, weight_, + weight_ = lfsr_tag_flipweight(weight_, lower_lower_id, lower_upper_id); lfs_swap32(&jump, &branch_); + printf("waa? 0x%x w%d 0x%x\n", alt, weight_, jump); } // push alt onto queue + LFS_ASSERT((lfs_ssize_t)weight_ >= 0); int err = lfsr_rbyd_p_push(lfs, rbyd_, p_alts, p_weights, p_jumps, alt, weight_, jump); @@ -2303,50 +2382,74 @@ stem:; lfs_size_t weight = 0; lfs_off_t jump = 0; + printf("! (0x%x %d) < (0x%x %d) < (0x%x %d)\n", lower_tag_, lower_id_, tag, id, upper_tag_, upper_id_); + if (lfsr_tag_isrm(lower_tag_)) { // no split needed, prune the removed 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 = lower_id_ - lower_lower_id; - jump = lower_branch; + && lfsr_tag_key(lower_tag_) < lfsr_tag_key(tag)) + || (lower_id_ == id + && tag == LFSR_TAG_RM + && !(upper_id_ > id))) { + // TODO ugh, separate RM condition? + if (tag == LFSR_TAG_RM) { + alt = LFSR_TAG_ALT(B, LE, 0xffff); // TODO hm, was trying to avoid this + weight = lower_id_ - lower_lower_id - 1; + jump = lower_branch; + } else { + // 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 = lower_id_ - lower_lower_id; + jump = lower_branch; + } } else if (lfsr_tag_ismk(tag)) { if (upper_id_ >= id) { + // increase weight when creating alt = LFSR_TAG_ALT(B, GT, tag); - weight = (upper_upper_id-1 - id) + 1; + weight = upper_upper_id - id - 1 + 1; jump = upper_branch; } -// } else if (lfsr_tag_suptype(tag) == LFSR_TAG_RM) { + } else if (tag == LFSR_TAG_RM) { // if (lfsr_tag_weight(upper_tag_) // >= lfsr_tag_weight(tag & ~0x7fff)+0x8000) { -// // decrease biased weight when deleting -// alt = LFSR_ALT(B, GT, -// upper_upper-0x8000 - lower_lower); -// jump = upper_branch; -// } -// -// } else if (lfsr_tag_isrm(tag)) { + if (upper_id_ > id) { + // decrease weight when deleting + alt = LFSR_TAG_ALT(B, GT, 0); + printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH\n"); + weight = upper_upper_id - lower_lower_id - 1; + printf("%d = %d - %d - 1\n", weight, upper_upper_id, lower_lower_id); + jump = upper_branch; + } + + } else if (lfsr_tag_isrm(tag)) { + if (upper_id_ > id + || (upper_id_ == id + && lfsr_tag_key(upper_tag_) > lfsr_tag_key(tag))) { + // hide our tag during removes + alt = LFSR_TAG_ALT(B, GT, 0); + weight = upper_upper_id - lower_lower_id; + jump = upper_branch; + } // if (lfsr_tag_weight(upper_tag_) > lfsr_tag_weight(tag)) { // // hide our tag during removes // alt = LFSR_ALT(B, GT, // upper_upper - lower_lower); // jump = upper_branch; // } -// + } 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 = upper_upper_id-1 - id; + weight = upper_upper_id - id - 1; jump = upper_branch; } @@ -2393,6 +2496,8 @@ leaf:; // if (lfsr_tag_ismk(tag)) { rbyd_->weight += 1; + } else if (tag == LFSR_TAG_RM) { + rbyd_->weight -= 1; } return 0; diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index 5898c50b..b7a1cd2c 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -57,15 +57,21 @@ def tagrepr(tag, id, size, off=None): 'branch' if ((tag & 0x3f0) >> 4) == 0x00 else 'reg' if ((tag & 0x3f0) >> 4) == 0x01 else 'dir' if ((tag & 0x3f0) >> 4) == 0x02 - else ' 0x%02x' % subtype, + else ' 0x%02x' % ((tag & 0x3f0) >> 4), id, size) + elif (tag & ~0x3f0) == 0x0402: + return 'rm%s id%d%s' % ( + ' 0x%02x' % ((tag & 0x3f0) >> 4) + if ((tag & 0x3f0) >> 4) else '', + id, + ' %d' % size if size else '') elif (tag & ~0xff2) == 0x2000: return '%suattr 0x%02x%s%s' % ( - 'rm' if tag & 0x1 else '', + 'rm' if tag & 0x2 else '', (tag & 0xff0) >> 4, ' id%d' % id if id != -1 else '', - ' %d' % size if not tag & 0x1 else '') + ' %d' % size if not tag & 0x2 or size else '') elif (tag & ~0x10) == 0x24: return 'crc%x%s %d' % ( 1 if tag & 0x10 else 0, @@ -154,66 +160,64 @@ def show_log(block_size, data, rev, off, *, # preprocess lifetimes if args.get('lifetimes'): - count = 0 - max_count = 0 + weight = 0 + max_weight = 0 lifetimes = {} ids = [] ids_i = 0 j_ = 4 while j_ < (block_size if args.get('all') else off): j = j_ - v, tag, size, delta = fromtag(data[j_:]) + v, tag, id, size, delta = fromtag(data[j_:]) j_ += delta - if not tag & 0x4: + if not tag & 0x8: j_ += size - if (tag & 0x7807) == 0x0800: - count += 1 - max_count = max(max_count, count) - ids.insert(((tag >> 15) & 0xffff)-1, - COLORS[ids_i % len(COLORS)]) + if (tag & ~0x3f0) == 0x0400: + weight += 1 + max_weight = max(max_weight, weight) + ids.insert(id, COLORS[ids_i % len(COLORS)]) ids_i += 1 lifetimes[j] = ( ''.join( '%s%s%s' % ( - '\x1b[%sm' % ids[id] if color else '', - '.' if id == ((tag >> 15) & 0xffff)-1 - else '\ ' if id > ((tag >> 15) & 0xffff)-1 + '\x1b[%sm' % ids[id_] if color else '', + '.' if id_ == id + else '\ ' if id_ > id else '| ', '\x1b[m' if color else '') - for id in range(count)) + for id_ in range(weight)) + ' ', - count) - elif ((tag & 0x7807) == 0x0801 - and ((tag >> 15) & 0xffff)-1 < len(ids)): + weight) + elif ((tag & ~0x3f0) == 0x0402 and id < len(ids)): lifetimes[j] = ( ''.join( '%s%s%s' % ( - '\x1b[%sm' % ids[id] if color else '', - '\'' if id == ((tag >> 15) & 0xffff)-1 - else '/ ' if id > ((tag >> 15) & 0xffff)-1 + '\x1b[%sm' % ids[id_] if color else '', + '\'' if id_ == id + else '/ ' if id_ > id else '| ', '\x1b[m' if color else '') - for id in range(count)) + for id_ in range(weight)) + ' ', - count) - count -= 1 - ids.pop(((tag >> 15) & 0xffff)-1) + weight) + weight -= 1 + ids.pop(id) else: lifetimes[j] = ( ''.join( '%s%s%s' % ( - '\x1b[%sm' % ids[id] if color else '', - '* ' if not tag & 0x4 - and id == ((tag >> 15) & 0xffff)-1 + '\x1b[%sm' % ids[id_] if color else '', + '* ' if not tag & 0x8 + and id_ == id else '| ', '\x1b[m' if color else '') - for id in range(count)), - count) + for id_ in range(weight)), + weight) def lifetimerepr(j): - lifetime, count = lifetimes.get(j, ('', 0)) - return '%s%*s' % (lifetime, 2*(max_count-count), '') + lifetime, weight = lifetimes.get(j, ('', 0)) + return '%s%*s' % (lifetime, 2*(max_weight-weight), '') # prepare other things if args.get('rbyd'): @@ -254,13 +258,13 @@ def show_log(block_size, data, rev, off, *, notes.append('crc!=%08x' % crc) j_ += size -# # adjust count? +# # adjust weight? # if args.get('lifetimes'): -# if (tag & 0x7807) == 0x0800: -# count += 1 -# elif ((tag & 0x7807) == 0x0801 +# if (tag & ~0x3f0) == 0x0400: +# weight += 1 +# elif ((tag & ~0x3f0) == 0x0402 # and ((tag >> 15) & 0xffff)-1 < len(ids)): -# count -= 1 +# weight -= 1 # show human-readable tag representation print('%s%08x:%s %s%s%-57s%s%s' % ( @@ -336,7 +340,7 @@ def show_log(block_size, data, rev, off, *, alts = [] -def show_tree(block_size, data, rev, trunk, count, *, +def show_tree(block_size, data, rev, trunk, weight, *, color=False, **args): if trunk is None: @@ -346,7 +350,7 @@ def show_tree(block_size, data, rev, trunk, count, *, # purposes def lookup(tag): lower = 0 - upper = (count+1) << 15 + upper = (weight+1) << 15 path = [] # descend down tree @@ -578,8 +582,8 @@ def main(disk, block_size, block1, block2=None, *, j_ = 4 trunk = None trunk_ = None - count = 0 - count_ = 0 + weight = 0 + weight_ = 0 wastrunk = False while j_ < block_size: v, tag, id, size, delta = fromtag(data[j_:]) @@ -595,11 +599,11 @@ def main(disk, block_size, block1, block2=None, *, if not tag & 0x8: if (tag & ~0x10) != 0x24: crc = crc32c(data[j_:j_+size], crc) -# # keep track of id count -# if (tag & 0x7807) == 0x0800: -# count_ += 1 -# elif (tag & 0x7807) == 0x0801: -# count_ = max(count_ - 1, 0) + # keep track of weight + if (tag & ~0x3f0) == 0x0400: + weight_ += 1 + elif (tag & ~0x3f0) == 0x0402: + weight_ = max(weight_ - 1, 0) # found a crc? else: crc_, = struct.unpack(' 1 else '')) if args.get('log'): @@ -643,7 +648,7 @@ def main(disk, block_size, block1, block2=None, *, color=color, **args) else: - show_tree(block_size, data, rev, trunk, count, + show_tree(block_size, data, rev, trunk, weight, color=color, **args) diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index bb745694..2f170b29 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -2110,15 +2110,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -2209,15 +2209,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -2655,808 +2655,1006 @@ code = ''' ''' -#### Removal testing ### -# -#[cases.test_rbyd_remove] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // add and remove one attribute -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &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, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // commit with two attributes, remove the first one -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &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, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // commit with two attributes, remove the second one -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0, NULL)) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(1), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &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, &off, &size) -# => LFSR_TAG_UATTR(1), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_remove_permutations] -#defines.N = 'range(1, 7)' -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= N+1' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // keep track of the worst case log size -# lfs_size_t worst_size = 0; -# -# // test all permutations of a given size -# uint8_t perm[N]; -# unsigned stack[N]; -# for (uint8_t i = 0; i < N; i++) { -# perm[i] = i; -# stack[i] = 0; -# } -# -# unsigned i = 1; -# while (i < N) { -# // print permutation to help debugging -# printf("--- permutation: ["); -# for (unsigned j = 0; j < N; j++) { -# if (j > 0) { -# printf(", "); -# } -# printf("%d", perm[j]+1); -# } -# printf("] ---\n"); -# -# // create given permutation with multiple commits -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, -# NULL)) => 0; -# } -# -# // copy block so we can reset after each remove -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# -# // try removing each tag -# for (unsigned j = 0; j < N; j++) { -# // print what we are removing to help debugging -# printf("--- remove: %d ---\n", j+1); -# -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, j+1, -1, NULL, 0, NULL)) => 0; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, -# cfg->block_size, NULL) => 0; -# for (unsigned k = 0; k < N; k++) { -# lfsr_stag_t tag = lfsr_rbyd_lookup(&lfs, &rbyd, -# LFSR_TAG_UATTR(k+1), -1, &off, &size); -# if (k == j) { -# if (j == N-1) { -# assert(tag == LFS_ERR_NOENT); -# } else { -# assert(tag == LFSR_TAG_UATTR(j+1+1), -1); -# } -# } else { -# assert(tag == LFSR_TAG_UATTR(k+1), -1); -# } -# } -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# } -# -# // next permutation using Heap's algorithm -# if (stack[i] < i) { -# if (i % 2 == 0) { -# uint8_t t = perm[0]; -# perm[0] = perm[i]; -# perm[i] = t; -# } else { -# uint8_t t = perm[stack[i]]; -# perm[stack[i]] = perm[i]; -# perm[i] = t; -# } -# stack[i] += 1; -# i = 1; -# } else { -# stack[i] = 0; -# i += 1; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes -# lfs_size_t n = 1 + N + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); -# } -#''' -# -#[cases.test_rbyd_remove_missing] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 4' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // create a tree two attributes -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, -# NULL))) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // try to remove tags that aren't there, this should do nothing -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // one last fetch to make sure nothing was broken -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_remove_again] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 8' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // create a tree -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, -# LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4, -# NULL)))))) => 0; -# // remove several attributes -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, -# LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, -# LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)))) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // try to remove tags that aren't there, this should do nothing -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // try to remove the tags again, just to make sure (keep in mind -# // these removes still commit to the rbyd) -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 3, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 5, -1, NULL, 0, NULL)) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // one last fetch to make sure nothing was broken -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFSR_TAG_UATTR(2), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, &off, &size) -# => LFSR_TAG_UATTR(4), -1; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, &off, &size) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_remove_all] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // commit with one attribute, remove it -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL)) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &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, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // commit with two attributes, remove both -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, -# LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0, NULL))) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &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, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -# -# // commit with two attributes, remove both in the other order -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, 2, -1, NULL, 0, -# LFSR_ATTR2(RMUATTR, 1, -1, NULL, 0, NULL))) => 0; -# -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &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, &off, &size) -# => LFS_ERR_NOENT; -# lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, &off, &size) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_remove_all_permutations] -#defines.N = 'range(1, 7)' -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2*N+1' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // keep track of the worst case log size -# lfs_size_t worst_size = 0; -# -# // create one consistent block -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa", 4, -# NULL)) => 0; -# } -# -# // copy block so we can reset after each remove -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# -# // test all permutations of a given size -# uint8_t perm[N]; -# unsigned stack[N]; -# for (uint8_t i = 0; i < N; i++) { -# perm[i] = i; -# stack[i] = 0; -# } -# -# unsigned i = 1; -# while (i < N) { -# // print permutation to help debugging -# printf("--- permutation: ["); -# for (unsigned j = 0; j < N; j++) { -# if (j > 0) { -# printf(", "); -# } -# printf("%d", perm[j]+1); -# } -# printf("] ---\n"); -# -# // restore backup -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# // remove each tag in permutation order -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, perm[j]+1, -1, NULL, 0, NULL)) => 0; -# } -# -# // check that all tags are now removed -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_lookup(&lfs, &rbyd, -# LFSR_TAG_UATTR(j+1), -1, &off, &size) -# => LFS_ERR_NOENT; -# } -# -# // try resuming from all tags being removed -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, -# NULL)) => 0; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# lfsr_rbyd_lookup(&lfs, &rbyd, -# LFSR_TAG_UATTR(1), -1, &off, &size) -# => LFSR_TAG_UATTR(1), -1; -# for (unsigned j = 1; j < N; j++) { -# lfsr_rbyd_lookup(&lfs, &rbyd, -# LFSR_TAG_UATTR(j+1), -1, &off, &size) -# => LFS_ERR_NOENT; -# } -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# -# // next permutation using Heap's algorithm -# if (stack[i] < i) { -# if (i % 2 == 0) { -# uint8_t t = perm[0]; -# perm[0] = perm[i]; -# perm[i] = t; -# } else { -# uint8_t t = perm[stack[i]]; -# perm[stack[i]] = perm[i]; -# perm[i] = t; -# } -# stack[i] += 1; -# i = 1; -# } else { -# stack[i] = 0; -# i += 1; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes -# lfs_size_t n = 1 + N + N + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); -# } -#''' -# -#[cases.test_rbyd_remove_append_permutations] -#defines.N = 'range(1, 6)' -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= N+2' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# lfs_off_t off; -# lfs_size_t size; -# -# // keep track of the worst case log size -# lfs_size_t worst_size = 0; -# -# // test all permutations of a given size -# uint8_t perm[N]; -# unsigned stack[N]; -# for (uint8_t i = 0; i < N; i++) { -# perm[i] = i; -# stack[i] = 0; -# } -# -# unsigned i = 1; -# while (i < N) { -# // print permutation to help debugging -# printf("--- permutation: ["); -# for (unsigned j = 0; j < N; j++) { -# if (j > 0) { -# printf(", "); -# } -# printf("%d", perm[j]+1); -# } -# printf("] ---\n"); -# -# // create given permutation with multiple commits -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, -# NULL)) => 0; -# } -# -# // copy block so we can reset after each remove -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# -# // try removing each tag -# for (unsigned j = 0; j < N; j++) { -# for (unsigned l = 0; l < N; l++) { -# // print what we are removing to help debugging -# printf("--- remove: %d, append: %d ---\n", j+1, l+1); -# -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# // remove -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(RMUATTR, j+1, -1, NULL, 0, NULL)) => 0; -# -# // try appending each tag to make sure the rbyd tree -# // is still usable -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(l+1), -1, -# "\xaa\xaa\xaa\xaa\xaa\xaa", 6, -# NULL)) => 0; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, -# cfg->block_size, NULL) => 0; -# for (unsigned k = 0; k < N; k++) { -# lfsr_stag_t tag = lfsr_rbyd_lookup(&lfs, &rbyd, -# LFSR_TAG_UATTR(k+1), -1, &off, &size); -# if (k == l) { -# assert(tag == LFSR_TAG_UATTR(l+1), -1); -# assert(size == 6); -# } else if (k == j) { -# if (j == N-1) { -# assert(tag == LFS_ERR_NOENT); -# } else { -# assert(tag == LFSR_TAG_UATTR(j+1+1), -1); -# } -# } else { -# assert(tag == LFSR_TAG_UATTR(k+1), -1); -# assert(size == 4); -# } -# } -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# } -# } -# -# // next permutation using Heap's algorithm -# if (stack[i] < i) { -# if (i % 2 == 0) { -# uint8_t t = perm[0]; -# perm[0] = perm[i]; -# perm[i] = t; -# } else { -# uint8_t t = perm[stack[i]]; -# perm[stack[i]] = perm[i]; -# perm[i] = t; -# } -# stack[i] += 1; -# i = 1; -# } else { -# stack[i] = 0; -# i += 1; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes -# lfs_size_t n = 1 + N + 1 + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); -# } -#''' +### Removal testing ### + +[cases.test_rbyd_remove] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // add and remove one attribute + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -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_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // commit with two attributes, remove the first one + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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(2)); + 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; + + // commit with two attributes, remove the second one + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(2), -1, NULL, 0, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &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_) => LFS_ERR_NOENT; + 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_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(3), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_remove_permutations] +defines.N = 'range(1, 7)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N+1' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, + NULL)) => 0; + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // try removing each tag + for (unsigned j = 0; j < N; j++) { + // print what we are removing to help debugging + printf("--- remove: %d ---\n", j+1); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + for (unsigned k = 0; k < N; k++) { + int err = lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_UATTR(k+1), -1, + &tag_, &id_, &off_, &size_); + assert(!err || err == LFS_ERR_NOENT); + if (k == j) { + if (j == N-1) { + assert(err == LFS_ERR_NOENT); + } else { + assert(tag_ == LFSR_TAG_UATTR(j+1+1)); + assert(id_ == -1); + assert(size_ == 4); + } + } else { + assert(tag_ == LFSR_TAG_UATTR(k+1)); + assert(id_ == -1); + assert(size_ == 4); + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + } + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_remove_missing] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 4' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // create a tree two attributes + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, + NULL))) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // try to remove tags that aren't there, this should do nothing + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // one last fetch to make sure nothing was broken + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_remove_again] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 8' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // create a tree + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(3), -1, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(4), -1, "\xdd\xdd\xdd\xdd", 4, + LFSR_ATTR(UATTR(5), -1, "\xee\xee\xee\xee", 4, + NULL)))))) => 0; + // remove several attributes + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, + LFSR_ATTR(RMUATTR(3), -1, NULL, 0, + LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // try to remove tags that aren't there, this should do nothing + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // try to remove the tags again, just to make sure (keep in mind + // these removes still commit to the rbyd) + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(3), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(5), -1, NULL, 0, NULL)) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // one last fetch to make sure nothing was broken + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(2)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &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_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(4), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(4)); + assert(id_ == -1); + assert(size_ == 4); + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(5), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_remove_all] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // commit with one attribute, remove it + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL)) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -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_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // commit with two attributes, remove both + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, + LFSR_ATTR(RMUATTR(2), -1, NULL, 0, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -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_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + + // commit with two attributes, remove both in the other order + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(2), -1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(2), -1, NULL, 0, + LFSR_ATTR(RMUATTR(1), -1, NULL, 0, NULL))) => 0; + + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -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_) => LFS_ERR_NOENT; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(2), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_remove_all_permutations] +defines.N = 'range(1, 7)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2*N+1' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // create one consistent block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa", 4, + NULL)) => 0; + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // restore backup + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // remove each tag in permutation order + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(perm[j]+1), -1, NULL, 0, NULL)) => 0; + } + + // check that all tags are now removed + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + } + + // try resuming from all tags being removed + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(1)); + assert(id_ == -1); + assert(size_ == 6); + for (unsigned j = 1; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1, + &tag_, &id_, &off_, &size_) => LFS_ERR_NOENT; + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + N + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_remove_append_permutations] +defines.N = 'range(1, 6)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N+2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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_; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, + NULL)) => 0; + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // try removing each tag + for (unsigned j = 0; j < N; j++) { + for (unsigned l = 0; l < N; l++) { + // print what we are removing to help debugging + printf("--- remove: %d, append: %d ---\n", j+1, l+1); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // remove + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0; + + // try appending each tag to make sure the rbyd tree + // is still usable + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(l+1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + for (unsigned k = 0; k < N; k++) { + int err = lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_UATTR(k+1), -1, + &tag_, &id_, &off_, &size_); + assert(!err || err == LFS_ERR_NOENT); + if (k == l) { + assert(tag_ == LFSR_TAG_UATTR(l+1)); + assert(id_ == -1); + assert(size_ == 6); + } else if (k == j) { + if (j == N-1) { + assert(err == LFS_ERR_NOENT); + } else { + assert(tag_ == LFSR_TAG_UATTR(j+1+1)); + assert(id_ == -1); + assert(size_ == 4 || size_ == 6); + } + } else { + assert(tag_ == LFSR_TAG_UATTR(k+1)); + assert(id_ == -1); + assert(size_ == 4); + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + } + } + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + 1 + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' ### Insertion testing ### @@ -3872,15 +4070,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -3983,15 +4181,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -5119,15 +5317,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -5241,15 +5439,15 @@ code = ''' } // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); + worst_size, n, 12*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)); + worst_size / n, n, 12*(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)); + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); } ''' @@ -5854,718 +6052,682 @@ code = ''' ''' -#### Deletion testing ### -# -#[cases.test_rbyd_delete] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# uint8_t buffer[4]; -# -# // try to delete one id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the other id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the largest of three -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the smallest of three -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the middle -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_delete_range] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .rev = 1, -# .off = 0, -# .crc = 0, -# .trunk = 0, -# .weight = 0, -# .erased = true, -# }; -# lfsr_rbyd_t rbyd; -# uint8_t buffer[4]; -# -# // try to delete one id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the other id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the largest of three -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 2, NULL, 0, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the smallest of three -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xcc\xcc", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 2); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xbb\xbb", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xcc\xcc", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -# -# // try to delete the middle -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xcc\xcc", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, 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_TAG2(UATTR, 1, 0), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xaa\xaa", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) -# => 4; -# assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 1), buffer, 4) -# => 2; -# assert(memcmp(buffer, "\xcc\xcc", 2) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG2(UATTR, 1, 2), buffer, 4) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_delete_permutations] -#defines.N = 'range(1, 7)' -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= N+1' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .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"); -# -# // create given permutation with multiple commits -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# // adjust id based on future insertions -# uint16_t id = perm[j]; -# for (unsigned k = j+1; k < N; k++) { -# if (perm[j] > perm[k]) { -# id -= 1; -# } -# } -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, -# NULL)) => 0; -# } -# assert(rbyd.weight == N); -# -# // copy block so we can reset after each delete -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# -# // try deleting each id -# for (unsigned j = 0; j < N; j++) { -# // print what we are deleting to help debugging -# printf("--- delete: %d ---\n", j+1); -# -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0; -# assert(rbyd.weight == N-1); -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, -# cfg->block_size, NULL) => 0; -# for (unsigned k = 0; k < N-1; k++) { -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, k, buffer, 4) => 4; -# if (k >= j) { -# assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); -# } else { -# assert(memcmp(buffer, names[k % 6], 4) == 0); -# } -# } -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, N-1, buffer, 4) -# => LFS_ERR_NOENT; -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# } -# -# // next permutation using Heap's algorithm -# if (stack[i] < i) { -# if (i % 2 == 0) { -# uint16_t t = perm[0]; -# perm[0] = perm[i]; -# perm[i] = t; -# } else { -# uint16_t t = perm[stack[i]]; -# perm[stack[i]] = perm[i]; -# perm[i] = t; -# } -# stack[i] += 1; -# i = 1; -# } else { -# stack[i] = 0; -# i += 1; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes -# lfs_size_t n = 1 + N + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); -# } -#''' -# -#[cases.test_rbyd_delete_range_permutations] -#defines.N = 'range(1, 7)' -#defines.M = 'range(1, 4)' -#in = 'lfs.c' -#if = ''' -# BLOCK_SIZE/PROG_SIZE >= N+N*M+1 -# && BLOCK_SIZE >= 4096 -#''' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, cfg) => 0; -# -# lfsr_rbyd_t init_rbyd = { -# .block = 0, -# .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"); -# -# // create given permutation with multiple commits -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# // adjust id based on future insertions -# uint16_t id = perm[j]; -# for (unsigned k = j+1; k < N; k++) { -# if (perm[j] > perm[k]) { -# id -= 1; -# } -# } -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, -# NULL)) => 0; -# // note uattrs have a smaller size to help debugging -# for (unsigned u = 0; u < M; u++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2, -# NULL)) => 0; -# } -# } -# assert(rbyd.weight == N); -# -# // copy block so we can reset after each delete -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# -# // try deleting each id -# for (unsigned j = 0; j < N; j++) { -# // print what we are deleting to help debugging -# printf("--- delete: %d ---\n", j+1); -# -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0; -# assert(rbyd.weight == N-1); -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, -# cfg->block_size, NULL) => 0; -# for (unsigned k = 0; k < N-1; k++) { -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, k, buffer, 4) => 4; -# if (k >= j) { -# assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); -# } else { -# assert(memcmp(buffer, names[k % 6], 4) == 0); -# } -# -# for (unsigned u = 0; u < M; u++) { -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG2(UATTR, u+1, k), buffer, 4) => 2; -# if (k >= j) { -# assert(memcmp(buffer, names[(k+1) % 6], 2) == 0); -# } else { -# assert(memcmp(buffer, names[k % 6], 2) == 0); -# } -# } -# } -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, N-1, buffer, 4) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG2(UATTR, 1, N-1), buffer, 4) -# => LFS_ERR_NOENT; -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# } -# -# // next permutation using Heap's algorithm -# if (stack[i] < i) { -# if (i % 2 == 0) { -# uint16_t t = perm[0]; -# perm[0] = perm[i]; -# perm[i] = t; -# } else { -# uint16_t t = perm[stack[i]]; -# perm[stack[i]] = perm[i]; -# perm[i] = t; -# } -# stack[i] += 1; -# i = 1; -# } else { -# stack[i] = 0; -# i += 1; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes -# lfs_size_t n = 1 + N+N*M + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 8*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 8*(2*lfs_nlog2(n)+1)); -# } -#''' -# +### Deletion testing ### + +[cases.test_rbyd_delete] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .rev = 1, + .off = 0, + .crc = 0, + .trunk = 0, + .weight = 0, + .erased = true, + }; + lfsr_rbyd_t rbyd; + uint8_t buffer[4]; + + // try to delete one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => LFS_ERR_NOENT; + + // try to delete the other id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => LFS_ERR_NOENT; + + // try to delete the largest of three + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; + + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; + + // try to delete the smallest of three + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; + + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; + + // try to delete the middle + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; + + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_delete_range] +in = 'lfs.c' +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; + uint8_t buffer[4]; + + // try to delete one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(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_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => LFS_ERR_NOENT; + + // try to delete the other id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(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_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => LFS_ERR_NOENT; + + // try to delete the largest of three + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(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; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 2, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; + + // try to delete the smallest of three + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(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; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xbb\xbb", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; + + // try to delete the middle + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(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; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 2); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => 4; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 0, buffer, 4) + => 2; + assert(memcmp(buffer, "\xaa\xaa", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 1, buffer, 4) + => 4; + assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 1, buffer, 4) + => 2; + assert(memcmp(buffer, "\xcc\xcc", 2) == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 2, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), 2, buffer, 4) + => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_delete_permutations] +defines.N = 'range(1, 7)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N+1' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + } + assert(rbyd.weight == N); + + // copy block so we can reset after each delete + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // try deleting each id + for (unsigned j = 0; j < N; j++) { + // print what we are deleting to help debugging + printf("--- delete: %d ---\n", j+1); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0; + assert(rbyd.weight == N-1); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + assert(rbyd.weight == N-1); + for (unsigned k = 0; k < N-1; k++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4; + if (k >= j) { + assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); + } else { + assert(memcmp(buffer, names[k % 6], 4) == 0); + } + } + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, N-1, buffer, 4) + => LFS_ERR_NOENT; + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + } + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_delete_range_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +if = ''' + BLOCK_SIZE/PROG_SIZE >= N+N*M+1 + && BLOCK_SIZE >= 4096 +''' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + + lfsr_rbyd_t init_rbyd = { + .block = 0, + .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"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + // adjust id based on future insertions + uint16_t id = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + id -= 1; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2, + NULL)) => 0; + } + } + assert(rbyd.weight == N); + + // copy block so we can reset after each delete + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // try deleting each id + for (unsigned j = 0; j < N; j++) { + // print what we are deleting to help debugging + printf("--- delete: %d ---\n", j+1); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, j, NULL, 0, NULL)) => 0; + assert(rbyd.weight == N-1); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + assert(rbyd.weight == N-1); + for (unsigned k = 0; k < N-1; k++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4; + if (k >= j) { + assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); + } else { + assert(memcmp(buffer, names[k % 6], 4) == 0); + } + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), k, buffer, 4) => 2; + if (k >= j) { + assert(memcmp(buffer, names[(k+1) % 6], 2) == 0); + } else { + assert(memcmp(buffer, names[k % 6], 2) == 0); + } + } + } + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, N-1, buffer, 4) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), N-1, buffer, 4) + => LFS_ERR_NOENT; + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + } + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint16_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint16_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N+N*M + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + #[cases.test_rbyd_delete_all] #in = 'lfs.c' #if = 'BLOCK_SIZE/PROG_SIZE >= 2' @@ -6706,7 +6868,7 @@ code = ''' # rbyd = init_rbyd; # lfs_bd_erase(&lfs, rbyd.block) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, +# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, # LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, # LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; @@ -6724,9 +6886,9 @@ code = ''' # rbyd = init_rbyd; # lfs_bd_erase(&lfs, rbyd.block) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, +# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, # LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, +# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, # LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, # LFSR_ATTR(RM, 0, NULL, 0, @@ -6745,9 +6907,9 @@ code = ''' # rbyd = init_rbyd; # lfs_bd_erase(&lfs, rbyd.block) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, +# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, # LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, +# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, # LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, # LFSR_ATTR(RM, 1, NULL, 0, @@ -6766,11 +6928,11 @@ code = ''' # rbyd = init_rbyd; # lfs_bd_erase(&lfs, rbyd.block) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, +# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, # LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, +# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, # LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, +# LFSR_ATTR(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, # LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, # LFSR_ATTR(RM, 0, NULL, 0, @@ -6790,11 +6952,11 @@ code = ''' # rbyd = init_rbyd; # lfs_bd_erase(&lfs, rbyd.block) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR2(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, +# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, # LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR2(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, +# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, # LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR2(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, +# LFSR_ATTR(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, # LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; # lfsr_rbyd_commit(&lfs, &rbyd, # LFSR_ATTR(RM, 2, NULL, 0, @@ -6951,15 +7113,15 @@ code = ''' # } # # // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes # lfs_size_t n = 1 + 2*N + 1; # printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); +# worst_size, n, 12*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)); +# worst_size / n, n, 12*(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)); +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); # } #''' # @@ -7090,7 +7252,7 @@ code = ''' # assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); # for (unsigned u = 0; u < M; u++) { # lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG2(UATTR, u+1, 0), buffer, 6) +# LFSR_TAG_UATTR(u+1), 0, buffer, 6) # => 3; # assert(memcmp(buffer, "\xaa\xaa\xaa", 3) == 0); # } @@ -7098,7 +7260,7 @@ code = ''' # LFSR_TAG_MKREG, 1, buffer, 6) # => LFS_ERR_NOENT; # lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG2(UATTR, 1, 1), buffer, 6) +# LFSR_TAG_UATTR(1), 1, buffer, 6) # => LFS_ERR_NOENT; # # // keep track of the worst size @@ -7124,15 +7286,15 @@ code = ''' # } # # // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes # lfs_size_t n = 1 + N+N*M + N + 1+M; # printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); +# worst_size, n, 12*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)); +# worst_size / n, n, 12*(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)); +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); # } #''' # @@ -7283,15 +7445,15 @@ code = ''' # } # # // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes # lfs_size_t n = 1 + N + 1 + 1; # printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); +# worst_size, n, 12*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)); +# worst_size / n, n, 12*(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)); +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); # } #''' # @@ -7433,7 +7595,7 @@ code = ''' # # for (unsigned u = 0; u < M; u++) { # size = lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG2(UATTR, u+1, k), buffer, 6); +# LFSR_TAG_UATTR(u+1), k, buffer, 6); # if (k == l) { # assert(size == 3); # assert(memcmp(buffer, names[l % 6], 3) == 0); @@ -7476,14 +7638,14 @@ code = ''' # } # # // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are roughly ~8 bytes +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes # lfs_size_t n = 1 + N+N*M + 1 + 1+M; # printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 8*n*(2*lfs_nlog2(n)+1)); +# worst_size, n, 12*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)); +# worst_size / n, n, 12*(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)); +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); # } #'''