Implemented lfsr_btree_pop and btree merges
B-tree remove/merge is the most annoying part of B-trees. The implementation here follows the same ideas implemented in push/split: 1. Defer splits/merges until compaction. 2. Assume our split/merge will succeed and play it out into the rbyd. 3. On the first sign of failure, revert any unnecessary changes by appending deletes. 4. Do all of this in a single commit to avoid issues with single-prog blocks. Mapping this onto B-tree merge, the condition that triggers merge is when our rbyd is <1/4 the block_size after compaction, and the condition that aborts a merge is when our rbyd is >1/2 the block_size, since that would trigger a split on a later compact. Weaving this into lfsr_btree_commit is a bit subtle, but relatively straightforward all things considered. One downside is it's not physically possible to try merging with both siblings, so we have to choose just one to attempt a merge. We handle the corner case of merging the last sibling in a block explicitly, and in theory the other sibling will eventually trigger a merge during its own compaction. Extra annoying are the corner cases with merges in the root rbyd that make the root rbyd degenerate. We really should avoid a compaction in this case, as otherwise we would erase a block that we immediately inline at a significant cost. However determining if our root rbyd is degenerate is tricky. We can determine a degenerate root with children by checking if our rbyd's weight matches the B-tree's weight when we merge. But determining a degenerate root that is a leaf requires manually looking up both children in lfsr_btree_pop to see if they will result in a degenerate root. Ugh. On the bright side, this does all seem to be working now. Which completes the last of the core B-tree algorithms.
This commit is contained in:
@@ -530,6 +530,10 @@ static inline bool lfsr_tag_ismk(lfsr_tag_t tag) {
|
||||
return (tag & ~0x03f0) == LFSR_TAG_MK;
|
||||
}
|
||||
|
||||
static inline bool lfsr_tag_isstruct(lfsr_tag_t tag) {
|
||||
return (tag & ~0x03f0) == LFSR_TAG_STRUCT;
|
||||
}
|
||||
|
||||
//static inline bool lfsr_tag_isfound(lfsr_tag_t tag) {
|
||||
// // note that this is only for driver bookkeeping and never
|
||||
// // exists on disk
|
||||
@@ -3181,7 +3185,7 @@ static lfs_ssize_t lfsr_btree_lookup(lfs_t *lfs,
|
||||
|
||||
// fetch the next branch
|
||||
uint8_t buf[LFSR_BRANCH_DSIZE];
|
||||
lfs_size_t delta = lfs_min(LFSR_BRANCH_DSIZE, size_);
|
||||
lfs_ssize_t delta = lfs_min(LFSR_BRANCH_DSIZE, size_);
|
||||
err = lfs_bd_read(lfs,
|
||||
&lfs->pcache, &lfs->rcache, delta,
|
||||
rbyd_->block, off_, buf, delta);
|
||||
@@ -3189,9 +3193,9 @@ static lfs_ssize_t lfsr_btree_lookup(lfs_t *lfs,
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t delta_ = lfsr_branch_fromdisk(&branch, buf);
|
||||
if (delta_ < 0) {
|
||||
return delta_;
|
||||
delta = lfsr_branch_fromdisk(&branch, buf);
|
||||
if (delta < 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
// printf("branch %x#%x\n", branch.block, branch.limit);
|
||||
@@ -3213,7 +3217,7 @@ static lfs_ssize_t lfsr_btree_lookup(lfs_t *lfs,
|
||||
}
|
||||
|
||||
// TODO should we make sure this is a noop if buffer size is zero?
|
||||
lfs_size_t delta = lfs_min(size, size_);
|
||||
lfs_ssize_t delta = lfs_min(size, size_);
|
||||
err = lfs_bd_read(lfs,
|
||||
&lfs->pcache, &lfs->rcache, delta,
|
||||
rbyd_->block, off_, buffer, delta);
|
||||
@@ -3280,7 +3284,7 @@ static int lfsr_btree_parent(lfs_t *lfs,
|
||||
|
||||
// fetch the next branch
|
||||
uint8_t buf[LFSR_BRANCH_DSIZE];
|
||||
lfs_size_t delta = lfs_min(LFSR_BRANCH_DSIZE, size_);
|
||||
lfs_ssize_t delta = lfs_min(LFSR_BRANCH_DSIZE, size_);
|
||||
err = lfs_bd_read(lfs,
|
||||
&lfs->pcache, &lfs->rcache, delta,
|
||||
rbyd_->block, off_, buf, delta);
|
||||
@@ -3288,9 +3292,9 @@ static int lfsr_btree_parent(lfs_t *lfs,
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t delta_ = lfsr_branch_fromdisk(&branch, buf);
|
||||
if (delta_ < 0) {
|
||||
return delta_;
|
||||
delta = lfsr_branch_fromdisk(&branch, buf);
|
||||
if (delta < 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
// found our child?
|
||||
@@ -3316,6 +3320,8 @@ static lfs_ssize_t lfsr_btree_get(lfs_t *lfs,
|
||||
buffer, size);
|
||||
}
|
||||
|
||||
// TODO drop the root during merges
|
||||
// TODO inline?
|
||||
static int lfsr_btree_commit(lfs_t *lfs,
|
||||
lfsr_btree_t *btree, lfs_size_t id,
|
||||
lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs) {
|
||||
@@ -3355,44 +3361,115 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO can we deduplicate these somehow?
|
||||
// TODO btree merges
|
||||
if (!err) {
|
||||
// done?
|
||||
if (pid == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
lfs_ssize_t delta = lfsr_branch_todisk(
|
||||
&(const lfsr_branch_t){rbyd->block, rbyd->off},
|
||||
scratch_buf1);
|
||||
if (delta < 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
// TODO can we combine weight changes with normal tag updates?
|
||||
// maybe this should be looked at again
|
||||
scratch_attrs[0] = *LFSR_ATTR(
|
||||
BRANCH, pid, scratch_buf1, delta,
|
||||
&scratch_attrs[1]);
|
||||
// note grow/shrink with 0 is treated as a noop in rbyd
|
||||
if (rbyd->weight >= pweight) {
|
||||
scratch_attrs[1] = *LFSR_ATTR(
|
||||
GROW, pid-(pweight-1), NULL, rbyd->weight-pweight,
|
||||
NULL);
|
||||
} else {
|
||||
scratch_attrs[1] = *LFSR_ATTR(
|
||||
SHRINK, pid-(pweight-1), NULL, pweight-rbyd->weight,
|
||||
NULL);
|
||||
}
|
||||
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
if (err == LFS_ERR_RANGE) {
|
||||
goto compact;
|
||||
}
|
||||
|
||||
|
||||
// TODO can we deduplicate these somehow?
|
||||
// done?
|
||||
if (pid == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
lfs_ssize_t delta = lfsr_branch_todisk(
|
||||
&(const lfsr_branch_t){rbyd->block, rbyd->off},
|
||||
scratch_buf1);
|
||||
if (delta < 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
// TODO can we combine weight changes with normal tag updates?
|
||||
// maybe this should be looked at again
|
||||
scratch_attrs[0] = *LFSR_ATTR(
|
||||
BRANCH, pid, scratch_buf1, delta,
|
||||
&scratch_attrs[1]);
|
||||
// note grow/shrink with 0 is treated as a noop in rbyd
|
||||
if (rbyd->weight >= pweight) {
|
||||
scratch_attrs[1] = *LFSR_ATTR(
|
||||
GROW, pid-(pweight-1), NULL, rbyd->weight-pweight,
|
||||
NULL);
|
||||
} else {
|
||||
scratch_attrs[1] = *LFSR_ATTR(
|
||||
SHRINK, pid-(pweight-1), NULL, pweight-rbyd->weight,
|
||||
NULL);
|
||||
}
|
||||
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
|
||||
// no? try to compact
|
||||
compact:;
|
||||
// // wait are we root? is our root+attrs degenerate? we might be able to
|
||||
// // inline/collapse our root
|
||||
// //
|
||||
// // we really need to figure this out before allocation/erasing, but
|
||||
// // determining if our root+attrs is degenerate without writing to the
|
||||
// // rbyd is a bit difficult
|
||||
// if (pid == -1) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// goddammit this doesn't work if we have attrs, needs more thought
|
||||
//
|
||||
// // wait, are we root and a single branch?
|
||||
// if (pid == -1) {
|
||||
// lfs_size_t weight;
|
||||
// err = lfsr_rbyd_lookup(lfs, rbyd, LFSR_TAG_MK, 0,
|
||||
// NULL, NULL, &weight, NULL, NULL);
|
||||
// if (err) {
|
||||
// assert(!err);
|
||||
// return err;
|
||||
// }
|
||||
//
|
||||
// if (weight == rbyd->weight) {
|
||||
// lfsr_tag_t tag;
|
||||
// lfs_off_t off;
|
||||
// lfs_size_t size;
|
||||
// err = lfsr_rbyd_lookup(lfs, rbyd, LFSR_TAG_STRUCT, weight-1,
|
||||
// &tag, NULL, NULL, &off, &size);
|
||||
// if (err) {
|
||||
// assert(!err);
|
||||
// return err;
|
||||
// }
|
||||
//
|
||||
// // TODO we can probably use the inlined buf in both branches
|
||||
// // TODO deduplicate?
|
||||
// if (tag == LFSR_TAG_BRANCH) {
|
||||
// uint8_t buf[LFSR_BRANCH_DSIZE];
|
||||
// // TODO wait, why min and not an assert?
|
||||
// lfs_ssize_t delta = lfs_min(LFSR_BRANCH_DSIZE, size);
|
||||
// err = lfs_bd_read(lfs,
|
||||
// &lfs->pcache, &lfs->rcache, delta,
|
||||
// rbyd->block, off, buf, delta);
|
||||
// if (err) {
|
||||
// assert(!err);
|
||||
// return err;
|
||||
// }
|
||||
//
|
||||
// delta = lfsr_branch_fromdisk(&btree->u.trunk, buf);
|
||||
// if (delta < 0) {
|
||||
// assert(!delta);
|
||||
// return delta;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// } else {
|
||||
// LFS_ASSERT(size <= LFSR_BTREE_INLINE_SIZE);
|
||||
// err = lfs_bd_read(lfs,
|
||||
// &lfs->pcache, &lfs->rcache, size,
|
||||
// rbyd->block, off, btree->u.inlined.buf, size);
|
||||
// if (err) {
|
||||
// assert(!err);
|
||||
// return err;
|
||||
// }
|
||||
//
|
||||
// btree->tag = tag;
|
||||
// btree->u.inlined.size = size;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO were we doing something funky with rev?
|
||||
// first allocate a new rbyd
|
||||
@@ -3402,6 +3479,7 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO wait do we need to guarantee an id boundary here? will we always shrink?
|
||||
// now copy over ids
|
||||
lfsr_tag_t tag = 0;
|
||||
lfs_ssize_t id = 0;
|
||||
@@ -3445,6 +3523,13 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
}
|
||||
}
|
||||
|
||||
// is our compacted size too small? try to merge with one of
|
||||
// our siblings
|
||||
if (rbyd_.off < lfs->cfg->block_size/4) {
|
||||
goto merge;
|
||||
abort:;
|
||||
}
|
||||
|
||||
// finalize commit with new attrs, it's up to upper
|
||||
// layers to make sure these always fit
|
||||
err = lfsr_rbyd_commit(lfs, &rbyd_, attrs);
|
||||
@@ -3460,7 +3545,7 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
}
|
||||
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
lfs_ssize_t delta = lfsr_branch_todisk(
|
||||
delta = lfsr_branch_todisk(
|
||||
&(const lfsr_branch_t){rbyd_.block, rbyd_.off},
|
||||
scratch_buf1);
|
||||
if (delta < 0) {
|
||||
@@ -3675,10 +3760,6 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
scratch_buf2, delta2,
|
||||
NULL);
|
||||
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
|
||||
// yes parent? push up split
|
||||
} else {
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
@@ -3729,10 +3810,199 @@ static int lfsr_btree_commit(lfs_t *lfs,
|
||||
scratch_buf2, delta2,
|
||||
NULL);
|
||||
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
}
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
|
||||
merge:;
|
||||
// no parent? can't merge
|
||||
if (pid == -1) {
|
||||
goto abort;
|
||||
}
|
||||
|
||||
// last child? try the left sibling
|
||||
lfs_ssize_t sid;
|
||||
lfs_ssize_t sdelta;
|
||||
if ((lfs_size_t)pid == parent.weight-1) {
|
||||
sid = pid-pweight;
|
||||
sdelta = 0;
|
||||
// not last child? try the right sibling
|
||||
} else {
|
||||
sid = pid+1;
|
||||
sdelta = pweight;
|
||||
}
|
||||
|
||||
// try looking up the sibling
|
||||
lfs_size_t sweight;
|
||||
err = lfsr_rbyd_lookup(lfs, &parent, LFSR_TAG_MK, sid,
|
||||
NULL, &sid, &sweight, NULL, NULL);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// no sibling? can't merge
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
goto abort;
|
||||
}
|
||||
|
||||
lfsr_tag_t stag;
|
||||
lfs_off_t off;
|
||||
lfs_size_t size;
|
||||
err = lfsr_rbyd_lookup(lfs, &parent, LFSR_TAG_BRANCH, sid,
|
||||
&stag, NULL, NULL, &off, &size);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// no sibling? can't merge
|
||||
if (err == LFS_ERR_NOENT || stag != LFSR_TAG_BRANCH) {
|
||||
goto abort;
|
||||
}
|
||||
|
||||
uint8_t buf[LFSR_BRANCH_DSIZE];
|
||||
delta = lfs_min(LFSR_BRANCH_DSIZE, size);
|
||||
err = lfs_bd_read(lfs,
|
||||
&lfs->pcache, &lfs->rcache, delta,
|
||||
parent.block, off, buf, delta);
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
lfsr_branch_t branch;
|
||||
delta = lfsr_branch_fromdisk(&branch, buf);
|
||||
if (delta < 0) {
|
||||
assert(!delta);
|
||||
return delta;
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_fetch(lfs, &sibling, branch.block, branch.limit, NULL);
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// try to add our sibling's tags to our rbyd
|
||||
tag = 0;
|
||||
id = 0;
|
||||
while (true) {
|
||||
lfs_size_t weight;
|
||||
err = lfsr_rbyd_lookup(lfs, &sibling, lfsr_tag_next(tag), id,
|
||||
&tag, &id, &weight, &off, &size);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO this is really wasteful and throws off our predicted
|
||||
// size, can we combine grows into the tag append in the rbyd
|
||||
// somehow?
|
||||
// create grows as necessary
|
||||
err = lfsr_rbyd_append(lfs, &rbyd_,
|
||||
LFSR_TAG_GROW,
|
||||
sdelta + id - (weight-1),
|
||||
// TODO also this is a weird way to use lfsr_data_t
|
||||
LFSR_DATA_BUF(NULL, weight));
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// append the attr
|
||||
err = lfsr_rbyd_append(lfs, &rbyd_,
|
||||
tag, sdelta + id, LFSR_DATA_DISK(sibling.block, off, size));
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// if we exceed our compaction threshold our merge has
|
||||
// failed, clean up ids and abort
|
||||
if (rbyd_.off > lfs->cfg->block_size/2) {
|
||||
err = lfsr_rbyd_append(lfs, &rbyd_,
|
||||
LFSR_TAG_SHRINK,
|
||||
sdelta,
|
||||
// TODO also this is a weird way to use lfsr_data_t
|
||||
LFSR_DATA_BUF(NULL, rbyd_.weight - pweight));
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
goto abort;
|
||||
}
|
||||
}
|
||||
|
||||
// finalize commit with new attrs, it's up to upper
|
||||
// layers to make sure these always fit
|
||||
for (const struct lfsr_attr *attr = attrs; attr; attr = attr->next) {
|
||||
err = lfsr_rbyd_append(lfs, &rbyd_,
|
||||
// TODO can this be expressed better?
|
||||
attr->tag, attr->id + (sdelta == 0 ? sweight : 0),
|
||||
LFSR_DATA_BUF(attr->buffer, attr->size));
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_commit(lfs, &rbyd_, NULL);
|
||||
if (err) {
|
||||
assert(!err);
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t mid = lfs_smax32(pid, sid);
|
||||
lfs_size_t mweight = pweight + sweight;
|
||||
|
||||
// we must have a parent at this point, but is our parent degenerate?
|
||||
LFS_ASSERT(pid != -1);
|
||||
if (mweight == btree->weight) {
|
||||
// collapse our parent, decreasing the height of the tree
|
||||
LFS_ASSERT(btree->u.trunk.block == parent.block
|
||||
&& btree->u.trunk.limit == parent.off);
|
||||
btree->weight = rbyd_.weight;
|
||||
btree->u.trunk.block = rbyd_.block;
|
||||
btree->u.trunk.limit = rbyd_.off;
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
// push up merge
|
||||
lfs_ssize_t delta1 = lfsr_branch_todisk(
|
||||
&(const lfsr_branch_t){rbyd_.block, rbyd_.off},
|
||||
scratch_buf1);
|
||||
if (delta1 < 0) {
|
||||
assert(!delta1);
|
||||
return delta1;
|
||||
}
|
||||
|
||||
scratch_attrs[0] = *LFSR_ATTR(
|
||||
SHRINK, mid-(mweight-1), NULL, mweight,
|
||||
&scratch_attrs[1]);
|
||||
|
||||
scratch_attrs[1] = *LFSR_ATTR(
|
||||
GROW, mid-(mweight-1),
|
||||
NULL, rbyd_.weight,
|
||||
&scratch_attrs[2]);
|
||||
scratch_attrs[2] = *LFSR_ATTR(
|
||||
MKBRANCH, mid-(mweight-1)+rbyd_.weight-1,
|
||||
NULL, 0,
|
||||
&scratch_attrs[3]);
|
||||
scratch_attrs[3] = *LFSR_ATTR(
|
||||
BRANCH, mid-(mweight-1)+rbyd_.weight-1,
|
||||
scratch_buf1, delta1,
|
||||
NULL);
|
||||
}
|
||||
|
||||
*rbyd = parent;
|
||||
attrs = scratch_attrs;
|
||||
continue;
|
||||
}
|
||||
|
||||
// at this point rbyd should be the trunk of our tree
|
||||
@@ -3863,6 +4133,94 @@ static int lfsr_btree_update(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
NULL))));
|
||||
}
|
||||
}
|
||||
|
||||
static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t id) {
|
||||
LFS_ASSERT(id < btree->weight);
|
||||
|
||||
// inlined btree?
|
||||
if (btree->tag) {
|
||||
LFS_ASSERT(id == btree->weight-1);
|
||||
btree->weight = 0;
|
||||
return 0;
|
||||
|
||||
// a normal btree
|
||||
} else {
|
||||
// lookup in which leaf our id resides
|
||||
lfsr_rbyd_t rbyd;
|
||||
lfsr_tag_t rtag;
|
||||
lfs_ssize_t rid;
|
||||
lfs_size_t rweight;
|
||||
lfs_ssize_t size = lfsr_btree_lookup(lfs, btree, id,
|
||||
&rtag, NULL, &rbyd, &rid, &rweight, NULL, 0);
|
||||
if (size < 0) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// can we collapse into an inlined btree?
|
||||
if (btree->u.trunk.block == rbyd.block
|
||||
&& btree->u.trunk.limit == rbyd.off) {
|
||||
// last child? try the left sibling
|
||||
lfs_ssize_t sid;
|
||||
if ((lfs_size_t)rid == rbyd.weight-1) {
|
||||
sid = rid-rweight;
|
||||
// not last child? try the right sibling
|
||||
} else {
|
||||
sid = rid+1;
|
||||
}
|
||||
|
||||
// try looking up the sibling
|
||||
lfs_size_t sweight;
|
||||
int err = lfsr_rbyd_lookup(lfs, &rbyd, LFSR_TAG_MK, sid,
|
||||
NULL, &sid, &sweight, NULL, NULL);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// no sibling? null btree
|
||||
if (err == LFS_ERR_NOENT) {
|
||||
btree->weight = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lfsr_tag_t stag;
|
||||
lfs_off_t off;
|
||||
lfs_size_t size;
|
||||
err = lfsr_rbyd_lookup(lfs, &rbyd, LFSR_TAG_STRUCT, sid,
|
||||
&stag, NULL, NULL, &off, &size);
|
||||
if (err && err != LFS_ERR_NOENT) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// no sibling? null btree
|
||||
if (err == LFS_ERR_NOENT || !lfsr_tag_isstruct(stag)) {
|
||||
btree->weight = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// one sibling? inline!
|
||||
if (rweight + sweight == btree->weight) {
|
||||
btree->tag = stag;
|
||||
btree->weight = sweight;
|
||||
LFS_ASSERT(size <= LFSR_BTREE_INLINE_SIZE);
|
||||
err = lfs_bd_read(lfs,
|
||||
&lfs->pcache, &lfs->rcache, size,
|
||||
rbyd.block, off, btree->u.inlined.buf, size);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
btree->u.inlined.size = size;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// remove our id, letting lfsr_btree_commit take care
|
||||
// of the rest
|
||||
return lfsr_btree_commit(lfs, btree, id, &rbyd,
|
||||
LFSR_ATTR(SHRINK, rid-(rweight-1), NULL, rweight,
|
||||
NULL));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -269,7 +269,7 @@ def show_log(block_size, data, rev, off, *,
|
||||
lifetimes_[i:i+1] = []
|
||||
shrinks.add(i + len(shrinks))
|
||||
|
||||
checkpoint(j, weights, lifetimes, set(), shrinks, shrinks)
|
||||
checkpoint(j, weights, lifetimes, set(), shrinks, {i})
|
||||
weights = weights_
|
||||
lifetimes = lifetimes_
|
||||
|
||||
|
||||
+892
-34
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user