Implemented lfsr_btree_update and added more tests
This was a rather simple exercise. lfsr_btree_commit does most of the work already, so all this needed was setting up the pending attributes correctly. Also: - Tweaked dbgrbyd.py's tree rendering to match dbgbtree.py's. - Added a print to each B-tree test to help find the resulting B-tree when debugging.
This commit is contained in:
@@ -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 ///
|
||||
|
||||
+5
-5
@@ -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)))
|
||||
|
||||
|
||||
+6
-3
@@ -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),
|
||||
|
||||
+474
-40
@@ -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]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user