diff --git a/lfs.c b/lfs.c index f890b94d..4c833523 100644 --- a/lfs.c +++ b/lfs.c @@ -662,7 +662,8 @@ enum lfsr_tag { // some in-device only tag modifiers LFSR_TAG_RM = 0x8000, LFSR_TAG_GROW = 0x4000, - LFSR_TAG_WIDE = 0x2000, + LFSR_TAG_SUPWIDE = 0x2000, + LFSR_TAG_SUBWIDE = 0x1000, // lfsr_rbyd_appendattr specific flags, also in-device only LFSR_TAG_DIVERGED = 0x4000, @@ -675,10 +676,11 @@ enum lfsr_tag { #define LFSR_TAG_TAG(tag) (tag) // some tag modifiers -#define LFSR_TAG_SHRUB(tag) (LFSR_TAG_SHRUB | LFSR_TAG_##tag) -#define LFSR_TAG_RM(tag) (LFSR_TAG_RM | LFSR_TAG_##tag) -#define LFSR_TAG_GROW(tag) (LFSR_TAG_GROW | LFSR_TAG_##tag) -#define LFSR_TAG_WIDE(tag) (LFSR_TAG_WIDE | LFSR_TAG_##tag) +#define LFSR_TAG_SHRUB(tag) (LFSR_TAG_SHRUB | LFSR_TAG_##tag) +#define LFSR_TAG_RM(tag) (LFSR_TAG_RM | LFSR_TAG_##tag) +#define LFSR_TAG_GROW(tag) (LFSR_TAG_GROW | LFSR_TAG_##tag) +#define LFSR_TAG_SUPWIDE(tag) (LFSR_TAG_SUPWIDE | LFSR_TAG_##tag) +#define LFSR_TAG_SUBWIDE(tag) (LFSR_TAG_SUBWIDE | LFSR_TAG_##tag) // some other tag encodings with their own subfields #define LFSR_TAG_ALT(d, c, key) \ @@ -746,8 +748,12 @@ static inline bool lfsr_tag_isgrow(lfsr_tag_t tag) { return tag & LFSR_TAG_GROW; } -static inline bool lfsr_tag_iswide(lfsr_tag_t tag) { - return tag & LFSR_TAG_WIDE; +static inline bool lfsr_tag_issupwide(lfsr_tag_t tag) { + return tag & LFSR_TAG_SUPWIDE; +} + +static inline bool lfsr_tag_issubwide(lfsr_tag_t tag) { + return tag & LFSR_TAG_SUBWIDE; } // lfsr_rbyd_appendattr diverged specific flags @@ -2339,7 +2345,7 @@ static int lfsr_rbyd_lookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd, return 0; } -static int lfsr_rbyd_lookupwide(lfs_t *lfs, const lfsr_rbyd_t *rbyd, +static int lfsr_rbyd_sublookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd, lfsr_srid_t rid, lfsr_tag_t tag, lfsr_tag_t *tag_, lfsr_data_t *data_) { // looking up a wide tag with subtype is probably a mistake @@ -2353,7 +2359,7 @@ static int lfsr_rbyd_lookupwide(lfs_t *lfs, const lfsr_rbyd_t *rbyd, return err; } - // the difference between lookup and lookupwide is we accept any + // the difference between lookup and sublookup is we accept any // subtype of the requested tag if (rid_ != rid || lfsr_tag_suptype(tag__) != tag) { return LFS_ERR_NOENT; @@ -2365,6 +2371,29 @@ static int lfsr_rbyd_lookupwide(lfs_t *lfs, const lfsr_rbyd_t *rbyd, return 0; } +static int lfsr_rbyd_suplookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd, + lfsr_srid_t rid, + lfsr_tag_t *tag_, lfsr_data_t *data_) { + lfsr_srid_t rid_; + lfsr_tag_t tag__; + int err = lfsr_rbyd_lookupnext(lfs, rbyd, rid, 0, + &rid_, &tag__, NULL, data_); + if (err) { + return err; + } + + // the difference between lookup and suplookup is we accept any tag + if (rid_ != rid) { + return LFS_ERR_NOENT; + } + + if (tag_) { + *tag_ = tag__; + } + return 0; +} + + // append a revision count // @@ -2530,8 +2559,6 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd, lfsr_tag_t tag_; lfsr_tag_t other_tag_; if (delta != 0 && !lfsr_tag_isgrow(tag)) { - LFS_ASSERT(!lfsr_tag_iswide(tag)); - if (delta > 0) { LFS_ASSERT(rid <= rbyd->weight); @@ -2562,7 +2589,10 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd, // note both normal and rm wide-tags have the same bounds, really it's // the normal non-wide-tags that are an outlier here - if (lfsr_tag_iswide(tag)) { + if (lfsr_tag_issupwide(tag)) { + tag_ = 0x1; + other_tag_ = tag_ + 0x800; + } else if (lfsr_tag_issubwide(tag)) { tag_ = lfsr_tag_supkey(tag); other_tag_ = tag_ + 0x100; } else if (lfsr_tag_isrm(tag) || !lfsr_tag_key(tag)) { @@ -2893,9 +2923,11 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd, && (upper_rid-1 < rid-lfs_smax32(-delta, 0) || (upper_rid-1 == rid-lfs_smax32(-delta, 0) && ((delta > 0 && !lfsr_tag_isgrow(tag)) - || ((lfsr_tag_iswide(tag)) - ? lfsr_tag_supkey(tag_) < lfsr_tag_supkey(tag) - : lfsr_tag_key(tag_) < lfsr_tag_key(tag)))))) { + || (!lfsr_tag_issupwide(tag) + && lfsr_tag_supkey(tag_) < lfsr_tag_supkey(tag)) + || (!lfsr_tag_issupwide(tag) + && !lfsr_tag_issubwide(tag) + && lfsr_tag_key(tag_) < lfsr_tag_key(tag)))))) { if (lfsr_tag_isrm(tag) || !lfsr_tag_key(tag)) { // if removed, make our tag unreachable alt = LFSR_TAG_ALT(GT, B, 0); @@ -2917,9 +2949,11 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd, && (upper_rid-1 > rid || (upper_rid-1 == rid && ((delta > 0 && !lfsr_tag_isgrow(tag)) - || ((lfsr_tag_iswide(tag)) - ? lfsr_tag_supkey(tag_) > lfsr_tag_supkey(tag) - : lfsr_tag_key(tag_) > lfsr_tag_key(tag)))))) { + || (!lfsr_tag_issupwide(tag) + && lfsr_tag_supkey(tag_) > lfsr_tag_supkey(tag)) + || (!lfsr_tag_issupwide(tag) + && !lfsr_tag_issubwide(tag) + && lfsr_tag_key(tag_) > lfsr_tag_key(tag)))))) { if (lfsr_tag_isrm(tag) || !lfsr_tag_key(tag)) { // if removed, make our tag unreachable alt = LFSR_TAG_ALT(GT, B, 0); @@ -3814,7 +3848,7 @@ static int lfsr_btree_lookupnext_(lfs_t *lfs, const lfsr_btree_t *btree, } if (lfsr_tag_suptype(tag__) == LFSR_TAG_NAME) { - err = lfsr_rbyd_lookupwide(lfs, &branch, rid__, LFSR_TAG_STRUCT, + err = lfsr_rbyd_sublookup(lfs, &branch, rid__, LFSR_TAG_STRUCT, &tag__, &data__); if (err) { LFS_ASSERT(err != LFS_ERR_NOENT); @@ -3911,7 +3945,7 @@ static int lfsr_btree_parent(lfs_t *lfs, const lfsr_btree_t *btree, } if (lfsr_tag_suptype(tag__) == LFSR_TAG_NAME) { - err = lfsr_rbyd_lookupwide(lfs, &branch, rid__, LFSR_TAG_STRUCT, + err = lfsr_rbyd_sublookup(lfs, &branch, rid__, LFSR_TAG_STRUCT, &tag__, &data__); if (err) { LFS_ASSERT(err != LFS_ERR_NOENT); @@ -4141,7 +4175,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, } if (sibling_tag == LFSR_TAG_NAME) { - err = lfsr_rbyd_lookupwide(lfs, &parent, + err = lfsr_rbyd_sublookup(lfs, &parent, sibling_rid, LFSR_TAG_STRUCT, &sibling_tag, &sibling_data); if (err) { @@ -4189,7 +4223,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, } if (sibling_tag == LFSR_TAG_NAME) { - err = lfsr_rbyd_lookupwide(lfs, &parent, + err = lfsr_rbyd_sublookup(lfs, &parent, sibling_rid, LFSR_TAG_STRUCT, &sibling_tag, &sibling_data); if (err) { @@ -4537,7 +4571,7 @@ static lfs_scmp_t lfsr_btree_namelookup(lfs_t *lfs, const lfsr_btree_t *btree, // the name may not match exactly, but indicates which branch to follow lfsr_tag_t tag__; lfsr_data_t data__; - int err = lfsr_rbyd_lookupwide(lfs, &branch, rid__, LFSR_TAG_STRUCT, + int err = lfsr_rbyd_sublookup(lfs, &branch, rid__, LFSR_TAG_STRUCT, &tag__, &data__); if (err) { LFS_ASSERT(err != LFS_ERR_NOENT); @@ -4639,7 +4673,7 @@ static int lfsr_btree_traverse_(lfs_t *lfs, const lfsr_btree_t *btree, } if (lfsr_tag_suptype(tag__) == LFSR_TAG_NAME) { - err = lfsr_rbyd_lookupwide(lfs, &btraversal->branch, + err = lfsr_rbyd_sublookup(lfs, &btraversal->branch, rid__, LFSR_TAG_STRUCT, &tag__, &data__); if (err) { @@ -5145,7 +5179,7 @@ static int lfsr_mdir_lookup(lfs_t *lfs, const lfsr_mdir_t *mdir, return 0; } -static int lfsr_mdir_lookupwide(lfs_t *lfs, const lfsr_mdir_t *mdir, +static int lfsr_mdir_sublookup(lfs_t *lfs, const lfsr_mdir_t *mdir, lfsr_smid_t mid, lfsr_tag_t tag, lfsr_tag_t *tag_, lfsr_data_t *data_) { // looking up a wide tag with subtype is probably a mistake @@ -5158,7 +5192,7 @@ static int lfsr_mdir_lookupwide(lfs_t *lfs, const lfsr_mdir_t *mdir, return err; } - // the difference between lookup and lookupwide is we accept any + // the difference between lookup and sublookup is we accept any // subtype of the requested tag if (lfsr_tag_suptype(tag__) != tag) { return LFS_ERR_NOENT; @@ -5170,6 +5204,23 @@ static int lfsr_mdir_lookupwide(lfs_t *lfs, const lfsr_mdir_t *mdir, return 0; } +static int lfsr_mdir_suplookup(lfs_t *lfs, const lfsr_mdir_t *mdir, + lfsr_smid_t mid, + lfsr_tag_t *tag_, lfsr_data_t *data_) { + lfsr_tag_t tag__; + int err = lfsr_mdir_lookupnext(lfs, mdir, mid, 0, + &tag__, data_); + if (err) { + return err; + } + + // the difference between lookup and sublookup is we accept any tag + if (tag_) { + *tag_ = tag__; + } + return 0; +} + // track opened mdirs to keep state in-sync static bool lfsr_isopened(lfs_t *lfs, const lfsr_opened_t *opened) { for (lfsr_opened_t *p = lfs->opened; p; p = p->next) { @@ -6224,7 +6275,7 @@ static int lfsr_mroot_commit_(lfs_t *lfs, uint8_t mrootchild_buf[LFSR_MPTR_DSIZE]; err = lfsr_mdir_commit__(lfs, &mrootanchor_, -1, -1, LFSR_ATTRS( LFSR_ATTR(-1, - WIDE(MROOT), 0, + SUBWIDE(MROOT), 0, FROMMPTR(lfsr_mdir_mptr(&mrootchild_), mrootchild_buf)))); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); @@ -6295,7 +6346,7 @@ static int lfsr_mtree_commit_(lfs_t *lfs, uint8_t mtree_buf[LFSR_BTREE_DSIZE]; err = lfsr_mroot_commit_(lfs, -1, 0, NULL, LFSR_ATTRS( LFSR_ATTR(-1, - WIDE(MTREE), 0, FROMBTREE(&mtree_, mtree_buf)))); + SUBWIDE(MTREE), 0, FROMBTREE(&mtree_, mtree_buf)))); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); return err; @@ -6542,7 +6593,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, // note we need to do this after playing out pending attrs in // case they introduce a new name! lfsr_data_t split_data; - err = lfsr_rbyd_lookupwide(lfs, &msibling_.rbyd, 0, LFSR_TAG_NAME, + err = lfsr_rbyd_sublookup(lfs, &msibling_.rbyd, 0, LFSR_TAG_NAME, NULL, &split_data); if (err) { LFS_ASSERT(err != LFS_ERR_NOENT); @@ -6661,7 +6712,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, uint8_t mdir_buf[LFSR_MPTR_DSIZE]; err = lfsr_mroot_commit_(lfs, -1, 0, NULL, LFSR_ATTRS( LFSR_ATTR(-1, - WIDE(MDIR), 0, + SUBWIDE(MDIR), 0, FROMMPTR(lfsr_mdir_mptr(&mdir_), mdir_buf)))); if (err) { return err; @@ -6718,12 +6769,18 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, // adjust opened mdirs? if (lfsr_mdir_cmp(&opened->mdir, mdir) == 0 && opened->mdir.mid >= attrs[i].rid) { - // removed? - if (opened->mdir.mid < attrs[i].rid - attrs[i].delta) { - // mark as zombied and move onto the next rid, upper - // layers should handle the repercussions - opened->flags |= LFS_F_ZOMBIE | LFS_F_UNSYNC | LFS_O_DESYNC; + // replaced? + if (opened->mdir.mid == attrs[i].rid - attrs[i].delta + && lfsr_tag_issupwide(attrs[i].tag)) { + opened->flags |= LFS_F_ZOMBIE + | LFS_F_UNSYNC + | LFS_O_DESYNC; opened->flags &= ~LFS_F_ORPHAN; + // removed? + } else if (opened->mdir.mid < attrs[i].rid - attrs[i].delta) { + // we should not be removing opened regular files + LFS_ASSERT(opened->type != LFS_TYPE_REG); + opened->flags |= LFS_F_ZOMBIE; opened->mdir.mid = attrs[i].rid; } else { opened->mdir.mid += attrs[i].delta; @@ -7152,7 +7209,7 @@ static int lfsr_traversal_read(lfs_t *lfs, lfsr_traversal_t *traversal, // lookup mroot, if we find one this is a fake mroot lfsr_tag_t tag; lfsr_data_t data; - err = lfsr_mdir_lookupwide(lfs, &traversal->file.mdir, + err = lfsr_mdir_sublookup(lfs, &traversal->file.mdir, -1, LFSR_TAG_STRUCT, &tag, &data); if (err) { @@ -8492,14 +8549,12 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { // commit our new directory into our parent, zeroing the grm in the // process err = lfsr_mdir_commit(lfs, &mdir, LFSR_ATTRS( - LFSR_ATTR(mdir.mid + ((exists) ? 1 : 0), - DIR, +1, CAT( + LFSR_ATTR(mdir.mid, + SUPWIDE(DIR), (!exists) ? +1 : 0, CAT( LFSR_DATA_LEB128(did), LFSR_DATA_BUF(name, name_size))), - LFSR_ATTR(mdir.mid + ((exists) ? 1 : 0), + LFSR_ATTR(mdir.mid, DID, 0, LEB128(did_)), - LFSR_ATTR_IF((exists), - mdir.mid, RM, -1, NULL()), LFSR_ATTR(-1, GRM, 0, GRM(&((lfsr_grm_t){{-1, -1}}))))); if (err) { return err; @@ -8570,7 +8625,7 @@ int lfsr_remove(lfs_t *lfs, const char *path) { if (err != LFS_ERR_NOENT) { lfsr_tag_t bookmark_tag; - err = lfsr_mdir_lookupwide(lfs, &bookmark_mdir, + err = lfsr_mdir_sublookup(lfs, &bookmark_mdir, bookmark_mdir.mid, LFSR_TAG_NAME, &bookmark_tag, NULL); if (err) { @@ -8581,16 +8636,16 @@ int lfsr_remove(lfs_t *lfs, const char *path) { return LFS_ERR_NOTEMPTY; } } - - // adjust rid if grm is on the same mdir as our dir - if (lfsr_mid_bid(lfs, grm.rms[0]) == lfsr_mid_bid(lfs, mdir.mid) - && grm.rms[0] > mdir.mid) { - grm.rms[0] -= 1; - } } // are we removing an opened file? bool zombie = lfsr_mid_isopened(lfs, mdir.mid); + // adjust grm rid if grm is on the same mdir as our dir + if (!zombie + && lfsr_mid_bid(lfs, grm.rms[0]) == lfsr_mid_bid(lfs, mdir.mid) + && grm.rms[0] > mdir.mid) { + grm.rms[0] -= 1; + } // remove the metadata entry err = lfsr_mdir_commit(lfs, &mdir, LFSR_ATTRS( @@ -8598,11 +8653,11 @@ int lfsr_remove(lfs_t *lfs, const char *path) { // // we use a create+delete here to also clear any attrs // and trim the entry size - LFSR_ATTR_IF((zombie), - mdir.mid+1, ORPHAN, +1, CAT( + (zombie) + ? LFSR_ATTR(mdir.mid, SUPWIDE(ORPHAN), 0, CAT( LFSR_DATA_LEB128(did), - LFSR_DATA_BUF(name, name_size))), - LFSR_ATTR(mdir.mid, RM, -1, NULL()), + LFSR_DATA_BUF(name, name_size))) + : LFSR_ATTR(mdir.mid, RM, -1, NULL()), LFSR_ATTR(-1, GRM, 0, GRM(&grm)))); if (err) { return err; @@ -8733,7 +8788,7 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { if (err != LFS_ERR_NOENT) { lfsr_tag_t bookmark_tag; - err = lfsr_mdir_lookupwide(lfs, &bookmark_mdir, + err = lfsr_mdir_sublookup(lfs, &bookmark_mdir, bookmark_mdir.mid, LFSR_TAG_NAME, &bookmark_tag, NULL); if (err) { @@ -8750,14 +8805,12 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { // rename our entry, copying all tags associated with the old rid to the // new rid, while also marking the old rid for removal err = lfsr_mdir_commit(lfs, &new_mdir, LFSR_ATTRS( - LFSR_ATTR(new_mdir.mid + ((exists) ? 1 : 0), - TAG(old_tag), +1, CAT( + LFSR_ATTR(new_mdir.mid, + SUPWIDE(TAG(old_tag)), (!exists) ? +1 : 0, CAT( LFSR_DATA_LEB128(new_did), LFSR_DATA_BUF(new_name, new_name_size))), - LFSR_ATTR(new_mdir.mid + ((exists) ? 1 : 0), + LFSR_ATTR(new_mdir.mid, MOVE, 0, MOVE(&old_mdir)), - LFSR_ATTR_IF((exists), - new_mdir.mid, RM, -1, NULL()), LFSR_ATTR(-1, GRM, 0, GRM(&grm)))); if (err) { return err; @@ -8959,7 +9012,7 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) { // lookup the next name tag lfsr_tag_t tag; lfsr_data_t data; - err = lfsr_mdir_lookupwide(lfs, &dir->p.mdir, + err = lfsr_mdir_sublookup(lfs, &dir->p.mdir, dir->p.mdir.mid, LFSR_TAG_NAME, &tag, &data); if (err) { @@ -9941,7 +9994,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, err = lfsr_bshrub_commit(lfs, file, LFSR_ATTRS( LFSR_ATTR(bid_, - GROW(WIDE(DATA)), + GROW(SUBWIDE(DATA)), -(weight_ - lfs->cfg->fragment_size), DATA(lfsr_data_truncate(left_slice_, lfs->cfg->fragment_size))), @@ -9968,7 +10021,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, err = lfsr_bshrub_commit(lfs, file, LFSR_ATTRS( LFSR_ATTR(bid_, - GROW(WIDE(BLOCK)), + GROW(SUBWIDE(BLOCK)), -(weight_ - lfsr_data_size(&bptr_.data)), FROMBPTR(&bptr_, buf)), LFSR_ATTR(bid_ @@ -10002,14 +10055,14 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, .cksum = bptr_.cksum, }; attrs[attr_count++] = LFSR_ATTR(bid_, - GROW(WIDE(BLOCK)), -(bid_+1 - pos), + GROW(SUBWIDE(BLOCK)), -(bid_+1 - pos), FROMBPTR(&bptr__, &buf[buf_size])); buf_size += LFSR_BPTR_DSIZE; // carve fragment? } else { attrs[attr_count++] = LFSR_ATTR(bid_, - GROW(WIDE(DATA)), -(bid_+1 - pos), + GROW(SUBWIDE(DATA)), -(bid_+1 - pos), DATA(left_slice_)); } @@ -11006,7 +11059,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { } attrs[attr_count++] = LFSR_ATTR(file->m.mdir.mid, - WIDE(REG), 0, DATA(data)); + SUBWIDE(REG), 0, DATA(data)); } // commit the file state @@ -11014,22 +11067,22 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { // null? no attr? if (lfsr_f_isunflush(file->m.flags) && file->buffer_size == 0) { attrs[attr_count++] = LFSR_ATTR(file->m.mdir.mid, - WIDE(RM(STRUCT)), 0, + SUBWIDE(RM(STRUCT)), 0, NULL()); // small file inlined in mdir? } else if (lfsr_f_isunflush(file->m.flags)) { attrs[attr_count++] = LFSR_ATTR(file->m.mdir.mid, - WIDE(DATA), 0, + SUBWIDE(DATA), 0, BUF(file->buffer, file->buffer_size)); // bshrub? } else if (lfsr_bshrub_isbshrub(&file->m.mdir, &file->bshrub)) { attrs[attr_count++] = LFSR_ATTR(file->m.mdir.mid, - WIDE(SHRUBTRUNK), 0, + SUBWIDE(SHRUBTRUNK), 0, SHRUBTRUNK(&file->bshrub_.u.bshrub)); // btree? } else if (lfsr_bshrub_isbtree(&file->m.mdir, &file->bshrub)) { attrs[attr_count++] = LFSR_ATTR(file->m.mdir.mid, - WIDE(BTREE), 0, + SUBWIDE(BTREE), 0, FROMBTREE(&file->bshrub.u.btree, &buf[buf_size])); buf_size += LFSR_BTREE_DSIZE; } else { diff --git a/tests/test_btree.toml b/tests/test_btree.toml index 487c7afc..84770dbe 100644 --- a/tests/test_btree.toml +++ b/tests/test_btree.toml @@ -50,7 +50,7 @@ code = ''' // name attributes, the name attribute holds the weight not // the struct tag return lfsr_btree_commit(lfs, btree, LFSR_ATTRS( - LFSR_ATTR(bid, WIDE(TAG(tag)), 0, DATA(data)), + LFSR_ATTR(bid, SUBWIDE(TAG(tag)), 0, DATA(data)), LFSR_ATTR(bid, GROW, weight - weight_, NULL()))); } diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index dbb54a96..43e89b83 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -11584,9 +11584,10 @@ code = ''' ''' -### Wide-tag things ### +### Supertype/subtype-wide things ### -[cases.test_rbyd_wide_lookup_permutations] +# subtype-wide +[cases.test_rbyd_subwide_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -11660,7 +11661,7 @@ code = ''' for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; - lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); @@ -11671,7 +11672,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_remove_permutations] +[cases.test_rbyd_subwide_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -11761,7 +11762,7 @@ code = ''' // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; + LFSR_ATTR(j, RM(SUBWIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; @@ -11792,10 +11793,10 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); @@ -11810,7 +11811,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_replace_permutations] +[cases.test_rbyd_subwide_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -11900,7 +11901,7 @@ code = ''' // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, + LFSR_ATTR(j, SUBWIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 3)))) => 0; // try traversing over the tags @@ -11936,7 +11937,7 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); @@ -11953,7 +11954,7 @@ code = ''' } ''' -[cases.test_rbyd_wide_mixed_lookup_permutations] +[cases.test_rbyd_subwide_mixed_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12029,7 +12030,7 @@ code = ''' for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; - lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); @@ -12040,7 +12041,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_mixed_remove_permutations] +[cases.test_rbyd_subwide_mixed_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12132,7 +12133,7 @@ code = ''' // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; + LFSR_ATTR(j, RM(SUBWIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; @@ -12170,10 +12171,10 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); @@ -12188,7 +12189,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_mixed_replace_permutations] +[cases.test_rbyd_subwide_mixed_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12280,7 +12281,7 @@ code = ''' // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, + LFSR_ATTR(j, SUBWIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 3)))) => 0; // try traversing over the tags @@ -12323,7 +12324,7 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); @@ -12340,7 +12341,7 @@ code = ''' } ''' -[cases.test_rbyd_wide_weighted_lookup_permutations] +[cases.test_rbyd_subwide_weighted_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12413,7 +12414,7 @@ code = ''' for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; - lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); @@ -12424,7 +12425,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_weighted_remove_permutations] +[cases.test_rbyd_subwide_weighted_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12513,7 +12514,7 @@ code = ''' // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; + LFSR_ATTR(j, RM(SUBWIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; @@ -12547,10 +12548,10 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 4); @@ -12565,7 +12566,7 @@ code = ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly -[cases.test_rbyd_wide_weighted_replace_permutations] +[cases.test_rbyd_subwide_weighted_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations @@ -12654,7 +12655,7 @@ code = ''' // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( - LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, + LFSR_ATTR(j, SUBWIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 6)))) => 0; // try traversing over the tags @@ -12683,7 +12684,7 @@ code = ''' // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { - lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); @@ -12699,3 +12700,374 @@ code = ''' free(backup_block); } ''' + +# supertype-wide +[cases.test_rbyd_supwide_lookup_permutations] +defines.N = 'range(1, 7)' +defines.SHIFT = [0, 3, -3] +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, CFG) => 0; + + lfsr_rbyd_t init_rbyd = { + .blocks[0] = 0, + .eoff = 0, + .cksum = 0, + .trunk = 0, + .weight = 0, + }; + 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", + }; + + // test all permutations of a given size + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < ((PERMUTATION == -1) ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = (PERMUTATION == -1) ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); + + // print permutation to help debugging + printf("--- permutation: %zd [", perm_i); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]); + } + printf("] ---\n"); + + // build the attribute list for the current permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust rid based on future insertions + uint16_t rid = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + rid -= 1; + } + } + + // give each attr a subtype based on its rid + SHIFT + lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( + LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), + LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, + BUF(names[perm[j] % 6], 2)))) => 0; + } + assert(rbyd.weight == N); + + // a supwide attr lookup only gets the file type + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; + assert(rbyd.weight == N); + + for (unsigned j = 0; j < N; j++) { + lfsr_tag_t tag_; + lfsr_data_t data_; + lfsr_rbyd_suplookup(&lfs, &rbyd, j, + &tag_, &data_) => 0; + + assert(tag_ == LFSR_TAG_REG); + assert(lfsr_data_size(&data_) == 4); + } + } +''' + +# NOTE if we separate physical/logical block sizes we may be able to +# use emubd's copy-on-write copy to speed this up significantly +[cases.test_rbyd_supwide_remove_permutations] +defines.N = 'range(1, 7)' +defines.SHIFT = [0, 3, -3] +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 +# large progs take too long for now +if = 'PROG_SIZE < 512' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, CFG) => 0; + + lfsr_rbyd_t init_rbyd = { + .blocks[0] = 0, + .eoff = 0, + .cksum = 0, + .trunk = 0, + .weight = 0, + }; + 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", + }; + + // test all permutations of a given size + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < ((PERMUTATION == -1) ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = (PERMUTATION == -1) ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); + + // print permutation to help debugging + printf("--- permutation: %zd [", perm_i); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]); + } + printf("] ---\n"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust rid based on future insertions + uint16_t rid = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + rid -= 1; + } + } + + // give each attr a subtype based on its rid + SHIFT + lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( + LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), + LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, + BUF(names[perm[j] % 6], 2)))) => 0; + } + assert(rbyd.weight == N); + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t *backup_block = malloc(rbyd.eoff); + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, + rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; + + // try removing each tag + for (unsigned j = 0; j < N; j++) { + // print what we are removing to help debugging + printf("--- remove: %d ---\n", j); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.blocks[0], 0, backup_block, rbyd.eoff, + NULL) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, + NULL) => 0; + + // remove with a wide tag + lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( + LFSR_ATTR(j, RM(SUPWIDE(UATTR)), 0, NULL()))) => 0; + + // try traversing over the tags + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; + lfsr_tag_t tag_ = 0; + lfs_ssize_t rid_ = -1; + lfs_size_t weight_; + lfsr_data_t data_; + for (unsigned k = 0; k < N; k++) { + if (k != j) { + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => 0; + assert(rid_ == k); + assert(tag_ == LFSR_TAG_REG); + assert(weight_ == ((k == j+1) ? 2 : 1)); + assert(lfsr_data_size(&data_) == 4); + + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => 0; + assert(rid_ == k); + assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); + assert(weight_ == 0); + assert(lfsr_data_size(&data_) == 2); + } + } + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; + + // also test that we can lookup each tag with a wide lookup + for (unsigned k = 0; k < N; k++) { + if (k == j) { + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, + &tag_, &data_) => LFS_ERR_NOENT; + } else { + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, + &tag_, &data_) => 0; + assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); + assert(lfsr_data_size(&data_) == 2); + } + } + } + + // cleanup + free(backup_block); + } +''' + +# NOTE if we separate physical/logical block sizes we may be able to +# use emubd's copy-on-write copy to speed this up significantly +[cases.test_rbyd_supwide_replace_permutations] +defines.N = 'range(1, 7)' +defines.SHIFT = [0, 3, -3] +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 +# large progs take too long for now +if = 'PROG_SIZE < 512' +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, CFG) => 0; + + lfsr_rbyd_t init_rbyd = { + .blocks[0] = 0, + .eoff = 0, + .cksum = 0, + .trunk = 0, + .weight = 0, + }; + lfsr_rbyd_t rbyd; + const uint8_t names[6][6] = { + "\xaa\xaa\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee\xee\xee", + "\xff\xff\xff\xff\xff\xff", + }; + + // test all permutations of a given size + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < ((PERMUTATION == -1) ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = (PERMUTATION == -1) ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); + + // print permutation to help debugging + printf("--- permutation: %zd [", perm_i); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]); + } + printf("] ---\n"); + + // create given permutation with multiple commits + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; + for (unsigned j = 0; j < N; j++) { + // adjust rid based on future insertions + uint16_t rid = perm[j]; + for (unsigned k = j+1; k < N; k++) { + if (perm[j] > perm[k]) { + rid -= 1; + } + } + + // give each attr a subtype based on its rid + SHIFT + lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( + LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), + LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, + BUF(names[perm[j] % 6], 2)))) => 0; + } + assert(rbyd.weight == N); + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t *backup_block = malloc(rbyd.eoff); + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, + rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; + + // try replacing each tag + for (unsigned j = 0; j < N; j++) { + // print what we are replacing to help debugging + printf("--- replace: %d ---\n", j); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.blocks[0], 0, backup_block, rbyd.eoff, + NULL) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, + NULL) => 0; + + // replace with bitwise inverse + lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( + LFSR_ATTR(j, SUPWIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, + BUF(names[j % 6], 3)))) => 0; + + // try traversing over the tags + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; + lfsr_tag_t tag_ = 0; + lfs_ssize_t rid_ = -1; + lfs_size_t weight_; + lfsr_data_t data_; + for (unsigned k = 0; k < N; k++) { + if (k == j) { + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => 0; + assert(rid_ == k); + assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); + assert(weight_ == 1); + assert(lfsr_data_size(&data_) == 3); + + } else { + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => 0; + assert(rid_ == k); + assert(tag_ == LFSR_TAG_REG); + assert(weight_ == 1); + assert(lfsr_data_size(&data_) == 4); + + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => 0; + assert(rid_ == k); + assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); + assert(weight_ == 0); + assert(lfsr_data_size(&data_) == 2); + } + } + lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, + &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; + + // also test that we can lookup each tag with a wide lookup + for (unsigned k = 0; k < N; k++) { + lfsr_rbyd_sublookup(&lfs, &rbyd, k, LFSR_TAG_UATTR, + &tag_, &data_) => 0; + if (k == j) { + assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); + assert(lfsr_data_size(&data_) == 3); + } else { + assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); + assert(lfsr_data_size(&data_) == 2); + } + } + } + + // cleanup + free(backup_block); + } +'''