Attempted impl of lfsr_rbyd_pendinglookup with two passes
1. Search backwards through our tags to find the most recent, best matching id. 2. Replay tags after the found id to adjust for any pending changes. In theory this should work in controlled cases, but there are a lot of corner cases around grows and shrinks. Tests are written, and failing, but I think it may be simpler and more efficient to implement this in a single pass, with tighter assumptions about what grow/shrinks are allowed.
This commit is contained in:
@@ -679,6 +679,9 @@ typedef struct lfsr_data {
|
|||||||
} u;
|
} u;
|
||||||
} lfsr_data_t;
|
} lfsr_data_t;
|
||||||
|
|
||||||
|
#define LFSR_DATA_NULL \
|
||||||
|
((lfsr_data_t){.u.buf=NULL, .len=0})
|
||||||
|
|
||||||
#define LFSR_DATA_BUF(_buf, _len) \
|
#define LFSR_DATA_BUF(_buf, _len) \
|
||||||
((lfsr_data_t){.u.buf=_buf, .len=_len})
|
((lfsr_data_t){.u.buf=_buf, .len=_len})
|
||||||
|
|
||||||
@@ -1608,119 +1611,184 @@ static lfs_ssize_t lfsr_rbyd_get(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
|
|||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//// TODO should we merge this into lfsr_rbyd_lookup?
|
// TODO should we merge this into lfsr_rbyd_lookup?
|
||||||
//static int lfsr_rbyd_lookupattrs(lfs_t *lfs,
|
static int lfsr_rbyd_pendinglookup(lfs_t *lfs,
|
||||||
// const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs,
|
const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs,
|
||||||
// lfsr_tag_t tag, lfs_ssize_t id,
|
lfsr_tag_t tag, lfs_ssize_t id,
|
||||||
// lfsr_tag_t *tag_, lfs_ssize_t *id_,
|
lfsr_tag_t *tag_, lfs_ssize_t *id_,
|
||||||
// lfsr_data_t *data_, lfs_size_t *weight_) {
|
lfsr_data_t *data_, lfs_size_t *weight_) {
|
||||||
// // tag must be non-zero! zero tags may deceptively look like they work but
|
printf("pendinglookup(%x, %d)\n", tag, id);
|
||||||
// // fail when the tree contains a deleted id0
|
|
||||||
// LFS_ASSERT(tag != 0);
|
again:;
|
||||||
//
|
// tag must be non-zero! zero tags may deceptively look like they work but
|
||||||
// lfs_ssize_t lower_id = -1;
|
// fail when the tree contains a deleted id0
|
||||||
// lfs_ssize_t upper_id = rbyd->weight;
|
LFS_ASSERT(tag != 0);
|
||||||
// lfsr_tag_t upper_tag = -1;
|
|
||||||
//
|
lfsr_tag_t tag__ = 0xffff;
|
||||||
//
|
lfsr_data_t data__ = LFSR_DATA_NULL;
|
||||||
// // TODO hmm, reverse iteration over a linked-list? this is a bad design
|
lfs_size_t weight = 0;
|
||||||
// // first try to find tag in the unwritten attributes
|
const struct lfsr_attr *attrs__ = attrs;
|
||||||
// lfs_ssize_t attr_best_id = 0;
|
|
||||||
// lfs_tag_t attr_best_tag = 0;
|
// first search backwards through tags to find the smallest, non-deleted,
|
||||||
// lfs_ssize_t attr_weight = 0;
|
// >= id, then search through tags in-order to find the most recent update,
|
||||||
// unsigned attr_count = 0;
|
// two passes are required to adjust for weight changes.
|
||||||
// for (const struct lfsr_attr *attr = attrs; attr; attr = attr->next) {
|
// TODO hmm, reverse iteration over a linked-list? this is a bad design
|
||||||
// attr_count += 1;
|
unsigned attr_count = 0;
|
||||||
// }
|
for (const struct lfsr_attr *attr = attrs; attr; attr = attr->next) {
|
||||||
// for (unsigned i = 0; i < count; i++) {
|
attr_count += 1;
|
||||||
// const struct lfsr_attr *attr = attrs;
|
}
|
||||||
// for (unsigned j = 0; j < count-1-i; j++) {
|
for (unsigned i = 0; i < attr_count; i++) {
|
||||||
// attr = attr->next;
|
const struct lfsr_attr *attr = attrs;
|
||||||
// }
|
for (unsigned j = 0; j < attr_count-1-i; j++) {
|
||||||
//
|
attr = attr->next;
|
||||||
// // TODO can this be simplified a bit?
|
}
|
||||||
// if (lfsr_tag_ismk(attr->tag)) {
|
|
||||||
// if (attr->id < id) {
|
if (attr->tag == LFSR_TAG_GROW) {
|
||||||
// id -= 1;
|
printf("g %d %d\n", attr->id, attr->size);
|
||||||
// }
|
if (id >= attr->id) {
|
||||||
// } else if (attr->tag == LFSR_TAG_RM) {
|
if (id < attr->id + attr->size) {
|
||||||
// if (attr->id < id) {
|
// TODO
|
||||||
// id += attr->size;
|
id = attr->id + attr->size-1;
|
||||||
// }
|
weight = attr->size;
|
||||||
// } else if (attr->tag == LFSR_TAG_GROW) {
|
attrs__ = attr->next;
|
||||||
// if (attr->id < id) {
|
goto grown;
|
||||||
// id -= attr->size;
|
}
|
||||||
// }
|
id -= attr->size;
|
||||||
// } else if (attr->tag == LFSR_TAG_SHRINK) {
|
}
|
||||||
// if (attr->id < id) {
|
} else if (attr->tag == LFSR_TAG_SHRINK) {
|
||||||
// id += attr->size;
|
if (id >= attr->id) {
|
||||||
// }
|
id += attr->size;
|
||||||
// } else if (attr->tag == LFSR_TAG_FROM) {
|
}
|
||||||
// // TODO
|
} else if (attr->tag == LFSR_TAG_FROM) {
|
||||||
// LFS_ASSERT(false);
|
// TODO
|
||||||
// } else if (lfsr_tag_isrm(attr->tag)) {
|
LFS_ASSERT(false);
|
||||||
// } else {
|
|
||||||
// if (attr->id == id
|
}
|
||||||
// && attr->tag >= tag
|
}
|
||||||
// && (attr->id < best_attr_id
|
|
||||||
// || (attr->id == best_attr_id
|
// this id can't exist in our rbyd
|
||||||
// && attr->tag < best_attr_tag))) {
|
if (id >= (lfs_ssize_t)rbyd->weight) {
|
||||||
// // TODO track best attr?
|
printf("? %d >= %d\n", id, rbyd->weight);
|
||||||
// best_attr_id = attr->id;
|
return LFS_ERR_NOENT;
|
||||||
// best_attr_tag = attr->tag;
|
}
|
||||||
// }
|
|
||||||
// }
|
// if not created in attr list our id must have been created in the rbyd
|
||||||
// }
|
lfs_off_t off__;
|
||||||
//
|
lfs_size_t size__;
|
||||||
// lfs_off_t off_;
|
int err = lfsr_rbyd_lookup(lfs, rbyd, tag, id,
|
||||||
// lfs_size_t size_;
|
&tag__, &id, &weight, &off__, &size__);
|
||||||
// int err = lfsr_rbyd_lookup(lfs, rbyd, tag, id,
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
// tag_, id_, weight_, &off_, &size_);
|
return err;
|
||||||
// if (err) {
|
}
|
||||||
// return err;
|
|
||||||
// }
|
if (err != LFS_ERR_NOENT) {
|
||||||
//
|
data__ = LFSR_DATA_DISK(rbyd->block, off__, size__);
|
||||||
// if (data_) {
|
}
|
||||||
// *data_ = LFSR_DATA_DISK(rbyd->block, off_, size_);
|
|
||||||
// }
|
grown:;
|
||||||
//
|
// TODO different way to encode weight?
|
||||||
// return 0;
|
lfs_ssize_t lower = id-weight+1;
|
||||||
//}
|
printf("raw %d %d (%d)\n", id, weight, lower);
|
||||||
//
|
|
||||||
//// TODO do we need this function?
|
// now replay the attr list, keeping track of changes to id, weight, tag
|
||||||
//static lfs_ssize_t lfsr_rbyd_getattrs(lfs_t *lfs,
|
for (const struct lfsr_attr *attr = attrs__; attr; attr = attr->next) {
|
||||||
// const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs,
|
if (attr->tag == LFSR_TAG_GROW) {
|
||||||
// lfsr_tag_t tag, lfs_ssize_t id, void *buffer, lfs_size_t size) {
|
if (lower >= attr->id) {
|
||||||
// lfsr_tag_t tag_;
|
lower += attr->size;
|
||||||
// lfs_ssize_t id_;
|
}
|
||||||
// lfsr_data_t data_;
|
if (id >= attr->id) {
|
||||||
// int err = lfsr_rbyd_lookupattrs(lfs, rbyd, attrs, tag, id,
|
id += attr->size;
|
||||||
// &tag_, &id_, &data_, NULL);
|
}
|
||||||
// if (err) {
|
} else if (attr->tag == LFSR_TAG_SHRINK) {
|
||||||
// return err;
|
if (lower >= attr->id) {
|
||||||
// }
|
lower -= attr->size;
|
||||||
//
|
}
|
||||||
// // lookup finds the next-smallest tag, for get, we need to fail
|
if (id >= attr->id) {
|
||||||
// // if it's not an exact match
|
id -= attr->size;
|
||||||
// if (id_ != id || tag_ != tag) {
|
}
|
||||||
// return LFS_ERR_NOENT;
|
} else if (attr->tag == LFSR_TAG_FROM) {
|
||||||
// }
|
// TODO
|
||||||
//
|
LFS_ASSERT(false);
|
||||||
// // TODO should this be its own lfsr_data_ function?
|
|
||||||
// lfs_size_t delta = lfs_min(size, lfsr_data_len(data_));
|
} else if (attr->id == id
|
||||||
// if (!lfsr_data_ondisk(data_)) {
|
&& lfsr_tag_key(attr->tag) >= lfsr_tag_key(tag)
|
||||||
// memcpy(buffer, data_.u.buf, delta);
|
&& lfsr_tag_key(attr->tag) <= lfsr_tag_key(tag__)) {
|
||||||
// } else {
|
tag__ = attr->tag;
|
||||||
// err = lfs_bd_read(lfs,
|
data__ = LFSR_DATA_BUF(attr->buffer, attr->size);
|
||||||
// &lfs->pcache, &lfs->rcache, delta,
|
}
|
||||||
// data_.u.disk.block, data_.u.disk.off, buffer, delta);
|
}
|
||||||
// if (err) {
|
|
||||||
// return err;
|
// TODO if we can't get rid of this we can at least move it into the
|
||||||
// }
|
// below condition
|
||||||
// }
|
weight = id-lower+1;
|
||||||
//
|
printf("fix %d %d (%d)\n", id, weight, lower);
|
||||||
// return lfsr_data_len(data_);
|
|
||||||
//}
|
// not found? increase id
|
||||||
|
if (tag__ == 0xffff) {
|
||||||
|
tag = 0x10;
|
||||||
|
id = id + 1;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found rm? should continue
|
||||||
|
if (lfsr_tag_isrm(tag__)) {
|
||||||
|
tag = tag__ + 0x10;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
// found
|
||||||
|
|
||||||
|
// TODO how many of these should be conditional?
|
||||||
|
if (tag_) {
|
||||||
|
*tag_ = tag__;
|
||||||
|
}
|
||||||
|
if (id_) {
|
||||||
|
*id_ = id;
|
||||||
|
}
|
||||||
|
if (data_) {
|
||||||
|
*data_ = data__;
|
||||||
|
}
|
||||||
|
if (weight_) {
|
||||||
|
*weight_ = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO do we need this function?
|
||||||
|
static lfs_ssize_t lfsr_rbyd_pendingget(lfs_t *lfs,
|
||||||
|
const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs,
|
||||||
|
lfsr_tag_t tag, lfs_ssize_t id, void *buffer, lfs_size_t size) {
|
||||||
|
lfsr_tag_t tag_;
|
||||||
|
lfs_ssize_t id_;
|
||||||
|
lfsr_data_t data_;
|
||||||
|
int err = lfsr_rbyd_pendinglookup(lfs, rbyd, attrs, tag, id,
|
||||||
|
&tag_, &id_, &data_, NULL);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup finds the next-smallest tag, for get, we need to fail
|
||||||
|
// if it's not an exact match
|
||||||
|
if (id_ != id || tag_ != tag) {
|
||||||
|
return LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO should this be its own lfsr_data_ function?
|
||||||
|
lfs_size_t delta = lfs_min(size, lfsr_data_len(data_));
|
||||||
|
if (!lfsr_data_ondisk(data_)) {
|
||||||
|
memcpy(buffer, data_.u.buf, delta);
|
||||||
|
} else {
|
||||||
|
err = lfs_bd_read(lfs,
|
||||||
|
&lfs->pcache, &lfs->rcache, delta,
|
||||||
|
data_.u.disk.block, data_.u.disk.off, buffer, delta);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lfsr_data_len(data_);
|
||||||
|
}
|
||||||
|
|
||||||
static lfs_ssize_t lfsr_rbyd_compactedsize(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
|
static lfs_ssize_t lfsr_rbyd_compactedsize(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
|
||||||
lfs_ssize_t start, lfs_ssize_t stop) {
|
lfs_ssize_t start, lfs_ssize_t stop) {
|
||||||
|
|||||||
@@ -11390,3 +11390,833 @@ code = '''
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
## Test unwritten attributes
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_permutations]
|
||||||
|
defines.N = 'range(1, 7)'
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
lfsr_tag_t tag_ = 0;
|
||||||
|
lfs_ssize_t id_ = -1;
|
||||||
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
||||||
|
lfs_size_t weight_ = 0;
|
||||||
|
|
||||||
|
// test all permutations of a given size
|
||||||
|
uint16_t perm[N];
|
||||||
|
unsigned stack[N];
|
||||||
|
for (uint16_t i = 0; i < N; i++) {
|
||||||
|
perm[i] = i;
|
||||||
|
stack[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i = 1;
|
||||||
|
while (i < N) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
// print permutation to help debugging
|
||||||
|
printf("--- permutation: [");
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
if (j > 0) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
printf("%d", perm[j]);
|
||||||
|
}
|
||||||
|
printf("], written: %d/%jd ---\n", w, N);
|
||||||
|
|
||||||
|
// build the attribute lists for the current permutation
|
||||||
|
struct lfsr_attr attrs[N];
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
attrs[j] = *LFSR_ATTR(
|
||||||
|
UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4,
|
||||||
|
(j+1 < N && j+1 != w) ? &attrs[j+1] : NULL);
|
||||||
|
}
|
||||||
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
||||||
|
struct lfsr_attr *unwritten = w < N ? &attrs[w] : NULL;
|
||||||
|
|
||||||
|
// create rbyd with written attr
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
||||||
|
rbyd.block, cfg->block_size, NULL) => 0;
|
||||||
|
|
||||||
|
// test lookup both written/unwritten
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
LFSR_TAG_UATTR(j+1), -1,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_UATTR(j+1));
|
||||||
|
assert(id_ == -1);
|
||||||
|
assert(lfsr_data_len(data_) == 4);
|
||||||
|
assert(weight_ == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test traverse both written/unwritten
|
||||||
|
tag_ = 0;
|
||||||
|
id_ = -1;
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_UATTR(j+1));
|
||||||
|
assert(id_ == -1);
|
||||||
|
assert(lfsr_data_len(data_) == 4);
|
||||||
|
assert(weight_ == 0);
|
||||||
|
}
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next permutation using Heap's algorithm
|
||||||
|
if (stack[i] < i) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
uint16_t t = perm[0];
|
||||||
|
perm[0] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
} else {
|
||||||
|
uint16_t t = perm[stack[i]];
|
||||||
|
perm[stack[i]] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
}
|
||||||
|
stack[i] += 1;
|
||||||
|
i = 1;
|
||||||
|
} else {
|
||||||
|
stack[i] = 0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_random]
|
||||||
|
defines.N = 'range(1, 13)'
|
||||||
|
defines.ITER = 1000
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
// iterate through seeds so we can reproduce easily
|
||||||
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N);
|
||||||
|
printf("perm: [");
|
||||||
|
uint32_t prng = seed;
|
||||||
|
for (unsigned i = 0; i < N; i++) {
|
||||||
|
// choose an attr
|
||||||
|
uint8_t attr = TEST_PRNG(&prng) % N;
|
||||||
|
// choose append or remove
|
||||||
|
if (TEST_PRNG(&prng) & 1) {
|
||||||
|
printf("a0x%02x=%c", attr, alpha[i % 26]);
|
||||||
|
} else {
|
||||||
|
printf("r0x%02x", attr);
|
||||||
|
}
|
||||||
|
if (i < N-1) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
// set up a simulation to compare against
|
||||||
|
char *sim = malloc(N);
|
||||||
|
memset(sim, 0, N);
|
||||||
|
|
||||||
|
// set up rbyd block
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
|
||||||
|
// set up our unwritten attr list
|
||||||
|
struct lfsr_attr *attrs = malloc(N*sizeof(struct lfsr_attr));
|
||||||
|
unsigned j = 0;
|
||||||
|
|
||||||
|
prng = seed;
|
||||||
|
for (unsigned i = 0; i < N; i++) {
|
||||||
|
// choose an attr
|
||||||
|
uint8_t attr = TEST_PRNG(&prng) % N;
|
||||||
|
// choose append or remove
|
||||||
|
if (TEST_PRNG(&prng) & 1) {
|
||||||
|
// update our sim
|
||||||
|
sim[attr] = alpha[i % 26];
|
||||||
|
// update our rbyd
|
||||||
|
if (i < w) {
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
||||||
|
LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1,
|
||||||
|
NULL)) => 0;
|
||||||
|
// append to our unwritten attrs
|
||||||
|
} else {
|
||||||
|
if (j > 0) {
|
||||||
|
attrs[j-1].next = &attrs[j];
|
||||||
|
}
|
||||||
|
attrs[j] = *LFSR_ATTR(
|
||||||
|
UATTR(attr), -1, &alpha[i % 26], 1,
|
||||||
|
NULL);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// update our sim
|
||||||
|
sim[attr] = '\0';
|
||||||
|
// update our rbyd
|
||||||
|
if (i < w) {
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
||||||
|
LFSR_ATTR(RMUATTR(attr), -1, NULL, 0,
|
||||||
|
NULL)) => 0;
|
||||||
|
// append to our unwritten attrs
|
||||||
|
} else {
|
||||||
|
if (j > 0) {
|
||||||
|
attrs[j-1].next = &attrs[j];
|
||||||
|
}
|
||||||
|
attrs[j] = *LFSR_ATTR(
|
||||||
|
RMUATTR(attr), -1, NULL, 0,
|
||||||
|
NULL);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare rbyd vs simulation
|
||||||
|
printf("expd: [");
|
||||||
|
bool first = true;
|
||||||
|
for (unsigned attr = 0; attr < N; attr++) {
|
||||||
|
if (sim[attr]) {
|
||||||
|
if (!first) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
printf("0x%02x=%c", attr, sim[attr]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
printf("rbyd: [");
|
||||||
|
first = true;
|
||||||
|
for (unsigned attr = 0; attr < N; attr++) {
|
||||||
|
lfs_ssize_t size = lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
||||||
|
LFSR_TAG_UATTR(attr), -1, buffer, 4);
|
||||||
|
if (size >= 0) {
|
||||||
|
if (!first) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
printf("0x%02x=%.*s", attr, size, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
for (unsigned attr = 0; attr < N; attr++) {
|
||||||
|
lfs_ssize_t size = lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
||||||
|
LFSR_TAG_UATTR(attr), -1, buffer, 4);
|
||||||
|
if (sim[attr]) {
|
||||||
|
assert(size == 1);
|
||||||
|
assert(memcmp(&sim[attr], buffer, 1) == 0);
|
||||||
|
} else {
|
||||||
|
assert(size == LFS_ERR_NOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
free(sim);
|
||||||
|
free(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_create_permutations]
|
||||||
|
defines.N = 'range(1, 7)'
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
lfsr_tag_t tag_ = 0;
|
||||||
|
lfs_ssize_t id_ = -1;
|
||||||
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
||||||
|
lfs_size_t weight_ = 0;
|
||||||
|
const uint8_t names[6][4] = {
|
||||||
|
"\xaa\xaa\xaa\xaa",
|
||||||
|
"\xbb\xbb\xbb\xbb",
|
||||||
|
"\xcc\xcc\xcc\xcc",
|
||||||
|
"\xdd\xdd\xdd\xdd",
|
||||||
|
"\xee\xee\xee\xee",
|
||||||
|
"\xff\xff\xff\xff",
|
||||||
|
};
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
// test all permutations of a given size
|
||||||
|
uint16_t perm[N];
|
||||||
|
unsigned stack[N];
|
||||||
|
for (uint16_t i = 0; i < N; i++) {
|
||||||
|
perm[i] = i;
|
||||||
|
stack[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i = 1;
|
||||||
|
while (i < N) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
// print permutation to help debugging
|
||||||
|
printf("--- permutation: [");
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
if (j > 0) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
printf("%d", perm[j]);
|
||||||
|
}
|
||||||
|
printf("], written: %d/%jd ---\n", w, N);
|
||||||
|
|
||||||
|
// build the attribute lists for the current permutation
|
||||||
|
struct lfsr_attr attrs[2*N];
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
// adjust id based on future insertions
|
||||||
|
uint16_t id = perm[j];
|
||||||
|
for (unsigned k = j+1; k < N; k++) {
|
||||||
|
if (perm[j] > perm[k]) {
|
||||||
|
id -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs[2*j+0] = *LFSR_ATTR(
|
||||||
|
GROW, id, NULL, 1,
|
||||||
|
&attrs[2*j+1]);
|
||||||
|
attrs[2*j+1] = *LFSR_ATTR(
|
||||||
|
MKREG, id, names[perm[j] % 6], 4,
|
||||||
|
(j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL);
|
||||||
|
}
|
||||||
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
||||||
|
struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL;
|
||||||
|
|
||||||
|
// create rbyd with written attr
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
||||||
|
rbyd.block, cfg->block_size, NULL) => 0;
|
||||||
|
assert(rbyd.weight == w);
|
||||||
|
|
||||||
|
// test lookup both written/unwritten
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
||||||
|
LFSR_TAG_MKREG, j, buffer, 4) => 4;
|
||||||
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test traverse both written/unwritten
|
||||||
|
tag_ = 0;
|
||||||
|
id_ = -1;
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_MKREG);
|
||||||
|
assert(id_ == j);
|
||||||
|
assert(lfsr_data_len(data_) == 4);
|
||||||
|
assert(weight_ == 1);
|
||||||
|
}
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next permutation using Heap's algorithm
|
||||||
|
if (stack[i] < i) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
uint16_t t = perm[0];
|
||||||
|
perm[0] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
} else {
|
||||||
|
uint16_t t = perm[stack[i]];
|
||||||
|
perm[stack[i]] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
}
|
||||||
|
stack[i] += 1;
|
||||||
|
i = 1;
|
||||||
|
} else {
|
||||||
|
stack[i] = 0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_create_random]
|
||||||
|
defines.N = 'range(1, 13)'
|
||||||
|
defines.ITER = 1000
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
const char *alpha = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
// iterate through seeds so we can reproduce easily
|
||||||
|
for (uint32_t seed = 1; seed < ITER+1; seed++) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N);
|
||||||
|
printf("perm: [");
|
||||||
|
uint32_t prng = seed;
|
||||||
|
lfs_size_t count = 0;
|
||||||
|
for (unsigned i = 0; i < N; i++) {
|
||||||
|
// choose an id
|
||||||
|
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
|
||||||
|
// choose create or delete
|
||||||
|
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
|
||||||
|
printf("c%d=%c", id, alpha[i % 26]);
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
printf("d%d", id);
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
if (i < N-1) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
// set up a simulation to compare against, fun fact this performs
|
||||||
|
// worst than our actual rbyd block!
|
||||||
|
char *sim = malloc(N);
|
||||||
|
memset(sim, 0, N);
|
||||||
|
|
||||||
|
// set up rbyd block
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
|
||||||
|
// set up attr list
|
||||||
|
struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr));
|
||||||
|
unsigned j = 0;
|
||||||
|
|
||||||
|
prng = seed;
|
||||||
|
count = 0;
|
||||||
|
for (unsigned i = 0; i < N; i++) {
|
||||||
|
// choose an id
|
||||||
|
lfs_ssize_t id = TEST_PRNG(&prng) % (count+1);
|
||||||
|
// choose create or delete
|
||||||
|
if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) {
|
||||||
|
// update our sim
|
||||||
|
memmove(sim+id+1, sim+id, count-id);
|
||||||
|
sim[id] = alpha[i % 26];
|
||||||
|
count += 1;
|
||||||
|
// update our rbyd
|
||||||
|
if (i < w) {
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
||||||
|
LFSR_ATTR(GROW, id, NULL, 1,
|
||||||
|
LFSR_ATTR(MKREG, id, &alpha[i % 26], 1,
|
||||||
|
NULL))) => 0;
|
||||||
|
} else {
|
||||||
|
if (j > 0) {
|
||||||
|
attrs[j-1].next = &attrs[j];
|
||||||
|
}
|
||||||
|
attrs[j] = *LFSR_ATTR(
|
||||||
|
GROW, id, NULL, 1,
|
||||||
|
&attrs[j+1]);
|
||||||
|
attrs[j+1] = *LFSR_ATTR(
|
||||||
|
MKREG, id, &alpha[i % 26], 1,
|
||||||
|
NULL);
|
||||||
|
j += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// update our sim
|
||||||
|
memmove(sim+id, sim+id+1, count-id-1);
|
||||||
|
count -= 1;
|
||||||
|
// update our rbyd
|
||||||
|
if (i < w) {
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd,
|
||||||
|
LFSR_ATTR(SHRINK, id, NULL, 1,
|
||||||
|
NULL)) => 0;
|
||||||
|
} else {
|
||||||
|
if (j > 0) {
|
||||||
|
attrs[j-1].next = &attrs[j];
|
||||||
|
}
|
||||||
|
attrs[j] = *LFSR_ATTR(
|
||||||
|
SHRINK, id, NULL, 1,
|
||||||
|
NULL);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare rbyd vs simulation
|
||||||
|
printf("expd: [");
|
||||||
|
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
|
||||||
|
printf("%c", sim[id]);
|
||||||
|
if (id < (lfs_ssize_t)count-1) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
printf("rbyd: [");
|
||||||
|
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
|
||||||
|
lfs_ssize_t size = lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
||||||
|
LFSR_TAG_MKREG, id, buffer, 4);
|
||||||
|
if (size >= 0) {
|
||||||
|
printf("%.*s", size, buffer);
|
||||||
|
} else {
|
||||||
|
printf("?");
|
||||||
|
}
|
||||||
|
if (id < (lfs_ssize_t)count-1) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) {
|
||||||
|
lfsr_rbyd_pendingget(&lfs, &rbyd, attrs,
|
||||||
|
LFSR_TAG_MKREG, id, buffer, 4) => 1;
|
||||||
|
assert(memcmp(&sim[id], buffer, 1) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
free(sim);
|
||||||
|
free(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_mixed_permutations]
|
||||||
|
defines.N = 'range(1, 7)'
|
||||||
|
defines.M = 'range(1, 4)'
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
lfsr_tag_t tag_ = 0;
|
||||||
|
lfs_ssize_t id_ = -1;
|
||||||
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
||||||
|
lfs_size_t weight_ = 0;
|
||||||
|
const uint8_t names[6][4] = {
|
||||||
|
"\xaa\xaa\xaa\xaa",
|
||||||
|
"\xbb\xbb\xbb\xbb",
|
||||||
|
"\xcc\xcc\xcc\xcc",
|
||||||
|
"\xdd\xdd\xdd\xdd",
|
||||||
|
"\xee\xee\xee\xee",
|
||||||
|
"\xff\xff\xff\xff",
|
||||||
|
};
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
// test all permutations of a given size
|
||||||
|
uint16_t perm[N];
|
||||||
|
unsigned stack[N];
|
||||||
|
for (uint16_t i = 0; i < N; i++) {
|
||||||
|
perm[i] = i;
|
||||||
|
stack[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i = 1;
|
||||||
|
while (i < N) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
// print permutation to help debugging
|
||||||
|
printf("--- permutation: [");
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
if (j > 0) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
printf("%d", perm[j]);
|
||||||
|
}
|
||||||
|
printf("], written: %d/%jd ---\n", w, N);
|
||||||
|
|
||||||
|
// build the attribute lists for the current permutation
|
||||||
|
struct lfsr_attr attrs[(2+M)*N];
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
// adjust id based on future insertions
|
||||||
|
uint16_t id = perm[j];
|
||||||
|
for (unsigned k = j+1; k < N; k++) {
|
||||||
|
if (perm[j] > perm[k]) {
|
||||||
|
id -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs[(2+M)*j+0] = *LFSR_ATTR(
|
||||||
|
GROW, id, NULL, 1,
|
||||||
|
&attrs[(2+M)*j+1]);
|
||||||
|
attrs[(2+M)*j+1] = *LFSR_ATTR(
|
||||||
|
MKREG, id, names[perm[j] % 6], 4,
|
||||||
|
&attrs[(2+M)*j+2]);
|
||||||
|
for (unsigned u = 0; u < M; u++) {
|
||||||
|
attrs[(2+M)*j+2+u] = *LFSR_ATTR(
|
||||||
|
UATTR(u+1), id, names[perm[j] % 6], 2,
|
||||||
|
&attrs[(2+M)*j+2+u+1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (w > 0) {
|
||||||
|
attrs[(2+M)*w-1].next = NULL;
|
||||||
|
}
|
||||||
|
attrs[(2+M)*N-1].next = NULL;
|
||||||
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
||||||
|
struct lfsr_attr *unwritten = w < N ? &attrs[(2+M)*w] : NULL;
|
||||||
|
|
||||||
|
// create rbyd with written attr
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
||||||
|
rbyd.block, cfg->block_size, NULL) => 0;
|
||||||
|
assert(rbyd.weight == w);
|
||||||
|
|
||||||
|
// test lookup both written/unwritten
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
||||||
|
LFSR_TAG_MKREG, j, buffer, 4) => 4;
|
||||||
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
||||||
|
|
||||||
|
for (unsigned u = 0; u < M; u++) {
|
||||||
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
||||||
|
LFSR_TAG_UATTR(u+1), j, buffer, 4) => 2;
|
||||||
|
assert(memcmp(buffer, names[j % 6], 2) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test traverse both written/unwritten
|
||||||
|
tag_ = 0;
|
||||||
|
id_ = -1;
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_MKREG);
|
||||||
|
assert(id_ == j);
|
||||||
|
assert(lfsr_data_len(data_) == 4);
|
||||||
|
assert(weight_ == 1);
|
||||||
|
|
||||||
|
for (unsigned u = 0; u < M; u++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_UATTR(u+1));
|
||||||
|
assert(id_ == j);
|
||||||
|
assert(lfsr_data_len(data_) == 2);
|
||||||
|
assert(weight_ == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next permutation using Heap's algorithm
|
||||||
|
if (stack[i] < i) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
uint16_t t = perm[0];
|
||||||
|
perm[0] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
} else {
|
||||||
|
uint16_t t = perm[stack[i]];
|
||||||
|
perm[stack[i]] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
}
|
||||||
|
stack[i] += 1;
|
||||||
|
i = 1;
|
||||||
|
} else {
|
||||||
|
stack[i] = 0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
[cases.test_rbyd_unwritten_sparse_permutations]
|
||||||
|
defines.N = 'range(1, 7)'
|
||||||
|
defines.W = 5
|
||||||
|
# large progs take too long for now
|
||||||
|
if = 'PROG_SIZE < 512'
|
||||||
|
in = 'lfs.c'
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfs_init(&lfs, cfg) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_t init_rbyd = {
|
||||||
|
.block = 0,
|
||||||
|
.rev = 1,
|
||||||
|
.off = 0,
|
||||||
|
.crc = 0,
|
||||||
|
.trunk = 0,
|
||||||
|
.weight = 0,
|
||||||
|
.erased = true,
|
||||||
|
};
|
||||||
|
lfsr_rbyd_t rbyd;
|
||||||
|
lfsr_tag_t tag_ = 0;
|
||||||
|
lfs_ssize_t id_ = -1;
|
||||||
|
lfsr_data_t data_ = LFSR_DATA_NULL;
|
||||||
|
lfs_size_t weight_ = 0;
|
||||||
|
const uint8_t names[6][4] = {
|
||||||
|
"\xaa\xaa\xaa\xaa",
|
||||||
|
"\xbb\xbb\xbb\xbb",
|
||||||
|
"\xcc\xcc\xcc\xcc",
|
||||||
|
"\xdd\xdd\xdd\xdd",
|
||||||
|
"\xee\xee\xee\xee",
|
||||||
|
"\xff\xff\xff\xff",
|
||||||
|
};
|
||||||
|
uint8_t buffer[4];
|
||||||
|
|
||||||
|
// test all permutations of a given size
|
||||||
|
uint16_t perm[N];
|
||||||
|
unsigned stack[N];
|
||||||
|
for (uint16_t i = 0; i < N; i++) {
|
||||||
|
perm[i] = i;
|
||||||
|
stack[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned i = 1;
|
||||||
|
while (i < N) {
|
||||||
|
// test each number of written/unwritten tags, this gives us a quick
|
||||||
|
// way to test several unwritten situations
|
||||||
|
for (unsigned w = 0; w <= N; w++) {
|
||||||
|
// print permutation to help debugging
|
||||||
|
printf("--- permutation: [");
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
if (j > 0) {
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
printf("%d", perm[j]);
|
||||||
|
}
|
||||||
|
printf("], written: %d/%jd ---\n", w, N);
|
||||||
|
|
||||||
|
// build the attribute lists for the current permutation
|
||||||
|
struct lfsr_attr attrs[2*N];
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
// adjust id based on future insertions
|
||||||
|
uint16_t id = perm[j];
|
||||||
|
for (unsigned k = j+1; k < N; k++) {
|
||||||
|
if (perm[j] > perm[k]) {
|
||||||
|
id -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs[2*j+0] = *LFSR_ATTR(
|
||||||
|
GROW, id*W, NULL, W,
|
||||||
|
&attrs[2*j+1]);
|
||||||
|
attrs[2*j+1] = *LFSR_ATTR(
|
||||||
|
MKREG, id*W+W-1, names[perm[j] % 6], 4,
|
||||||
|
(j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL);
|
||||||
|
}
|
||||||
|
struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL;
|
||||||
|
struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL;
|
||||||
|
|
||||||
|
// create rbyd with written attr
|
||||||
|
rbyd = init_rbyd;
|
||||||
|
lfs_bd_erase(&lfs, rbyd.block) => 0;
|
||||||
|
lfsr_rbyd_commit(&lfs, &rbyd, written) => 0;
|
||||||
|
|
||||||
|
lfsr_rbyd_fetch(&lfs, &rbyd,
|
||||||
|
rbyd.block, cfg->block_size, NULL) => 0;
|
||||||
|
assert(rbyd.weight == w*W);
|
||||||
|
|
||||||
|
// test lookup both written/unwritten
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten,
|
||||||
|
LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4;
|
||||||
|
assert(memcmp(buffer, names[j % 6], 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test traverse both written/unwritten
|
||||||
|
tag_ = 0;
|
||||||
|
id_ = -1;
|
||||||
|
for (unsigned j = 0; j < N; j++) {
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, &weight_) => 0;
|
||||||
|
assert(tag_ == LFSR_TAG_MKREG);
|
||||||
|
assert(id_ == j*W+W-1);
|
||||||
|
assert(lfsr_data_len(data_) == 4);
|
||||||
|
assert(weight_ == W);
|
||||||
|
}
|
||||||
|
lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten,
|
||||||
|
lfsr_tag_next(tag_), id_,
|
||||||
|
&tag_, &id_, &data_, NULL) => LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next permutation using Heap's algorithm
|
||||||
|
if (stack[i] < i) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
uint16_t t = perm[0];
|
||||||
|
perm[0] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
} else {
|
||||||
|
uint16_t t = perm[stack[i]];
|
||||||
|
perm[stack[i]] = perm[i];
|
||||||
|
perm[i] = t;
|
||||||
|
}
|
||||||
|
stack[i] += 1;
|
||||||
|
i = 1;
|
||||||
|
} else {
|
||||||
|
stack[i] = 0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|||||||
Reference in New Issue
Block a user