Simplified lfsr_bshrub_t representation

Now that we don't use bmoss or bsprouts anymore, we can drop the
LFSR_BSHRUB_ISNULLORBMOSSORBPTR flag and simplify our lfsr_bshrub_t
struct quite a bit.

However, we do still need a bnull representation, which is surprisingly
tricky... And annoying...

Current solution: Bnulls are bshrubs with weight=0. This works, but
unfortunately does mean we need to update bnull blocks on mdir
relocation/compaction, and risks bnull blocks falling out-of-sync, which
is a really weird thing to worry about:

  bnull:               bshrub:              btree:
  .---+---+---+---. .. .---+---+---+---. .. .---+---+---+---.
  |    weight=0   |    |    weight>0   |    |    weight     |
  +---+---+---+---+    +---+---+---+---+    +---+---+---+---+
  |   block=mdir  |    |   block=mdir  |    |  block!=mdir  |
  +---+---+---+---+ .. +---+---+---+---+    +---+---+---+---+
  |    (unused)   |    |    (unused)   |    |    (unused)   |
  +               +    +---+---+---+---+    +---+---+---+---+
  |               |    |     trunk     |    |     trunk     |
  +               +    +---+---+---+---+ .. +---+---+---+---+
  |               |    |    estimate   |    |     eoff      |
  +               +    +---+---+---+---+    +---+---+---+---+
  |               |    |    (unused)   |    |     cksum     |
  '---+---+---+---'    '---+---+---+---'    '---+---+---+---'

Note we can't just assume all weight=0 files are bnulls, or else we
won't use erased-state in empty btree roots. This risks thrashing in
files oscillating around weight=0.

Technically, weight=0 bshrubs _are_ slightly different than bnulls (
bshrubs point to a null tag, while bnulls simply have no tree), but
unlike btrees, there's no reason to keep weight=0 bshrubs around. Any
bshrub erased-state can still be used by the mdir.

This change also makes LFS_O_TRUNC and lfsr_file_truncate/fruncate
behave slightly differently, with LFS_O_TRUNC unconditionally reverting
to a bnull, while lfsr_file_truncate/fruncate tries to keep the btree
root around. This may be worth revisiting...

---

Despite the awkward encoding, this simplification still ends up saving a
nice bit of code and stack:

           code          stack          ctx
  before: 36668           2616          640
  after:  36460 (-0.6%)   2608 (-0.3%)  640 (+0.0%)
This commit is contained in:
Christopher Haster
2025-02-01 15:47:54 -06:00
parent a2b50d2463
commit 475ca76cdf
2 changed files with 131 additions and 194 deletions
+124 -182
View File
@@ -5946,60 +5946,6 @@ static int lfsr_btree_traverse(lfs_t *lfs, const lfsr_btree_t *btree,
/// B-shrub operations /// /// B-shrub operations ///
#define LFSR_BSHRUB_ISBNULLORBMOSSORBPTR 0x80000000
// create an empty bshrub
static void lfsr_bshrub_init(lfsr_bshrub_t *bshrub) {
bshrub->u.size = LFSR_BSHRUB_ISBNULLORBMOSSORBPTR | 0;
}
static inline bool lfsr_bshrub_isbnull(const lfsr_bshrub_t *bshrub) {
return (lfs_size_t)bshrub->u.size
== (LFSR_BSHRUB_ISBNULLORBMOSSORBPTR | 0);
}
static inline bool lfsr_bshrub_isbmoss(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return (lfs_size_t)bshrub->u.size
> (LFSR_BSHRUB_ISBNULLORBMOSSORBPTR | 0)
&& bshrub->u.bmoss.u.disk.block == mdir->rbyd.blocks[0];
}
static inline bool lfsr_bshrub_isbptr(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return (lfs_size_t)bshrub->u.size
> (LFSR_BSHRUB_ISBNULLORBMOSSORBPTR | 0)
&& bshrub->u.bmoss.u.disk.block != mdir->rbyd.blocks[0];
}
static inline bool lfsr_bshrub_isbshrub(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return !(bshrub->u.size & LFSR_BSHRUB_ISBNULLORBMOSSORBPTR)
&& bshrub->u.bshrub.blocks[0] == mdir->rbyd.blocks[0];
}
static inline bool lfsr_bshrub_isbtree(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return !(bshrub->u.size & LFSR_BSHRUB_ISBNULLORBMOSSORBPTR)
&& bshrub->u.bshrub.blocks[0] != mdir->rbyd.blocks[0];
}
static inline bool lfsr_bshrub_isbnullorbmossorbptr(
const lfsr_bshrub_t *bshrub) {
return bshrub->u.size & LFSR_BSHRUB_ISBNULLORBMOSSORBPTR;
}
static inline bool lfsr_bshrub_isbshruborbtree(
const lfsr_bshrub_t *bshrub) {
return !(bshrub->u.size & LFSR_BSHRUB_ISBNULLORBMOSSORBPTR);
}
// the on-disk size/weight lines up to the same word across all unions
static inline lfs_off_t lfsr_bshrub_size(const lfsr_bshrub_t *bshrub) {
return bshrub->u.size & ~LFSR_BSHRUB_ISBNULLORBMOSSORBPTR;
}
// shrub things // shrub things
// create an empty shrub // create an empty shrub
@@ -6104,8 +6050,6 @@ static lfs_ssize_t lfsr_shrub_estimate(lfs_t *lfs,
const lfsr_shrub_t *last = NULL; const lfsr_shrub_t *last = NULL;
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_o_isbshrub(o->flags) if (lfsr_o_isbshrub(o->flags)
&& lfsr_bshrub_isbshrub(&o->mdir,
&((lfsr_obshrub_t*)o)->bshrub)
&& lfsr_shrub_cmp( && lfsr_shrub_cmp(
&((lfsr_obshrub_t*)o)->bshrub.u.bshrub, &((lfsr_obshrub_t*)o)->bshrub.u.bshrub,
shrub) == 0) { shrub) == 0) {
@@ -6138,15 +6082,12 @@ static int lfsr_shrub_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
// this should include our current bshrub // this should include our current bshrub
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_o_isbshrub(o->flags) if (lfsr_o_isbshrub(o->flags)
&& lfsr_bshrub_isbshrub(&o->mdir,
&((lfsr_obshrub_t*)o)->bshrub)
&& lfsr_shrub_cmp( && lfsr_shrub_cmp(
&((lfsr_obshrub_t*)o)->bshrub.u.bshrub, &((lfsr_obshrub_t*)o)->bshrub.u.bshrub,
shrub) == 0) { shrub) == 0) {
lfsr_obshrub_t *bshrub = (lfsr_obshrub_t*)o; ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.blocks[0] = rbyd_->blocks[0];
bshrub->bshrub_.u.bshrub.blocks[0] = rbyd_->blocks[0]; ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.trunk = rbyd_->trunk;
bshrub->bshrub_.u.bshrub.trunk = rbyd_->trunk; ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.weight = rbyd_->weight;
bshrub->bshrub_.u.bshrub.weight = rbyd_->weight;
} }
} }
@@ -6198,6 +6139,28 @@ static int lfsr_shrub_commit(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
// ok, actual bshrub things // ok, actual bshrub things
// create an empty bshrub
static void lfsr_bshrub_init(
const lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub) {
lfsr_shrub_init(&bshrub->u.bshrub, mdir->rbyd.blocks[0]);
}
static inline bool lfsr_bshrub_isbshrub(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return bshrub->u.bshrub.blocks[0] == mdir->rbyd.blocks[0];
}
static inline bool lfsr_bshrub_isbtree(
const lfsr_mdir_t *mdir, const lfsr_bshrub_t *bshrub) {
return bshrub->u.bshrub.blocks[0] != mdir->rbyd.blocks[0];
}
static inline int lfsr_bshrub_cmp(
const lfsr_bshrub_t *a,
const lfsr_bshrub_t *b) {
return lfsr_btree_cmp(&a->u.btree, &b->u.btree);
}
// needed in lfsr_bshrub_estimate // needed in lfsr_bshrub_estimate
static int lfsr_mdir_lookupnext(lfs_t *lfs, const lfsr_mdir_t *mdir, static int lfsr_mdir_lookupnext(lfs_t *lfs, const lfsr_mdir_t *mdir,
lfsr_tag_t tag, lfsr_tag_t tag,
@@ -6238,16 +6201,16 @@ static lfs_ssize_t lfsr_bshrub_estimate(lfs_t *lfs,
// this includes our current shrub // this includes our current shrub
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_o_isbshrub(o->flags) if (lfsr_o_isbshrub(o->flags)
&& o->mdir.mid == mdir->mid) { && o->mdir.mid == mdir->mid
if (lfsr_bshrub_isbshrub(&o->mdir, && lfsr_bshrub_isbshrub(
&((lfsr_obshrub_t*)o)->bshrub)) { &o->mdir, &((lfsr_obshrub_t*)o)->bshrub)
lfs_ssize_t dsize = lfsr_shrub_estimate(lfs, && ((lfsr_obshrub_t*)o)->bshrub.u.weight > 0) {
&((lfsr_obshrub_t*)o)->bshrub.u.bshrub); lfs_ssize_t dsize = lfsr_shrub_estimate(lfs,
if (dsize < 0) { &((lfsr_obshrub_t*)o)->bshrub.u.bshrub);
return dsize; if (dsize < 0) {
} return dsize;
estimate += dsize;
} }
estimate += dsize;
} }
} }
@@ -6261,13 +6224,9 @@ static int lfsr_bshrub_lookupnext(lfs_t *lfs,
lfsr_bptr_t *bptr_) { lfsr_bptr_t *bptr_) {
(void)mdir; (void)mdir;
// out of bounds? // out of bounds?
if (bid >= lfsr_bshrub_size(bshrub)) { if (bid >= bshrub->u.weight) {
return LFS_ERR_NOENT; return LFS_ERR_NOENT;
} }
// the above size check should make this impossible
LFS_ASSERT(!lfsr_bshrub_isbnull(bshrub));
// file must be a bshrub/btree here
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
// bshrub/btree? // bshrub/btree?
lfsr_bid_t bid__; lfsr_bid_t bid__;
@@ -6314,13 +6273,6 @@ static int lfsr_bshrub_traverse(lfs_t *lfs,
lfsr_btraversal_t *bt, lfsr_btraversal_t *bt,
lfsr_bid_t *bid_, lfsr_tag_t *tag_, lfsr_bptr_t *bptr_) { lfsr_bid_t *bid_, lfsr_tag_t *tag_, lfsr_bptr_t *bptr_) {
(void)mdir; (void)mdir;
// bnull does nothing
if (lfsr_bshrub_isbnull(bshrub)) {
return LFS_ERR_NOENT;
}
// file must be a bshrub/btree here
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
lfsr_tag_t tag; lfsr_tag_t tag;
lfsr_data_t data; lfsr_data_t data;
int err = lfsr_btree_traverse(lfs, &bshrub->u.btree, bt, int err = lfsr_btree_traverse(lfs, &bshrub->u.btree, bt,
@@ -6356,17 +6308,12 @@ static int lfsr_bshrub_commit_(lfs_t *lfs,
lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub, lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub,
lfsr_bid_t bid, lfsr_rbyd_t *rbyd, lfsr_srid_t rid, lfsr_bid_t bid, lfsr_rbyd_t *rbyd, lfsr_srid_t rid,
const lfsr_rat_t *rats, lfs_size_t rat_count) { const lfsr_rat_t *rats, lfs_size_t rat_count) {
// file must be a bshrub/btree here
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
// before we touch anything, we need to mark all other btree references // before we touch anything, we need to mark all other btree references
// as unerased // as unerased
if (lfsr_bshrub_isbtree(mdir, bshrub)) { if (lfsr_bshrub_isbtree(mdir, bshrub)) {
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_o_isbshrub(o->flags) if (lfsr_o_isbshrub(o->flags)
&& &((lfsr_obshrub_t*)o)->bshrub != bshrub && &((lfsr_obshrub_t*)o)->bshrub != bshrub
&& lfsr_bshrub_isbshruborbtree(
&((lfsr_obshrub_t*)o)->bshrub)
&& lfsr_btree_cmp( && lfsr_btree_cmp(
&((lfsr_obshrub_t*)o)->bshrub.u.btree, &((lfsr_obshrub_t*)o)->bshrub.u.btree,
&bshrub->u.btree) == 0) { &bshrub->u.btree) == 0) {
@@ -6454,8 +6401,8 @@ static int lfsr_bshrub_commit_(lfs_t *lfs,
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
if (lfsr_o_isbshrub(o->flags) if (lfsr_o_isbshrub(o->flags)
&& o->mdir.mid == mdir->mid && o->mdir.mid == mdir->mid
&& lfsr_bshrub_isbshrub(&o->mdir, && lfsr_bshrub_isbshrub(
&((lfsr_obshrub_t*)o)->bshrub)) { &o->mdir, &((lfsr_obshrub_t*)o)->bshrub)) {
((lfsr_obshrub_t*)o)->bshrub.u.bshrub.estimate = estimate; ((lfsr_obshrub_t*)o)->bshrub.u.bshrub.estimate = estimate;
} }
} }
@@ -6528,8 +6475,6 @@ relocate:;
static int lfsr_bshrub_commit(lfs_t *lfs, static int lfsr_bshrub_commit(lfs_t *lfs,
lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub, lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub,
lfsr_bid_t bid, const lfsr_rat_t *rats, lfs_size_t rat_count) { lfsr_bid_t bid, const lfsr_rat_t *rats, lfs_size_t rat_count) {
// file must be a bshrub/btree here
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
return lfsr_bshrub_commit_(lfs, mdir, bshrub, bid, NULL, -1, return lfsr_bshrub_commit_(lfs, mdir, bshrub, bid, NULL, -1,
rats, rat_count); rats, rat_count);
} }
@@ -7596,25 +7541,32 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
// we're not quite done! we also need to bring over any // we're not quite done! we also need to bring over any
// unsynced files // unsynced files
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
// belongs to our mid? if (lfsr_o_isbshrub(o->flags)
if (!(lfsr_o_isbshrub(o->flags) // belongs to our mid?
&& o->mdir.mid == mdir__->mid)) { && o->mdir.mid == mdir__->mid
continue; // is a bshrub?
} && lfsr_bshrub_isbshrub(
lfsr_obshrub_t *bshrub = (lfsr_obshrub_t*)o; &o->mdir, &((lfsr_obshrub_t*)o)->bshrub)
// only compact once, first compact should
// inlined shrub? // stage the new block
if (lfsr_bshrub_isbshrub( && ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.blocks[0]
&bshrub->o.mdir, &bshrub->bshrub)
// only compact once, first compact should stage
// the new block
&& bshrub->bshrub_.u.bshrub.blocks[0]
!= mdir->rbyd.blocks[0]) { != mdir->rbyd.blocks[0]) {
int err = lfsr_shrub_compact(lfs, &mdir->rbyd, // empty bshrubs are a little bit weird in that
&bshrub->bshrub_.u.bshrub, // we don't want to write anything, but we still
&bshrub->bshrub.u.bshrub); // need to update the block
if (err) { if (((lfsr_obshrub_t*)o)->bshrub.u.weight == 0) {
return err; ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.weight = 0;
((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.blocks[0]
= mdir->rbyd.blocks[0];
((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.trunk
= LFSR_RBYD_ISSHRUB | 0;
} else {
int err = lfsr_shrub_compact(lfs, &mdir->rbyd,
&((lfsr_obshrub_t*)o)->bshrub_.u.bshrub,
&((lfsr_obshrub_t*)o)->bshrub.u.bshrub);
if (err) {
return err;
}
} }
} }
} }
@@ -7826,19 +7778,16 @@ static lfs_ssize_t lfsr_mdir_estimate__(lfs_t *lfs, const lfsr_mdir_t *mdir,
// files, I suppose if this becomes a problem we could sort // files, I suppose if this becomes a problem we could sort
// opened files by mid // opened files by mid
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
// belongs to our mdir + rid? if (lfsr_o_isbshrub(o->flags)
if (!(lfsr_o_isbshrub(o->flags) // belongs to our mdir + rid?
&& lfsr_mdir_cmp(&o->mdir, mdir) == 0 && lfsr_mdir_cmp(&o->mdir, mdir) == 0
&& lfsr_mid_rid(lfs, o->mdir.mid) == a_rid)) { && lfsr_mid_rid(lfs, o->mdir.mid) == a_rid
continue; // is a bshrub?
} && lfsr_bshrub_isbshrub(
lfsr_obshrub_t *bshrub = (lfsr_obshrub_t*)o; &o->mdir, &((lfsr_obshrub_t*)o)->bshrub)
&& ((lfsr_obshrub_t*)o)->bshrub.u.weight > 0) {
// inlined shrub?
if (lfsr_bshrub_isbshrub(&bshrub->o.mdir,
&bshrub->bshrub)) {
lfs_ssize_t dsize__ = lfsr_shrub_estimate(lfs, lfs_ssize_t dsize__ = lfsr_shrub_estimate(lfs,
&bshrub->bshrub.u.bshrub); &((lfsr_obshrub_t*)o)->bshrub.u.bshrub);
if (dsize__ < 0) { if (dsize__ < 0) {
return dsize__; return dsize__;
} }
@@ -7953,26 +7902,36 @@ static int lfsr_mdir_compact__(lfs_t *lfs, lfsr_mdir_t *mdir_,
// we're not quite done! we also need to bring over any unsynced files // we're not quite done! we also need to bring over any unsynced files
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
// belongs to our mdir? if (lfsr_o_isbshrub(o->flags)
if (!(lfsr_o_isbshrub(o->flags) // belongs to our mdir?
&& lfsr_mdir_cmp(&o->mdir, mdir) == 0 && lfsr_mdir_cmp(&o->mdir, mdir) == 0
&& lfsr_mid_rid(lfs, o->mdir.mid) >= start_rid && lfsr_mid_rid(lfs, o->mdir.mid) >= start_rid
&& (lfsr_rid_t)lfsr_mid_rid(lfs, o->mdir.mid) && (lfsr_rid_t)lfsr_mid_rid(lfs, o->mdir.mid)
< (lfsr_rid_t)end_rid)) { < (lfsr_rid_t)end_rid
continue; // is a bshrub?
} && lfsr_bshrub_isbshrub(
lfsr_obshrub_t *bshrub = (lfsr_obshrub_t*)o; &o->mdir, &((lfsr_obshrub_t*)o)->bshrub)
// only compact once, first compact should
// inlined shrub? // stage the new block
if (lfsr_bshrub_isbshrub(&bshrub->o.mdir, &bshrub->bshrub) && ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.blocks[0]
// only compact once, first compact should stage the new block
&& bshrub->bshrub_.u.bshrub.blocks[0]
!= mdir_->rbyd.blocks[0]) { != mdir_->rbyd.blocks[0]) {
err = lfsr_shrub_compact(lfs, &mdir_->rbyd, // empty bshrubs are a little bit weird in that
&bshrub->bshrub_.u.bshrub, &bshrub->bshrub.u.bshrub); // we don't want to write anything, but we still
if (err) { // need to update the block
LFS_ASSERT(err != LFS_ERR_RANGE); if (((lfsr_obshrub_t*)o)->bshrub.u.weight == 0) {
return err; ((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.weight = 0;
((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.blocks[0]
= mdir_->rbyd.blocks[0];
((lfsr_obshrub_t*)o)->bshrub_.u.bshrub.trunk
= LFSR_RBYD_ISSHRUB | 0;
} else {
int err = lfsr_shrub_compact(lfs, &mdir_->rbyd,
&((lfsr_obshrub_t*)o)->bshrub_.u.bshrub,
&((lfsr_obshrub_t*)o)->bshrub.u.bshrub);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
} }
} }
} }
@@ -10851,14 +10810,14 @@ static inline lfs_size_t lfsr_file_inlinesize(lfs_t *lfs,
static inline lfs_off_t lfsr_file_size_(const lfsr_file_t *file) { static inline lfs_off_t lfsr_file_size_(const lfsr_file_t *file) {
return lfs_max( return lfs_max(
file->buffer.pos + file->buffer.size, file->buffer.pos + file->buffer.size,
lfsr_bshrub_size(&file->o.bshrub)); file->o.bshrub.u.weight);
} }
// file operations // file operations
static int lfsr_file_fetch(lfs_t *lfs, lfsr_file_t *file, bool trunc) { static int lfsr_file_fetch(lfs_t *lfs, lfsr_file_t *file, bool trunc) {
// default data state // default data state
lfsr_bshrub_init(&file->o.bshrub); lfsr_bshrub_init(&file->o.o.mdir, &file->o.bshrub);
// discard the current buffer // discard the current buffer
file->buffer.pos = 0; file->buffer.pos = 0;
file->buffer.size = 0; file->buffer.size = 0;
@@ -11294,7 +11253,7 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
} }
// any data in our btree? // any data in our btree?
if (pos_ < lfsr_bshrub_size(&file->o.bshrub)) { if (pos_ < file->o.bshrub.u.weight) {
// bypass buffer? // bypass buffer?
if ((lfs_size_t)d >= lfsr_file_buffersize(lfs, file)) { if ((lfs_size_t)d >= lfsr_file_buffersize(lfs, file)) {
lfs_ssize_t d_ = lfsr_file_readnext(lfs, file, lfs_ssize_t d_ = lfsr_file_readnext(lfs, file,
@@ -11354,8 +11313,6 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
static int lfsr_file_commit(lfs_t *lfs, lfsr_file_t *file, static int lfsr_file_commit(lfs_t *lfs, lfsr_file_t *file,
lfs_off_t pos, const lfsr_rat_t *rats, lfs_size_t rat_count) { lfs_off_t pos, const lfsr_rat_t *rats, lfs_size_t rat_count) {
// file must be a bshrub/btree here
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(&file->o.bshrub));
return lfsr_bshrub_commit(lfs, return lfsr_bshrub_commit(lfs,
&file->o.o.mdir, &file->o.bshrub, &file->o.o.mdir, &file->o.bshrub,
pos, rats, rat_count); pos, rats, rat_count);
@@ -11379,7 +11336,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file,
// possible in case we ever don't track temporary copies. // possible in case we ever don't track temporary copies.
// try to merge commits where possible // try to merge commits where possible
lfsr_bid_t bid = lfsr_bshrub_size(&file->o.bshrub); lfsr_bid_t bid = file->o.bshrub.u.weight;
lfsr_rat_t rats[5]; lfsr_rat_t rats[5];
lfs_size_t rat_count = 0; lfs_size_t rat_count = 0;
union { union {
@@ -11391,36 +11348,27 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file,
uint8_t buf[LFSR_BPTR_DSIZE]; uint8_t buf[LFSR_BPTR_DSIZE];
} right; } right;
// always convert to bshrub/btree when this function is called
if (!lfsr_bshrub_isbshruborbtree(&file->o.bshrub)) {
// file must be a bnull here
LFS_ASSERT(lfsr_bshrub_isbnull(&file->o.bshrub));
// initialize bshrub's shrub
lfsr_shrub_init(&file->o.bshrub.u.bshrub,
file->o.o.mdir.rbyd.blocks[0]);
}
// need a hole? // need a hole?
if (pos > lfsr_bshrub_size(&file->o.bshrub)) { if (pos > file->o.bshrub.u.weight) {
// can we coalesce? // can we coalesce?
if (lfsr_bshrub_size(&file->o.bshrub) > 0) { if (file->o.bshrub.u.weight > 0) {
bid = lfs_min(bid, lfsr_bshrub_size(&file->o.bshrub)-1); bid = lfs_min(bid, file->o.bshrub.u.weight-1);
rats[rat_count++] = LFSR_RAT( rats[rat_count++] = LFSR_RAT(
LFSR_TAG_GROW, +(pos - lfsr_bshrub_size(&file->o.bshrub)), LFSR_TAG_GROW, +(pos - file->o.bshrub.u.weight),
LFSR_DATA_NULL()); LFSR_DATA_NULL());
// new hole // new hole
} else { } else {
bid = lfs_min(bid, lfsr_bshrub_size(&file->o.bshrub)); bid = lfs_min(bid, file->o.bshrub.u.weight);
rats[rat_count++] = LFSR_RAT( rats[rat_count++] = LFSR_RAT(
LFSR_TAG_DATA, +(pos - lfsr_bshrub_size(&file->o.bshrub)), LFSR_TAG_DATA, +(pos - file->o.bshrub.u.weight),
LFSR_DATA_NULL()); LFSR_DATA_NULL());
} }
} }
// try to carve any existing data // try to carve any existing data
lfsr_rat_t right_rat_ = {.tag=0}; lfsr_rat_t right_rat_ = {.tag=0};
while (pos < lfsr_bshrub_size(&file->o.bshrub)) { while (pos < file->o.bshrub.u.weight) {
lfsr_tag_t tag_; lfsr_tag_t tag_;
lfsr_bid_t weight_; lfsr_bid_t weight_;
lfsr_bptr_t bptr_; lfsr_bptr_t bptr_;
@@ -11610,21 +11558,21 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file,
if (weight + rat.weight > 0) { if (weight + rat.weight > 0) {
// can we coalesce a hole? // can we coalesce a hole?
if (lfsr_rat_size(rat) == 0 && pos > 0) { if (lfsr_rat_size(rat) == 0 && pos > 0) {
bid = lfs_min(bid, lfsr_bshrub_size(&file->o.bshrub)-1); bid = lfs_min(bid, file->o.bshrub.u.weight-1);
rats[rat_count++] = LFSR_RAT( rats[rat_count++] = LFSR_RAT(
LFSR_TAG_GROW, +(weight + rat.weight), LFSR_TAG_GROW, +(weight + rat.weight),
LFSR_DATA_NULL()); LFSR_DATA_NULL());
// need a new hole? // need a new hole?
} else if (lfsr_rat_size(rat) == 0) { } else if (lfsr_rat_size(rat) == 0) {
bid = lfs_min(bid, lfsr_bshrub_size(&file->o.bshrub)); bid = lfs_min(bid, file->o.bshrub.u.weight);
rats[rat_count++] = LFSR_RAT( rats[rat_count++] = LFSR_RAT(
LFSR_TAG_DATA, +(weight + rat.weight), LFSR_TAG_DATA, +(weight + rat.weight),
LFSR_DATA_NULL()); LFSR_DATA_NULL());
// append new fragment/bptr? // append new fragment/bptr?
} else { } else {
bid = lfs_min(bid, lfsr_bshrub_size(&file->o.bshrub)); bid = lfs_min(bid, file->o.bshrub.u.weight);
rats[rat_count++] = LFSR_RAT_( rats[rat_count++] = LFSR_RAT_(
rat.tag, +(weight + rat.weight), rat.tag, +(weight + rat.weight),
rat.cat, rat.count); rat.cat, rat.count);
@@ -11674,8 +11622,8 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
if (pos > 0 if (pos > 0
&& lfs->cfg->crystal_thresh > 0 && lfs->cfg->crystal_thresh > 0
&& (lfs_soff_t)(pos - (lfs->cfg->crystal_thresh-1)) && (lfs_soff_t)(pos - (lfs->cfg->crystal_thresh-1))
< (lfs_soff_t)lfsr_bshrub_size(&file->o.bshrub) < (lfs_soff_t)file->o.bshrub.u.weight
&& lfsr_bshrub_size(&file->o.bshrub) > 0 && file->o.bshrub.u.weight > 0
// don't bother to lookup left after the first block // don't bother to lookup left after the first block
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -11728,14 +11676,14 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// if we haven't already exceeded our crystallization threshold, // if we haven't already exceeded our crystallization threshold,
// find right crystal neighbor // find right crystal neighbor
if (crystal_end - crystal_start < lfs->cfg->crystal_thresh if (crystal_end - crystal_start < lfs->cfg->crystal_thresh
&& lfsr_bshrub_size(&file->o.bshrub) > 0) { && file->o.bshrub.u.weight > 0) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_tag_t tag; lfsr_tag_t tag;
lfsr_bid_t weight; lfsr_bid_t weight;
int err = lfsr_file_lookupnext(lfs, file, int err = lfsr_file_lookupnext(lfs, file,
lfs_min( lfs_min(
crystal_start + (lfs->cfg->crystal_thresh-1), crystal_start + (lfs->cfg->crystal_thresh-1),
lfsr_bshrub_size(&file->o.bshrub)-1), file->o.bshrub.u.weight-1),
&bid, &tag, &weight, &bptr); &bid, &tag, &weight, &bptr);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT); LFS_ASSERT(err != LFS_ERR_NOENT);
@@ -11771,7 +11719,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// crystal for this // crystal for this
block_start = crystal_start; block_start = crystal_start;
if (crystal_start > 0 if (crystal_start > 0
&& lfsr_bshrub_size(&file->o.bshrub) > 0 && file->o.bshrub.u.weight > 0
// don't bother to lookup left after the first block // don't bother to lookup left after the first block
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -11780,7 +11728,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
int err = lfsr_file_lookupnext(lfs, file, int err = lfsr_file_lookupnext(lfs, file,
lfs_min( lfs_min(
crystal_start-1, crystal_start-1,
lfsr_bshrub_size(&file->o.bshrub)-1), file->o.bshrub.u.weight-1),
&bid, &tag, &weight, &bptr); &bid, &tag, &weight, &bptr);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT); LFS_ASSERT(err != LFS_ERR_NOENT);
@@ -11847,14 +11795,14 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
+ (lfs->cfg->block_size - bptr.data.u.disk.off), + (lfs->cfg->block_size - bptr.data.u.disk.off),
lfs_max( lfs_max(
pos + size, pos + size,
lfsr_bshrub_size(&file->o.bshrub)))) { file->o.bshrub.u.weight))) {
// keep track of the next highest priority data offset // keep track of the next highest priority data offset
lfs_ssize_t d = lfs_min( lfs_ssize_t d = lfs_min(
block_start block_start
+ (lfs->cfg->block_size - bptr.data.u.disk.off), + (lfs->cfg->block_size - bptr.data.u.disk.off),
lfs_max( lfs_max(
pos + size, pos + size,
lfsr_bshrub_size(&file->o.bshrub))) - pos_; file->o.bshrub.u.weight)) - pos_;
// any data in our buffer? // any data in our buffer?
if (pos_ < pos + size && size > 0) { if (pos_ < pos + size && size > 0) {
@@ -11889,7 +11837,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
} }
// any data on disk? // any data on disk?
if (pos_ < lfsr_bshrub_size(&file->o.bshrub)) { if (pos_ < file->o.bshrub.u.weight) {
lfsr_bid_t bid_; lfsr_bid_t bid_;
lfsr_tag_t tag_; lfsr_tag_t tag_;
lfsr_bid_t weight_; lfsr_bid_t weight_;
@@ -12059,7 +12007,7 @@ fragment:;
// do we have a left sibling? // do we have a left sibling?
if (fragment_start > 0 if (fragment_start > 0
&& lfsr_bshrub_size(&file->o.bshrub) >= fragment_start && file->o.bshrub.u.weight >= fragment_start
// don't bother to lookup left after first fragment // don't bother to lookup left after first fragment
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -12107,7 +12055,7 @@ fragment:;
// do we have a right sibling? // do we have a right sibling?
// //
// note this may the same as our left sibling // note this may the same as our left sibling
if (fragment_end < lfsr_bshrub_size(&file->o.bshrub) if (fragment_end < file->o.bshrub.u.weight
// don't bother to lookup right if fragment is already full // don't bother to lookup right if fragment is already full
&& fragment_end - fragment_start < lfs->cfg->fragment_size) { && fragment_end - fragment_start < lfs->cfg->fragment_size) {
lfsr_bid_t bid; lfsr_bid_t bid;
@@ -12382,13 +12330,6 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
goto failed; goto failed;
} }
// note because of small-file caching and our current write
// strategy, we never actually end up with only a direct data
// or bptr
//
// this is convenient because bptrs are a bit annoying to commit
LFS_ASSERT(!lfsr_bshrub_isbmoss(&file->o.o.mdir, &file->o.bshrub));
LFS_ASSERT(!lfsr_bshrub_isbptr(&file->o.o.mdir, &file->o.bshrub));
// uncreated files must be unsynced // uncreated files must be unsynced
LFS_ASSERT(!lfsr_o_isuncreat(file->o.o.flags) LFS_ASSERT(!lfsr_o_isuncreat(file->o.o.flags)
|| lfsr_o_isunsync(file->o.o.flags)); || lfsr_o_isunsync(file->o.o.flags));
@@ -12422,8 +12363,9 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
goto failed; goto failed;
} }
// null? no rat? // bnull? no rat?
if (lfsr_file_size_(file) == 0) { if (lfsr_bshrub_isbshrub(&file->o.o.mdir, &file->o.bshrub)
&& file->o.bshrub.u.weight == 0) {
rats[rat_count++] = LFSR_RAT( rats[rat_count++] = LFSR_RAT(
LFSR_TAG_RM | LFSR_TAG_SUB | LFSR_TAG_STRUCT, 0, LFSR_TAG_RM | LFSR_TAG_SUB | LFSR_TAG_STRUCT, 0,
LFSR_DATA_NULL()); LFSR_DATA_NULL());
+7 -12
View File
@@ -694,20 +694,15 @@ typedef struct lfsr_bptr {
} lfsr_bptr_t; } lfsr_bptr_t;
// the lfsr_bshrub_t struct represents the on-disk component of a file // the lfsr_bshrub_t struct represents the on-disk component of a file
//
// navigating this union is a bit tricky, and relies on the related
// mdir block:
// block==mdir.block, weight==0 => bnull
// block==mdir.block, weight!=0 => bshrub
// block!=mdir.block => btree
typedef struct lfsr_bshrub { typedef struct lfsr_bshrub {
// navigating this union is a bit tricky, and relies on the related
// mdir's block:
//
// sign(size)=1, data.size==0 => bnull
// sign(size)=1, data.block==mdir.block => bmoss
// sign(size)=1, data.block!=mdir.block => bptr
// sign(size)=0, data.block==mdir.block => bshrub
// sign(size)=0, data.block!=mdir.block => btree
//
union { union {
lfs_off_t size; lfs_size_t weight;
lfsr_data_t bmoss;
lfsr_bptr_t bsprout;
lfsr_shrub_t bshrub; lfsr_shrub_t bshrub;
lfsr_btree_t btree; lfsr_btree_t btree;
} u; } u;