Cleaned up shrub/tree reading/lookups quite a bit

- Added lfsr_shrub_lookupnext/lfsr_tree_lookupnext to deduplicate
  the various tree lookups that need to support inlined sprouts/bptrs.

  Also moved implicit bptr dereferencing here, though this may need
  to be tweaked a bit to support data checksumming.

- Moved inlined sprouts/bptrs into readnext (well, lookupnext really).

  This simplifies things anywhere we just need to read data from these
  trees.

- Renamed lfsr_file_carveshrub/lfsr_file_carvetree ->
  lfsr_shrub_carve/lfsr_tree_carve and changed parameters appropriately.

  Though these aren't so clear cut. lfsr_shrub_carve still needs the
  related file structure to know which mdir to commit to.

  lfsr_shrub_carve will also need significant tweaking to support
  recovery from failed file writes.

This also ends up losing the shrub/tree lookup reuse. There might still
be a way to deduplicate the read logic after shrub/tree lookup, but it's
probably not worth it considering this logic has become a rather small
part of the lookupnext/readnext machinery

Both shrubs and trees end up calling rbyd/btree lookupnext anyways...
This commit is contained in:
Christopher Haster
2023-10-25 14:09:56 -05:00
parent bcefc3ef06
commit 36e8f01261
+281 -336
View File
@@ -9113,40 +9113,197 @@ int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) {
return err;
}
// common iterator over data elements in a rbyd/btree
// lookups/reads/iterators over data elements in shrubs/trees/files
static int lfsr_shrub_lookupnext(lfs_t *lfs, const lfsr_shrub_t *shrub,
lfs_off_t pos,
lfsr_rid_t *rid_,
lfsr_tag_t *tag_, lfsr_rid_t *weight_, lfsr_data_t *data_) {
// out of range?
if (pos > lfsr_shrub_size(shrub)) {
return LFS_ERR_NOENT;
}
// sprout or shrub?
if (!lfsr_shrub_hasshrub(shrub)) {
if (rid_) {
*rid_ = lfsr_data_size(&shrub->u.data)-1;
}
if (tag_) {
*tag_ = LFSR_TAG_DATA;
}
if (weight_) {
*weight_ = lfsr_data_size(&shrub->u.data);
}
if (data_) {
*data_ = shrub->u.data;
}
return 0;
} else {
lfsr_srid_t rid;
lfsr_tag_t tag;
lfsr_rid_t weight;
lfsr_data_t data;
int err = lfsr_rbyd_lookupnext(lfs, &shrub->u.rbyd, pos, 0,
&rid, &tag, &weight, &data);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag == LFSR_TAG_SHRUB(DATA));
LFS_ASSERT(lfsr_data_size(&data) <= weight);
if (rid_) {
*rid_ = rid;
}
if (tag_) {
*tag_ = tag;
}
if (weight_) {
*weight_ = weight;
}
if (data_) {
*data_ = data;
}
return 0;
}
}
static int lfsr_shrub_readnext(lfs_t *lfs, const lfsr_shrub_t *shrub,
lfs_off_t pos, lfs_off_t size,
lfs_off_t *weight_, lfsr_data_t *data_) {
lfsr_rid_t rid;
lfsr_tag_t tag;
lfsr_rid_t weight;
lfsr_data_t data;
int err = lfsr_shrub_lookupnext(lfs, shrub, pos,
&rid, &tag, &weight, &data);
if (err) {
return err;
}
if (pos < rid-(weight-1) + lfsr_data_size(&data)) {
// note one important side-effect here is any reads to this
// data get a strict read hint
lfs_off_t d = lfs_min32(
size,
lfsr_data_size(&data) - (pos - (rid-(weight-1))));
if (weight_) {
*weight_ = d;
}
if (data_) {
*data_ = LFSR_DATA_DISK(
data.u.disk.block,
data.u.disk.off + (pos - (rid-(weight-1))),
d);
}
return 0;
}
// found a hole, just make sure next leaf takes priority
lfs_off_t d = lfs_min32(size, rid+1 - pos);
// TODO should we have a special LFSR_DATA_HOLE representation?
// found a hole?
if (weight_) {
*weight_ = d;
}
if (data_) {
*data_ = LFSR_DATA_NULL;
}
return 0;
}
static int lfsr_tree_lookupnext(lfs_t *lfs, const lfsr_tree_t *tree,
lfs_off_t pos,
lfsr_bid_t *bid_,
lfsr_tag_t *tag_, lfsr_bid_t *weight_, lfsr_data_t *data_) {
// out of range?
if (pos > lfsr_tree_size(tree)) {
return LFS_ERR_NOENT;
}
// direct bptr or btree?
if (!lfsr_tree_hasbtree(tree)) {
if (bid_) {
// TODO lfsr_bptr_size()?
*bid_ = lfsr_tree_size(tree)-1;
}
if (tag_) {
*tag_ = LFSR_TAG_BLOCK;
}
if (weight_) {
*weight_ = lfsr_tree_size(tree);
}
if (data_) {
// TODO bptr_t should have a data field or something
*data_ = LFSR_DATA_DISK(
tree->u.bptr.block,
tree->u.bptr.off,
lfsr_tree_size(tree));
}
return 0;
} else {
lfsr_bid_t bid;
lfsr_tag_t tag;
lfsr_bid_t weight;
lfsr_data_t data;
int err = lfsr_btree_lookupnext(lfs, &tree->u.btree, pos,
&bid, &tag, &weight, &data);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag == LFSR_TAG_DATA
|| tag == LFSR_TAG_BLOCK);
if (bid_) {
*bid_ = bid;
}
if (tag_) {
*tag_ = tag;
}
if (weight_) {
*weight_ = weight;
}
if (data_) {
// decode bptrs
if (tag == LFSR_TAG_BLOCK) {
lfsr_bptr_t bptr;
err = lfsr_data_readbptr(lfs, &data, &bptr);
if (err) {
return err;
}
// TODO bptr_t should have a data field or something
data = LFSR_DATA_DISK(
bptr.block,
bptr.off,
bptr.size);
}
LFS_ASSERT(lfsr_data_size(&data) <= weight);
*data_ = data;
}
return 0;
}
}
static int lfsr_tree_readnext(lfs_t *lfs, const lfsr_tree_t *tree,
lfs_off_t pos, lfs_off_t size,
lfs_off_t *weight_, lfsr_data_t *data_) {
// TODO should we move the bptr/data reads here?
// assume we're a tree here
LFS_ASSERT(lfsr_tree_hasbtree(tree));
lfsr_bid_t bid;
lfsr_tag_t tag;
lfsr_bid_t weight;
lfsr_data_t data;
int err = lfsr_btree_lookupnext(lfs, &tree->u.btree, pos,
int err = lfsr_tree_lookupnext(lfs, tree, pos,
&bid, &tag, &weight, &data);
if (err) {
return err;
}
LFS_ASSERT(lfsr_tag_key(tag) == LFSR_TAG_DATA
|| lfsr_tag_key(tag) == LFSR_TAG_BLOCK);
// decode bptrs
if (tag == LFSR_TAG_BLOCK) {
lfsr_bptr_t bptr;
err = lfsr_data_readbptr(lfs, &data, &bptr);
if (err) {
return err;
}
data = LFSR_DATA_DISK(
bptr.block,
bptr.off,
bptr.size);
}
LFS_ASSERT(lfsr_data_size(&data) <= weight);
if (pos < bid-(weight-1) + lfsr_data_size(&data)) {
// note one important side-effect here is any reads to this
@@ -9182,14 +9339,6 @@ static int lfsr_tree_readnext(lfs_t *lfs, const lfsr_tree_t *tree,
return 0;
}
static int lfsr_shrub_readnext(lfs_t *lfs, const lfsr_shrub_t *tree,
lfs_off_t pos, lfs_off_t size,
lfs_off_t *weight_, lfsr_data_t *data_) {
// we can pretend the shrub is a single-rbyd btree
return lfsr_tree_readnext(lfs, (const lfsr_tree_t*)tree, pos, size,
weight_, data_);
}
static int lfsr_file_readnext(lfs_t *lfs, const lfsr_file_t *file,
lfs_off_t pos, lfs_off_t size,
lfs_off_t *weight_, lfsr_data_t *data_) {
@@ -9223,30 +9372,8 @@ static int lfsr_file_readnext(lfs_t *lfs, const lfsr_file_t *file,
d = lfs_min32(d, file->buffer_pos - pos);
}
// has a sprout?
if (lfsr_shrub_hassprout(&file->shrub)
&& pos < lfsr_shrub_size(&file->shrub)) {
// note one important side-effect here is any reads to this
// data get a strict read hint
d = lfs_min32(
d,
lfsr_data_size(&file->shrub.u.data) - pos);
if (weight_) {
*weight_ = d;
}
if (data_) {
*data_ = LFSR_DATA_DISK(
file->shrub.u.data.u.disk.block,
file->shrub.u.data.u.disk.off + pos,
d);
}
return 0;
// has a shrub?
} else if (lfsr_shrub_hasshrub(&file->shrub)
&& pos < lfsr_shrub_size(&file->shrub)) {
// we can pretend the shrub is a single-rbyd btree
// any data in our shrub?
if (pos < lfsr_shrub_size(&file->shrub)) {
lfs_off_t weight;
lfsr_data_t data;
int err = lfsr_shrub_readnext(lfs, &file->shrub, pos, d,
@@ -9271,28 +9398,8 @@ static int lfsr_file_readnext(lfs_t *lfs, const lfsr_file_t *file,
d = lfs_min32(d, weight);
}
// has a direct block?
if (lfsr_tree_hasbptr(&file->tree)
&& pos < lfsr_tree_size(&file->tree)) {
d = lfs_min32(
d,
file->tree.u.bptr.size - pos);
if (weight_) {
*weight_ = d;
}
if (data_) {
*data_ = LFSR_DATA_DISK(
file->tree.u.bptr.block,
file->tree.u.bptr.off + pos,
d);
}
return 0;
// has an indirect btree?
} else if (lfsr_tree_hasbtree(&file->tree)
&& pos < lfsr_tree_size(&file->tree)) {
// we can pretend our shrub is a small btree to save a bit of code
// any data in our tree?
if (pos < lfsr_tree_size(&file->tree)) {
lfs_off_t weight;
lfsr_data_t data;
int err = lfsr_tree_readnext(lfs, &file->tree, pos, d,
@@ -9379,11 +9486,9 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
}
// needed by lfsr_file_flushbuffer
static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file);
// TODO can lfsr_file_carveshrub and lfsr_file_carvetree be combined somehow?
static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
// TODO does this actually support any shrubs outside of the file?
static int lfsr_shrub_carve(lfs_t *lfs, lfsr_file_t *file, lfsr_shrub_t *shrub,
lfs_off_t pos, lfs_off_t weight, lfs_soff_t delta,
lfsr_tag_t tag, lfsr_data_t data) {
// only inlined data is supported in shrubs
@@ -9400,46 +9505,36 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
lfs_off_t rm = 0;
// TODO just use another variable in file for shrub estimate? I don't
// think unionizing this variable gets us anything
lfs_off_t estimate = (lfsr_shrub_hasshrub(&file->shrub))
? file->shrub.u.shrub.estimate
lfs_off_t estimate = (lfsr_shrub_hasshrub(shrub))
? shrub->u.shrub.estimate
: 0;
// do we need to add sprout data to our shrub?
//
// we only do this here if we aren't carving
if (lfsr_shrub_hassprout(&file->shrub)
&& (pos >= lfsr_shrub_size(&file->shrub)
if (lfsr_shrub_hassprout(shrub)
&& (pos >= lfsr_shrub_size(shrub)
|| pos + weight == 0)) {
attrs[attr_count++] = LFSR_ATTR(0,
SHRUB(DATA), +lfsr_shrub_size(&file->shrub),
DATA(file->shrub.u.data));
SHRUB(DATA), +lfsr_shrub_size(shrub),
DATA(shrub->u.data));
// update our estimate
estimate += LFSR_ATTR_ESTIMATE + lfsr_shrub_size(&file->shrub);
estimate += LFSR_ATTR_ESTIMATE + lfsr_shrub_size(shrub);
}
// try to carve any existing data
lfs_off_t pos_ = pos;
while (pos_ < lfs_min32(pos+weight, lfsr_shrub_size(&file->shrub))) {
lfsr_srid_t rid_;
while (pos_ < lfs_min32(pos+weight, lfsr_shrub_size(shrub))) {
lfsr_rid_t rid_;
lfsr_tag_t tag_;
lfsr_rid_t weight_;
lfsr_data_t data_;
// sprout or shrub?
if (!lfsr_shrub_hasshrub(&file->shrub)) {
rid_ = lfsr_data_size(&file->shrub.u.data)-1;
tag_ = LFSR_TAG_DATA;
weight_ = lfsr_data_size(&file->shrub.u.data);
data_ = file->shrub.u.data;
} else {
int err = lfsr_rbyd_lookupnext(lfs, &file->shrub.u.rbyd,
pos_, 0,
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_SHRUB(DATA));
int err = lfsr_shrub_lookupnext(lfs, shrub, pos_,
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// note an entry can be both a left and right sibling!
@@ -9458,7 +9553,7 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
// some special cases require a new tag: if we're creating a
// new shrub
if (!lfsr_shrub_hasshrub(&file->shrub)) {
if (!lfsr_shrub_hasshrub(shrub)) {
attrs[attr_count++] = LFSR_ATTR(0,
SHRUB(DATA), +(weight_ - overlap_),
DATA(slice_));
@@ -9485,7 +9580,7 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
}
// found right sibling?
if (pos + weight < (lfs_off_t)rid_+1) {
if (pos + weight < rid_+1) {
lfs_off_t overlap_ = (pos + weight) - (rid_-(weight_-1));
LFS_ASSERT((lfs_soff_t)overlap_ >= 0);
@@ -9500,7 +9595,7 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
// some special cases require a new tag: if we're creating a
// new btree, or if left/right siblings are the same
if (!lfsr_shrub_hasshrub(&file->shrub)
if (!lfsr_shrub_hasshrub(shrub)
|| overlap_ > weight) {
// can we coalesce a hole?
if (lfsr_data_size(&slice_) == 0) {
@@ -9537,8 +9632,8 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
// found fully overwritten data?
if (pos <= rid_-(weight_-1)
&& pos + weight >= (lfs_off_t)rid_+1
&& lfsr_shrub_hasshrub(&file->shrub)) {
&& pos + weight >= rid_+1
&& lfsr_shrub_hasshrub(shrub)) {
// combine all rms into a single attr
rm += weight_;
@@ -9556,10 +9651,10 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
}
// need a hole?
if (pos > lfsr_shrub_size(&file->shrub)
if (pos > lfsr_shrub_size(shrub)
// if we have no data we can coalesce our hole here
|| (weight + delta > 0 && lfsr_data_size(&data) == 0)) {
lfs_off_t pos_ = lfs_min32(pos, lfsr_shrub_size(&file->shrub));
lfs_off_t pos_ = lfs_min32(pos, lfsr_shrub_size(shrub));
lfs_off_t hole = pos - pos_
+ ((lfsr_data_size(&data) == 0) ? weight + delta : 0);
@@ -9608,10 +9703,13 @@ static int lfsr_file_carveshrub(lfs_t *lfs, lfsr_file_t *file,
}
// update our estimate
file->shrub.u.shrub.estimate = estimate;
shrub->u.shrub.estimate = estimate;
return 0;
}
// needed by lfsr_file_flushbuffer
static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file);
// TODO this should also take in extra data during writes
static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// Two things we need to do:
@@ -9639,27 +9737,16 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
if (pos > 0 && lfsr_shrub_size(&file->shrub) >= pos
// don't bother to lookup left after first fragment
&& pos == file->buffer_pos) {
// TODO should these shrub lookups be deduplicated?
// lfsr_shrub_lookupnext?
lfsr_srid_t rid_;
lfsr_rid_t rid_;
lfsr_tag_t tag_;
lfsr_rid_t weight_;
lfsr_data_t data_;
// sprout or shrub?
if (!lfsr_shrub_hasshrub(&file->shrub)) {
rid_ = lfsr_data_size(&file->shrub.u.data)-1;
tag_ = LFSR_TAG_DATA;
weight_ = lfsr_data_size(&file->shrub.u.data);
data_ = file->shrub.u.data;
} else {
int err = lfsr_rbyd_lookupnext(lfs, &file->shrub.u.rbyd,
pos-1, 0,
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_SHRUB(DATA));
int err = lfsr_shrub_lookupnext(lfs, &file->shrub,
pos-1,
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// can we coalesce?
@@ -9692,27 +9779,16 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
if (pos + lfsr_data_size(&data) < lfsr_shrub_size(&file->shrub)
// don't bother to lookup right if fragment is already full
&& lfsr_data_size(&data) < lfs->cfg->fragment_size) {
// TODO should these shrub lookups be deduplicated?
// lfsr_shrub_lookupnext?
lfsr_srid_t rid_;
lfsr_rid_t rid_;
lfsr_tag_t tag_;
lfsr_rid_t weight_;
lfsr_data_t data_;
// sprout or shrub?
if (!lfsr_shrub_hasshrub(&file->shrub)) {
rid_ = lfsr_data_size(&file->shrub.u.data)-1;
tag_ = LFSR_TAG_DATA;
weight_ = lfsr_data_size(&file->shrub.u.data);
data_ = file->shrub.u.data;
} else {
int err = lfsr_rbyd_lookupnext(lfs, &file->shrub.u.rbyd,
pos+lfsr_data_size(&data), 0,
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_SHRUB(DATA));
int err = lfsr_shrub_lookupnext(lfs, &file->shrub,
pos+lfsr_data_size(&data),
&rid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// can we coalesce?
@@ -9736,7 +9812,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// once we've figured out what fragment to write, carve it into
// our shrub
int err = lfsr_file_carveshrub(lfs, file,
int err = lfsr_shrub_carve(lfs, file, &file->shrub,
pos, lfsr_data_size(&data), 0,
LFSR_TAG_SHRUB(DATA), data);
if (err && err != LFS_ERR_RANGE) {
@@ -9760,10 +9836,10 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
}
// TODO can carvetree/carveshrub be combined somehow?
static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
static int lfsr_tree_carve(lfs_t *lfs, lfsr_tree_t *tree,
lfs_off_t pos, lfs_off_t weight, lfs_soff_t delta,
lfsr_tag_t tag, lfsr_data_t data) {
// this is basically the same as lfsr_file_carveshrub, except we apply
// this is basically the same as lfsr_shrub_carve, except we apply
// changes immediately since we can't commit attrs across rbyds
//
// we also need to handle bptrs here, and even fragment bptrs if they
@@ -9773,7 +9849,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// TODO do we ever create direct bptrs with this strategy?
// do we need a new btree?
if (!lfsr_tree_hasbtree(&file->tree)) {
if (!lfsr_tree_hasbtree(tree)) {
// TODO btree alloc?
lfsr_btree_t btree_;
int err = lfsr_rbyd_alloc(lfs, &btree_.u.rbyd);
@@ -9782,50 +9858,32 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
}
// append direct bptr if we have one
if (lfsr_tree_hasbptr(&file->tree)) {
if (lfsr_tree_hasbptr(tree)) {
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_btree_commit(lfs, &btree_, LFSR_ATTRS(
LFSR_ATTR(0,
BLOCK, +lfsr_tree_size(&file->tree),
FROMBPTR(&file->tree.u.bptr, bptr_buf))));
BLOCK, +lfsr_tree_size(tree),
FROMBPTR(&tree->u.bptr, bptr_buf))));
if (err) {
return err;
}
}
file->tree.u.btree = btree_;
tree->u.btree = btree_;
}
// try to carve any existing data
while (pos < lfsr_tree_size(&file->tree) && weight > 0) {
while (pos < lfsr_tree_size(tree) && weight > 0) {
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
lfsr_data_t data_;
int err = lfsr_btree_lookupnext(lfs, &file->tree.u.btree,
pos,
int err = lfsr_tree_lookupnext(lfs, tree, pos,
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
// decode bptrs
if (tag_ == LFSR_TAG_BLOCK) {
lfsr_bptr_t bptr_;
err = lfsr_data_readbptr(lfs, &data_, &bptr_);
if (err) {
return err;
}
data_ = LFSR_DATA_DISK(
bptr_.block,
bptr_.off,
bptr_.size);
}
LFS_ASSERT(lfsr_data_size(&data_) <= weight_);
// note an entry can be both a left and right sibling!
@@ -9844,7 +9902,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// we can get away with a grow attribute in some cases, avoiding
// a data copy
if (lfsr_data_size(&data_) == lfsr_data_size(&slice_)) {
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
GROW, -overlap_, NULL)));
if (err) {
@@ -9861,7 +9919,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
};
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
GROW(WIDE(BLOCK)), -overlap_,
FROMBPTR(&bptr_, bptr_buf))));
@@ -9879,7 +9937,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// otherwise we carve, potentially converting a bptr into
// a fragment
} else {
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
GROW(WIDE(DATA)), -overlap_,
DATA(slice_))));
@@ -9919,7 +9977,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
};
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_btree_commit(lfs, &file->tree.u.btree,
err = lfsr_btree_commit(lfs, &tree->u.btree,
LFSR_ATTRS(
LFSR_ATTR(pos,
BLOCK, +(weight_ - overlap_),
@@ -9931,7 +9989,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// otherwise we carve, potentially converting a bptr into
// a fragment
} else {
err = lfsr_btree_commit(lfs, &file->tree.u.btree,
err = lfsr_btree_commit(lfs, &tree->u.btree,
LFSR_ATTRS(
LFSR_ATTR(pos,
DATA, +(weight_ - overlap_),
@@ -9959,7 +10017,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// can we coalesce a hole?
if (lfsr_data_size(&slice_) == 0) {
delta += bid_+1 - (pos + weight);
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
RM, -weight_, NULL)));
if (err) {
@@ -9976,7 +10034,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
};
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
GROW(WIDE(BLOCK)), -overlap_,
FROMBPTR(&bptr_, bptr_buf))));
@@ -9987,7 +10045,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// otherwise we carve, potentially converting a bptr into
// a fragment
} else {
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
GROW(WIDE(DATA)), -overlap_,
DATA(slice_))));
@@ -9999,7 +10057,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// found fully overwritten data?
} else {
// remove
err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(bid_,
RM, -weight_, NULL)));
if (err) {
@@ -10012,16 +10070,16 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
}
// need a hole?
if (pos > lfsr_tree_size(&file->tree)
if (pos > lfsr_tree_size(tree)
// if we have no data we can coalesce our hole here
|| (weight + delta > 0 && lfsr_data_size(&data) == 0)) {
lfs_off_t pos_ = lfs_min32(pos, lfsr_tree_size(&file->tree));
lfs_off_t pos_ = lfs_min32(pos, lfsr_tree_size(tree));
lfs_off_t hole = pos - pos_
+ ((lfsr_data_size(&data) == 0) ? weight + delta : 0);
// we can usually get away with a simple grow attribute
if (pos_ > 0) {
int err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
int err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(pos_-1,
GROW, +hole, NULL)));
if (err) {
@@ -10030,7 +10088,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// otherwise we need a hole attr
} else {
int err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
int err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(pos_,
DATA, +hole, NULL)));
if (err) {
@@ -10045,7 +10103,7 @@ static int lfsr_file_carvetree(lfs_t *lfs, lfsr_file_t *file,
// finally append our data
if (weight + delta > 0 && lfsr_data_size(&data) != 0) {
int err = lfsr_btree_commit(lfs, &file->tree.u.btree, LFSR_ATTRS(
int err = lfsr_btree_commit(lfs, &tree->u.btree, LFSR_ATTRS(
LFSR_ATTR(pos,
TAG(tag), +(weight + delta), DATA(data))));
if (err) {
@@ -10087,23 +10145,8 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
d = lfs_min32(d, file->buffer_pos - pos);
}
// has a sprout?
if (lfsr_shrub_hassprout(&file->shrub)
&& pos < lfsr_shrub_size(&file->shrub)) {
d = lfs_min32(
d,
lfsr_data_size(&file->shrub.u.data) - pos);
data = LFSR_DATA_DISK(
file->shrub.u.data.u.disk.block,
file->shrub.u.data.u.disk.off + pos,
d);
goto flush;
// has a shrub?
} else if (lfsr_shrub_hasshrub(&file->shrub)
&& pos < lfsr_shrub_size(&file->shrub)) {
// we can pretend the shrub is a single-rbyd btree
// any data in our shrub?
if (pos < lfsr_shrub_size(&file->shrub)) {
lfs_off_t weight;
int err = lfsr_shrub_readnext(lfs, &file->shrub, pos, d,
&weight, &data);
@@ -10138,29 +10181,14 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// tree too small? align arbitrarily
left_align = pos;
} else {
// TODO should these tree lookups be deduplicated?
// lfsr_tree_lookupnext?
// TODO shrub/tree readnext should handle inlined
// sprouts/bptrs too...
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
// direct bptr or btree?
if (!lfsr_tree_hasbtree(&file->tree)) {
// TODO lfsr_bptr_size()?
bid_ = lfsr_tree_size(&file->tree)-1;
tag_ = LFSR_TAG_BLOCK;
weight_ = lfsr_tree_size(&file->tree)-1;
} else {
int err = lfsr_btree_lookupnext(lfs, &file->tree.u.btree,
pos - lfs->cfg->block_size,
&bid_, &tag_, &weight_, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
int err = lfsr_tree_lookupnext(lfs, &file->tree,
pos - lfs->cfg->block_size,
&bid_, NULL, &weight_, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// our current pos can't belong in the left block, so align to next
@@ -10175,29 +10203,14 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// tree too small? align to end of tree
right_align = lfsr_tree_size(&file->tree);
} else {
// TODO should these tree lookups be deduplicated?
// lfsr_tree_lookupnext?
// TODO shrub/tree readnext should handle inlined
// sprouts/bptrs too...
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
// direct bptr or btree?
if (!lfsr_tree_hasbtree(&file->tree)) {
// TODO lfsr_bptr_size()?
bid_ = lfsr_tree_size(&file->tree)-1;
tag_ = LFSR_TAG_BLOCK;
weight_ = lfsr_tree_size(&file->tree)-1;
} else {
int err = lfsr_btree_lookupnext(lfs, &file->tree.u.btree,
left_align + lfs->cfg->block_size,
&bid_, &tag_, &weight_, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
int err = lfsr_tree_lookupnext(lfs, &file->tree,
left_align + lfs->cfg->block_size,
&bid_, NULL, &weight_, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// our block can't reside in the right block, so squish our block
@@ -10295,7 +10308,7 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// and write it into our tree
uint8_t bptr_buf[LFSR_BPTR_DSIZE];
err = lfsr_file_carvetree(lfs, file,
err = lfsr_tree_carve(lfs, &file->tree,
left_align, right_align - left_align, 0,
LFSR_TAG_BLOCK, lfsr_data_frombptr(&bptr, bptr_buf));
if (err) {
@@ -10328,50 +10341,16 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// TODO can we do this here?
// don't bother to lookup left after first fragment
//&& i == 0) {
// TODO should these tree lookups be deduplicated?
// lfsr_tree_lookupnext?
// TODO shrub/tree readnext should handle inlined
// sprouts/bptrs too...
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
lfsr_data_t data_;
// direct bptr or btree?
if (!lfsr_tree_hasbtree(&file->tree)) {
// TODO lfsr_bptr_size()?
bid_ = lfsr_tree_size(&file->tree)-1;
tag_ = LFSR_TAG_BLOCK;
weight_ = lfsr_tree_size(&file->tree)-1;
// TODO bptr_t should have a data field or something
data_ = LFSR_DATA_DISK(
file->tree.u.bptr.block,
file->tree.u.bptr.off,
lfsr_tree_size(&file->tree));
} else {
int err = lfsr_btree_lookupnext(lfs, &file->tree.u.btree,
pos-1,
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
// decode bptrs
if (tag_ == LFSR_TAG_BLOCK) {
lfsr_bptr_t bptr_;
err = lfsr_data_readbptr(lfs, &data_, &bptr_);
if (err) {
return err;
}
data_ = LFSR_DATA_DISK(
bptr_.block,
bptr_.off,
bptr_.size);
}
LFS_ASSERT(lfsr_data_size(&data_) <= weight_);
int err = lfsr_tree_lookupnext(lfs, &file->tree,
pos-1,
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// can we coalesce?
@@ -10416,50 +10395,16 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
if (pos + lfsr_data_size(&data) < lfsr_tree_size(&file->tree)
// don't bother to lookup right if fragment is already full
&& lfsr_data_size(&data) < lfs->cfg->fragment_size) {
// TODO should these tree lookups be deduplicated?
// lfsr_tree_lookupnext?
// TODO shrub/tree readnext should handle inlined
// sprouts/bptrs too...
lfsr_bid_t bid_;
lfsr_tag_t tag_;
lfsr_bid_t weight_;
lfsr_data_t data_;
// direct bptr or btree?
if (!lfsr_tree_hasbtree(&file->tree)) {
// TODO lfsr_bptr_size()?
bid_ = lfsr_tree_size(&file->tree)-1;
tag_ = LFSR_TAG_BLOCK;
weight_ = lfsr_tree_size(&file->tree)-1;
// TODO bptr_t should have a data field or something
data_ = LFSR_DATA_DISK(
file->tree.u.bptr.block,
file->tree.u.bptr.off,
lfsr_tree_size(&file->tree));
} else {
int err = lfsr_btree_lookupnext(lfs, &file->tree.u.btree,
pos+lfsr_data_size(&data),
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
LFS_ASSERT(tag_ == LFSR_TAG_DATA
|| tag_ == LFSR_TAG_BLOCK);
// decode bptrs
if (tag_ == LFSR_TAG_BLOCK) {
lfsr_bptr_t bptr_;
err = lfsr_data_readbptr(lfs, &data_, &bptr_);
if (err) {
return err;
}
data_ = LFSR_DATA_DISK(
bptr_.block,
bptr_.off,
bptr_.size);
}
LFS_ASSERT(lfsr_data_size(&data_) <= weight_);
int err = lfsr_tree_lookupnext(lfs, &file->tree,
pos+lfsr_data_size(&data),
&bid_, &tag_, &weight_, &data_);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
// can we coalesce?
@@ -10487,7 +10432,7 @@ static int lfsr_file_flushshrub(lfs_t *lfs, lfsr_file_t *file) {
// once we've figured out what fragment to write, carve it into
// our tree
int err = lfsr_file_carvetree(lfs, file,
int err = lfsr_tree_carve(lfs, &file->tree,
pos, lfsr_data_size(&data), 0,
LFSR_TAG_DATA, data);
if (err && err != LFS_ERR_RANGE) {
@@ -10768,7 +10713,7 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
// lfsr_file_sync to update the shrub?
// otherwise, we need to modify our sprout/shrub/bptr/btree
} else {
int err = lfsr_file_carveshrub(lfs, file,
int err = lfsr_shrub_carve(lfs, file, &file->shrub,
lfs_min32(file->size, size),
file->size - lfs_min32(file->size, size),
+size - file->size,
@@ -10781,7 +10726,7 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
}
// TODO avoid transforming into trees all the time?
err = lfsr_file_carvetree(lfs, file,
err = lfsr_tree_carve(lfs, &file->tree,
lfs_min32(file->size, size),
file->size - lfs_min32(file->size, size),
+size - file->size,
@@ -10843,7 +10788,7 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
>= (lfs_soff_t)lfsr_shrub_size(&file->shrub)) {
file->shrub.u.data = LFSR_DATA_DISK(0, 0, 0);
} else {
int err = lfsr_file_carveshrub(lfs, file,
int err = lfsr_shrub_carve(lfs, file, &file->shrub,
0,
lfs_smax32(file->size - size, 0),
+size - file->size,
@@ -10872,7 +10817,7 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
file->tree.u.btree = LFSR_BTREE_NULL;
} else {
// TODO avoid transforming into trees all the time?
int err = lfsr_file_carvetree(lfs, file,
int err = lfsr_tree_carve(lfs, &file->tree,
0,
lfs_smax32(file->size - size, 0),
+size - file->size,