Reworked grm encoding a bit

This drops the leading count/mode byte, and instead uses mid=0 to
terminate grms. This shaves off 1 bytes from grmdeltas.

Previously, we needed the count/mode byte for a couple reasons:

- We needed to know the number of grm entries somehow, and there wasn't
  always an obvious sentinel value. mid=-1, for example, is
  unrepresentable with our unsigned leb128 encoding.

  But now that development has settled, we can use mid=0.0 to figure out
  the end-of-queue. mid=0.0 should always map to the root bookmark,
  which doesn't make sense to delete, so it makes for a reasonable null
  terminator here.

- It provided a route for future grm extensions, which could use the >2
  count/mode encodings.

  But I think we can use additional grm tag encodings for this.

  There's only one gdelta tag so far, but the current plan for future
  gdelta tags is to carve out the bottom 2 bits for redund like we do
  with the struct tags:

    LFSR_TAG_GDELTA        0x01tt  v--- ---1 -ttt ttrr
    LFSR_TAG_GRMDELTA      0x0100  v--- ---1 ---- ----
    LFSR_TAG_GBMAPDELTA    0x0104  v--- ---1 ---- -1rr
    LFSR_TAG_GDDTREEDELTA  0x0108  v--- ---1 ---- 1-rr
    LFSR_TAG_GPTREEDELTA   0x010c  v--- ---1 ---- 11rr
    ...

  Decoding is a bit more complicated for gstate, since we will need to
  xor those bits if mutable, but this avoids needing a full byte just
  for redund in every auxiliary tree.

  Long story short, we can leverage the lower 2 bits of the grm tag for
  future extensions using the same mechanism.

This may seem like a lot of effort for only a handful of bytes, but keep
in mind each gdelta lives in more-or-less every mdir in the filesystem.

Also saves a bit of code/ctx:

           code          stack          ctx
  before: 35772           2368          640
  after:  35768 (-0.0%)   2368 (+0.0%)  636 (-0.6%)
This commit is contained in:
Christopher Haster
2025-04-29 12:32:01 -05:00
parent 98b4aaccc5
commit f2e6b60f36
7 changed files with 145 additions and 153 deletions
+75 -69
View File
@@ -7185,29 +7185,34 @@ static void lfsr_fs_mkdirty(lfs_t *lfs) {
/// Global-state things ///
// grm (global remove) things
static inline uint8_t lfsr_grm_count_(const lfsr_grm_t *grm) {
return (grm->mids[0] >= 0) + (grm->mids[1] >= 0);
static inline lfs_size_t lfsr_grm_count_(const lfsr_grm_t *grm) {
return (grm->queue[0] != 0) + (grm->queue[1] != 0);
}
static inline uint8_t lfsr_grm_count(const lfs_t *lfs) {
static inline lfs_size_t lfsr_grm_count(const lfs_t *lfs) {
return lfsr_grm_count_(&lfs->grm);
}
static inline void lfsr_grm_push(lfs_t *lfs, lfsr_smid_t mid) {
LFS_ASSERT(lfs->grm.mids[1] == -1);
lfs->grm.mids[1] = lfs->grm.mids[0];
lfs->grm.mids[0] = mid;
// note mid=0.0 always maps to the root bookmark and should never
// be grmed
LFS_ASSERT(mid != 0);
LFS_ASSERT(lfs->grm.queue[1] == 0);
lfs->grm.queue[1] = lfs->grm.queue[0];
lfs->grm.queue[0] = mid;
}
static inline lfsr_smid_t lfsr_grm_pop(lfs_t *lfs) {
lfsr_smid_t mid = lfs->grm.mids[0];
lfs->grm.mids[0] = lfs->grm.mids[1];
lfs->grm.mids[1] = -1;
lfsr_smid_t mid = lfs->grm.queue[0];
lfs->grm.queue[0] = lfs->grm.queue[1];
lfs->grm.queue[1] = 0;
return mid;
}
static inline bool lfsr_grm_ismidrm(const lfs_t *lfs, lfsr_smid_t mid) {
return lfs->grm.mids[0] == mid || lfs->grm.mids[1] == mid;
return mid != 0
&& (lfs->grm.queue[0] == mid
|| lfs->grm.queue[1] == mid);
}
#define LFSR_DATA_GRM(_grm, _buffer) \
@@ -7218,15 +7223,11 @@ static lfsr_data_t lfsr_data_fromgrm(const lfsr_grm_t *grm,
// make sure to zero so we don't leak any info
lfs_memset(buffer, 0, LFSR_GRM_DSIZE);
// first encode the number of grms, this can be 0, 1, or 2 and may
// be extended to a general purpose leb128 type field in the future
uint8_t mode = lfsr_grm_count_(grm);
// encode grms
lfs_size_t count = lfsr_grm_count_(grm);
lfs_ssize_t d = 0;
buffer[d] = mode;
d += 1;
for (uint8_t i = 0; i < mode; i++) {
lfs_ssize_t d_ = lfs_toleb128(grm->mids[i], &buffer[d], 5);
for (lfs_size_t i = 0; i < count; i++) {
lfs_ssize_t d_ = lfs_toleb128(grm->queue[i], &buffer[d], 5);
if (d_ < 0) {
LFS_UNREACHABLE();
}
@@ -7242,28 +7243,26 @@ static inline lfsr_mid_t lfsr_mtree_weight(lfs_t *lfs);
static int lfsr_data_readgrm(lfs_t *lfs, lfsr_data_t *data,
lfsr_grm_t *grm) {
// clear first
grm->mids[0] = -1;
grm->mids[1] = -1;
grm->queue[0] = 0;
grm->queue[1] = 0;
// first read the mode field
uint8_t mode;
lfs_ssize_t d = lfsr_data_read(lfs, data, &mode, 1);
if (d < 0) {
return d;
}
LFS_ASSERT(d == 1);
// unknown mode? return an error, we may be able to mount read-only
if (mode > 2) {
return LFS_ERR_CORRUPT;
}
for (uint8_t i = 0; i < mode; i++) {
int err = lfsr_data_readleb128(lfs, data, (lfsr_mid_t*)&grm->mids[i]);
// decode grms, these are terminated by either a null (mid=0) or the
// size of the grm buffer
for (lfs_size_t i = 0; i < 2; i++) {
lfsr_mid_t mid;
int err = lfsr_data_readleb128(lfs, data, &mid);
if (err) {
return err;
}
LFS_ASSERT((lfsr_mid_t)grm->mids[i] < lfsr_mtree_weight(lfs));
// null grm?
if (!mid) {
break;
}
// grm inside mtree?
LFS_ASSERT(mid < lfsr_mtree_weight(lfs));
grm->queue[i] = mid;
}
return 0;
@@ -7271,24 +7270,31 @@ static int lfsr_data_readgrm(lfs_t *lfs, lfsr_data_t *data,
// some mdir-related gstate things we need
static void lfsr_fs_flushgdelta(lfs_t *lfs) {
// zero any pending gdeltas
static void lfsr_fs_flushgdelta(lfs_t *lfs) {
// zero the gcksumdelta
lfs->gcksum_d = 0;
// zero the grmdelta
lfs_memset(lfs->grm_d, 0, LFSR_GRM_DSIZE);
}
static void lfsr_fs_commitgdelta(lfs_t *lfs) {
// commit any pending gdeltas
static void lfsr_fs_commitgdelta(lfs_t *lfs) {
// keep track of the on-disk gcksum
lfs->gcksum_p = lfs->gcksum;
// keep track of the on-disk grm
lfsr_data_fromgrm(&lfs->grm, lfs->grm_p);
}
static void lfsr_fs_revertgdelta(lfs_t *lfs) {
// revert gstate to on-disk state
static void lfsr_fs_revertgdelta(lfs_t *lfs) {
// revert to the on-disk gcksum
lfs->gcksum = lfs->gcksum_p;
// revert to the on-disk grm
int err = lfsr_data_readgrm(lfs,
&LFSR_DATA_BUF(lfs->grm_p, LFSR_GRM_DSIZE),
&lfs->grm);
@@ -8526,13 +8532,13 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
// adjust pending grms?
} else {
for (int j = 0; j < 2; j++) {
if (lfsr_mbid(lfs, lfs->grm.mids[j]) == lfsr_mbid(lfs, mid_)
&& lfs->grm.mids[j] >= mid_) {
if (lfsr_mbid(lfs, lfs->grm.queue[j]) == lfsr_mbid(lfs, mid_)
&& lfs->grm.queue[j] >= mid_) {
// deleting a pending grm doesn't really make sense
LFS_ASSERT(lfs->grm.mids[j] >= mid_ - rattrs[i].weight);
LFS_ASSERT(lfs->grm.queue[j] >= mid_ - rattrs[i].weight);
// adjust the grm
lfs->grm.mids[j] += rattrs[i].weight;
lfs->grm.queue[j] += rattrs[i].weight;
}
}
}
@@ -8825,16 +8831,16 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
// patch any pending grms
for (int j = 0; j < 2; j++) {
if (lfsr_mbid(lfs, lfs->grm.mids[j])
if (lfsr_mbid(lfs, lfs->grm.queue[j])
== lfsr_mbid(lfs, lfs_smax(mdir->mid, 0))) {
if (mdelta > 0
&& lfsr_mrid(lfs, lfs->grm.mids[j])
&& lfsr_mrid(lfs, lfs->grm.queue[j])
>= (lfsr_srid_t)mdir_[0].rbyd.weight) {
lfs->grm.mids[j]
lfs->grm.queue[j]
+= (1 << lfs->mbits) - mdir_[0].rbyd.weight;
}
} else if (lfs->grm.mids[j] > mdir->mid) {
lfs->grm.mids[j] += mdelta;
} else if (lfs->grm.queue[j] > mdir->mid) {
lfs->grm.queue[j] += mdelta;
}
}
@@ -10286,7 +10292,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) {
if (err) {
return err;
}
LFS_ASSERT(lfs->grm.mids[0] == mdir.mid);
LFS_ASSERT(lfs->grm.queue[0] == mdir.mid);
// committing our bookmark may have changed the mid of our metadata entry,
// we need to look it up again, we can at least avoid the full path walk
@@ -10644,7 +10650,7 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
// update moved files with the new mdir
} else if (lfsr_o_type(o->flags) == LFS_TYPE_REG
&& o->mdir.mid == lfs->grm.mids[0]) {
&& o->mdir.mid == lfs->grm.queue[0]) {
o->mdir = new_mdir;
// mark any removed dirs as zombied
@@ -10662,8 +10668,8 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
}
if (((lfsr_dir_t*)o)->did == old_did
&& o->mdir.mid >= lfs->grm.mids[0]) {
if (o->mdir.mid == lfs->grm.mids[0]) {
&& o->mdir.mid >= lfs->grm.queue[0]) {
if (o->mdir.mid == lfs->grm.queue[0]) {
o->mdir.mid += 1;
} else {
((lfsr_dir_t*)o)->pos -= 1;
@@ -10673,7 +10679,7 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
// clobber entangled traversals
} else if (lfsr_o_type(o->flags) == LFS_type_TRAVERSAL
&& ((exists && o->mdir.mid == new_mdir.mid)
|| o->mdir.mid == lfs->grm.mids[0])) {
|| o->mdir.mid == lfs->grm.queue[0])) {
lfsr_traversal_clobber(lfs, (lfsr_traversal_t*)o);
}
}
@@ -13486,8 +13492,8 @@ static int lfs_init(lfs_t *lfs, uint32_t flags,
lfs->gcksum_p = 0;
lfs->gcksum_d = 0;
lfs->grm.mids[0] = -1;
lfs->grm.mids[1] = -1;
lfs->grm.queue[0] = -1;
lfs->grm.queue[1] = -1;
lfs_memset(lfs->grm_p, 0, LFSR_GRM_DSIZE);
lfs_memset(lfs->grm_d, 0, LFSR_GRM_DSIZE);
@@ -14019,14 +14025,14 @@ static int lfsr_mountinited(lfs_t *lfs) {
// found pending grms? this should only happen if we lost power
if (lfsr_grm_count(lfs) == 2) {
LFS_INFO("Found pending grm %"PRId32".%"PRId32" %"PRId32".%"PRId32,
lfsr_dbgmbid(lfs, lfs->grm.mids[0]),
lfsr_dbgmrid(lfs, lfs->grm.mids[0]),
lfsr_dbgmbid(lfs, lfs->grm.mids[1]),
lfsr_dbgmrid(lfs, lfs->grm.mids[1]));
lfsr_dbgmbid(lfs, lfs->grm.queue[0]),
lfsr_dbgmrid(lfs, lfs->grm.queue[0]),
lfsr_dbgmbid(lfs, lfs->grm.queue[1]),
lfsr_dbgmrid(lfs, lfs->grm.queue[1]));
} else if (lfsr_grm_count(lfs) == 1) {
LFS_INFO("Found pending grm %"PRId32".%"PRId32,
lfsr_dbgmbid(lfs, lfs->grm.mids[0]),
lfsr_dbgmrid(lfs, lfs->grm.mids[0]));
lfsr_dbgmbid(lfs, lfs->grm.queue[0]),
lfsr_dbgmrid(lfs, lfs->grm.queue[0]));
}
return 0;
@@ -14434,22 +14440,22 @@ lfs_ssize_t lfsr_fs_size(lfs_t *lfs) {
static int lfsr_fs_fixgrm(lfs_t *lfs) {
if (lfsr_grm_count(lfs) == 2) {
LFS_INFO("Fixing grm %"PRId32".%"PRId32" %"PRId32".%"PRId32,
lfsr_dbgmbid(lfs, lfs->grm.mids[0]),
lfsr_dbgmrid(lfs, lfs->grm.mids[0]),
lfsr_dbgmbid(lfs, lfs->grm.mids[1]),
lfsr_dbgmrid(lfs, lfs->grm.mids[1]));
lfsr_dbgmbid(lfs, lfs->grm.queue[0]),
lfsr_dbgmrid(lfs, lfs->grm.queue[0]),
lfsr_dbgmbid(lfs, lfs->grm.queue[1]),
lfsr_dbgmrid(lfs, lfs->grm.queue[1]));
} else if (lfsr_grm_count(lfs) == 1) {
LFS_INFO("Fixing grm %"PRId32".%"PRId32,
lfsr_dbgmbid(lfs, lfs->grm.mids[0]),
lfsr_dbgmrid(lfs, lfs->grm.mids[0]));
lfsr_dbgmbid(lfs, lfs->grm.queue[0]),
lfsr_dbgmrid(lfs, lfs->grm.queue[0]));
}
while (lfsr_grm_count(lfs) > 0) {
LFS_ASSERT(lfs->grm.mids[0] != -1);
LFS_ASSERT(lfs->grm.queue[0] != -1);
// find our mdir
lfsr_mdir_t mdir;
int err = lfsr_mtree_lookup(lfs, lfs->grm.mids[0],
int err = lfsr_mtree_lookup(lfs, lfs->grm.queue[0],
&mdir);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
+4 -6
View File
@@ -812,18 +812,16 @@ typedef struct lfsr_traversal {
//} lfs_gstate_t;
// grm encoding:
// .---. mode: 1 leb128 1 byte
// |mod| mids: 2 leb128s <=2x5 bytes
// +- -+- -+- -+- -+- -. total: <=11 bytes
// ' mid x mod '
// .- -+- -+- -+- -+- -. mids: 2 leb128s <=2x5 bytes
// ' mids ' total: <=10 bytes
// + +
// ' '
// '- -+- -+- -+- -+- -'
//
#define LFSR_GRM_DSIZE (1+5+5)
#define LFSR_GRM_DSIZE (5+5)
typedef struct lfsr_grm {
lfsr_smid_t mids[2];
lfsr_smid_t queue[2];
} lfsr_grm_t;
#ifdef LFS_CKPARITY
+9 -13
View File
@@ -2726,26 +2726,22 @@ class Gstate:
def __init__(self, mtree, tag, gdeltas):
super().__init__(mtree, tag, gdeltas)
queue = []
d = 0
count, d_ = fromleb128(self.data, d); d += d_
rms = []
if count <= 2:
for _ in range(count):
for _ in range(2):
mid, d_ = fromleb128(self.data, d); d += d_
# a null mid (mid=0.0) terminates the grm queue
if not mid:
break
mid = mtree.mid(mid)
# map mbids -> -1 if mroot-inlined
if mtree.mtree is None:
mid = mtree.mid(-1, mid.mrid)
rms.append(mid)
self.count = count
self.rms = rms
queue.append(mid)
self.queue = queue
def repr(self):
return 'grm %s' % (
'none' if self.count == 0
else ' '.join(mid.repr() for mid in self.rms)
if self.count <= 2
else '0x%x %d' % (self.count, len(self.data)))
return 'grm [%s]' % ', '.join(mid.repr() for mid in self.queue)
# keep track of known gstate
_known = [g for g in Gstate.__subclasses__() if g.tag is not None]
@@ -2961,7 +2957,7 @@ class Lfs:
if not isinstance(mid, Mid):
mid = self.mid(mid)
return mid in self.gstate.grm.rms
return mid in self.gstate.grm.queue
# lookup operations
def lookup(self, mid, mdir=None, *,
+9 -13
View File
@@ -2756,26 +2756,22 @@ class Gstate:
def __init__(self, mtree, tag, gdeltas):
super().__init__(mtree, tag, gdeltas)
queue = []
d = 0
count, d_ = fromleb128(self.data, d); d += d_
rms = []
if count <= 2:
for _ in range(count):
for _ in range(2):
mid, d_ = fromleb128(self.data, d); d += d_
# a null mid (mid=0.0) terminates the grm queue
if not mid:
break
mid = mtree.mid(mid)
# map mbids -> -1 if mroot-inlined
if mtree.mtree is None:
mid = mtree.mid(-1, mid.mrid)
rms.append(mid)
self.count = count
self.rms = rms
queue.append(mid)
self.queue = queue
def repr(self):
return 'grm %s' % (
'none' if self.count == 0
else ' '.join(mid.repr() for mid in self.rms)
if self.count <= 2
else '0x%x %d' % (self.count, len(self.data)))
return 'grm [%s]' % ', '.join(mid.repr() for mid in self.queue)
# keep track of known gstate
_known = [g for g in Gstate.__subclasses__() if g.tag is not None]
@@ -2991,7 +2987,7 @@ class Lfs:
if not isinstance(mid, Mid):
mid = self.mid(mid)
return mid in self.gstate.grm.rms
return mid in self.gstate.grm.queue
# lookup operations
def lookup(self, mid, mdir=None, *,
+9 -13
View File
@@ -2683,26 +2683,22 @@ class Gstate:
def __init__(self, mtree, tag, gdeltas):
super().__init__(mtree, tag, gdeltas)
queue = []
d = 0
count, d_ = fromleb128(self.data, d); d += d_
rms = []
if count <= 2:
for _ in range(count):
for _ in range(2):
mid, d_ = fromleb128(self.data, d); d += d_
# a null mid (mid=0.0) terminates the grm queue
if not mid:
break
mid = mtree.mid(mid)
# map mbids -> -1 if mroot-inlined
if mtree.mtree is None:
mid = mtree.mid(-1, mid.mrid)
rms.append(mid)
self.count = count
self.rms = rms
queue.append(mid)
self.queue = queue
def repr(self):
return 'grm %s' % (
'none' if self.count == 0
else ' '.join(mid.repr() for mid in self.rms)
if self.count <= 2
else '0x%x %d' % (self.count, len(self.data)))
return 'grm [%s]' % ', '.join(mid.repr() for mid in self.queue)
# keep track of known gstate
_known = [g for g in Gstate.__subclasses__() if g.tag is not None]
@@ -2918,7 +2914,7 @@ class Lfs:
if not isinstance(mid, Mid):
mid = self.mid(mid)
return mid in self.gstate.grm.rms
return mid in self.gstate.grm.queue
# lookup operations
def lookup(self, mid, mdir=None, *,
+8 -8
View File
@@ -7976,8 +7976,8 @@ code = '''
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
@@ -8112,8 +8112,8 @@ code = '''
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
@@ -8262,8 +8262,8 @@ code = '''
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
@@ -8418,8 +8418,8 @@ code = '''
lfsr_fs_mkconsistent(&lfs) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
struct lfs_fsinfo fsinfo;
+16 -16
View File
@@ -6461,8 +6461,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -6603,8 +6603,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should be able to clean up grms
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
// if we introduce actual orphans, me _must not_ clear the orphan flag
if (ORPHANS >= 3) {
assert(lfs.flags & LFS_I_MKCONSISTENT);
@@ -6762,8 +6762,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -6920,8 +6920,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -7089,8 +7089,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -7256,8 +7256,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -7421,8 +7421,8 @@ code = '''
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
// we should have cleaned up all grms/orphans
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
assert(!(lfs.flags & LFS_I_MKCONSISTENT));
// which means there shouldn't be that many files left
@@ -7614,8 +7614,8 @@ code = '''
lfsr_traversal_close(&lfs, &t) => 0;
// we should be able to clean up grms
assert(lfs.grm.mids[0] == -1);
assert(lfs.grm.mids[1] == -1);
assert(lfs.grm.queue[0] == 0);
assert(lfs.grm.queue[1] == 0);
// if we introduce actual orphans, me _must not_ clear the orphan flag
if (ORPHANS >= 3) {
assert(lfs.flags & LFS_I_MKCONSISTENT);