trv: Moved cycle detection tortoise into the shrub leaf
This forces our cycle detection tortoise (previously trv.u.mtortoise),
into the unused shrub leaf via pointer shenanigans.
This reclaims the remaining stack (and apparently code) we theoretically
gained from the btree traversal rework, up until the compiler got in the
way:
code stack ctx
before: 36876 2384 684
after: 36852 (-0.1%) 2368 (-0.7%) 684 (+0.0%)
And it only required some _questionably_ defined behavior.
---
It's probably not well-defined behavior, but trying to understand what
the standard actually means on this is giving me a headache. I think I
have to agree C99+strict-aliasing lost the plot on this one. Note
mtortoise is only ever written/read through the same type.
What I want:
lfs3_trv_t: lfs3_bshrub_t: lfs3_handle_t:
.---+---+---+---. .. .---+---+---+---. .. .---+---+---+---.
| handle | | handle | | handle |
| | | | | |
+---+---+---+---+ +---+---+---+---+ .. '---+---+---+---'
| root rbyd | | root rbyd |
| | | | lfs3_mtortoise_t:
+---+---+---+---+ +---+---+---+---+ .. .---+---+---+---.
| leaf rbyd | | leaf rbyd | | mtortoise |
| | | | | |
+---+---+---+---+ +---+---+---+---+ .. '---+---+---+---'
| staging rbyd | | staging rbyd |
| | | |
+---+---+---+---+ .. '---+---+---+---'
| |
: :
But I'm starting to think this is simply not possible in modern C.
At least this shows what is theoretically possible if we didn't have to
fight the compiler.
This commit is contained in:
@@ -9783,6 +9783,13 @@ enum lfs3_tstate {
|
|||||||
LFS3_TSTATE_DONE = 8,
|
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) {
|
static void lfs3_trv_init(lfs3_trv_t *trv, uint32_t flags) {
|
||||||
trv->b.h.flags = lfs3_o_typeflags(LFS3_type_TRV)
|
trv->b.h.flags = lfs3_o_typeflags(LFS3_type_TRV)
|
||||||
| lfs3_t_tstateflags(LFS3_TSTATE_MROOTANCHOR)
|
| lfs3_t_tstateflags(LFS3_TSTATE_MROOTANCHOR)
|
||||||
@@ -9792,11 +9799,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[0] = -1;
|
||||||
trv->b.h.mdir.r.blocks[1] = -1;
|
trv->b.h.mdir.r.blocks[1] = -1;
|
||||||
lfs3_bshrub_init(&trv->b);
|
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;
|
||||||
trv->h = NULL;
|
trv->h = NULL;
|
||||||
trv->u.mtortoise.blocks[0] = -1;
|
|
||||||
trv->u.mtortoise.blocks[1] = -1;
|
|
||||||
trv->u.mtortoise.step = 0;
|
|
||||||
trv->u.mtortoise.power = 0;
|
|
||||||
trv->gcksum = 0;
|
trv->gcksum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9859,22 +9870,26 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|||||||
// btree inner nodes require checksums of their pointers,
|
// btree inner nodes require checksums of their pointers,
|
||||||
// so creating a valid cycle is actually quite difficult
|
// 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;
|
||||||
if (lfs3_mptr_cmp(
|
if (lfs3_mptr_cmp(
|
||||||
trv->b.h.mdir.r.blocks,
|
trv->b.h.mdir.r.blocks,
|
||||||
trv->u.mtortoise.blocks) == 0) {
|
mtortoise->blocks) == 0) {
|
||||||
LFS3_ERROR("Cycle detected during mtree traversal "
|
LFS3_ERROR("Cycle detected during mtree traversal "
|
||||||
"0x{%"PRIx32",%"PRIx32"}",
|
"0x{%"PRIx32",%"PRIx32"}",
|
||||||
trv->b.h.mdir.r.blocks[0],
|
trv->b.h.mdir.r.blocks[0],
|
||||||
trv->b.h.mdir.r.blocks[1]);
|
trv->b.h.mdir.r.blocks[1]);
|
||||||
return LFS3_ERR_CORRUPT;
|
return LFS3_ERR_CORRUPT;
|
||||||
}
|
}
|
||||||
if (trv->u.mtortoise.step == (1U << trv->u.mtortoise.power)) {
|
if (mtortoise->dist == (1U << mtortoise->power)) {
|
||||||
trv->u.mtortoise.blocks[0] = trv->b.h.mdir.r.blocks[0];
|
mtortoise->blocks[0] = trv->b.h.mdir.r.blocks[0];
|
||||||
trv->u.mtortoise.blocks[1] = trv->b.h.mdir.r.blocks[1];
|
mtortoise->blocks[1] = trv->b.h.mdir.r.blocks[1];
|
||||||
trv->u.mtortoise.step = 0;
|
mtortoise->dist = 0;
|
||||||
trv->u.mtortoise.power += 1;
|
mtortoise->power += 1;
|
||||||
}
|
}
|
||||||
trv->u.mtortoise.step += 1;
|
mtortoise->dist += 1;
|
||||||
|
|
||||||
bptr_->d.u.buffer = (const uint8_t*)&trv->b.h.mdir;
|
bptr_->d.u.buffer = (const uint8_t*)&trv->b.h.mdir;
|
||||||
return LFS3_TAG_MDIR;
|
return LFS3_TAG_MDIR;
|
||||||
@@ -9889,7 +9904,7 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transition to traversing the mtree
|
// transition to traversing the mtree
|
||||||
trv->u.bid = -2;
|
trv->bid = -2;
|
||||||
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_MTREE);
|
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_MTREE);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -9945,7 +9960,7 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|||||||
// here, lfs3_bshrub_fetch ignores these for us
|
// here, lfs3_bshrub_fetch ignores these for us
|
||||||
if (err != LFS3_ERR_NOENT) {
|
if (err != LFS3_ERR_NOENT) {
|
||||||
// start traversing
|
// start traversing
|
||||||
trv->u.bid = -2;
|
trv->bid = -2;
|
||||||
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_BTREE);
|
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_BTREE);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -9990,7 +10005,7 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|||||||
// transition to traversing the file
|
// transition to traversing the file
|
||||||
const lfs3_file_t *file = (const lfs3_file_t*)trv->h;
|
const lfs3_file_t *file = (const lfs3_file_t*)trv->h;
|
||||||
trv->b.shrub = file->b.shrub;
|
trv->b.shrub = file->b.shrub;
|
||||||
trv->u.bid = -2;
|
trv->bid = -2;
|
||||||
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_HBTREE);
|
lfs3_t_settstate(&trv->b.h.flags, LFS3_TSTATE_HBTREE);
|
||||||
continue;
|
continue;
|
||||||
#endif
|
#endif
|
||||||
@@ -10002,8 +10017,8 @@ static lfs3_stag_t lfs3_mtree_traverse_(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|||||||
case LFS3_TSTATE_BTREE:;
|
case LFS3_TSTATE_BTREE:;
|
||||||
case LFS3_TSTATE_HBTREE:;
|
case LFS3_TSTATE_HBTREE:;
|
||||||
// traverse through our bshrub/btree
|
// traverse through our bshrub/btree
|
||||||
tag = lfs3_bshrub_traverse(lfs3, &trv->b, trv->u.bid+1,
|
tag = lfs3_bshrub_traverse(lfs3, &trv->b, trv->bid+1,
|
||||||
&trv->u.bid, NULL, &data);
|
&trv->bid, NULL, &data);
|
||||||
if (tag < 0) {
|
if (tag < 0) {
|
||||||
if (tag == LFS3_ERR_NOENT) {
|
if (tag == LFS3_ERR_NOENT) {
|
||||||
// clear the bshrub state
|
// clear the bshrub state
|
||||||
|
|||||||
@@ -771,20 +771,12 @@ typedef struct lfs3_dir {
|
|||||||
// littlefs traversal type
|
// littlefs traversal type
|
||||||
typedef struct lfs3_trv {
|
typedef struct lfs3_trv {
|
||||||
// mdir/bshrub/btree state, this also includes our traversal
|
// mdir/bshrub/btree state, this also includes our traversal
|
||||||
// state machine
|
// state machine and cycle detection state
|
||||||
lfs3_bshrub_t b;
|
lfs3_bshrub_t b;
|
||||||
// opened file state
|
// opened file state
|
||||||
lfs3_handle_t *h;
|
lfs3_handle_t *h;
|
||||||
union {
|
// bshrub/btree traversal state
|
||||||
// cycle detection state, only valid when traversing the mroot chain
|
lfs3_sbid_t bid;
|
||||||
struct {
|
|
||||||
lfs3_block_t blocks[2];
|
|
||||||
lfs3_block_t step;
|
|
||||||
uint8_t power;
|
|
||||||
} mtortoise;
|
|
||||||
// btree traversal state
|
|
||||||
lfs3_sbid_t bid;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
// recalculate gcksum when traversing with ckmeta
|
// recalculate gcksum when traversing with ckmeta
|
||||||
uint32_t gcksum;
|
uint32_t gcksum;
|
||||||
|
|||||||
Reference in New Issue
Block a user