From 14d1f4778f74b37a2d75b781f8dbba3e4590b1af Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 22 Jul 2025 12:50:39 -0500 Subject: [PATCH] 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%) --- lfs3.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/lfs3.c b/lfs3.c index 6f2d7eb3..4ea12f70 100644 --- a/lfs3.c +++ b/lfs3.c @@ -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;