trv: mtortoise: Gave up on a reasonable type, abuse shrub fields

I think modern C simply doesn't let us do what we want to do here, so
I'm giving up, discarding the lfs3_mtortoise_t type, and just abusing
various unrelated shrub fields to implement the tortoise. This
sacrifices readability, but at least avoids undefined behavior without
a RAM penalty:

- shrub.blocks => tortoise blocks
- shrub.weight => cycle distance
- shrub.eoff => power-of-two bound

Note this keeps trunk=0, which is a nice safety net in case some code
ever tries to read from the shrub in the future.

Fortunately the mtortoise logic is fairly self-contained in
lfs3_mtree_traverse_, so with enough comments hopefully the code is not
too confusing.

---

Apparently shaves off a couple more bytes of code. I'm guessing this is
just because of the slightly different struct offsets (we're reusing the
root's rbyd instead of the leaf's rbyd now):

           code          stack          ctx
  before: 36852           2368          684
  after:  36844 (-0.0%)   2368 (+0.0%)  684 (+0.0%)
This commit is contained in:
Christopher Haster
2025-07-22 12:50:39 -05:00
parent fadf0cbd0e
commit 14d1f4778f
+24 -26
View File
@@ -9783,13 +9783,6 @@ enum lfs3_tstate {
LFS3_TSTATE_DONE = 8,
};
// tortoise for cycle detection
typedef struct lfs3_mtortoise {
lfs3_block_t blocks[2];
lfs3_block_t dist;
uint8_t power;
} lfs3_mtortoise_t;
static void lfs3_trv_init(lfs3_trv_t *trv, uint32_t flags) {
trv->b.h.flags = lfs3_o_typeflags(LFS3_type_TRV)
| lfs3_t_tstateflags(LFS3_TSTATE_MROOTANCHOR)
@@ -9799,14 +9792,15 @@ static void lfs3_trv_init(lfs3_trv_t *trv, uint32_t flags) {
trv->b.h.mdir.r.blocks[0] = -1;
trv->b.h.mdir.r.blocks[1] = -1;
lfs3_bshrub_init(&trv->b);
// bit of a hack, but we reuse the shrub's leaf as a tortoise for
// cycle detection to save memory, see lfs3_mtree_traverse_
lfs3_mtortoise_t *mtortoise = (lfs3_mtortoise_t*)&trv->b.shrub.leaf.r;
LFS3_ASSERT(sizeof(lfs3_mtortoise_t) <= sizeof(trv->b.shrub.leaf.r));
mtortoise->blocks[0] = -1;
mtortoise->blocks[1] = -1;
mtortoise->dist = 0;
mtortoise->power = 0;
// reuse the shrub as a tortoise to save memory, see
// lfs3_mtree_traverse_:
// - shrub.blocks => tortoise blocks
// - shrub.weight => cycle distance
// - shrub.eoff => power-of-two bound
trv->b.shrub.r.blocks[0] = -1;
trv->b.shrub.r.blocks[1] = -1;
trv->b.shrub.r.weight = 0;
trv->b.shrub.r.eoff = 0;
trv->h = NULL;
trv->gcksum = 0;
}
@@ -9870,26 +9864,30 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
// btree inner nodes require checksums of their pointers,
// so creating a valid cycle is actually quite difficult
//
// a bit of a hack, but we reuse the shrub's leaf as a
// tortoise to save memory
lfs3_mtortoise_t *mtortoise
= (lfs3_mtortoise_t*)&trv->b.shrub.leaf.r;
// the big hack here is we repurpose our shrub as a tortoise
// to save memory, unfortunately there's not an easy way to
// union these in C:
//
// - shrub.blocks => tortoise blocks
// - shrub.weight => cycle distance
// - shrub.eoff => power-of-two bound
//
if (lfs3_mptr_cmp(
trv->b.h.mdir.r.blocks,
mtortoise->blocks) == 0) {
trv->b.shrub.r.blocks) == 0) {
LFS3_ERROR("Cycle detected during mtree traversal "
"0x{%"PRIx32",%"PRIx32"}",
trv->b.h.mdir.r.blocks[0],
trv->b.h.mdir.r.blocks[1]);
return LFS3_ERR_CORRUPT;
}
if (mtortoise->dist == (1U << mtortoise->power)) {
mtortoise->blocks[0] = trv->b.h.mdir.r.blocks[0];
mtortoise->blocks[1] = trv->b.h.mdir.r.blocks[1];
mtortoise->dist = 0;
mtortoise->power += 1;
if (trv->b.shrub.r.weight == (1U << trv->b.shrub.r.eoff)) {
trv->b.shrub.r.blocks[0] = trv->b.h.mdir.r.blocks[0];
trv->b.shrub.r.blocks[1] = trv->b.h.mdir.r.blocks[1];
trv->b.shrub.r.weight = 0;
trv->b.shrub.r.eoff += 1;
}
mtortoise->dist += 1;
trv->b.shrub.r.weight += 1;
bptr_->d.u.buffer = (const uint8_t*)&trv->b.h.mdir;
return LFS3_TAG_MDIR;