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:
Christopher Haster
2023-02-28 12:54:11 -06:00
parent 6f4704474b
commit c0ee405cf2
2 changed files with 1011 additions and 113 deletions
+830
View File
@@ -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;
}
}
'''