diff --git a/lfs.c b/lfs.c index 6ba203ba..fb8d1d08 100644 --- a/lfs.c +++ b/lfs.c @@ -3952,40 +3952,21 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree, if ((lfs_size_t)estimate <= lfs->cfg->block_size/4 // no parent? can't merge && rid != -1) { - for (uint8_t i = 0; i < 2; i++) { - lfs_ssize_t sibling_rid; - // try the right sibling - if (i == 0) { - sibling_rid = rid+1; - // no right sibling? can't merge - if (sibling_rid >= (lfs_ssize_t)parent.weight) { - continue; - } - - // try the left sibling - } else { - sibling_rid = rid-rbyd.weight; - // no left sibling? can't merge - if (sibling_rid < 0) { - continue; - } - } - + // try the right sibling + if (rid+1 < (lfs_ssize_t)parent.weight) { // try looking up the sibling // TODO do we really need to fetch sibling_weight if we get // it in our btree struct? + lfs_ssize_t sibling_rid; lfsr_tag_t sibling_tag; lfs_size_t sibling_weight; lfsr_data_t sibling_data; err = lfsr_rbyd_lookupnext(lfs, &parent, - sibling_rid, LFSR_TAG_NAME, + rid+1, LFSR_TAG_NAME, &sibling_rid, &sibling_tag, &sibling_weight, &sibling_data); if (err) { - // no sibling? can't merge - if (err == LFS_ERR_NOENT) { - continue; - } + LFS_ASSERT(err != LFS_ERR_NOENT); return err; } @@ -4011,22 +3992,71 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree, &sibling, -1, -1, NULL); if (sibling_estimate < 0) { - return estimate; + return sibling_estimate; } // fits? try to merge if ((lfs_size_t)(estimate + sibling_estimate) < lfs->cfg->block_size/2) { - if (i == 1) { - // if we're merging our left sibling, swap our rbyds - // so our sibling is on the right - bid -= sibling.weight; - rid -= rbyd.weight; + goto merge; + } + } - rbyd_ = sibling; - sibling = rbyd; - rbyd = rbyd_; + // try the left sibling + if (rid-(lfs_ssize_t)rbyd.weight >= 0) { + // try looking up the sibling + // TODO do we really need to fetch sibling_weight if we get + // it in our btree struct? + lfs_ssize_t sibling_rid; + lfsr_tag_t sibling_tag; + lfs_size_t sibling_weight; + lfsr_data_t sibling_data; + err = lfsr_rbyd_lookupnext(lfs, &parent, + rid-rbyd.weight, LFSR_TAG_NAME, + &sibling_rid, &sibling_tag, &sibling_weight, + &sibling_data); + if (err) { + LFS_ASSERT(err != LFS_ERR_NOENT); + return err; + } + + if (sibling_tag == LFSR_TAG_NAME) { + err = lfsr_rbyd_lookup(lfs, &parent, + sibling_rid, LFSR_TAG_WIDE(STRUCT), + &sibling_tag, &sibling_data); + if (err) { + LFS_ASSERT(err != LFS_ERR_NOENT); + return err; } + } + + LFS_ASSERT(sibling_tag == LFSR_TAG_BRANCH); + err = lfsr_data_readbranch(lfs, &sibling_data, sibling_weight, + &sibling); + if (err) { + return err; + } + + // estimate if our sibling will fit + lfs_ssize_t sibling_estimate = lfsr_rbyd_estimate(lfs, + &sibling, -1, -1, + NULL); + if (sibling_estimate < 0) { + return sibling_estimate; + } + + // fits? try to merge + if ((lfs_size_t)(estimate + sibling_estimate) + < lfs->cfg->block_size/2) { + // if we're merging our left sibling, swap our rbyds + // so our sibling is on the right + bid -= sibling.weight; + rid -= rbyd.weight; + + rbyd_ = sibling; + sibling = rbyd; + rbyd = rbyd_; + goto merge; } }