diff --git a/lfs.c b/lfs.c index 64d905cc..7f8c2ed1 100644 --- a/lfs.c +++ b/lfs.c @@ -3848,12 +3848,107 @@ static int lfsr_btree_parent(lfs_t *lfs, // core btree algorithm -static int lfsr_btree_commit(lfs_t *lfs, - lfsr_btree_t *btree, lfs_size_t bid, - lfsr_rbyd_t *rbyd, +static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree, const lfsr_attr_t *attrs, lfs_size_t attr_count) { - // other layers should check for inlined btrees before this - LFS_ASSERT(!lfsr_btree_isinlined(btree)); + // first find the effective bid and any changes to the number of tags + lfs_size_t bid = -1; + lfs_ssize_t tag_delta = 0; + for (lfs_size_t i = 0; i < attr_count; i++) { + // note unsigned min here chooses non-negative bids + bid = lfs_min32(bid, attrs[i].rid); + + // non-grow tags with deltas change the number of tags + if (!lfsr_tag_isgrow(attrs[i].tag)) { + tag_delta += lfs_sclamp32(attrs[i].delta, -1, +1); + } + } + LFS_ASSERT(bid <= lfsr_btree_weight(btree)); + + // inlined btree? staying inlined? + if (lfsr_btree_isinlined(btree)) { + // staying inlined? + if (lfs_clamp32(lfsr_btree_weight(btree), 0, 1) + tag_delta <= 1) { + for (lfs_size_t i = 0; i < attr_count; i++) { + // update our inlined tag, make sure to strip wide/grow bits + if (lfsr_tag_suptype(lfsr_tag_key(attrs[i].tag)) + == LFSR_TAG_STRUCT) { + btree->u.i.tag = lfsr_tag_key(attrs[i].tag); + + lfsr_data_t data_ = attrs[i].data; + lfs_ssize_t d = lfsr_data_read(lfs, &data_, + btree->u.i.buf, LFSR_BTREE_INLINESIZE); + if (d < 0) { + return d; + } + btree->u.i.size = d; + } + + // update our btree weight + LFS_ASSERT((lfs_ssize_t)lfsr_btree_weight(btree) + + attrs[i].delta >= 0); + btree->u.i.weight += attrs[i].delta; + } + return 0; + + // uninlining? + } else { + // allocate a root rbyd + lfsr_rbyd_t rbyd; + int err = lfsr_rbyd_alloc(lfs, &rbyd); + if (err) { + return err; + } + + // prepend inlined tag if we have one + if (lfsr_btree_weight(btree) > 0) { + err = lfsr_rbyd_append(lfs, &rbyd, + 0, btree->u.i.tag, +lfsr_btree_weight(btree), + LFSR_DATA_BUF(btree->u.i.buf, btree->u.i.size)); + if (err) { + return err; + } + } + + // commit our attrs + err = lfsr_rbyd_appendall(lfs, &rbyd, 0, -1, -1, + attrs, attr_count); + if (err) { + return err; + } + + err = lfsr_rbyd_appendcksum(lfs, &rbyd); + if (err) { + return err; + } + + // save our new root + btree->u.r.rbyd = rbyd; + return 0; + } + } + + // lookup in which leaf our bids resides + // + // for lfsr_btree_commit operations to work out, we need to + // limit our bid to an rid in the tree, which is what this min + // is doing + // + // note it's entirely possible for our btree to have a weight of + // zero here + lfsr_rbyd_t rbyd = btree->u.r.rbyd; + if (lfsr_btree_weight(btree) > 0) { + lfs_ssize_t rid; + int err = lfsr_btree_lookupnext_(lfs, btree, + lfs_min32(bid, lfsr_btree_weight(btree)-1), + &bid, &rbyd, &rid, NULL, NULL, NULL); + if (err) { + LFS_ASSERT(err != LFS_ERR_NOENT); + return err; + } + + // adjust bid to indicate the zero-most rid + bid -= rid; + } // we need some scratch space for tail-recursive attrs here lfsr_attr_t scratch_attrs[4]; @@ -3864,7 +3959,7 @@ static int lfsr_btree_commit(lfs_t *lfs, // we will always need our parent, so go ahead and find it lfsr_rbyd_t parent; lfs_ssize_t pid; - int err = lfsr_btree_parent(lfs, btree, bid, rbyd, &parent, &pid); + int err = lfsr_btree_parent(lfs, btree, bid, &rbyd, &parent, &pid); if (err && err != LFS_ERR_NOENT) { return err; } @@ -3872,7 +3967,7 @@ static int lfsr_btree_commit(lfs_t *lfs, // mark pid as -1 if we have no parent pid = -1; } - lfs_size_t pweight = rbyd->weight; + lfs_size_t pweight = rbyd.weight; // fetch our rbyd so we can mutate it // @@ -3880,15 +3975,15 @@ static int lfsr_btree_commit(lfs_t *lfs, // will fail to fetch so we need to check that this rbyd is unfetched // // a strange benefit is we cache the root of our btree this way - if (!lfsr_rbyd_isfetched(rbyd)) { - err = lfsr_rbyd_fetch(lfs, rbyd, rbyd->block, rbyd->trunk); + if (!lfsr_rbyd_isfetched(&rbyd)) { + err = lfsr_rbyd_fetch(lfs, &rbyd, rbyd.block, rbyd.trunk); if (err) { return err; } } // make a copy so we have a reference to the old trunk in case of split - lfsr_rbyd_t rbyd_ = *rbyd; + lfsr_rbyd_t rbyd_ = rbyd; // is rbyd erased? can we sneak our commit into any remaining // erased bytes? note that the btree trunk field prevents this from @@ -3912,7 +4007,7 @@ static int lfsr_btree_commit(lfs_t *lfs, } // TODO do we really need to save rbyd_? - *rbyd = rbyd_; + rbyd = rbyd_; // done? if (pid == -1) { @@ -3920,7 +4015,7 @@ static int lfsr_btree_commit(lfs_t *lfs, break; } - lfs_ssize_t scratch_dsize = lfsr_branch_todisk(lfs, rbyd, scratch_buf); + lfs_ssize_t scratch_dsize = lfsr_branch_todisk(lfs, &rbyd, scratch_buf); if (scratch_dsize < 0) { return scratch_dsize; } @@ -3930,24 +4025,24 @@ static int lfsr_btree_commit(lfs_t *lfs, // note that since we defer merges to compaction time, we can // end up removing an rbyd here bid -= pid - (pweight-1); - if (rbyd->weight == 0) { + if (rbyd.weight == 0) { scratch_attrs[0] = LFSR_ATTR( - bid+pid, RM, +rbyd->weight-pweight, + bid+pid, RM, +rbyd.weight-pweight, BUF(scratch_buf, scratch_dsize)); attrs = scratch_attrs; attr_count = 1; } else { scratch_attrs[0] = LFSR_ATTR( - bid+pid, GROW(RM), +rbyd->weight-pweight, + bid+pid, GROW(RM), +rbyd.weight-pweight, NULL); scratch_attrs[1] = LFSR_ATTR( - bid+pid+rbyd->weight-pweight, BRANCH, 0, + bid+pid+rbyd.weight-pweight, BRANCH, 0, BUF(scratch_buf, scratch_dsize)); attrs = scratch_attrs; attr_count = 2; } - *rbyd = parent; + rbyd = parent; continue; compact:; @@ -3956,7 +4051,7 @@ static int lfsr_btree_commit(lfs_t *lfs, // check if we're within our compaction threshold, otherwise we // need to split lfs_size_t split_rid; - lfs_ssize_t estimate = lfsr_rbyd_estimateall(lfs, rbyd, -1, -1, + lfs_ssize_t estimate = lfsr_rbyd_estimateall(lfs, &rbyd, -1, -1, &split_rid); if (estimate < 0) { return estimate; @@ -3974,7 +4069,7 @@ static int lfsr_btree_commit(lfs_t *lfs, } // try to compact - err = lfsr_rbyd_compact(lfs, &rbyd_, -1, -1, rbyd); + err = lfsr_rbyd_compact(lfs, &rbyd_, -1, -1, &rbyd); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); return err; @@ -4008,7 +4103,7 @@ static int lfsr_btree_commit(lfs_t *lfs, return err; } - *rbyd = rbyd_; + rbyd = rbyd_; // done? if (pid == -1) { @@ -4016,7 +4111,7 @@ static int lfsr_btree_commit(lfs_t *lfs, break; } - scratch_dsize = lfsr_branch_todisk(lfs, rbyd, scratch_buf); + scratch_dsize = lfsr_branch_todisk(lfs, &rbyd, scratch_buf); if (scratch_dsize < 0) { return scratch_dsize; } @@ -4026,29 +4121,29 @@ static int lfsr_btree_commit(lfs_t *lfs, // note that since we defer merges to compaction time, we can // end up removing an rbyd here bid -= pid - (pweight-1); - if (rbyd->weight == 0) { + if (rbyd.weight == 0) { scratch_attrs[0] = LFSR_ATTR( - bid+pid, RM, +rbyd->weight-pweight, + bid+pid, RM, +rbyd.weight-pweight, BUF(scratch_buf, scratch_dsize)); attrs = scratch_attrs; attr_count = 1; } else { scratch_attrs[0] = LFSR_ATTR( - bid+pid, GROW(RM), +rbyd->weight-pweight, + bid+pid, GROW(RM), +rbyd.weight-pweight, NULL); scratch_attrs[1] = LFSR_ATTR( - bid+pid+rbyd->weight-pweight, BRANCH, 0, + bid+pid+rbyd.weight-pweight, BRANCH, 0, BUF(scratch_buf, scratch_dsize)); attrs = scratch_attrs; attr_count = 2; } - *rbyd = parent; + rbyd = parent; continue; split:; // we should have something to split here - LFS_ASSERT(split_rid > 0 && split_rid < rbyd->weight); + LFS_ASSERT(split_rid > 0 && split_rid < rbyd.weight); // allocate a new rbyd err = lfsr_rbyd_alloc(lfs, &rbyd_); @@ -4064,7 +4159,7 @@ static int lfsr_btree_commit(lfs_t *lfs, } // copy over tags < split_rid - err = lfsr_rbyd_compact(lfs, &rbyd_, 0, split_rid, rbyd); + err = lfsr_rbyd_compact(lfs, &rbyd_, 0, split_rid, &rbyd); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); return err; @@ -4089,7 +4184,7 @@ static int lfsr_btree_commit(lfs_t *lfs, } // copy over tags >= split_rid - err = lfsr_rbyd_compact(lfs, &sibling, split_rid, -1, rbyd); + err = lfsr_rbyd_compact(lfs, &sibling, split_rid, -1, &rbyd); if (err) { LFS_ASSERT(err != LFS_ERR_RANGE); return err; @@ -4195,7 +4290,7 @@ static int lfsr_btree_commit(lfs_t *lfs, attr_count = 4; } - *rbyd = parent; + rbyd = parent; continue; merge:; @@ -4373,7 +4468,7 @@ static int lfsr_btree_commit(lfs_t *lfs, LFS_ASSERT(pid != -1); if (pweight+sweight == lfsr_btree_weight(btree)) { // collapse our parent, decreasing the height of the tree - *rbyd = rbyd_; + rbyd = rbyd_; break; } else { @@ -4403,235 +4498,24 @@ static int lfsr_btree_commit(lfs_t *lfs, attr_count = 3; } - *rbyd = parent; + rbyd = parent; continue; } // at this point rbyd should be the trunk of our tree - btree->u.r.rbyd = *rbyd; + btree->u.r.rbyd = rbyd; return 0; } -static int lfsr_btree_commit__(lfs_t *lfs, lfsr_btree_t *btree, - const lfsr_attr_t *attrs, lfs_size_t attr_count) { - // first find the effective bid and any changes to the number of tags - lfs_size_t bid = -1; - lfs_ssize_t tag_delta = 0; - for (lfs_size_t i = 0; i < attr_count; i++) { - // note unsigned min here chooses non-negative bids - bid = lfs_min32(bid, attrs[i].rid); - - // non-grow tags with deltas change the number of tags - if (!lfsr_tag_isgrow(attrs[i].tag)) { - tag_delta += lfs_sclamp32(attrs[i].delta, -1, +1); - } - } - LFS_ASSERT(bid <= lfsr_btree_weight(btree)); - - // inlined btree? staying inlined? - if (lfsr_btree_isinlined(btree)) { - // staying inlined? - if (lfs_clamp32(lfsr_btree_weight(btree), 0, 1) + tag_delta <= 1) { - for (lfs_size_t i = 0; i < attr_count; i++) { - // update our inlined tag, make sure to strip wide/grow bits - if (lfsr_tag_suptype(lfsr_tag_key(attrs[i].tag)) - == LFSR_TAG_STRUCT) { - btree->u.i.tag = lfsr_tag_key(attrs[i].tag); - - lfsr_data_t data_ = attrs[i].data; - lfs_ssize_t d = lfsr_data_read(lfs, &data_, - btree->u.i.buf, LFSR_BTREE_INLINESIZE); - if (d < 0) { - return d; - } - btree->u.i.size = d; - } - - // update our btree weight - LFS_ASSERT((lfs_ssize_t)lfsr_btree_weight(btree) - + attrs[i].delta >= 0); - btree->u.i.weight += attrs[i].delta; - } - return 0; - - // uninlining? - } else { - // allocate a root rbyd - lfsr_rbyd_t rbyd; - int err = lfsr_rbyd_alloc(lfs, &rbyd); - if (err) { - return err; - } - - // prepend inlined tag if we have one - if (lfsr_btree_weight(btree) > 0) { - err = lfsr_rbyd_append(lfs, &rbyd, - 0, btree->u.i.tag, +lfsr_btree_weight(btree), - LFSR_DATA_BUF(btree->u.i.buf, btree->u.i.size)); - if (err) { - return err; - } - } - - // commit our attrs - err = lfsr_rbyd_appendall(lfs, &rbyd, 0, -1, -1, - attrs, attr_count); - if (err) { - return err; - } - - err = lfsr_rbyd_appendcksum(lfs, &rbyd); - if (err) { - return err; - } - - // save our new root - btree->u.r.rbyd = rbyd; - return 0; - } - } - - // lookup in which leaf our bids reside - // - // for lfsr_btree_commit operations to work out, we need to - // limit our bid to an rid in the tree, which is what this min - // is doing - // - // note it's entirely possible for our btree to have a weight of - // zero here - lfsr_rbyd_t rbyd = btree->u.r.rbyd; - if (lfsr_btree_weight(btree) > 0) { - lfs_ssize_t rid; - int err = lfsr_btree_lookupnext_(lfs, btree, - lfs_min32(bid, lfsr_btree_weight(btree)-1), - &bid, &rbyd, &rid, NULL, NULL, NULL); - if (err) { - LFS_ASSERT(err != LFS_ERR_NOENT); - return err; - } - - // adjust bid to indicate the zero-most rid - bid -= rid; - } - - // commit our rid into the tree, letting lfsr_btree_commit take care - // of the rest - int err = lfsr_btree_commit(lfs, btree, bid, &rbyd, - attrs, attr_count); - if (err) { - return err; - } - - return 0; -} - -// TODO +// TODO move to tests static int lfsr_btree_push(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight, lfsr_data_t data) { LFS_ASSERT(bid <= lfsr_btree_weight(btree)); - return lfsr_btree_commit__(lfs, btree, LFSR_ATTRS( + return lfsr_btree_commit(lfs, btree, LFSR_ATTRS( LFSR_ATTR(bid, TAG(tag), +weight, DATA(data)))); } -// TODO -//static int lfsr_btree_push_(lfs_t *lfs, lfsr_btree_t *btree, -// lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight, lfsr_data_t data) { -// LFS_ASSERT(bid <= lfsr_btree_weight(btree)); -// -// // null btree? -// if (lfsr_btree_isinlined(btree) && lfsr_btree_weight(btree) == 0) { -// LFS_ASSERT(bid == 0); -// -// // mark as inlined -// btree->u.i.weight = lfsr_btree_setinlined(weight); -// btree->u.i.tag = tag; -// -// lfs_ssize_t d = lfsr_data_read(lfs, &data, -// btree->u.i.buf, LFSR_BTREE_INLINESIZE); -// if (d < 0) { -// return d; -// } -// LFS_ASSERT(d <= LFSR_BTREE_INLINESIZE); -// btree->u.i.size = d; -// return 0; -// -// // inlined btree, need to expand into an rbyd -// } else if (lfsr_btree_isinlined(btree)) { -// lfsr_rbyd_t rbyd; -// int err = lfsr_rbyd_alloc(lfs, &rbyd); -// if (err) { -// return err; -// } -// -// // commit our entries -// err = lfsr_rbyd_commit(lfs, &rbyd, LFSR_ATTRS( -// LFSR_ATTR(0, TAG(btree->u.i.tag), +lfsr_btree_weight(btree), -// BUF(btree->u.i.buf, btree->u.i.size)), -// LFSR_ATTR(bid, TAG(tag), +weight, DATA(data)))); -// if (err) { -// return err; -// } -// -// btree->u.r.rbyd = rbyd; -// return 0; -// -// // a normal btree -// } else { -// // lookup in which leaf our rid resides -// // -// // for lfsr_btree_commit operations to work out, we need to -// // limit our bid to an rid in the tree, which is what this min -// // is doing -// // -// // note it is possible for our btree to have a weight of zero here, -// // since we defer inlining until compaction time -// lfs_size_t bid_ = lfs_min32(bid, -// lfs_smax32(lfsr_btree_weight(btree)-1, 0)); -// lfsr_rbyd_t rbyd = btree->u.r.rbyd; -// lfs_ssize_t rid = -1; -// lfs_size_t rweight = 0; -// int err = lfsr_btree_lookupnext_(lfs, btree, bid_, -// NULL, &rbyd, &rid, NULL, &rweight, NULL); -// if (err && err != LFS_ERR_NOENT) { -// return err; -// } -// -// // adjust rid for push -// if (bid >= lfsr_btree_weight(btree)) { -// rid += 1; -// } else { -// rid -= rweight-1; -// } -// -// // commit our rid into the tree, letting lfsr_btree_commit take care -// // of the rest -// int degenerate = lfsr_btree_commit(lfs, btree, bid_, 0, &rbyd, -// LFSR_ATTRS( -// LFSR_ATTR(rid, TAG(tag), +weight, DATA(data)))); -// if (degenerate < 0) { -// return degenerate; -// } -// -// // revert to an inlined btree -// if (degenerate) { -// // mark as inlined -// btree->u.i.weight = lfsr_btree_setinlined(weight); -// btree->u.i.tag = tag; -// -// lfs_ssize_t d = lfsr_data_read(lfs, &data, -// btree->u.i.buf, LFSR_BTREE_INLINESIZE); -// if (d < 0) { -// return d; -// } -// LFS_ASSERT(d <= LFSR_BTREE_INLINESIZE); -// btree->u.i.size = d; -// } -// -// return 0; -// } -//} - -// TODO +// TODO move to tests static int lfsr_btree_set(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight, lfsr_data_t data) { LFS_ASSERT(bid < lfsr_btree_weight(btree)); @@ -4653,77 +4537,12 @@ static int lfsr_btree_set(lfs_t *lfs, lfsr_btree_t *btree, // note we need a second tag here in case our entry has a // name attributes, the name attribute holds the weight not // the struct tag - return lfsr_btree_commit__(lfs, btree, LFSR_ATTRS( + return lfsr_btree_commit(lfs, btree, LFSR_ATTRS( LFSR_ATTR(bid, WIDE(TAG(tag)), 0, DATA(data)), LFSR_ATTR(bid, GROW(RM), weight - weight_, NULL))); } -// TODO -//static int lfsr_btree_set(lfs_t *lfs, lfsr_btree_t *btree, -// lfs_size_t bid, lfsr_tag_t tag, lfs_size_t weight, lfsr_data_t data) { -// LFS_ASSERT(bid < lfsr_btree_weight(btree)); -// LFS_ASSERT(lfsr_btree_weight(btree) > 0); -// -// // inlined btree? -// if (lfsr_btree_isinlined(btree)) { -// LFS_ASSERT(bid == lfsr_btree_weight(btree)-1); -// -// // mark as inlined -// btree->u.i.weight = lfsr_btree_setinlined(weight); -// btree->u.i.tag = tag; -// -// lfs_ssize_t d = lfsr_data_read(lfs, &data, -// btree->u.i.buf, LFSR_BTREE_INLINESIZE); -// if (d < 0) { -// return d; -// } -// LFS_ASSERT(d <= LFSR_BTREE_INLINESIZE); -// btree->u.i.size = d; -// return 0; -// -// // a normal btree -// } else { -// // lookup in which leaf our rid resides -// lfsr_rbyd_t rbyd; -// lfsr_tag_t rtag; -// lfs_ssize_t rid; -// lfs_size_t rweight; -// int err = lfsr_btree_lookupnext_(lfs, btree, bid, -// NULL, &rbyd, &rid, &rtag, &rweight, NULL); -// if (err) { -// return err; -// } -// -// // commit our rid into the tree, letting lfsr_btree_commit take care -// // of the rest -// int degenerate = lfsr_btree_commit(lfs, btree, bid, 1, &rbyd, -// LFSR_ATTRS( -// LFSR_ATTR(rid, WIDE(TAG(tag)), 0, DATA(data)), -// LFSR_ATTR(rid, GROW(RM), +weight-rweight, NULL))); -// if (degenerate < 0) { -// return degenerate; -// } -// -// // revert to an inlined btree -// if (degenerate) { -// // mark as inlined -// btree->u.i.weight = lfsr_btree_setinlined(weight); -// btree->u.i.tag = tag; -// -// lfs_ssize_t d = lfsr_data_read(lfs, &data, -// btree->u.i.buf, LFSR_BTREE_INLINESIZE); -// if (d < 0) { -// return d; -// } -// LFS_ASSERT(d <= LFSR_BTREE_INLINESIZE); -// btree->u.i.size = d; -// } -// -// return 0; -// } -//} - -// TODO +// TODO move to tests static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid) { LFS_ASSERT(bid < lfsr_btree_weight(btree)); LFS_ASSERT(lfsr_btree_weight(btree) > 0); @@ -4741,98 +4560,11 @@ static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid) { return err; } - return lfsr_btree_commit__(lfs, btree, LFSR_ATTRS( + return lfsr_btree_commit(lfs, btree, LFSR_ATTRS( LFSR_ATTR(bid, RM, -weight_, NULL))); } -//static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t bid) { -// LFS_ASSERT(bid < lfsr_btree_weight(btree)); -// LFS_ASSERT(lfsr_btree_weight(btree) > 0); -// -// // inlined btree? -// if (lfsr_btree_isinlined(btree)) { -// LFS_ASSERT(bid == lfsr_btree_weight(btree)-1); -// *btree = LFSR_BTREE_NULL; -// return 0; -// -// // a normal btree -// } else { -// // lookup in which leaf our rid resides -// lfsr_rbyd_t rbyd; -// lfsr_tag_t rtag; -// lfs_ssize_t rid; -// lfs_size_t rweight; -// int err = lfsr_btree_lookupnext_(lfs, btree, bid, -// NULL, &rbyd, &rid, &rtag, &rweight, NULL); -// if (err) { -// return err; -// } -// -// // remove our rid, letting lfsr_btree_commit take care -// // of the rest -// // -// // note we use a cutoff of 2 here, if we have 2 entries before -// // the commit, we should have 1 entry after the commit and can -// // revert to an inlined btree -// int degenerate = lfsr_btree_commit(lfs, btree, bid, 2, &rbyd, -// LFSR_ATTRS( -// LFSR_ATTR(rid, RM, -rweight, NULL))); -// if (degenerate < 0) { -// return degenerate; -// } -// -// // revert to a null btree -// if (degenerate && rweight >= rbyd.weight) { -// *btree = LFSR_BTREE_NULL; -// -// // revert to an inlined btree -// } else if (degenerate) { -// lfs_ssize_t sid; -// // left sibling -// if ((lfs_size_t)rid == rbyd.weight-1) { -// sid = rid-rweight; -// // right sibling -// } else { -// sid = rid+1; -// } -// -// lfsr_tag_t stag; -// lfs_size_t sweight; -// lfsr_data_t sdata; -// int err = lfsr_rbyd_lookupnext(lfs, &rbyd, sid, LFSR_TAG_NAME, -// &sid, &stag, &sweight, &sdata); -// if (err) { -// LFS_ASSERT(err == LFS_ERR_NOENT); -// return err; -// } -// -// if (lfsr_tag_suptype(stag) == LFSR_TAG_NAME) { -// err = lfsr_rbyd_lookup(lfs, &rbyd, sid, LFSR_TAG_WIDE(STRUCT), -// &stag, &sdata); -// if (err) { -// LFS_ASSERT(err == LFS_ERR_NOENT); -// return err; -// } -// } -// -// LFS_ASSERT(sweight+rweight == rbyd.weight); -// // mark as inlined -// btree->u.i.weight = lfsr_btree_setinlined(sweight); -// btree->u.i.tag = stag; -// -// LFS_ASSERT(lfsr_data_size(&sdata) <= LFSR_BTREE_INLINESIZE); -// err = lfsr_bd_read(lfs, sdata.u.d.block, sdata.u.d.off, 0, -// btree->u.i.buf, lfsr_data_size(&sdata)); -// if (err) { -// return err; -// } -// btree->u.i.size = lfsr_data_size(&sdata); -// } -// -// return 0; -// } -//} - +// TODO move to tests // lfsr_btree_split can be done with a update+push, but this function // does all this in one commit, which is much more efficient // @@ -4859,7 +4591,7 @@ static int lfsr_btree_split(lfs_t *lfs, lfsr_btree_t *btree, return err; } - return lfsr_btree_commit__(lfs, btree, LFSR_ATTRS( + return lfsr_btree_commit(lfs, btree, LFSR_ATTRS( LFSR_ATTR(bid, GROW(RM), +weight1-weight_, NULL), LFSR_ATTR(bid-(weight_-1)+weight1-1, TAG(tag1), 0, DATA(data1)), (lfsr_data_size(&name) > 0 @@ -4873,75 +4605,6 @@ static int lfsr_btree_split(lfs_t *lfs, lfsr_btree_t *btree, TAG(tag2), +weight2, DATA(data2))))); } -//static int lfsr_btree_split(lfs_t *lfs, lfsr_btree_t *btree, -// lfs_size_t bid, lfsr_data_t name, -// lfsr_tag_t tag1, lfs_size_t weight1, lfsr_data_t data1, -// lfsr_tag_t tag2, lfs_size_t weight2, lfsr_data_t data2) { -// LFS_ASSERT(bid < lfsr_btree_weight(btree)); -// LFS_ASSERT(lfsr_btree_weight(btree) > 0); -// -// // inlined btree, need to expand into an rbyd -// if (lfsr_btree_isinlined(btree)) { -// lfsr_rbyd_t rbyd; -// int err = lfsr_rbyd_alloc(lfs, &rbyd); -// if (err) { -// return err; -// } -// -// // commit our entries -// err = lfsr_rbyd_commit(lfs, &rbyd, LFSR_ATTRS( -// LFSR_ATTR(0, TAG(tag1), +weight1, DATA(data1)), -// (lfsr_data_size(&name) > 0 -// ? LFSR_ATTR(weight1, BNAME, +weight2, DATA(name)) -// : LFSR_ATTR_NOOP), -// (lfsr_data_size(&name) > 0 -// ? LFSR_ATTR(weight1+weight2-1, TAG(tag2), 0, DATA(data2)) -// : LFSR_ATTR(weight1, TAG(tag2), +weight2, DATA(data2))))); -// if (err) { -// return err; -// } -// -// btree->u.r.rbyd = rbyd; -// return 0; -// -// // a normal btree -// } else { -// // lookup in which leaf our bid resides -// lfsr_rbyd_t rbyd; -// lfs_ssize_t rid; -// lfs_size_t rweight; -// int err = lfsr_btree_lookupnext_(lfs, btree, bid, -// NULL, &rbyd, &rid, NULL, &rweight, NULL); -// if (err) { -// return err; -// } -// -// // commit our bid into the tree, letting lfsr_btree_commit take care -// // of the rest -// int degenerate = lfsr_btree_commit(lfs, btree, bid, &rbyd, -// LFSR_ATTRS( -// LFSR_ATTR(rid, GROW(RM), +weight1-rweight, NULL), -// LFSR_ATTR(rid-(rweight-1)+weight1-1, TAG(tag1), 0, -// DATA(data1)), -// (lfsr_data_size(&name) > 0 -// ? LFSR_ATTR(rid-(rweight-1)+weight1, -// BNAME, +weight2, DATA(name)) -// : LFSR_ATTR_NOOP), -// (lfsr_data_size(&name) > 0 -// ? LFSR_ATTR(rid-(rweight-1)+weight1+weight2-1, -// TAG(tag2), 0, DATA(data2)) -// : LFSR_ATTR(rid-(rweight-1)+weight1, -// TAG(tag2), +weight2, DATA(data2))))); -// if (degenerate < 0) { -// return degenerate; -// } -// -// // this should never happen -// LFS_ASSERT(!degenerate); -// return 0; -// } -//} - // lookup in a btree by name static int lfsr_btree_namelookup(lfs_t *lfs, const lfsr_btree_t *btree, lfs_size_t did, const char *name, lfs_size_t name_size, diff --git a/tests/test_btree.toml b/tests/test_btree.toml index b391b817..c4284b7e 100644 --- a/tests/test_btree.toml +++ b/tests/test_btree.toml @@ -2448,264 +2448,6 @@ code = ''' free(sim); ''' -# TODO -## test we reinline (go from uninlined to inlined) correctly, this is a bit -## tricky since our btrees lazily reinline -#[cases.test_btree_reinline_pop_set] -#in = 'lfs.c' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, CFG) => 0; -# // create free lookahead -# memset(lfs.lookahead.buffer, 0, CFG->lookahead_size); -# lfs.lookahead.start = 0; -# lfs.lookahead.size = lfs_min(8*CFG->lookahead_size, -# CFG->block_count); -# lfs.lookahead.next = 0; -# lfs_alloc_ack(&lfs); -# -# // create an uninlined tree -# lfsr_btree_t btree = LFSR_BTREE_NULL; -# lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("a", 1)) => 0; -# lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, -# LFSR_DATA("b", 1)) => 0; -# assert(lfsr_btree_weight(&btree) == 2); -# assert(!lfsr_btree_isinlined(&btree)); -# -# // pop! our btree should now be reinlinable -# lfsr_btree_pop(&lfs, &btree, 0) => 0; -# -# // but thanks to lazy reinlining, our btree won't reinline until -# // it is compacted, so we need to add commits until it is compacted -# lfs_block_t before_block = btree.u.r.rbyd.block; -# for (lfs_block_t i = 0;; i++) { -# // a bit hacky, but this catches infinite loops -# assert(i < BLOCK_SIZE); -# -# // commit to btree -# lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("b", 1)) => 0; -# -# assert(lfsr_btree_weight(&btree) == 1); -# -# // try looking up tag to hopefully catch if something breaks -# uint8_t buffer[4]; -# lfsr_tag_t tag_; -# lfs_size_t weight_; -# -# lfsr_btree_get(&lfs, &btree, 0, -# &tag_, &weight_, buffer, 4) => 1; -# assert(tag_ == LFSR_TAG_INLINED); -# assert(weight_ == 1); -# assert(memcmp(buffer, "b", 1) == 0); -# -# // inlined? consider this a success -# if (lfsr_btree_isinlined(&btree)) { -# break; -# } -# -# // assert if a compaction occurred that wasn't inlined -# assert(btree.u.r.rbyd.block == before_block); -# } -# -# printf("btree: w%d 0x%x.%x\n", -# btree.u.r.rbyd.weight, -# btree.u.r.rbyd.block, -# btree.u.r.rbyd.trunk); -#''' -# -#[cases.test_btree_reinline_pop_pop_push] -#in = 'lfs.c' -#defines.SHIFT = 'range(5)' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, CFG) => 0; -# // create free lookahead -# memset(lfs.lookahead.buffer, 0, CFG->lookahead_size); -# lfs.lookahead.start = 0; -# lfs.lookahead.size = lfs_min(8*CFG->lookahead_size, -# CFG->block_count); -# lfs.lookahead.next = 0; -# lfs_alloc_ack(&lfs); -# -# // create an uninlined tree -# lfsr_btree_t btree = LFSR_BTREE_NULL; -# lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("a", 1)) => 0; -# lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, -# LFSR_DATA("b", 1)) => 0; -# assert(lfsr_btree_weight(&btree) == 2); -# assert(!lfsr_btree_isinlined(&btree)); -# -# // pop! our btree should now be reinlinable -# lfsr_btree_pop(&lfs, &btree, 0) => 0; -# -# // It's difficult to test reinlining during push or pop, since we can't just -# // repeat the action until compaction occurs. -# // -# // What we do here is alternate between 0 and 1 entries, eventually we -# // will compact during one of either a push or pop. To try to cover both, -# // test with some number of extra commits to hopefully adjust where the -# // compaction ends up. -# for (lfs_size_t i = 0; i < SHIFT; i++) { -# lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("b", 1)) => 0; -# } -# -# // alternate between push/pop until compaction occurs -# lfs_block_t before_block = btree.u.r.rbyd.block; -# for (lfs_block_t i = 0;; i++) { -# // a bit hacky, but this catches infinite loops -# assert(i < BLOCK_SIZE); -# -# // pop! -# lfsr_btree_pop(&lfs, &btree, 0) => 0; -# -# assert(lfsr_btree_weight(&btree) == 0); -# -# // inlined? consider this a success -# if (lfsr_btree_isinlined(&btree)) { -# break; -# } -# -# // assert if a compaction occurred that wasn't inlined -# assert(btree.u.r.rbyd.block == before_block); -# -# // push! -# lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("c", 1)) => 0; -# -# // try looking up tag to hopefully catch if something breaks -# uint8_t buffer[4]; -# lfsr_tag_t tag_; -# lfs_size_t weight_; -# -# lfsr_btree_get(&lfs, &btree, 0, -# &tag_, &weight_, buffer, 4) => 1; -# assert(tag_ == LFSR_TAG_INLINED); -# assert(weight_ == 1); -# assert(memcmp(buffer, "c", 1) == 0); -# -# // inlined? consider this a success -# if (lfsr_btree_isinlined(&btree)) { -# break; -# } -# -# // assert if a compaction occurred that wasn't inlined -# assert(btree.u.r.rbyd.block == before_block); -# } -# -# printf("btree: w%d 0x%x.%x\n", -# btree.u.r.rbyd.weight, -# btree.u.r.rbyd.block, -# btree.u.r.rbyd.trunk); -#''' -# -#[cases.test_btree_reinline_pop_push] -#in = 'lfs.c' -#defines.SIBLING = [0, 1] -#defines.SHIFT = 'range(5)' -#code = ''' -# lfs_t lfs; -# lfs_init(&lfs, CFG) => 0; -# // create free lookahead -# memset(lfs.lookahead.buffer, 0, CFG->lookahead_size); -# lfs.lookahead.start = 0; -# lfs.lookahead.size = lfs_min(8*CFG->lookahead_size, -# CFG->block_count); -# lfs.lookahead.next = 0; -# lfs_alloc_ack(&lfs); -# -# // create an uninlined tree -# lfsr_btree_t btree = LFSR_BTREE_NULL; -# lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("a", 1)) => 0; -# lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, -# LFSR_DATA("b", 1)) => 0; -# assert(lfsr_btree_weight(&btree) == 2); -# assert(!lfsr_btree_isinlined(&btree)); -# -# // It's difficult to test reinlining during push or pop, since we can't just -# // repeat the action until compaction occurs. -# // -# // Here we alternate between 1 and 2 entries, with the hope that compaction -# // occurs on the pop. We try this with some number of extra commits to make -# // it more likely pop is tested. -# // -# // It's possible our commits line up so compaction always occurs on a push! -# // For this reason, we end the test if an non-reinlining compaction occurs. -# for (lfs_size_t i = 0; i < SHIFT; i++) { -# lfsr_btree_set(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, -# LFSR_DATA("a", 1)) => 0; -# } -# -# // alternate between push/pop until compaction occurs -# lfs_block_t before_block = btree.u.r.rbyd.block; -# for (lfs_block_t i = 0;; i++) { -# // a bit hacky, but this catches infinite loops -# assert(i < BLOCK_SIZE); -# -# // pop! -# lfsr_btree_pop(&lfs, &btree, SIBLING) => 0; -# -# assert(lfsr_btree_weight(&btree) == 1); -# -# // try looking up tag to hopefully catch if something breaks -# uint8_t buffer[4]; -# lfsr_tag_t tag_; -# lfs_size_t weight_; -# -# lfsr_btree_get(&lfs, &btree, 0, -# &tag_, &weight_, buffer, 4) => 1; -# assert(tag_ == LFSR_TAG_INLINED); -# assert(weight_ == 1); -# assert(memcmp(buffer, (SIBLING == 1 ? "a" : "b"), 1) == 0); -# -# // inlined? consider this a success -# if (lfsr_btree_isinlined(&btree)) { -# break; -# } -# -# // abort if a compaction occurs -# if (btree.u.r.rbyd.block != before_block) { -# break; -# } -# -# // push! -# lfsr_btree_push(&lfs, &btree, SIBLING, LFSR_TAG_INLINED, 1, -# LFSR_DATA("c", 1)) => 0; -# -# // try looking up tag to hopefully catch if something breaks -# lfsr_btree_get(&lfs, &btree, 0, -# &tag_, &weight_, buffer, 4) => 1; -# assert(tag_ == LFSR_TAG_INLINED); -# assert(weight_ == 1); -# assert(memcmp(buffer, (SIBLING == 1 ? "a" : "c"), 1) == 0); -# -# lfsr_btree_get(&lfs, &btree, 1, -# &tag_, &weight_, buffer, 4) => 1; -# assert(tag_ == LFSR_TAG_INLINED); -# assert(weight_ == 1); -# assert(memcmp(buffer, (SIBLING == 1 ? "c" : "b"), 1) == 0); -# -# // inlined? consider this a success -# if (lfsr_btree_isinlined(&btree)) { -# break; -# } -# -# // abort if a compaction occurs -# if (btree.u.r.rbyd.block != before_block) { -# break; -# } -# } -# -# printf("btree: w%d 0x%x.%x\n", -# btree.u.r.rbyd.weight, -# btree.u.r.rbyd.block, -# btree.u.r.rbyd.trunk); -#''' - # Some more general fuzz testing [cases.test_btree_general_fuzz]