t: Implemented gc_compact_thresh over btree nodes
Note, gc_compact_thresh over bshrubs is not yet implemented... That's
_another_ can of worms since we need to be able to commit to non-tracked
bshrubs somehow...
But at least this proves gc_compact_thresh over btrees is possible.
Now, if LFS_T_COMPACT is provided, any btree nodes > gc_compact_thresh
will be compacted during traversal/gc operations.
To make this work required a rather deep modification to the
lfsr_btree_commit/lfsr_bshrub_commit code paths to expose direct-rbyd
commit functions that can commit to arbitrary btree nodes:
- lfsr_btree_commit - bid, attrs, attr_count
- lfsr_bshrub_commit - bid, attrs, attr_count
- lfsr_btree_commit_ - bid, rbyd, rid, attrs, attr_count
- lfsr_bshrub_commit_ - bid, rbyb, rid, attrs, attr_count
- lfsr_btree_commit__ - bscratch, bid, rbyd, rid, attrs, attr_count
These are good to have, and will also be useful for implementing
metadata redundancy in the future.
Unfortunately, all of this comes at a significant code/stack cost:
code stack
before: 34652 2640
after: 35268 (+1.8%) 2776 (+5.2%)
This commit is contained in:
@@ -4291,12 +4291,12 @@ static int lfsr_btree_parent(lfs_t *lfs, const lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
|
||||
// extra state needed for non-terminating lfsr_btree_commit_ calls
|
||||
typedef struct lfsr_btree_scratch {
|
||||
// extra state needed for non-terminating lfsr_btree_commit__ calls
|
||||
typedef struct lfsr_bscratch {
|
||||
lfsr_attr_t attrs[4];
|
||||
lfsr_data_t split_data;
|
||||
uint8_t buf[2*LFSR_BRANCH_DSIZE];
|
||||
} lfsr_btree_scratch_t;
|
||||
} lfsr_bscratch_t;
|
||||
|
||||
// core btree algorithm
|
||||
//
|
||||
@@ -4304,53 +4304,31 @@ typedef struct lfsr_btree_scratch {
|
||||
// 1. we need a new root
|
||||
// 2. we have a shrub root
|
||||
//
|
||||
static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_btree_scratch_t *scratch,
|
||||
lfsr_bid_t *bid_,
|
||||
static int lfsr_btree_commit__(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_bscratch_t *bscratch,
|
||||
lfsr_bid_t bid, lfsr_rbyd_t *rbyd, lfsr_srid_t *rid_,
|
||||
const lfsr_attr_t **attrs_, lfs_size_t *attr_count_) {
|
||||
lfsr_bid_t bid = *bid_;
|
||||
LFS_ASSERT(bid <= (lfsr_bid_t)btree->weight);
|
||||
lfsr_srid_t rid = *rid_;
|
||||
const lfsr_attr_t *attrs = *attrs_;
|
||||
lfs_size_t attr_count = *attr_count_;
|
||||
|
||||
// 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
|
||||
lfsr_rbyd_t rbyd = *btree;
|
||||
lfsr_srid_t rid = bid;
|
||||
if (btree->weight > 0) {
|
||||
lfsr_srid_t rid_;
|
||||
int err = lfsr_btree_lookupnext_(lfs, btree,
|
||||
lfs_min(bid, btree->weight-1),
|
||||
&bid, &rbyd, &rid_, NULL, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// adjust bid to point to the zero-most rid
|
||||
bid -= rid_;
|
||||
rid -= bid;
|
||||
}
|
||||
|
||||
// tail-recursively commit to btree
|
||||
while (true) {
|
||||
// we will always need our parent, so go ahead and find it
|
||||
lfsr_rbyd_t parent = {.trunk=0, .weight=0};
|
||||
lfsr_srid_t pid = 0;
|
||||
// are we root?
|
||||
if (rbyd.blocks[0] == btree->blocks[0]
|
||||
|| !lfsr_rbyd_trunk(&rbyd)) {
|
||||
if (rbyd->blocks[0] == btree->blocks[0]
|
||||
|| !lfsr_rbyd_trunk(rbyd)) {
|
||||
// new root? shrub root? yield the final root commit to
|
||||
// higher-level btree/bshrub logic
|
||||
if (!lfsr_rbyd_trunk(&rbyd)
|
||||
if (!lfsr_rbyd_trunk(rbyd)
|
||||
|| lfsr_rbyd_isshrub(btree)) {
|
||||
*bid_ = rid;
|
||||
*rid_ = rid;
|
||||
*attrs_ = attrs;
|
||||
*attr_count_ = attr_count;
|
||||
return (!lfsr_rbyd_trunk(&rbyd)) ? LFS_ERR_RANGE : 0;
|
||||
return (!lfsr_rbyd_trunk(rbyd)) ? LFS_ERR_RANGE : 0;
|
||||
}
|
||||
|
||||
// mark btree as unerased in case of failure, our btree rbyd and
|
||||
@@ -4359,7 +4337,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
btree->eoff = -1;
|
||||
|
||||
} else {
|
||||
int err = lfsr_btree_parent(lfs, btree, bid, &rbyd,
|
||||
int err = lfsr_btree_parent(lfs, btree, bid, rbyd,
|
||||
&parent, &pid);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
@@ -4374,10 +4352,10 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// unfetched
|
||||
//
|
||||
// a funny benefit is we cache the root of our btree this way
|
||||
if (!lfsr_rbyd_isfetched(&rbyd)) {
|
||||
int err = lfsr_rbyd_fetchck(lfs, &rbyd,
|
||||
rbyd.blocks[0], lfsr_rbyd_trunk(&rbyd),
|
||||
rbyd.cksum);
|
||||
if (!lfsr_rbyd_isfetched(rbyd)) {
|
||||
int err = lfsr_rbyd_fetchck(lfs, rbyd,
|
||||
rbyd->blocks[0], lfsr_rbyd_trunk(rbyd),
|
||||
rbyd->cksum);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -4386,7 +4364,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// is rbyd erased? can we sneak our commit into any remaining
|
||||
// erased bytes? note that the btree trunk field prevents this from
|
||||
// interacting with other references to the rbyd
|
||||
lfsr_rbyd_t rbyd_ = rbyd;
|
||||
lfsr_rbyd_t rbyd_ = *rbyd;
|
||||
int err = lfsr_rbyd_commit(lfs, &rbyd_, rid,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
@@ -4401,7 +4379,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
compact:;
|
||||
// estimate our compacted size
|
||||
lfsr_srid_t split_rid;
|
||||
lfs_ssize_t estimate = lfsr_rbyd_estimate(lfs, &rbyd, -1, -1,
|
||||
lfs_ssize_t estimate = lfsr_rbyd_estimate(lfs, rbyd, -1, -1,
|
||||
&split_rid);
|
||||
if (estimate < 0) {
|
||||
return estimate;
|
||||
@@ -4467,14 +4445,14 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// try the left sibling
|
||||
if (pid-(lfsr_srid_t)rbyd.weight >= 0) {
|
||||
if (pid-(lfsr_srid_t)rbyd->weight >= 0) {
|
||||
// try looking up the sibling
|
||||
lfsr_srid_t sibling_rid;
|
||||
lfsr_tag_t sibling_tag;
|
||||
lfsr_rid_t sibling_weight;
|
||||
lfsr_data_t sibling_data;
|
||||
err = lfsr_rbyd_lookupnext(lfs, &parent,
|
||||
pid-rbyd.weight, LFSR_TAG_NAME,
|
||||
pid-rbyd->weight, LFSR_TAG_NAME,
|
||||
&sibling_rid, &sibling_tag, &sibling_weight,
|
||||
&sibling_data);
|
||||
if (err) {
|
||||
@@ -4514,11 +4492,11 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// so our sibling is on the right
|
||||
bid -= sibling.weight;
|
||||
rid += sibling.weight;
|
||||
pid -= rbyd.weight;
|
||||
pid -= rbyd->weight;
|
||||
|
||||
rbyd_ = sibling;
|
||||
sibling = rbyd;
|
||||
rbyd = rbyd_;
|
||||
sibling = *rbyd;
|
||||
*rbyd = rbyd_;
|
||||
|
||||
goto merge;
|
||||
}
|
||||
@@ -4533,7 +4511,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// try to compact
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, &rbyd, -1, -1);
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, rbyd, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
@@ -4561,7 +4539,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
split:;
|
||||
// we should have something to split here
|
||||
LFS_ASSERT(split_rid > 0
|
||||
&& split_rid < (lfsr_srid_t)rbyd.weight);
|
||||
&& split_rid < (lfsr_srid_t)rbyd->weight);
|
||||
|
||||
split_relocate_l:;
|
||||
// allocate a new rbyd
|
||||
@@ -4571,7 +4549,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// copy over tags < split_rid
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, &rbyd, -1, split_rid);
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd_, rbyd, -1, split_rid);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
@@ -4615,7 +4593,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// copy over tags >= split_rid
|
||||
err = lfsr_rbyd_compact(lfs, &sibling, &rbyd, split_rid, -1);
|
||||
err = lfsr_rbyd_compact(lfs, &sibling, rbyd, split_rid, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
@@ -4666,7 +4644,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// they introduce a new name!
|
||||
lfsr_tag_t split_tag;
|
||||
err = lfsr_rbyd_lookupnext(lfs, &sibling, 0, LFSR_TAG_NAME,
|
||||
NULL, &split_tag, NULL, &scratch->split_data);
|
||||
NULL, &split_tag, NULL, &bscratch->split_data);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
@@ -4678,48 +4656,48 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
attr_count = 0;
|
||||
// new root?
|
||||
if (!lfsr_rbyd_trunk(&parent)) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, +rbyd_.weight,
|
||||
LFSR_DATA_BRANCH_(
|
||||
&rbyd_,
|
||||
&scratch->buf[0*LFSR_BRANCH_DSIZE]));
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
&bscratch->buf[0*LFSR_BRANCH_DSIZE]));
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, +sibling.weight,
|
||||
LFSR_DATA_BRANCH_(
|
||||
&sibling,
|
||||
&scratch->buf[1*LFSR_BRANCH_DSIZE]));
|
||||
&bscratch->buf[1*LFSR_BRANCH_DSIZE]));
|
||||
if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR_CAT_(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR_CAT_(
|
||||
LFSR_TAG_NAME, 0,
|
||||
&scratch->split_data, 1);
|
||||
&bscratch->split_data, 1);
|
||||
}
|
||||
// split root?
|
||||
} else {
|
||||
bid -= pid - (rbyd.weight-1);
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bid -= pid - (rbyd->weight-1);
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, 0,
|
||||
LFSR_DATA_BRANCH_(
|
||||
&rbyd_,
|
||||
&scratch->buf[0*LFSR_BRANCH_DSIZE]));
|
||||
if (rbyd_.weight != rbyd.weight) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight,
|
||||
&bscratch->buf[0*LFSR_BRANCH_DSIZE]));
|
||||
if (rbyd_.weight != rbyd->weight) {
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd->weight + rbyd_.weight,
|
||||
LFSR_DATA_NULL());
|
||||
}
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, +sibling.weight,
|
||||
LFSR_DATA_BRANCH_(
|
||||
&sibling,
|
||||
&scratch->buf[1*LFSR_BRANCH_DSIZE]));
|
||||
&bscratch->buf[1*LFSR_BRANCH_DSIZE]));
|
||||
if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR_CAT_(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR_CAT_(
|
||||
LFSR_TAG_NAME, 0,
|
||||
&scratch->split_data, 1);
|
||||
&bscratch->split_data, 1);
|
||||
}
|
||||
}
|
||||
attrs = scratch->attrs;
|
||||
attrs = bscratch->attrs;
|
||||
|
||||
rbyd = parent;
|
||||
*rbyd = parent;
|
||||
rid = pid;
|
||||
continue;
|
||||
|
||||
@@ -4732,7 +4710,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// merge the siblings together
|
||||
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, &rbyd, -1, -1);
|
||||
err = lfsr_rbyd_appendcompactrbyd(lfs, &rbyd_, rbyd, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
@@ -4778,7 +4756,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// we must have a parent at this point, but is our parent the root
|
||||
// and is the root degenerate?
|
||||
LFS_ASSERT(lfsr_rbyd_trunk(&parent));
|
||||
if (rbyd.weight+sibling.weight == btree->weight) {
|
||||
if (rbyd->weight+sibling.weight == btree->weight) {
|
||||
// collapse the root, decreasing the height of the tree
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
@@ -4788,20 +4766,20 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// prepare commit to parent, tail recursing upwards
|
||||
LFS_ASSERT(rbyd_.weight > 0);
|
||||
attr_count = 0;
|
||||
bid -= pid - (rbyd.weight-1);
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bid -= pid - (rbyd->weight-1);
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_RM, -sibling.weight, LFSR_DATA_NULL());
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, 0,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, scratch->buf));
|
||||
if (rbyd_.weight != rbyd.weight) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, bscratch->buf));
|
||||
if (rbyd_.weight != rbyd->weight) {
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd->weight + rbyd_.weight,
|
||||
LFSR_DATA_NULL());
|
||||
}
|
||||
attrs = scratch->attrs;
|
||||
attrs = bscratch->attrs;
|
||||
|
||||
rbyd = parent;
|
||||
*rbyd = parent;
|
||||
rid = pid + sibling.weight;
|
||||
continue;
|
||||
|
||||
@@ -4815,7 +4793,7 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
}
|
||||
|
||||
// is our parent the root and is the root degenerate?
|
||||
if (rbyd.weight == btree->weight) {
|
||||
if (rbyd->weight == btree->weight) {
|
||||
// collapse the root, decreasing the height of the tree
|
||||
*btree = rbyd_;
|
||||
*attr_count_ = 0;
|
||||
@@ -4827,35 +4805,35 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
// note that since we defer merges to compaction time, we can
|
||||
// end up removing an rbyd here
|
||||
attr_count = 0;
|
||||
bid -= pid - (rbyd.weight-1);
|
||||
bid -= pid - (rbyd->weight-1);
|
||||
if (rbyd_.weight == 0) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_RM, -rbyd.weight, LFSR_DATA_NULL());
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_RM, -rbyd->weight, LFSR_DATA_NULL());
|
||||
} else {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_BRANCH, 0,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, scratch->buf));
|
||||
if (rbyd_.weight != rbyd.weight) {
|
||||
scratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight,
|
||||
LFSR_DATA_BRANCH_(&rbyd_, bscratch->buf));
|
||||
if (rbyd_.weight != rbyd->weight) {
|
||||
bscratch->attrs[attr_count++] = LFSR_ATTR(
|
||||
LFSR_TAG_GROW, -rbyd->weight + rbyd_.weight,
|
||||
LFSR_DATA_NULL());
|
||||
}
|
||||
}
|
||||
attrs = scratch->attrs;
|
||||
attrs = bscratch->attrs;
|
||||
|
||||
rbyd = parent;
|
||||
*rbyd = parent;
|
||||
rid = pid;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// commit to a btree, this is atomic
|
||||
static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_bid_t bid, const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_bid_t bid, lfsr_rbyd_t *rbyd, lfsr_srid_t rid,
|
||||
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
// try to commit to the btree
|
||||
lfsr_btree_scratch_t scratch;
|
||||
int err = lfsr_btree_commit_(lfs, btree, &scratch,
|
||||
&bid, &attrs, &attr_count);
|
||||
lfsr_bscratch_t bscratch;
|
||||
int err = lfsr_btree_commit__(lfs, btree, &bscratch,
|
||||
bid, rbyd, &rid, &attrs, &attr_count);
|
||||
if (err && err != LFS_ERR_RANGE) {
|
||||
return err;
|
||||
}
|
||||
@@ -4865,13 +4843,12 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
LFS_ASSERT(attr_count > 0);
|
||||
|
||||
relocate:;
|
||||
lfsr_rbyd_t rbyd;
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd);
|
||||
err = lfsr_rbyd_alloc(lfs, rbyd);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_commit(lfs, &rbyd, bid, attrs, attr_count);
|
||||
err = lfsr_rbyd_commit(lfs, rbyd, rid, attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
// bad prog? try another block
|
||||
@@ -4881,13 +4858,51 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
return err;
|
||||
}
|
||||
|
||||
*btree = rbyd;
|
||||
*btree = *rbyd;
|
||||
}
|
||||
|
||||
LFS_ASSERT(lfsr_rbyd_trunk(btree));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lfsr_btree_compact_(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_bid_t bid, lfsr_rbyd_t *rbyd) {
|
||||
// the easiest way to do this is to just mark rbyd as unerased
|
||||
// and call lfsr_btree_commit_
|
||||
rbyd->eoff = -1;
|
||||
return lfsr_btree_commit_(lfs, btree, bid, rbyd, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
// commit to a btree, this is atomic
|
||||
static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
|
||||
lfsr_bid_t bid, const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
// 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
|
||||
lfsr_rbyd_t rbyd = *btree;
|
||||
lfsr_srid_t rid = bid;
|
||||
if (btree->weight > 0) {
|
||||
lfsr_srid_t rid_;
|
||||
int err = lfsr_btree_lookupnext_(lfs, btree,
|
||||
lfs_min(bid, btree->weight-1),
|
||||
&bid, &rbyd, &rid_, NULL, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// adjust bid to point to the zero-most rid
|
||||
bid -= rid_;
|
||||
rid -= bid;
|
||||
}
|
||||
|
||||
return lfsr_btree_commit_(lfs, btree, bid, &rbyd, rid,
|
||||
attrs, attr_count);
|
||||
}
|
||||
|
||||
// lookup in a btree by name
|
||||
static lfs_scmp_t lfsr_btree_namelookup(lfs_t *lfs, const lfsr_btree_t *btree,
|
||||
lfsr_did_t did, const char *name, lfs_size_t name_size,
|
||||
@@ -5602,14 +5617,14 @@ static int lfsr_bshrub_traverse(lfs_t *lfs,
|
||||
}
|
||||
}
|
||||
|
||||
// needed in lfsr_bshrub_commit
|
||||
// needed in lfsr_bshrub_commit_
|
||||
static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
|
||||
const lfsr_attr_t *attrs, lfs_size_t attr_count);
|
||||
|
||||
// commit to a bshrub, this is atomic
|
||||
static int lfsr_bshrub_commit(lfs_t *lfs,
|
||||
static int lfsr_bshrub_commit_(lfs_t *lfs,
|
||||
lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub,
|
||||
lfsr_bid_t bid, const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
lfsr_bid_t bid, lfsr_rbyd_t *rbyd, lfsr_srid_t rid,
|
||||
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
// file must be a bshrub/btree here
|
||||
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
|
||||
|
||||
@@ -5631,9 +5646,9 @@ static int lfsr_bshrub_commit(lfs_t *lfs,
|
||||
}
|
||||
|
||||
// try to commit to the btree
|
||||
lfsr_btree_scratch_t scratch;
|
||||
int err = lfsr_btree_commit_(lfs, &bshrub->u.btree, &scratch,
|
||||
&bid, &attrs, &attr_count);
|
||||
lfsr_bscratch_t bscratch;
|
||||
int err = lfsr_btree_commit__(lfs, &bshrub->u.btree, &bscratch,
|
||||
bid, rbyd, &rid, &attrs, &attr_count);
|
||||
if (err && err != LFS_ERR_RANGE) {
|
||||
return err;
|
||||
}
|
||||
@@ -5698,7 +5713,7 @@ static int lfsr_bshrub_commit(lfs_t *lfs,
|
||||
int err = lfsr_mdir_commit(lfs, mdir, LFSR_ATTRS(
|
||||
LFSR_ATTR_SHRUBCOMMIT(
|
||||
LFSR_TAG_SHRUBCOMMIT, 0,
|
||||
&bshrub->u.bshrub, bid, attrs, attr_count)));
|
||||
&bshrub->u.bshrub, rid, attrs, attr_count)));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -5723,15 +5738,14 @@ static int lfsr_bshrub_commit(lfs_t *lfs,
|
||||
|
||||
relocate:;
|
||||
// convert to btree
|
||||
lfsr_rbyd_t rbyd;
|
||||
err = lfsr_rbyd_alloc(lfs, &rbyd);
|
||||
err = lfsr_rbyd_alloc(lfs, rbyd);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// note this may be a new root
|
||||
if (!alloc) {
|
||||
err = lfsr_rbyd_compact(lfs, &rbyd,
|
||||
err = lfsr_rbyd_compact(lfs, rbyd,
|
||||
&bshrub->u.btree, -1, -1);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
@@ -5743,7 +5757,7 @@ relocate:;
|
||||
}
|
||||
}
|
||||
|
||||
err = lfsr_rbyd_commit(lfs, &rbyd, bid,
|
||||
err = lfsr_rbyd_commit(lfs, rbyd, rid,
|
||||
attrs, attr_count);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_RANGE);
|
||||
@@ -5754,10 +5768,55 @@ relocate:;
|
||||
return err;
|
||||
}
|
||||
|
||||
bshrub->u.btree = rbyd;
|
||||
bshrub->u.btree = *rbyd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lfsr_bshrub_compact_(lfs_t *lfs,
|
||||
lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub,
|
||||
lfsr_bid_t bid, lfsr_rbyd_t *rbyd) {
|
||||
// the easiest way to do this is to just mark rbyd as unerased
|
||||
// and call lfsr_btree_commit_
|
||||
rbyd->eoff = -1;
|
||||
return lfsr_bshrub_commit_(lfs, mdir, bshrub, bid, rbyd, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
// commit to a bshrub, this is atomic
|
||||
static int lfsr_bshrub_commit(lfs_t *lfs,
|
||||
lfsr_mdir_t *mdir, lfsr_bshrub_t *bshrub,
|
||||
lfsr_bid_t bid, const lfsr_attr_t *attrs, lfs_size_t attr_count) {
|
||||
// file must be a bshrub/btree here
|
||||
LFS_ASSERT(lfsr_bshrub_isbshruborbtree(bshrub));
|
||||
|
||||
// TODO can we dedup the lookup logic?
|
||||
// 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
|
||||
lfsr_rbyd_t rbyd = bshrub->u.btree;
|
||||
lfsr_srid_t rid = bid;
|
||||
if (bshrub->u.btree.weight > 0) {
|
||||
lfsr_srid_t rid_;
|
||||
int err = lfsr_btree_lookupnext_(lfs, &bshrub->u.btree,
|
||||
lfs_min(bid, bshrub->u.btree.weight-1),
|
||||
&bid, &rbyd, &rid_, NULL, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// adjust bid to point to the zero-most rid
|
||||
bid -= rid_;
|
||||
rid -= bid;
|
||||
}
|
||||
|
||||
return lfsr_bshrub_commit_(lfs, mdir, bshrub, bid, &rbyd, rid,
|
||||
attrs, attr_count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8514,7 +8573,10 @@ static int lfsr_mtree_traverse(lfs_t *lfs,
|
||||
}
|
||||
|
||||
// validate btree nodes? note mdirs are already validated
|
||||
if ((lfsr_t_isckmeta(mt->flags) || lfsr_t_isck(mt->flags))
|
||||
if ((lfsr_t_isckmeta(mt->flags)
|
||||
|| lfsr_t_isck(mt->flags)
|
||||
// we also need to fetch to know if we need to compact
|
||||
|| lfsr_t_iscompact(mt->flags))
|
||||
&& mtinfo->tag == LFSR_TAG_BRANCH) {
|
||||
err = lfsr_rbyd_fetchck(lfs, &mtinfo->u.rbyd,
|
||||
mtinfo->u.rbyd.blocks[0], mtinfo->u.rbyd.trunk,
|
||||
@@ -8590,21 +8652,86 @@ static int lfsr_mtree_gc(lfs_t *lfs,
|
||||
}
|
||||
mtinfo->u.mdir = *mdir;
|
||||
|
||||
// mark as dirty
|
||||
mt->flags |= LFS_F_DIRTY;
|
||||
}
|
||||
|
||||
// // TODO
|
||||
// // compacting btree nodes?
|
||||
// if (lfsr_t_iscompact(mt->flags)
|
||||
// && mtinfo->tag == LFSR_TAG_BTREE
|
||||
// // exceed compaction threshold?
|
||||
// && lfsr_rbyd_eoff(&mtinfo->u.rbyd)
|
||||
// > ((lfs->cfg->gc_compact_thresh)
|
||||
// ? lfs->cfg->gc_compact_thresh
|
||||
// : lfs->cfg->block_size - lfs->cfg->block_size/8)) {
|
||||
//
|
||||
// // TODO clobber?
|
||||
// }
|
||||
// compacting btree nodes?
|
||||
if (lfsr_t_iscompact(mt->flags)
|
||||
&& mtinfo->tag == LFSR_TAG_BRANCH
|
||||
// exceed compaction threshold?
|
||||
&& lfsr_rbyd_eoff(&mtinfo->u.rbyd)
|
||||
> ((lfs->cfg->gc_compact_thresh)
|
||||
? lfs->cfg->gc_compact_thresh
|
||||
: lfs->cfg->block_size - lfs->cfg->block_size/8)) {
|
||||
LFS_DEBUG("Compacting rbyd 0x%"PRIx32".%"PRIx32" "
|
||||
"(%"PRId32" > %"PRId32")",
|
||||
mtinfo->u.rbyd.blocks[0],
|
||||
lfsr_rbyd_trunk(&mtinfo->u.rbyd),
|
||||
lfsr_rbyd_eoff(&mtinfo->u.rbyd),
|
||||
(lfs->cfg->gc_compact_thresh)
|
||||
? lfs->cfg->gc_compact_thresh
|
||||
: lfs->cfg->block_size - lfs->cfg->block_size/8);
|
||||
|
||||
// TODO should we really have two btree copies flying around?
|
||||
LFS_ASSERT(lfsr_rbyd_cmp(&mt->u.bt.branch, &mtinfo->u.rbyd) == 0);
|
||||
if (mt->state == LFSR_MTRAVERSAL_MTREE) {
|
||||
int err = lfsr_btree_compact_(lfs, &mt->bshrub.u.btree,
|
||||
mt->u.bt.bid, &mt->u.bt.branch);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
int err = lfsr_bshrub_compact_(lfs, mdir, &mt->bshrub,
|
||||
mt->u.bt.bid, &mt->u.bt.branch);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (mt->state == LFSR_MTRAVERSAL_OBTREE) {
|
||||
// just update our opened file
|
||||
lfsr_file_t *file_ = (lfsr_file_t*)mt->o;
|
||||
file_->o.flags |= LFS_F_UNSYNC;
|
||||
file_->bshrub = mt->bshrub;
|
||||
|
||||
} else {
|
||||
// commit to mdir
|
||||
uint8_t buf[LFSR_BTREE_DSIZE];
|
||||
int err = lfsr_mdir_commit(lfs, mdir, LFSR_ATTRS(
|
||||
(mt->state == LFSR_MTRAVERSAL_MTREE)
|
||||
? LFSR_ATTR(
|
||||
LFSR_TAG_SUB | LFSR_TAG_MTREE, 0,
|
||||
LFSR_DATA_BTREE_(&mt->bshrub.u.btree, buf))
|
||||
: (lfsr_bshrub_isbshrub(mdir, &mt->bshrub))
|
||||
? LFSR_ATTR_SHRUBTRUNK(
|
||||
LFSR_TAG_SUB | LFSR_TAG_SHRUBTRUNK, 0,
|
||||
&mt->bshrub.u.bshrub)
|
||||
: LFSR_ATTR(
|
||||
LFSR_TAG_BTREE, 0,
|
||||
LFSR_DATA_BTREE_(&mt->bshrub.u.btree, buf))));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// update any open files
|
||||
for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) {
|
||||
if (o->type == LFS_TYPE_REG
|
||||
&& o->mdir.mid == mdir->mid
|
||||
&& !lfsr_f_isunsync(o->flags)) {
|
||||
lfsr_file_t *file_ = (lfsr_file_t*)o;
|
||||
file_->bshrub = mt->bshrub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset to btree root
|
||||
mt->u.bt.rid = mt->u.bt.bid;
|
||||
mt->u.bt.branch = mt->bshrub.u.btree;
|
||||
|
||||
// mark as dirty
|
||||
mt->flags |= LFS_F_DIRTY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+614
-1
@@ -5092,6 +5092,7 @@ code = '''
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mdir should have been compacted
|
||||
@@ -5256,6 +5257,7 @@ code = '''
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mrootanchor should have been compacted
|
||||
@@ -5284,6 +5286,82 @@ code = '''
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_mroot_extend]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
# force early relocations
|
||||
defines.BLOCK_RECYCLES = 0
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// write to our mdir until >gc_compact_thresh full
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
// hack, don't use the internals like this
|
||||
uint8_t wbuf[SIZE];
|
||||
while ((file.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
}
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// it's a bit unclear if we should follow the mroot or stay on the
|
||||
// mroot anchor during extends, so if this breaks in the future
|
||||
// I wouldn't worry too much about it
|
||||
//
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mdir should have been compacted
|
||||
assert((file.o.mdir.rbyd.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_mroot_split]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
@@ -5370,6 +5448,7 @@ code = '''
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mdirs should have been compacted
|
||||
@@ -5543,6 +5622,7 @@ code = '''
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mdirs should have been compacted
|
||||
@@ -5724,6 +5804,7 @@ code = '''
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mdirs should have been compacted
|
||||
@@ -5775,6 +5856,523 @@ code = '''
|
||||
|
||||
|
||||
|
||||
# btree/bshrub compactions are quite a bit more difficult
|
||||
|
||||
[cases.test_traversal_compact_mtree_btree]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.SIZE = 'FILE_BUFFER_SIZE/2'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
in = 'lfs.c'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create two files
|
||||
lfsr_file_t file1;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
uint8_t wbuf1[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file1) => 0;
|
||||
|
||||
lfsr_file_t file2;
|
||||
lfsr_file_open(&lfs, &file2, "octopus",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
uint8_t wbuf2[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file2) => 0;
|
||||
|
||||
// create enough files for mroot to split
|
||||
lfs_size_t i = 0;
|
||||
while (lfs.mtree.u.weight == 0x80000000) {
|
||||
char name[256];
|
||||
sprintf(name, "medusa%03x", i);
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, name,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// switch to early relocations after splitting
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
struct lfs_config cfg = *CFG;
|
||||
cfg.block_recycles = 0;
|
||||
lfsr_mount(&lfs, &cfg) => 0;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDWR) => 0;
|
||||
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDWR) => 0;
|
||||
|
||||
// rewrite a file until btree is >gc_compact_thresh full
|
||||
while (true) {
|
||||
// we need internals to check this
|
||||
lfsr_mdir_t mdir;
|
||||
// ckmeta needed for eoff
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
if (lfsr_rbyd_eoff(&mtinfo.u.rbyd) > GC_COMPACT_THRESH) {
|
||||
break;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file1) => 0;
|
||||
}
|
||||
|
||||
// switch back to default relocations to avoid mroot extension issues
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDWR) => 0;
|
||||
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDWR) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
// traverse mdir
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
// traverse mdir
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// mtree should have been compacted
|
||||
lfsr_mdir_t mdir;
|
||||
lfsr_mtraversal_t mt = LFSR_MTRAVERSAL(LFS_T_CKMETA);
|
||||
lfsr_mtinfo_t mtinfo;
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_MDIR);
|
||||
lfsr_mtree_traverse(&lfs, &mdir, &mt, &mtinfo) => 0;
|
||||
assert(mtinfo.tag == LFSR_TAG_BRANCH);
|
||||
assert(lfsr_rbyd_eoff(&mtinfo.u.rbyd) <= GC_COMPACT_THRESH);
|
||||
|
||||
// check we can still read the files
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
lfsr_file_open(&lfs, &file2, "octopus", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
||||
|
||||
lfsr_file_rewind(&lfs, &file2) => 0;
|
||||
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_btree]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# limit files to very simple btrees
|
||||
defines.INLINE_SIZE = 0
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = '2*FRAGMENT_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
// rewrite our file until btree is >gc_compact_thresh full
|
||||
uint8_t wbuf[SIZE];
|
||||
while ((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// btree should have been compacted
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
assert((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_btree_open]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# limit files to very simple btrees
|
||||
defines.INLINE_SIZE = 0
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = '2*FRAGMENT_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
// rewrite our file until btree is >gc_compact_thresh full
|
||||
uint8_t wbuf[SIZE];
|
||||
while ((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
lfsr_file_sync(&lfs, &file) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// btree should have been compacted
|
||||
assert((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_btree_orphan]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# limit files to very simple btrees
|
||||
defines.INLINE_SIZE = 0
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = '2*FRAGMENT_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a file
|
||||
lfsr_file_t file;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
|
||||
// rewrite our file until btree is >gc_compact_thresh full
|
||||
uint8_t wbuf[SIZE];
|
||||
while ((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// btree should have been compacted
|
||||
assert((file.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
||||
|
||||
// file should not have accidentally been created or anything
|
||||
struct lfs_info info;
|
||||
lfsr_stat(&lfs, "jellyfish", &info) => LFS_ERR_NOENT;
|
||||
|
||||
// check we can still read the file
|
||||
for (int remount = 0; remount < 2; remount++) {
|
||||
// remount?
|
||||
if (remount) {
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
lfsr_file_open(&lfs, &file, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
}
|
||||
|
||||
lfsr_file_rewind(&lfs, &file) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf, SIZE) == 0);
|
||||
}
|
||||
|
||||
lfsr_file_close(&lfs, &file) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_traversal_compact_btree_desync]
|
||||
defines.CKMETA = [false, true]
|
||||
defines.CK = [false, true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
# limit files to very simple btrees
|
||||
defines.INLINE_SIZE = 0
|
||||
defines.CRYSTAL_THRESH = -1
|
||||
defines.FRAGMENT_SIZE = 'BLOCK_SIZE/8'
|
||||
defines.SIZE = '2*FRAGMENT_SIZE'
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfsr_format(&lfs, CFG) => 0;
|
||||
lfsr_mount(&lfs, CFG) => 0;
|
||||
|
||||
uint32_t prng = 42;
|
||||
|
||||
// create a desync file
|
||||
lfsr_file_t file1;
|
||||
lfsr_file_open(&lfs, &file1, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0;
|
||||
|
||||
// rewrite our file until btree is >gc_compact_thresh full
|
||||
uint8_t wbuf1[SIZE];
|
||||
while ((file1.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) {
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file1, wbuf1, SIZE) => SIZE;
|
||||
}
|
||||
|
||||
// create some overlapping files, these should not get messed with
|
||||
lfsr_file_t file2;
|
||||
lfsr_file_open(&lfs, &file2, "jellyfish",
|
||||
LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
uint8_t wbuf2[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file2, wbuf2, SIZE) => SIZE;
|
||||
lfsr_file_sync(&lfs, &file2) => 0;
|
||||
|
||||
lfsr_file_t file3;
|
||||
lfsr_file_open(&lfs, &file3, "jellyfish",
|
||||
LFS_O_RDWR) => 0;
|
||||
uint8_t wbuf3[SIZE];
|
||||
for (lfs_size_t j = 0; j < SIZE; j++) {
|
||||
wbuf3[j] = 'a' + (TEST_PRNG(&prng) % 26);
|
||||
}
|
||||
lfsr_file_write(&lfs, &file3, wbuf3, SIZE) => SIZE;
|
||||
lfsr_file_desync(&lfs, &file3) => 0;
|
||||
|
||||
// try traversing and compacting
|
||||
lfsr_traversal_t t;
|
||||
lfsr_traversal_open(&lfs, &t,
|
||||
LFS_T_COMPACT
|
||||
| ((CKMETA) ? LFS_T_CKMETA : 0)
|
||||
| ((CK) ? LFS_T_CK : 0)
|
||||
| ((LOOKAHEAD) ? LFS_T_LOOKAHEAD : 0)) => 0;
|
||||
// traverse mroot
|
||||
struct lfs_tinfo tinfo;
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_MDIR);
|
||||
assert(tinfo.block == 0 || tinfo.block == 1);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
// traverse btree
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => 0;
|
||||
assert(tinfo.btype == LFS_BTYPE_BTREE);
|
||||
lfsr_traversal_read(&lfs, &t, &tinfo) => LFS_ERR_NOENT;
|
||||
lfsr_traversal_close(&lfs, &t) => 0;
|
||||
|
||||
// btree should have been compacted
|
||||
assert((file1.bshrub.u.btree.eoff & 0x7fffffff) <= GC_COMPACT_THRESH);
|
||||
|
||||
// check we can still read the files
|
||||
lfsr_file_rewind(&lfs, &file1) => 0;
|
||||
uint8_t rbuf[SIZE];
|
||||
lfsr_file_read(&lfs, &file1, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf1, SIZE) == 0);
|
||||
|
||||
lfsr_file_rewind(&lfs, &file2) => 0;
|
||||
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
||||
|
||||
lfsr_file_rewind(&lfs, &file3) => 0;
|
||||
lfsr_file_read(&lfs, &file3, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf3, SIZE) == 0);
|
||||
|
||||
// at least try closing/opening our synced file
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_file_open(&lfs, &file2, "jellyfish", LFS_O_RDONLY) => 0;
|
||||
lfsr_file_read(&lfs, &file2, rbuf, SIZE) => SIZE;
|
||||
assert(memcmp(rbuf, wbuf2, SIZE) == 0);
|
||||
|
||||
lfsr_file_close(&lfs, &file1) => 0;
|
||||
lfsr_file_close(&lfs, &file2) => 0;
|
||||
lfsr_file_close(&lfs, &file3) => 0;
|
||||
lfsr_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# TODO
|
||||
# [cases.test_traversal_compact_bshrub]
|
||||
# [cases.test_traversal_compact_bshrub_open]
|
||||
# [cases.test_traversal_compact_bshrub_orphan]
|
||||
# [cases.test_traversal_compact_bshrub_desync]
|
||||
|
||||
|
||||
|
||||
|
||||
# many/fuzz tests mixed with traversals
|
||||
#
|
||||
@@ -5789,6 +6387,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
||||
code = '''
|
||||
// test creating directories
|
||||
@@ -5897,6 +6497,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256]
|
||||
defines.OPS = '2*N'
|
||||
defines.SEED = 42
|
||||
@@ -6076,6 +6678,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
defines.SIZE = [
|
||||
'0',
|
||||
@@ -6178,6 +6782,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
defines.OPS = '2*N'
|
||||
defines.SIZE = [
|
||||
@@ -6418,7 +7024,10 @@ defines.EXCL = [false, true]
|
||||
defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# TODO !!! Enable this when bshrub compaction is working
|
||||
defines.COMPACT = [false]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.OPS = 20
|
||||
defines.SIZE = [
|
||||
'FILE_BUFFER_SIZE/2',
|
||||
@@ -6580,6 +7189,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
defines.OPS = '2*N'
|
||||
defines.SIZE = [
|
||||
@@ -6949,6 +7560,8 @@ defines.CKMETA = [true]
|
||||
defines.CK = [true]
|
||||
defines.LOOKAHEAD = [false, true]
|
||||
defines.COMPACT = [false, true]
|
||||
# set compact thresh to minimum
|
||||
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
|
||||
defines.N = [1, 2, 4, 8, 16, 32, 64]
|
||||
defines.OPS = '2*N'
|
||||
defines.SIZE = [
|
||||
|
||||
Reference in New Issue
Block a user