diff --git a/lfs.c b/lfs.c index dcafa74f..76e5744b 100644 --- a/lfs.c +++ b/lfs.c @@ -506,6 +506,10 @@ static inline bool lfsr_tag_isrm(lfsr_tag_t tag) { return tag & 0x2; } +static inline lfsr_tag_t lfsr_tag_mkrm(lfsr_tag_t tag) { + return tag | 0x2; +} + static inline bool lfsr_tag_hasdata(lfsr_tag_t tag) { return (tag & 0xe) <= 0x4; } @@ -741,6 +745,17 @@ struct lfsr_attr { #define LFSR_ATTR(_type, _id, _buffer, _size, _next) \ LFSR_ATTR_(LFSR_TAG_##_type, _id, _buffer, _size, _next) +#define LFSR_ATTR_IF_(_pred, _tag, _id, _buffer, _size, _next) \ + LFSR_ATTR_( \ + (_pred) ? (_tag) : LFSR_TAG_GROW, \ + _id, \ + _buffer, \ + (_pred) ? (_size) : 0, \ + _next) + +#define LFSR_ATTR_IF(_pred, _type, _id, _buffer, _size, _next) \ + LFSR_ATTR_IF_(_pred, LFSR_TAG_##_type, _id, _buffer, _size, _next) + struct lfsr_attr_from { const lfsr_rbyd_t *rbyd; const struct lfsr_attr *attrs; @@ -3718,226 +3733,6 @@ static int lfsr_btree_commit(lfs_t *lfs, attrs = scratch_attrs; continue; } - -// done:; -// -// -// // the first question is will we fit comfortably after compaction -// lfs_ssize_t predicted = lfsr_rbyd_predictedsize( -// lfs, rbyd, attrs, 0, rbyd->weight); -// if (predicted < 0) { -// return predicted; -// } -// -//// printf("predicted: %d/%d\n", predicted, lfs->cfg->block_size/2); -// -// // keep rbyd < 1/2 to avoid degenerate cases with full rbyd -// if ((lfs_size_t)predicted <= lfs->cfg->block_size/2) { -// lfsr_rbyd_t rbyd_ = {.erased=true}; -// err = lfs_alloc(lfs, &rbyd_.block); -// if (err) { -// return err; -// } -//// printf("compacting %x->%x...\n", rbyd->block, rbyd_.block); -// -// // TODO should erase be implicit in alloc eventually? -// err = lfs_bd_erase(lfs, rbyd_.block); -// if (err) { -// return err; -// } -// -// // TODO wait, should from reuse next for attrs? -// err = lfsr_rbyd_commit(lfs, &rbyd_, -// // TODO this extra +1 is a hack, need to get the -// // actual predictedweight from the attr list! -// LFSR_ATTR_FROM(0, rbyd, attrs, 0, rbyd->weight+1, NULL)); -// if (err) { -// printf("ah %d\n", err); -// return err; -// } -// -// // done? -// if (pid == -1) { -// *rbyd = rbyd_; -// 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; -// -// // time to split -// } else { -//// printf("splitting...\n"); -// lfsr_rbyd_t children[2] = {{.erased=true}, {.erased=true}}; -// // TODO is this a hack or the correct way to do this? -// lfs_size_t consumed = 0; -// for (unsigned i = 0; i < 2; i++) { -// err = lfs_alloc(lfs, &children[i].block); -// if (err) { -// return err; -// } -// -// // TODO should erase be implicit in alloc eventually? -// err = lfs_bd_erase(lfs, children[i].block); -// if (err) { -// return err; -// } -// -// // copy over half the ids, note we round up here to avoid -// // missing any ids during the copy -//// printf("child %d: %d..%d\n", i, (i+0)*((rbyd->weight+1)/2), (i+1)*((rbyd->weight+1)/2)); -// err = lfsr_rbyd_commit(lfs, &children[i], -// LFSR_ATTR_FROM(0, rbyd, attrs, -// // TODO this extra +1 is a hack, need to get the -// // actual predictedweight from the attr list! -// consumed, -// (i+1)*((rbyd->weight+1+1)/2), -// NULL)); -// if (err) { -// return err; -// } -// -// consumed += children[i].weight; -// } -// -// // no parent? introduce a new trunk -// if (pid == -1) { -// parent = (lfsr_rbyd_t){.erased=true}; -// err = lfs_alloc(lfs, &parent.block); -// if (err) { -// return err; -// } -// -// // TODO should erase be implicit in alloc eventually? -// err = lfs_bd_erase(lfs, parent.block); -// if (err) { -// return err; -// } -// -// // TODO this can also probably be deduplicated -// // prepare commit to parent, tail recursing upwards -// lfs_ssize_t delta1 = lfsr_branch_todisk( -// &(const lfsr_branch_t){ -// children[0].block, children[0].off}, -// scratch_buf1); -// if (delta1 < 0) { -// return delta1; -// } -// lfs_ssize_t delta2 = lfsr_branch_todisk( -// &(const lfsr_branch_t){ -// children[1].block, children[1].off}, -// scratch_buf2); -// if (delta2 < 0) { -// return delta2; -// } -// -// scratch_attrs[0] = *LFSR_ATTR( -// GROW, 0, -// NULL, children[0].weight, -// &scratch_attrs[1]); -// scratch_attrs[1] = *LFSR_ATTR( -// MKBRANCH, 0+children[0].weight-1, -// NULL, 0, -// &scratch_attrs[2]); -// scratch_attrs[2] = *LFSR_ATTR( -// BRANCH, 0+children[0].weight-1, -// scratch_buf1, delta1, -// &scratch_attrs[3]); -// -// scratch_attrs[3] = *LFSR_ATTR( -// GROW, 0+children[0].weight, -// NULL, children[1].weight, -// &scratch_attrs[4]); -// scratch_attrs[4] = *LFSR_ATTR( -// MKBRANCH, 0+children[0].weight+children[1].weight-1, -// NULL, 0, -// &scratch_attrs[5]); -// scratch_attrs[5] = *LFSR_ATTR( -// BRANCH, 0+children[0].weight+children[1].weight-1, -// scratch_buf2, delta2, -// NULL); -// -// *rbyd = parent; -// attrs = scratch_attrs; -// -// // yes parent? push up split -// } else { -// // prepare commit to parent, tail recursing upwards -// lfs_ssize_t delta1 = lfsr_branch_todisk( -// &(const lfsr_branch_t){ -// children[0].block, children[0].off}, -// scratch_buf1); -// if (delta1 < 0) { -// return delta1; -// } -// lfs_ssize_t delta2 = lfsr_branch_todisk( -// &(const lfsr_branch_t){ -// children[1].block, children[1].off}, -// scratch_buf2); -// if (delta2 < 0) { -// return delta2; -// } -// -// scratch_attrs[0] = *LFSR_ATTR( -// SHRINK, pid-(pweight-1), NULL, pweight, -// &scratch_attrs[1]); -// -// scratch_attrs[1] = *LFSR_ATTR( -// GROW, pid-(pweight-1), -// NULL, children[0].weight, -// &scratch_attrs[2]); -// scratch_attrs[2] = *LFSR_ATTR( -// MKBRANCH, pid-(pweight-1)+children[0].weight-1, -// NULL, 0, -// &scratch_attrs[3]); -// scratch_attrs[3] = *LFSR_ATTR( -// BRANCH, pid-(pweight-1)+children[0].weight-1, -// scratch_buf1, delta1, -// &scratch_attrs[4]); -// -// scratch_attrs[4] = *LFSR_ATTR( -// GROW, pid-(pweight-1)+children[0].weight, -// NULL, children[1].weight, -// &scratch_attrs[5]); -// scratch_attrs[5] = *LFSR_ATTR( -// MKBRANCH, pid-(pweight-1)+children[0].weight -// +children[1].weight-1, -// NULL, 0, -// &scratch_attrs[6]); -// scratch_attrs[6] = *LFSR_ATTR( -// BRANCH, pid-(pweight-1)+children[0].weight -// +children[1].weight-1, -// scratch_buf2, delta2, -// NULL); -// -// *rbyd = parent; -// attrs = scratch_attrs; -// } -// } } // at this point rbyd should be the trunk of our tree @@ -3947,13 +3742,11 @@ static int lfsr_btree_commit(lfs_t *lfs, return 0; } -static int lfsr_btree_push(lfs_t *lfs, - lfsr_btree_t *btree, +static int lfsr_btree_push(lfs_t *lfs, lfsr_btree_t *btree, lfs_size_t id, lfsr_tag_t tag, lfs_size_t weight, const void *buffer, lfs_size_t size) { LFS_ASSERT(id <= btree->weight); -// printf("- push(%d, %x, w%d) -\n", id, tag, weight); // null btree? if (btree->weight == 0) { LFS_ASSERT(id == 0); @@ -3998,8 +3791,6 @@ static int lfsr_btree_push(lfs_t *lfs, // a normal btree } else { // lookup in which leaf our id resides - // TODO currently using our neighbor id since id will just - // return ENOENT, is this ok? lfsr_rbyd_t rbyd; lfs_ssize_t rid; lfs_size_t rweight; @@ -4028,377 +3819,52 @@ static int lfsr_btree_push(lfs_t *lfs, } } -// -// -// -// -// -// // in range? -// if (id >= btree->weight) { -// return LFS_ERR_NOENT; -// } -// -// // an inlined tree? -// if (!btree->limit) { -// // TODO how many of these need to be conditional? -// if (id_) { -// *id_ = btree->weight-1; -// } -// if (weight_) { -// *weight_ = btree->weight; -// } -// if (value_) { -// *value_ = btree->trunk; -// } -// return 0; -// } -// -// // TODO this can be a different type (don't need weight?) -// lfsr_btree_t branch = *btree; -// while (true) { -// // descend down the tree looking for our id -// int err = lfsr_rbyd_fetch(lfs, rbyd, branch.trunk, branch.limit, NULL); -// if (err) { -// return err; -// } -// -// lfsr_tag_t tag__; -// lfs_ssize_t id__; -// lfs_size_t weight__; -// err = lfsr_rbyd_lookup(lfs, rbyd, LFSR_TAG_MK, id, -// &tag__, &id__, &weight__, NULL, NULL); -// if (err) { -// return err; -// } -// -// // found another branch -// if (tag__ == LFSR_TAG_MKBRANCH) { -// // TODO -// LFS_ASSERT(false); -//// // load the branch from the rbyd -//// uint8_t buf[LFSR_BRANCH_DSIZE]; -//// err = lfsr_rbyd_get(lfs, rbyd, LFSR_TAG_BRANCH, id_, -//// buf, LFSR_BRANCH_DSIZE); -//// if (err) { -//// return err; -//// } -//// -//// err = lfsr_btree_fromdisk(&branch, -//// branch.off+id_-(weight_-1), weight_, buf); -//// if (err) { -//// return err; -//// } -// // found our id? -// } else { -// // TODO how many of these need to be conditional? -// if (id_) { -// *id_ = id__; -// } -// if (weight_) { -// *weight_ = rbyd->weight; -// } -// if (value_) { -// uint8_t buf[5]; -// lfs_ssize_t delta = lfsr_rbyd_get(lfs, rbyd, -// LFSR_TAG_BLOCK, id__, buf, 5); -// if (delta < 0) { -// return delta; -// } -// -// delta = lfs_fromleb128(value_, buf, delta); -// if (delta < 0) { -// return delta; -// } -// } -// -// return 0; -// } -// } -//} +static int lfsr_btree_update(lfs_t *lfs, lfsr_btree_t *btree, + lfs_size_t id, lfsr_tag_t tag, lfs_size_t weight, + const void *buffer, lfs_size_t size) { + LFS_ASSERT(id < btree->weight); -//static int lfsr_btree_commit(lfs_t *lfs, -// lfsr_btree_t *btree, lfsr_rbyd_t *rbyd, -// const struct lfsr_attr *attrs) { -// // if our block is erased, just try to append to it, note the btree -// // limit field prevents this from mutating old copies of the tree -// int err = lfsr_rbyd_commit(lfs, rbyd, attrs); -// if (err && err != LFS_ERR_RANGE) { -// // TODO wait should we also move if there is corruption here? -// return err; -// } -// -// if (err != LFS_ERR_RANGE) { -// // TODO -// LFS_ASSERT(btree->trunk == rbyd->block); -// btree->weight = rbyd->weight; -// btree->trunk = rbyd->block; -// btree->limit = rbyd->off; -// return 0; -// } -// -// // TODO -// LFS_ASSERT(false); -// -//// // either our block isn't erased or we have filled the block, so now the -//// // question is do we fit after compaction? -//// lfs_ssize_t compacted = lfsr_rbyd_predictedsize(lfs, rbyd, 0, rbyd->weight); -//// if (compacted < 0) { -//// return compacted; -//// } -//// -//// // do we fit compacted? we're looking to fit in 1/2 a block in order to -//// // avoid degenerate cases with nearly-full rbyds. -//// if (compacted <= lfs->cfg->block_size/2) { -//// -//// -//// -//// int err = lfs_alloc(lfs, &rbyd.block); -//// if (err) { -//// return err; -//// } -//// -//// // TODO should erase be implicit in alloc eventually? -//// // erase the block and write the root of our tree -//// err = lfs_bd_erase(lfs, rbyd.block); -//// if (err) { -//// return err; -//// } -//// } else { -//// } -// return 0; -//} -// -// -////static int lfsr_btree_get(lfs_t *lfs, const lfsr_btree_t *btree, -//// lfs_size_t id, lfs_block_t *value_) { -//// lfsr_rbyd_t rbyd; -//// int err = lfsr_btree_lookup(lfs, btree, &rbyd, id, NULL, NULL, value_); -//// if (err) { -//// return err; -//// } -//// -//// return 0; -////} -// -//static int lfsr_btree_push(lfs_t *lfs, lfsr_btree_t *btree, -// lfsr_tag_t tag, lfs_size_t id, lfs_size_t weight, -// const void *buffer, lfs_size_t size); -//static int lfsr_btree_pop(lfs_t *lfs, lfsr_btree_t *btree, -// lfsr_tag_t tag, lfs_size_t id, lfs_size_t weight, -// const void *buffer, lfs_size_t size); -//static int lfsr_btree_update(lfs_t *lfs, lfsr_btree_t *btree, -// lfsr_tag_t tag, lfs_size_t id, lfs_size_t weight, -// const void *buffer, lfs_size_t size); -// -// -//static int lfsr_btree_set(lfs_t *lfs, lfsr_btree_t *btree, -// lfs_size_t id, lfs_size_t weight, lfs_block_t value) { -// // an inlined tree? -// if (btree->limit == 0) { -// if (btree->weight == 0 || id == btree->weight-1) { -// btree->weight = weight; -// btree->trunk = value; -// return 0; -// } -// -// // turn an inlined tree into a normal tree? -// -// // TODO should this be in lfsr_rbyd_alloc or something similar? -// // allocate an rbyd block -// lfsr_rbyd_t rbyd = { -// .block = 0, -// .rev = 0, -// .off = 0, -// .crc = 0, -// .trunk = 0, -// .weight = 0, -// .erased = true -// }; -// int err = lfs_alloc(lfs, &rbyd.block); -// if (err) { -// return err; -// } -// -// // TODO should erase be implicit in alloc eventually? -// // erase the block and write the root of our tree -// err = lfs_bd_erase(lfs, rbyd.block); -// if (err) { -// return err; -// } -// -// uint8_t buf1[5]; -// uint8_t buf2[5]; -// lfs_ssize_t delta1 = lfs_toleb128(btree->trunk, buf1, 5); -// if (delta1 < 0) { -// return delta1; -// } -// lfs_ssize_t delta2 = lfs_toleb128(value, buf2, 5); -// if (delta2 < 0) { -// return delta2; -// } -// -// // TODO should this actually be lfsr_btree_commit? -// LFS_ASSERT(btree->weight > 0); -// LFS_ASSERT(weight > 0); -// err = lfsr_rbyd_commit(lfs, &rbyd, -// LFSR_ATTR(MKREG, 0, NULL, 0, -// LFSR_ATTR(BLOCK, 0, buf1, delta1, -// LFSR_ATTR(GROW, 0, NULL, btree->weight-1, -// LFSR_ATTR(MKREG, id-(weight-1), NULL, 0, -// LFSR_ATTR(BLOCK, id-(weight-1), buf2, delta2, -// LFSR_ATTR(GROW, id-(weight-1), NULL, weight-1, -// NULL))))))); -// if (err) { -// return err; -// } -// -// btree->weight = rbyd.weight; -// btree->trunk = rbyd.block; -// btree->limit = rbyd.off; -// return 0; -// } -// -// // find which leaf we're operating on -// lfsr_rbyd_t rbyd; -// lfs_size_t id_; -// int err = lfsr_btree_lookup(lfs, btree, &rbyd, id, &id_, NULL, NULL); -// if (err) { -// return err; -// } -// -// // update leaf, note lfsr_btree_commit takes care of propagating btree -// // splits/merges/relocations etc recursively -// uint8_t buf[5]; -// lfs_ssize_t delta = lfs_toleb128(value, buf, 5); -// if (delta < 0) { -// return delta; -// } -// -// err = lfsr_btree_commit(lfs, btree, &rbyd, -// LFSR_ATTR(MKREG, id_-(weight-1), NULL, 0, -// LFSR_ATTR(BLOCK, id_-(weight-1), buf, delta, -// LFSR_ATTR(GROW, id_-(weight-1), NULL, weight-1, -// NULL)))); -// if (err) { -// return err; -// } -// -// return 0; -//} + // inlined btree? + if (btree->tag) { + LFS_ASSERT(id == btree->weight-1); + btree->tag = tag; + btree->weight = weight; + LFS_ASSERT(size <= LFSR_BTREE_INLINE_SIZE); + memcpy(btree->u.inlined.buf, buffer, size); + btree->u.inlined.size = size; + 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; + } + + // commit our id into the tree, letting lfsr_btree_commit take care + // of the rest + return lfsr_btree_commit(lfs, btree, id, &rbyd, + LFSR_ATTR_IF_(tag != rtag, + lfsr_tag_mkrm(rtag), rid, NULL, 0, + LFSR_ATTR_(tag, rid, buffer, size, + LFSR_ATTR_( + weight >= rweight ? LFSR_TAG_GROW : LFSR_TAG_SHRINK, + rid-(rweight-1), + NULL, + weight >= rweight ? weight - rweight : rweight - weight, + NULL)))); + } +} + -//static int lfsr_btree_alloc(lfs_t *lfs, lfsr_btree_t *btree, -// lfs_size_t weight, const struct lfsr_attr *attrs) { -// // create an rbyd block to act as the root of the tree -// // -// // note that for littlefs this should really only be called -// // when we have at least two entries, otherwise a smaller representation -// // should be used -// lfs_block_t block; -// int err = lfs_alloc(lfs, &block); -// if (err) { -// return err; -// } -// -// // read revision count so we make sure to change the contents of the block, -// // this is important if erase is a noop -// uint32_t rev; -// err = lfs_bd_read(lfs, -// NULL, &lfs->rcache, 0, -// block, 0, &rev, sizeof(uint32_t)); -// if (err) { -// return err; -// } -// -// // go ahead and erase the block -// err = lfs_bd_erase(lfs, block); -// if (err) { -// return err; -// } -// -// // write the new root of our tree -// lfsr_rbyd_t rbyd = { -// .block = block, -// .trunk = 0, -// .off = 0, -// .rev = rev + 1, -// .crc = 0, -// .count = 0, -// .erased = true -// }; -// -// err = lfsr_rbyd_commit(lfs, &rbyd, attrs); -// if (err) { -// return err; -// } -// -// btree->block = block; -// btree->limit = rbyd.off; -// btree->weight = weight; -// return 0; -//} -// -//static lfsr_stag_t lfsr_btree_lookup(lfs_t *lfs, const lfsr_btree_t *btree, -// lfsr_rbyd_t *rbyd, struct lfsr_pat *pattern) { -// // most of the work here is done by lfsr_rbyd_fetch, we just descend -// // down the tree until it fails -// lfsr_btree_t branch = *btree; -// while (true) { -// lfsr_stag_t tag = lfsr_rbyd_fetch(lfs, rbyd, -// branch.block, branch.limit, -// pattern); -// if (tag < 0 && tag != LFS_ERR_NOENT) { -// return tag; -// } -// -// // found? -// if (tag != LFS_ERR_NOENT && lfsr_tag_type(tag) != LFSR_TAG_MKBRANCH) { -// return 0; -// } -// -// // TODO do we? -// // TODO also can the pattern found on ENOENT be formed better for this? -// // -// // we always find ids <= our pattern, so if it's not found we descend -// // down the left branch, but we need to make sure this is actually a -// // btree branch -// if (tag == LFS_ERR_NOENT) { -// tag = lfsr_rbyd_lookup(lfs, rbyd, -// LFSR_TAG(MK, lfsr_tag_id(pattern->found) -// - lfs_min(1, lfsr_tag_id(pattern->found))), -// NULL, NULL); -// if (tag < 0) { -// return tag; -// } -// -// if (lfsr_tag_type(tag) != LFSR_TAG_MKBRANCH) { -// return LFS_ERR_NOENT; -// } -// } -// -// // continue search down tree -// uint8_t bbuf[LFSR_BTREE_DSIZE]; -// lfs_ssize_t delta = lfsr_rbyd_get(lfs, rbyd, -// LFSR_TAG(BTREE, lfsr_tag_id(tag)), -// bbuf, LFSR_BTREE_DSIZE); -// if (delta < 0) { -// return delta; -// } -// -// delta = lfsr_btree_fromdisk(&branch, bbuf); -// if (delta < 0) { -// return delta; -// } -// } -//} -// -//static int lfsr_btree_insert(lfs_t *lfs, lfsr_btree_t *btree, -// lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs) { -// // TODO -// LFS_ASSERT(false); -// return 0; -//} /// Metadata pair operations /// diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 31de3bb2..3a492e45 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -345,12 +345,12 @@ def main(disk, block_size=None, trunk=0, limit=None, *, branches_.append('+') elif i+1 < len(t_branches): if (id-(w-1) == t_branches[i+1][0] - and t_branches[i+1][0] == t_branches[i][0] + and t_branches[i][0] == t_branches[i+1][0] and (not args.get('inner') or (i == 0 and d == 0))): branches_.append('+-') elif (id-(w-1) == t_branches[i+1][0] - and t_branches[i+1][1]-1 == t_branches[i][1]-1 + and t_branches[i][1] == t_branches[i+1][1] and (not args.get('inner') or d == i)): branches_.append('\'-') elif (id-(w-1) == t_branches[i+1][0] @@ -358,7 +358,7 @@ def main(disk, block_size=None, trunk=0, limit=None, *, branches_.append('|-') elif (id-(w-1) >= t_branches[i][0] and id-(w-1) < t_branches[i][1] - and t_branches[i+1][1]-1 != t_branches[i][1]-1): + and t_branches[i][1] != t_branches[i+1][1]): branches_.append('| ') else: branches_.append(' ') @@ -366,9 +366,9 @@ def main(disk, block_size=None, trunk=0, limit=None, *, if (id-(w-1) == t_branches[i][0] and (not args.get('inner') or i == 0)): branches_.append('+-%s> ' % ('-'*2*(t_depth-i-1))) - elif id-(w-1) == t_branches[i][1]-1: + elif id == t_branches[i][1]-1: branches_.append('\'-%s> ' % ('-'*2*(t_depth-i-1))) - elif (id-(w-1) >= t_branches[i][0] + elif (id >= t_branches[i][0] and id-(w-1) < t_branches[i][1]): branches_.append('|-%s> ' % ('-'*2*(t_depth-i-1))) diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index aed16893..c5acfb97 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -486,6 +486,7 @@ def show_tree(block_size, data, rev, trunk, weight, *, return done, tag_, id_, w_, j, delta, jump, path # precompute tree + tree_width = 0 if args.get('tree'): tags = [] paths = {} @@ -510,6 +511,8 @@ def show_tree(block_size, data, rev, trunk, weight, *, # also find the maximum depth depth = max((x+1 for _, _, x in paths.keys()), default=0) + if depth > 0: + tree_width = 2*depth + 2 def treerepr(j): if depth == 0: @@ -571,7 +574,7 @@ def show_tree(block_size, data, rev, trunk, weight, *, seen = c if seen and x == depth-1: - path.append('%s>%s' % (c_start(seen), c_stop(seen))) + path.append('%s->%s' % (c_start(seen), c_stop(seen))) elif seen: path.append('%s-%s' % (c_start(seen), c_stop(seen))) else: @@ -583,7 +586,7 @@ def show_tree(block_size, data, rev, trunk, weight, *, w_width = 2*m.ceil(m.log10(max(1, weight)+1))+1 print('%-8s %*s%-*s %-22s %s' % ( 'off', - 2*depth+1 if args.get('tree') and depth > 0 else 0, '', + tree_width, '', w_width, 'ids', 'tag', 'data (truncated)' @@ -614,7 +617,7 @@ def show_tree(block_size, data, rev, trunk, weight, *, if args.get('device'): print('%8s %*s%*s %s' % ( '', - 2*depth+1 if args.get('tree') and depth > 0 else 0, '', + tree_width, '', w_width, '', '%-22s%s' % ( '%04x %08x %07x' % (tag, 0xffffffff & id, size), diff --git a/tests/test_btree.toml b/tests/test_btree.toml index 556bc371..255be889 100644 --- a/tests/test_btree.toml +++ b/tests/test_btree.toml @@ -20,6 +20,10 @@ code = ''' // create an empty tree lfsr_btree_t btree = LFSR_BTREE_NULL; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -49,6 +53,10 @@ code = ''' // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -87,6 +95,10 @@ code = ''' lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -132,6 +144,10 @@ code = ''' lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "b", 1) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -179,6 +195,10 @@ code = ''' lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0; lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "c", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -233,6 +253,10 @@ code = ''' lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "c", 1) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "b", 1) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // try looking up tags uint8_t buffer[4]; @@ -292,6 +316,10 @@ code = ''' lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, &alphas[i % 26], 1) => 0; } + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // check that the elements are in the tree uint8_t buffer[4]; @@ -336,6 +364,10 @@ code = ''' lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, &alphas[(N-1-i) % 26], 1) => 0; } + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // check that the elements are in the tree uint8_t buffer[4]; @@ -417,6 +449,10 @@ code = ''' printf("%c", sim[i]); } printf("]\n"); + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); assert(btree.weight == N); @@ -466,6 +502,10 @@ code = ''' lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, &alphas[i % 26], 1) => 0; } + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); // check that the elements are in the tree uint8_t buffer[4]; @@ -487,6 +527,21 @@ code = ''' lfsr_btree_get(&lfs, &btree, N*W, &tag_, &id_, &weight_, buffer, 4) => LFS_ERR_NOENT; + + // also test that we can traverse the tree without prior knowledge + id_ = -1; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == i*W+W-1); + assert(weight_ == W); + assert(memcmp(buffer, &alphas[i % 26], 1) == 0); + } + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; ''' [cases.test_btree_sparse_fuzz] @@ -568,6 +623,10 @@ code = ''' sim_weights[i], sim[i]); } printf("]\n"); + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < N; j++) { @@ -600,14 +659,194 @@ code = ''' &tag_, &id_, &weight_, buffer, 4) => LFS_ERR_NOENT; + // also test that we can traverse the tree without prior knowledge + id_ = -1; + for (lfs_size_t i = 0; i < N; i++) { + // calculate actual id in btree space + lfs_size_t weighted_id = 0; + for (lfs_size_t j = 0; j < i; j++) { + weighted_id += sim_weights[j]; + } + + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == weighted_id+sim_weights[i]-1); + assert(weight_ == sim_weights[i]); + assert(memcmp(buffer, &sim[i], 1) == 0); + } + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; + // clean up sim free(sim); } ''' -[cases.test_btree_traverse] -defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] -defines.W = 5 + +# test btree updates + +# try some small trees for easy corner cases first +[cases.test_btree_update_one] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + // create free lookahead + memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); + lfs.free.off = 0; + lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, + lfs.cfg->block_count); + lfs.free.i = 0; + lfs_alloc_ack(&lfs); + + // create a single-entry tree + lfsr_btree_t btree = LFSR_BTREE_NULL; + lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + // update the tree + lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + // try looking up tags + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + + lfsr_btree_get(&lfs, &btree, 0, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 0); + assert(weight_ == 1); + assert(memcmp(buffer, "A", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 1, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; +''' + +[cases.test_btree_update_two] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + // create free lookahead + memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); + lfs.free.off = 0; + lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, + lfs.cfg->block_count); + lfs.free.i = 0; + lfs_alloc_ack(&lfs); + + // create a two-entry tree + lfsr_btree_t btree = LFSR_BTREE_NULL; + lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0; + // update the tree + lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0; + lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "B", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + // try looking up tags + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + + lfsr_btree_get(&lfs, &btree, 0, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 0); + assert(weight_ == 1); + assert(memcmp(buffer, "A", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 1); + assert(weight_ == 1); + assert(memcmp(buffer, "B", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 2, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; +''' + +[cases.test_btree_update_three] +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + // create free lookahead + memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); + lfs.free.off = 0; + lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, + lfs.cfg->block_count); + lfs.free.i = 0; + lfs_alloc_ack(&lfs); + + // create a two-entry tree + lfsr_btree_t btree = LFSR_BTREE_NULL; + lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "a", 1) => 0; + lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "b", 1) => 0; + lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "c", 1) => 0; + // update the tree + lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, "A", 1) => 0; + lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, "B", 1) => 0; + lfsr_btree_update(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, "C", 1) => 0; + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + // try looking up tags + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + + lfsr_btree_get(&lfs, &btree, 0, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 0); + assert(weight_ == 1); + assert(memcmp(buffer, "A", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 1); + assert(weight_ == 1); + assert(memcmp(buffer, "B", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 2, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == 2); + assert(weight_ == 1); + assert(memcmp(buffer, "C", 1) == 0); + + lfsr_btree_get(&lfs, &btree, 3, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; +''' + +[cases.test_btree_update] +defines.N = [4, 8, 16, 32, 64, 128, 256, 512, 1024] in = 'lfs.c' code = ''' lfs_t lfs; @@ -623,39 +862,50 @@ code = ''' // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; + const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (lfs_size_t i = 0; i < N; i++) { - lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, + lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, &alphas[i % 26], 1) => 0; } - - // traverse the elements in the tree - uint8_t buffer[4]; - lfs_size_t id_ = -1; - lfsr_tag_t tag_; - lfs_size_t weight_; + // update the tree for (lfs_size_t i = 0; i < N; i++) { - lfsr_btree_get(&lfs, &btree, id_+1, + lfsr_btree_update(&lfs, &btree, i, LFSR_TAG_INLINED, 1, + &uppers[i % 26], 1) => 0; + } + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + // check that the elements are in the tree + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_get(&lfs, &btree, i, &tag_, &id_, &weight_, buffer, 4) => 1; assert(tag_ == LFSR_TAG_INLINED); - assert(id_ == i*W+W-1); - assert(weight_ == W); - assert(memcmp(buffer, &alphas[i % 26], 1) == 0); + assert(id_ == i); + assert(weight_ == 1); + assert(memcmp(buffer, &uppers[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree - lfsr_btree_get(&lfs, &btree, id_+1, + lfsr_btree_get(&lfs, &btree, N, &tag_, &id_, &weight_, buffer, 4) => LFS_ERR_NOENT; ''' -[cases.test_btree_traverse_fuzz] +[cases.test_btree_update_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] -defines.W = 5 defines.ITER = 10 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; + const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = 1; seed < ITER+1; seed++) { @@ -673,6 +923,174 @@ code = ''' // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, + &alphas[i % 26], 1) => 0; + } + + // set up a simulation to compare against + // + // fun fact this is slower than our actual tree! unfun fact this is + // starting to be a problem... + char *sim = malloc(N); + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = alphas[i % 26]; + } + + uint32_t prng = seed; + for (lfs_size_t i = 0; i < N; i++) { + // choose a pseudo-random id + lfs_size_t id = TEST_PRNG(&prng) % N; + + // update btree + lfsr_btree_update(&lfs, &btree, id, LFSR_TAG_INLINED, 1, + &uppers[i % 26], 1) => 0; + + // update sim + sim[id] = uppers[i % 26]; + } + + // check that btree matches sim + printf("expd: ["); + bool first = true; + for (lfs_size_t i = 0; i < N; i++) { + if (!first) { + printf(", "); + } + first = false; + printf("%c", sim[i]); + } + printf("]\n"); + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + assert(btree.weight == N); + + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_get(&lfs, &btree, i, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == i); + assert(weight_ == 1); + assert(memcmp(buffer, &sim[i], 1) == 0); + } + + // and no extra elements + lfsr_btree_get(&lfs, &btree, N, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; + + // clean up sim + free(sim); + } +''' + +[cases.test_btree_update_sparse] +defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] +defines.W = 5 +in = 'lfs.c' +code = ''' + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + // create free lookahead + memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); + lfs.free.off = 0; + lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, + lfs.cfg->block_count); + lfs.free.i = 0; + lfs_alloc_ack(&lfs); + + // create a tree with N elements + lfsr_btree_t btree = LFSR_BTREE_NULL; + const char *alphas = "abcdefghijklmnopqrstuvwxyz"; + const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, + &alphas[i % 26], 1) => 0; + } + // update the tree + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_update(&lfs, &btree, i*W+W-1, LFSR_TAG_INLINED, W, + &uppers[i % 26], 1) => 0; + } + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); + + // check that the elements are in the tree + uint8_t buffer[4]; + lfsr_tag_t tag_; + lfs_size_t id_; + lfs_size_t weight_; + + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_get(&lfs, &btree, i*W+W-1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == i*W+W-1); + assert(weight_ == W); + assert(memcmp(buffer, &uppers[i % 26], 1) == 0); + } + + // and check that we can't lookup elements that aren't in the tree + lfsr_btree_get(&lfs, &btree, N*W, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; + + // also test that we can traverse the tree without prior knowledge + id_ = -1; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == i*W+W-1); + assert(weight_ == W); + assert(memcmp(buffer, &uppers[i % 26], 1) == 0); + } + lfsr_btree_get(&lfs, &btree, id_+1, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; +''' + +[cases.test_btree_update_sparse_fuzz] +defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] +defines.W = 5 +defines.ITER = 10 +in = 'lfs.c' +code = ''' + const char *alphas = "abcdefghijklmnopqrstuvwxyz"; + const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + // iterate through severals seeds that we can reproduce easily + for (uint32_t seed = 1; seed < ITER+1; seed++) { + // create lfs here since we need to reset each iteration, we're + // space constrained and we can't expect gc to work at this point + lfs_t lfs; + lfs_init(&lfs, cfg) => 0; + // create free lookahead + memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); + lfs.free.off = 0; + lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, + lfs.cfg->block_count); + lfs.free.i = 0; + lfs_alloc_ack(&lfs); + + // create a btree + lfsr_btree_t btree = LFSR_BTREE_NULL; + for (lfs_size_t i = 0; i < N; i++) { + lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, + &alphas[i % 26], 1) => 0; + } // set up a simulation to compare against // @@ -680,14 +1098,15 @@ code = ''' // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); - lfs_size_t sim_size = 0; - memset(sim, 0, N); - memset(sim_weights, 0, N*sizeof(lfs_size_t)); + for (lfs_size_t i = 0; i < N; i++) { + sim[i] = alphas[i % 26]; + sim_weights[i] = W; + } uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random id - lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1); + lfs_size_t id = TEST_PRNG(&prng) % N; // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); @@ -697,17 +1116,14 @@ code = ''' weighted_id += sim_weights[j]; } - // add to btree - lfsr_btree_push(&lfs, &btree, weighted_id, LFSR_TAG_INLINED, weight, - &alphas[i % 26], 1) => 0; + // update btree + lfsr_btree_update(&lfs, &btree, + weighted_id+sim_weights[id]-1, LFSR_TAG_INLINED, weight, + &uppers[i % 26], 1) => 0; - // add to sim - memcpy(&sim[id+1], &sim[id], sim_size-id); - memcpy(&sim_weights[id+1], &sim_weights[id], - (sim_size-id)*sizeof(lfs_size_t)); - sim[id] = alphas[i % 26]; + // update sim + sim[id] = uppers[i % 26]; sim_weights[id] = weight; - sim_size += 1; } // check that btree matches sim @@ -728,6 +1144,10 @@ code = ''' sim_weights[i], sim[i]); } printf("]\n"); + printf("btree: 0x%x.%x w%d\n", + btree.u.trunk.block, + btree.u.trunk.limit, + btree.weight); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < N; j++) { @@ -736,8 +1156,8 @@ code = ''' assert(btree.weight == total_weight); uint8_t buffer[4]; - lfs_size_t id_ = -1; lfsr_tag_t tag_; + lfs_size_t id_; lfs_size_t weight_; for (lfs_size_t i = 0; i < N; i++) { // calculate actual id in btree space @@ -746,6 +1166,29 @@ code = ''' weighted_id += sim_weights[j]; } + lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, + &tag_, &id_, &weight_, + buffer, 4) => 1; + assert(tag_ == LFSR_TAG_INLINED); + assert(id_ == weighted_id+sim_weights[i]-1); + assert(weight_ == sim_weights[i]); + assert(memcmp(buffer, &sim[i], 1) == 0); + } + + // and no extra elements + lfsr_btree_get(&lfs, &btree, total_weight, + &tag_, &id_, &weight_, + buffer, 4) => LFS_ERR_NOENT; + + // also test that we can traverse the tree without prior knowledge + id_ = -1; + for (lfs_size_t i = 0; i < N; i++) { + // calculate actual id in btree space + lfs_size_t weighted_id = 0; + for (lfs_size_t j = 0; j < i; j++) { + weighted_id += sim_weights[j]; + } + lfsr_btree_get(&lfs, &btree, id_+1, &tag_, &id_, &weight_, buffer, 4) => 1; @@ -754,8 +1197,6 @@ code = ''' assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } - - // and no extra elements lfsr_btree_get(&lfs, &btree, id_+1, &tag_, &id_, &weight_, buffer, 4) => LFS_ERR_NOENT; @@ -765,13 +1206,6 @@ code = ''' } ''' -# [cases.test_btree_update] -# [cases.test_btree_update_fuzz] -# [cases.test_btree_update_sparse] -# [cases.test_btree_update_sparse_fuzz] -# [cases.test_btree_update_traverse] -# [cases.test_btree_update_traverse_fuzz] - # [cases.test_btree_pop] # [cases.test_btree_pop_fuzz]