From 56fbf4155bf2ce5db24271037b78e13b3cdf36d1 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 27 Jan 2023 10:05:20 -0600 Subject: [PATCH] Reenabling more tests, tracking down another difficult bug --- lfs.c | 246 +++-- tests/test_rbyd.toml | 2257 ++++++++++++++++++++++++++++-------------- 2 files changed, 1701 insertions(+), 802 deletions(-) diff --git a/lfs.c b/lfs.c index 618cd0f5..f3c600d0 100644 --- a/lfs.c +++ b/lfs.c @@ -2093,6 +2093,36 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, upper_upper_id_ = lower_upper_id_; upper_lower_tag = lower_lower_tag; upper_upper_tag = lower_upper_tag; + +// // TODO ??? +// // handle red edge before diverging +// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) { +// alt = lfsr_tag_mkblack(p_alts[0]); +// weight_ = p_weights[0]; +// branch_ = lower_branch; +// jump = p_jumps[0]; +// lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps); +// +// lfsr_tag_untrimweight(alt, weight_, +// &lower_lower_id, &lower_upper_id); +// goto redo_red; +// } + +// // make sure we are on the path we can take care of the red alt correctly? +// // TODO ??? +// if (p_alts[0] +// && lfsr_tag_isred(p_alts[0]) +// && lfsr_tag_isgt(p_alts[0])) { +// lfs_swap16(&lower_tag_, &upper_tag_); +// lfs_swaps32(&lower_id_, &upper_id_); +// lfs_swap32(&lower_branch, &upper_branch); +// lfs_swaps32(&lower_lower_id, &upper_lower_id); +// lfs_swaps32(&lower_upper_id, &upper_upper_id); +// lfs_swaps32(&lower_lower_id_, &upper_lower_id_); +// lfs_swaps32(&lower_upper_id_, &upper_upper_id_); +// lfs_swap16(&lower_lower_tag, &upper_lower_tag); +// lfs_swap16(&lower_upper_tag, &upper_upper_tag); +// } } // prune? @@ -2105,7 +2135,7 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_, // | | = 0); + printf("pushed alt%c%s 0x%x w%d 0x%x\n", + lfsr_tag_isred(alt) ? 'r' : 'b', + lfsr_tag_isgt(alt) ? "gt" : "lt", + lfsr_tag_key(alt), + weight_, + jump); int err = lfsr_rbyd_p_push(lfs, rbyd_, p_alts, p_weights, p_jumps, alt, weight_, jump); @@ -2440,14 +2548,18 @@ stem:; lfs_size_t weight = 0; lfs_off_t jump = 0; - printf("! (0x%x %d) < (0x%x %d) < (0x%x %d)\n", lower_tag_, lower_id_, tag, id, upper_tag_, upper_id_); + printf("! (0x%x id%d 0x%x) < (0x%x id%d) < (0x%x id%d 0x%x)\n", lower_tag_, lower_id_, lower_branch, tag, id, upper_tag_, upper_id_, upper_branch); if (lfsr_tag_isrm(lower_tag_)) { // no split needed, prune the removed tag + // TODO this is an ugly condition, but is it necessary? } else if ((lower_id_ < id || (lower_id_ == id && lfsr_tag_key(lower_tag_) < lfsr_tag_key(tag))) +// || (lower_id_ == id +// && lfsr_tag_key(lower_tag_) <= lfsr_tag_key(tag) +// && lfsr_tag_isrm(tag))) && tag != LFSR_TAG_RM) { // || (lower_id_ == id // && tag == LFSR_TAG_RM @@ -2458,6 +2570,7 @@ stem:; // weight = lower_id_ - lower_lower_id - 1; // jump = lower_branch; // } else { + // split less than // // note this is consistent for all appends and only happens when @@ -2480,9 +2593,7 @@ stem:; if (upper_id_ > id) { // decrease weight when deleting alt = LFSR_TAG_ALT(B, GT, 0); - printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH\n"); weight = upper_upper_id - lower_lower_id - 1 - 1; - printf("%d = %d - %d - 1\n", weight, upper_upper_id, lower_lower_id); jump = upper_branch; } @@ -2490,6 +2601,7 @@ stem:; if (upper_id_ > id || (upper_id_ == id && lfsr_tag_key(upper_tag_) > lfsr_tag_key(tag))) { + printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH\n"); // hide our tag during removes alt = LFSR_TAG_ALT(B, GT, 0); weight = upper_upper_id - lower_lower_id; diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index 2f170b29..0e6d8e72 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -2584,6 +2584,128 @@ code = ''' } ''' +[cases.test_rbyd_update_permutations] +defines.N = 'range(1, 8)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N' +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_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // create one consistent block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa", 4, + NULL)) => 0; + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // restore backup + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // update each tag in permutation order + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(perm[j]+1), -1, + "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + } + + // check that all tags have been updated + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(j+1), -1, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(j+1)); + assert(id_ == -1); + assert(size_ == 6); + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + N; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + [cases.test_rbyd_large] in = 'lfs.c' # ORDER: @@ -2851,6 +2973,7 @@ code = ''' if (j == N-1) { assert(err == LFS_ERR_NOENT); } else { + assert(!err); assert(tag_ == LFSR_TAG_UATTR(j+1+1)); assert(id_ == -1); assert(size_ == 4); @@ -2862,6 +2985,29 @@ code = ''' } } + // try appending the tag back to make sure things still work + printf("--- append: %d ---\n", j+1); + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(j+1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + for (unsigned k = 0; k < N; k++) { + lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_UATTR(k+1), -1, + &tag_, &id_, &off_, &size_) => 0; + if (k == j) { + assert(tag_ == LFSR_TAG_UATTR(k+1)); + assert(id_ == -1); + assert(size_ == 6); + } else { + assert(tag_ == LFSR_TAG_UATTR(k+1)); + assert(id_ == -1); + assert(size_ == 4); + } + } + // keep track of the worst size worst_size = lfs_max(worst_size, rbyd.off); } @@ -2887,7 +3033,7 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes - lfs_size_t n = 1 + N + 1; + lfs_size_t n = 1 + N + 2; printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); printf("avg height: %u B (N=%u, estimate=%u)\n", @@ -3509,152 +3655,155 @@ code = ''' } ''' -[cases.test_rbyd_remove_append_permutations] -defines.N = 'range(1, 6)' -in = 'lfs.c' -if = 'BLOCK_SIZE/PROG_SIZE >= N+2' -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_; - lfsr_sid_t id_; - lfs_off_t off_; - lfs_size_t size_; - - // keep track of the worst case log size - lfs_size_t worst_size = 0; - - // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } - - unsigned i = 1; - while (i < N) { - // print permutation to help debugging - printf("--- permutation: ["); - for (unsigned j = 0; j < N; j++) { - if (j > 0) { - printf(", "); - } - printf("%d", perm[j]+1); - } - printf("] ---\n"); - - // create given permutation with multiple commits - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, - NULL)) => 0; - } - - // copy block so we can reset after each remove - lfsr_rbyd_t backup_rbyd = rbyd; - uint8_t backup_block[BLOCK_SIZE]; - lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, - rbyd.block, 0, backup_block, rbyd.off) => 0; - - // try removing each tag - for (unsigned j = 0; j < N; j++) { - for (unsigned l = 0; l < N; l++) { - // print what we are removing to help debugging - printf("--- remove: %d, append: %d ---\n", j+1, l+1); - - rbyd = backup_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, - rbyd.block, 0, backup_block, rbyd.off) => 0; - lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; - - // remove - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0; - - // try appending each tag to make sure the rbyd tree - // is still usable - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(l+1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, - NULL)) => 0; - - lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, - cfg->block_size, NULL) => 0; - for (unsigned k = 0; k < N; k++) { - int err = lfsr_rbyd_lookup(&lfs, &rbyd, - LFSR_TAG_UATTR(k+1), -1, - &tag_, &id_, &off_, &size_); - assert(!err || err == LFS_ERR_NOENT); - if (k == l) { - assert(tag_ == LFSR_TAG_UATTR(l+1)); - assert(id_ == -1); - assert(size_ == 6); - } else if (k == j) { - if (j == N-1) { - assert(err == LFS_ERR_NOENT); - } else { - assert(tag_ == LFSR_TAG_UATTR(j+1+1)); - assert(id_ == -1); - assert(size_ == 4 || size_ == 6); - } - } else { - assert(tag_ == LFSR_TAG_UATTR(k+1)); - assert(id_ == -1); - assert(size_ == 4); - } - } - - // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - } - } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } - } - - // test that tree is self-balancing, we should be strictly bounded - // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes - lfs_size_t n = 1 + N + 1 + 1; - printf("worst size: %u B (N=%u, estimate=%u)\n", - worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); - printf("avg height: %u B (N=%u, estimate=%u)\n", - worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); - // note this only holds true with byte-level progs - if (PROG_SIZE == 1) { - assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); - } -''' +# TODO rm me +#[cases.test_rbyd_remove_append_permutations] +#defines.N = 'range(1, 6)' +#in = 'lfs.c' +#if = 'BLOCK_SIZE/PROG_SIZE >= N+2' +#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_; +# lfsr_sid_t id_; +# lfs_off_t off_; +# lfs_size_t size_; +# +# // keep track of the worst case log size +# lfs_size_t worst_size = 0; +# +# // test all permutations of a given size +# uint8_t perm[N]; +# unsigned stack[N]; +# for (uint8_t i = 0; i < N; i++) { +# perm[i] = i; +# stack[i] = 0; +# } +# +# unsigned i = 1; +# while (i < N) { +# // print permutation to help debugging +# printf("--- permutation: ["); +# for (unsigned j = 0; j < N; j++) { +# if (j > 0) { +# printf(", "); +# } +# printf("%d", perm[j]+1); +# } +# printf("] ---\n"); +# +# // create given permutation with multiple commits +# rbyd = init_rbyd; +# lfs_bd_erase(&lfs, rbyd.block) => 0; +# +# for (unsigned j = 0; j < N; j++) { +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, +# NULL)) => 0; +# } +# +# // copy block so we can reset after each remove +# lfsr_rbyd_t backup_rbyd = rbyd; +# uint8_t backup_block[BLOCK_SIZE]; +# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, +# rbyd.block, 0, backup_block, rbyd.off) => 0; +# +# // try removing each tag +# for (unsigned j = 0; j < N; j++) { +# for (unsigned l = 0; l < N; l++) { +# // print what we are removing to help debugging +# printf("--- remove: %d, append: %d ---\n", j+1, l+1); +# +# rbyd = backup_rbyd; +# lfs_bd_erase(&lfs, rbyd.block) => 0; +# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, +# rbyd.block, 0, backup_block, rbyd.off) => 0; +# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; +# +# // remove +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(RMUATTR(j+1), -1, NULL, 0, NULL)) => 0; +# +# // try appending each tag to make sure the rbyd tree +# // is still usable +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(UATTR(l+1), -1, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, +# NULL)) => 0; +# +# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, +# cfg->block_size, NULL) => 0; +# for (unsigned k = 0; k < N; k++) { +# int err = lfsr_rbyd_lookup(&lfs, &rbyd, +# LFSR_TAG_UATTR(k+1), -1, +# &tag_, &id_, &off_, &size_); +# assert(!err || err == LFS_ERR_NOENT); +# if (k == l) { +# assert(tag_ == LFSR_TAG_UATTR(l+1)); +# assert(id_ == -1); +# assert(size_ == 6); +# } else if (k == j) { +# if (j == N-1) { +# assert(err == LFS_ERR_NOENT); +# } else { +# assert(!err); +# assert(tag_ == LFSR_TAG_UATTR(j+1+1)); +# assert(id_ == -1); +# assert(size_ == 4 || size_ == 6); +# } +# } else { +# assert(!err); +# assert(tag_ == LFSR_TAG_UATTR(k+1)); +# assert(id_ == -1); +# assert(size_ == 4); +# } +# } +# +# // keep track of the worst size +# worst_size = lfs_max(worst_size, rbyd.off); +# } +# } +# +# // next permutation using Heap's algorithm +# if (stack[i] < i) { +# if (i % 2 == 0) { +# uint8_t t = perm[0]; +# perm[0] = perm[i]; +# perm[i] = t; +# } else { +# uint8_t t = perm[stack[i]]; +# perm[stack[i]] = perm[i]; +# perm[i] = t; +# } +# stack[i] += 1; +# i = 1; +# } else { +# stack[i] = 0; +# i += 1; +# } +# } +# +# // test that tree is self-balancing, we should be strictly bounded +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes +# lfs_size_t n = 1 + N + 1 + 1; +# printf("worst size: %u B (N=%u, estimate=%u)\n", +# worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); +# printf("avg height: %u B (N=%u, estimate=%u)\n", +# worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); +# // note this only holds true with byte-level progs +# if (PROG_SIZE == 1) { +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); +# } +#''' ### Insertion testing ### @@ -4690,6 +4839,8 @@ code = ''' } // check that we can at least lookup all the tags + // + // note with random order we can't check that stored values reliably lfsr_tag_t tag_; lfsr_sid_t id_; lfs_off_t off_; @@ -6051,6 +6202,581 @@ code = ''' } ''' +[cases.test_rbyd_mixed_update_permutations] +defines.N = 'range(1, 4)' +defines.M = 'range(1, 3)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N' +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, + }; + 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", + }; + lfsr_rbyd_t rbyd; + uint8_t buffer[4]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // create one consistent block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, j, names[j % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2, + NULL)) => 0; + } + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // test all permutations of a given size + uint8_t perm[N*M]; + unsigned stack[N*M]; + for (uint8_t i = 0; i < N*M; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N*M) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N*M; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // restore backup + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // update each tag in permutation order + for (unsigned j = 0; j < N*M; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(perm[j]%M+1), perm[j]/M, + names[(perm[j]/M) % 6], 3, + NULL)) => 0; + } + + // check that all tags have been updated + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j, buffer, 4) => 4; + assert(memcmp(buffer, names[j % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4) + => 3; + assert(memcmp(buffer, names[j % 6], 3) == 0); + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + N*M + N*M; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +# TODO this +# TODO also remove "all"? +[cases.test_rbyd_mixed_remove_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= N+1' +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, + }; + 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", + }; + lfsr_rbyd_t rbyd; + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + uint8_t buffer[4]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // test all permutations of a given size + uint8_t perm[N]; + unsigned stack[N]; + for (uint8_t i = 0; i < N; i++) { + perm[i] = i; + stack[i] = 0; + } + + unsigned i = 1; + while (i < N) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // create given permutation + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + 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; + } + } + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, id, names[perm[j] % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), id, names[perm[j] % 6], 2, + NULL)) => 0; + } + } + + // copy block so we can reset after each remove + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 0; + + // try removing each tag + for (unsigned j = 0; j < N*M; j++) { + // print what we are removing to help debugging + printf("--- remove: id%jd, %jd ---\n", j/M, (j%M)+1); + + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR((j%M)+1), j/M, NULL, 0, NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + for (unsigned k = 0; k < N; k++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4; + assert(memcmp(buffer, names[k % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + int err = lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), k, + &tag_, &id_, &off_, &size_); + assert(!err || err == LFS_ERR_NOENT); + if (k == j/M && u == j%M) { + if (u == M-1 && k == N-1) { + assert(err == LFS_ERR_NOENT); + } else if (u == M-1) { + assert(!err); + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == k+1); + assert(size_ == 4); + } else { + assert(!err); + assert(tag_ == LFSR_TAG_UATTR(u+1+1)); + assert(id_ == k); + assert(size_ == 2); + } + } else { + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == k); + assert(size_ == 2); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, names[k % 6], 2) == 0); + } + } + } + + // try append the tag back to make sure things still work + printf("--- append: id%jd, %jd ---\n", j/M, (j%M)+1); + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR((j%M)+1), j/M, names[(j/M)%6], 3, + NULL)) => 0; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + for (unsigned k = 0; k < N; k++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 4) => 4; + assert(memcmp(buffer, names[k % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), k, + &tag_, &id_, &off_, &size_) => 0; + if (k == j/M && u == j%M) { + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == k); + assert(size_ == 3); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 3; + assert(memcmp(buffer, names[k % 6], 3) == 0); + } else { + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == k); + assert(size_ == 2); + + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 2; + assert(memcmp(buffer, names[k % 6], 2) == 0); + } + } + } + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + } + + // next permutation using Heap's algorithm + if (stack[i] < i) { + if (i % 2 == 0) { + uint8_t t = perm[0]; + perm[0] = perm[i]; + perm[i] = t; + } else { + uint8_t t = perm[stack[i]]; + perm[stack[i]] = perm[i]; + perm[i] = t; + } + stack[i] += 1; + i = 1; + } else { + stack[i] = 0; + i += 1; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N + N; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +#[cases.test_rbyd_mixed_remove_all_permutations] +#defines.N = 'range(1, 4)' +#defines.M = 'range(1, 3)' +#in = 'lfs.c' +#if = 'BLOCK_SIZE/PROG_SIZE >= N' +#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, +# }; +# 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", +# }; +# lfsr_rbyd_t rbyd; +# uint8_t buffer[4]; +# +# // keep track of the worst case log size +# lfs_size_t worst_size = 0; +# +# // create one consistent block +# rbyd = init_rbyd; +# lfs_bd_erase(&lfs, rbyd.block) => 0; +# +# for (unsigned j = 0; j < N; j++) { +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(MKREG, j, names[j % 6], 4, +# NULL)) => 0; +# // note uattrs have a smaller size to help debugging +# for (unsigned u = 0; u < M; u++) { +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2, +# NULL)) => 0; +# } +# } +# +# // copy block so we can reset after each remove +# lfsr_rbyd_t backup_rbyd = rbyd; +# uint8_t backup_block[BLOCK_SIZE]; +# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, +# rbyd.block, 0, backup_block, rbyd.off) => 0; +# +# // test all permutations of a given size +# uint8_t perm[N*M]; +# unsigned stack[N*M]; +# for (uint8_t i = 0; i < N*M; i++) { +# perm[i] = i; +# stack[i] = 0; +# } +# +# unsigned i = 1; +# while (i < N*M) { +# // print permutation to help debugging +# printf("--- permutation: ["); +# for (unsigned j = 0; j < N*M; j++) { +# if (j > 0) { +# printf(", "); +# } +# printf("%d", perm[j]+1); +# } +# printf("] ---\n"); +# +# // restore backup +# rbyd = backup_rbyd; +# lfs_bd_erase(&lfs, rbyd.block) => 0; +# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, +# rbyd.block, 0, backup_block, rbyd.off) => 0; +# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; +# +# // remove each tag in permutation order +# for (unsigned j = 0; j < N*M; j++) { +# lfsr_rbyd_commit(&lfs, &rbyd, +# LFSR_ATTR(RMUATTR(perm[j]%M+1), perm[j]/M, NULL, 0, +# NULL)) => 0; +# } +# +# // check that all tags have been removed +# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; +# for (unsigned j = 0; j < N; j++) { +# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j, buffer, 4) => 4; +# assert(memcmp(buffer, names[j % 6], 4) == 0); +# +# for (unsigned u = 0; u < M; u++) { +# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), j, buffer, 4) +# => LFS_ERR_NOENT; +# } +# } +# +# // keep track of the worst size +# worst_size = lfs_max(worst_size, rbyd.off); +# +# // next permutation using Heap's algorithm +# if (stack[i] < i) { +# if (i % 2 == 0) { +# uint8_t t = perm[0]; +# perm[0] = perm[i]; +# perm[i] = t; +# } else { +# uint8_t t = perm[stack[i]]; +# perm[stack[i]] = perm[i]; +# perm[i] = t; +# } +# stack[i] += 1; +# i = 1; +# } else { +# stack[i] = 0; +# i += 1; +# } +# } +# +# // test that tree is self-balancing, we should be strictly bounded +# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes +# lfs_size_t n = 1 + N + N*M + N*M; +# printf("worst size: %u B (N=%u, estimate=%u)\n", +# worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); +# printf("avg height: %u B (N=%u, estimate=%u)\n", +# worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); +# // note this only holds true with byte-level progs +# if (PROG_SIZE == 1) { +# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); +# } +#''' + +[cases.test_rbyd_mixed_large] +in = 'lfs.c' +# ORDER: +# 0 = in-order +# 1 = reverse-order +# 2 = random-order +defines.ORDER = [0, 1, 2] +defines.M = 'range(1, 4)' +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 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", + }; + + // create the rbyd tree + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + // keep inserting tags until we run out of space + // + // note, the ids we create this way are both sparse and sometimes + // repeated, so we need to mod our current rbyd size to avoid invalid + // insertions + // + uint32_t prng = 42; + for (lfs_size_t i = 0;; i++) { + uint16_t x + = (ORDER == 0) ? (uint16_t)i + : (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i) + : (uint16_t)TEST_PRNG(&prng); + x = x % (rbyd.weight+1); + + // build a single attribute list with all attributes, if this fails + // it should fail atomically + struct lfsr_attr attrs[M+1]; + attrs[0] = *LFSR_ATTR( + MKREG, x, + names[x % 6], 4, M > 0 ? &attrs[1] : NULL); + for (unsigned u = 0; u < M; u++) { + attrs[1+u] = *LFSR_ATTR( + UATTR(u+1), x, + names[x % 6], 2, + (u+1 < M) ? &attrs[1+u+1] : NULL); + } + + int err = lfsr_rbyd_commit(&lfs, &rbyd, attrs); + // if we can't fit an fcrc, erased is set to false, but if we can, + // lfsr_rbyd_commit may error later with LFS_ERR_RANGE + if (!rbyd.erased || err == LFS_ERR_RANGE) { + break; + } + assert(err == 0); + } + + // check that we can at least lookup all the tags + // + // note with random order we can't check that stored values reliably + lfsr_tag_t tag_; + lfsr_sid_t id_; + lfs_off_t off_; + lfs_size_t size_; + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + for (uint16_t x = 0; x < rbyd.weight; x++) { + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_MKREG, x, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_MKREG); + assert(id_ == x); + assert(size_ == 4); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_lookup(&lfs, &rbyd, LFSR_TAG_UATTR(u+1), x, + &tag_, &id_, &off_, &size_) => 0; + assert(tag_ == LFSR_TAG_UATTR(u+1)); + assert(id_ == x); + assert(size_ == 2); + } + } +''' + ### Deletion testing ### @@ -6442,15 +7168,15 @@ code = ''' .erased = true, }; lfsr_rbyd_t rbyd; - 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", + const uint8_t names[6][6] = { + "\xaa\xaa\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee\xee\xee", + "\xff\xff\xff\xff\xff\xff", }; - uint8_t buffer[4]; + uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; @@ -6529,6 +7255,27 @@ code = ''' lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, N-1, buffer, 4) => LFS_ERR_NOENT; + // try recreating the id to make sure things still work + printf("--- create: %d ---\n", j+1); + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, j, names[j % 6], 6, NULL)) => 0; + assert(rbyd.weight == N); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + for (unsigned k = 0; k < N; k++) { + if (k == j) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6) + => 6; + assert(memcmp(buffer, names[k % 6], 6) == 0); + } else { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6) + => 4; + assert(memcmp(buffer, names[k % 6], 4) == 0); + } + } + // keep track of the worst size worst_size = lfs_max(worst_size, rbyd.off); } @@ -6554,7 +7301,7 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes - lfs_size_t n = 1 + N + 1; + lfs_size_t n = 1 + N + 2; printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); printf("avg height: %u B (N=%u, estimate=%u)\n", @@ -6587,15 +7334,15 @@ code = ''' .erased = true, }; lfsr_rbyd_t rbyd; - 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", + const uint8_t names[6][6] = { + "\xaa\xaa\xaa\xaa\xaa\xaa", + "\xbb\xbb\xbb\xbb\xbb\xbb", + "\xcc\xcc\xcc\xcc\xcc\xcc", + "\xdd\xdd\xdd\xdd\xdd\xdd", + "\xee\xee\xee\xee\xee\xee", + "\xff\xff\xff\xff\xff\xff", }; - uint8_t buffer[4]; + uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; @@ -6692,6 +7439,45 @@ code = ''' lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_UATTR(1), N-1, buffer, 4) => LFS_ERR_NOENT; + // try recreating the id to make sure things still work + printf("--- create: %d ---\n", j+1); + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, j, names[j % 6], 6, NULL)) => 0; + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), j, names[j % 6], 3, NULL)) => 0; + } + assert(rbyd.weight == N); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, + cfg->block_size, NULL) => 0; + assert(rbyd.weight == N); + for (unsigned k = 0; k < N; k++) { + if (k == j) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6) + => 6; + assert(memcmp(buffer, names[k % 6], 6) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), k, buffer, 6) + => 3; + assert(memcmp(buffer, names[k % 6], 3) == 0); + } + } else { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, k, buffer, 6) + => 4; + assert(memcmp(buffer, names[k % 6], 4) == 0); + + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), k, buffer, 6) + => 2; + assert(memcmp(buffer, names[k % 6], 2) == 0); + } + } + } + // keep track of the worst size worst_size = lfs_max(worst_size, rbyd.off); } @@ -6717,7 +7503,7 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes - lfs_size_t n = 1 + N+N*M + 1; + lfs_size_t n = 1 + N+N*M + 1 + 1+M; printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); printf("avg height: %u B (N=%u, estimate=%u)\n", @@ -6728,576 +7514,577 @@ code = ''' } ''' -#[cases.test_rbyd_delete_all] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#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; -# uint8_t buffer[4]; -# -# // create and delete one id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete two ids -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete two ids in the other order -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete three ids -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete three ids in the other order -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 2, NULL, 0, -# LFSR_ATTR(RM, 1, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_delete_all_range] -#in = 'lfs.c' -#if = 'BLOCK_SIZE/PROG_SIZE >= 2' -#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; -# uint8_t buffer[4]; -# -# // create and delete one id -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete two ids -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete two ids in the other order -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 1, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete three ids -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // create and delete three ids in the other order -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, 0, "\xaa\xaa\xaa\xaa", 4, -# LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, -# LFSR_ATTR(MKREG, 0, 1, "\xbb\xbb\xbb\xbb", 4, -# LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, -# LFSR_ATTR(MKREG, 0, 2, "\xcc\xcc\xcc\xcc", 4, -# LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, 2, NULL, 0, -# LFSR_ATTR(RM, 1, NULL, 0, -# LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; -# -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -#''' -# -#[cases.test_rbyd_delete_all_permutations] -#defines.N = 'range(1, 7)' -#in = 'lfs.c' -#if = ''' -# BLOCK_SIZE/PROG_SIZE >= 2*N+1 -# && BLOCK_SIZE >= 1024 -#''' -#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 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[6]; -# -# // keep track of the worst case log size -# lfs_size_t worst_size = 0; -# -# // create one consistent block -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, j, names[j % 6], 4, -# NULL)) => 0; -# } -# assert(rbyd.weight == N); -# -# // copy block so we can reset after each delete -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 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) { -# // print permutation to help debugging -# printf("--- permutation: ["); -# for (unsigned j = 0; j < N; j++) { -# if (j > 0) { -# printf(", "); -# } -# printf("%d", perm[j]+1); -# } -# printf("] ---\n"); -# -# // restore backup -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# // delete each id in permutation order -# for (unsigned j = 0; j < N; j++) { -# // adjust id based on previous deletions -# uint16_t id = perm[j]; -# for (unsigned k = 0; k < j; k++) { -# if (perm[k] < perm[j]) { -# id -= 1; -# } -# } -# -# uint16_t rbyd.weight_before = rbyd.weight; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0; -# assert(rbyd.weight == rbyd.weight_before-1); -# } -# -# // check that all tags are now removed -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try resuming from all tags being removed -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, -# NULL)) => 0; -# assert(rbyd.weight == 1); -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 0, buffer, 6) -# => 6; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 1, buffer, 6) -# => LFS_ERR_NOENT; -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# -# // 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; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes -# lfs_size_t n = 1 + 2*N + 1; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); -# } -#''' -# -#[cases.test_rbyd_delete_all_range_permutations] -#defines.N = 'range(1, 7)' -#defines.M = 'range(1, 4)' -#in = 'lfs.c' -#if = ''' -# BLOCK_SIZE/PROG_SIZE >= N+N*M + N + 1+M -# && BLOCK_SIZE >= 4096 -#''' -#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 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[6]; -# -# // keep track of the worst case log size -# lfs_size_t worst_size = 0; -# -# // create one consistent block -# rbyd = init_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# -# for (unsigned j = 0; j < N; j++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, j, names[j % 6], 4, -# NULL)) => 0; -# // note uattrs have a smaller size to help debugging -# for (unsigned u = 0; u < M; u++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2, -# NULL)) => 0; -# } -# } -# assert(rbyd.weight == N); -# -# // copy block so we can reset after each delete -# lfsr_rbyd_t backup_rbyd = rbyd; -# uint8_t backup_block[BLOCK_SIZE]; -# lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, -# rbyd.block, 0, backup_block, rbyd.off) => 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) { -# // print permutation to help debugging -# printf("--- permutation: ["); -# for (unsigned j = 0; j < N; j++) { -# if (j > 0) { -# printf(", "); -# } -# printf("%d", perm[j]+1); -# } -# printf("] ---\n"); -# -# // restore backup -# rbyd = backup_rbyd; -# lfs_bd_erase(&lfs, rbyd.block) => 0; -# lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, -# rbyd.block, 0, backup_block, rbyd.off) => 0; -# lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; -# -# // delete each id in permutation order -# for (unsigned j = 0; j < N; j++) { -# // adjust id based on previous deletions -# uint16_t id = perm[j]; -# for (unsigned k = 0; k < j; k++) { -# if (perm[k] < perm[j]) { -# id -= 1; -# } -# } -# -# uint16_t rbyd.weight_before = rbyd.weight; -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0; -# assert(rbyd.weight == rbyd.weight_before-1); -# } -# -# // check that all tags are now removed -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 0); -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 0, buffer, 4) -# => LFS_ERR_NOENT; -# -# // try resuming from all tags being removed -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, -# NULL)) => 0; -# for (unsigned u = 0; u < M; u++) { -# lfsr_rbyd_commit(&lfs, &rbyd, -# LFSR_ATTR(UATTR(u+1), 0, "\xaa\xaa\xaa", 3, -# NULL)) => 0; -# } -# assert(rbyd.weight == 1); -# -# lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; -# assert(rbyd.weight == 1); -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 0, buffer, 6) -# => 6; -# assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); -# for (unsigned u = 0; u < M; u++) { -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_UATTR(u+1), 0, buffer, 6) -# => 3; -# assert(memcmp(buffer, "\xaa\xaa\xaa", 3) == 0); -# } -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_MKREG, 1, buffer, 6) -# => LFS_ERR_NOENT; -# lfsr_rbyd_get(&lfs, &rbyd, -# LFSR_TAG_UATTR(1), 1, buffer, 6) -# => LFS_ERR_NOENT; -# -# // keep track of the worst size -# worst_size = lfs_max(worst_size, rbyd.off); -# -# // 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; -# } -# } -# -# // test that tree is self-balancing, we should be strictly bounded -# // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes -# lfs_size_t n = 1 + N+N*M + N + 1+M; -# printf("worst size: %u B (N=%u, estimate=%u)\n", -# worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); -# printf("avg height: %u B (N=%u, estimate=%u)\n", -# worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); -# // note this only holds true with byte-level progs -# if (PROG_SIZE == 1) { -# assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); -# } -#''' -# +[cases.test_rbyd_delete_all] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +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; + uint8_t buffer[4]; + + // create and delete one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, NULL)) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete two ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete two ids in the other order + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete three ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete three ids in the other order + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, NULL)))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 2, NULL, 0, + LFSR_ATTR(RM, 1, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_delete_all_range] +in = 'lfs.c' +if = 'BLOCK_SIZE/PROG_SIZE >= 2' +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; + uint8_t buffer[4]; + + // create and delete one id + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, NULL))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, NULL)) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete two ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete two ids in the other order + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, NULL))))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 1, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete three ids + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // create and delete three ids in the other order + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa", 4, + LFSR_ATTR(UATTR(1), 0, "\xaa\xaa", 2, + LFSR_ATTR(MKREG, 1, "\xbb\xbb\xbb\xbb", 4, + LFSR_ATTR(UATTR(1), 1, "\xbb\xbb", 2, + LFSR_ATTR(MKREG, 2, "\xcc\xcc\xcc\xcc", 4, + LFSR_ATTR(UATTR(1), 2, "\xcc\xcc", 2, NULL))))))) => 0; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, 2, NULL, 0, + LFSR_ATTR(RM, 1, NULL, 0, + LFSR_ATTR(RM, 0, NULL, 0, NULL)))) => 0; + + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; +''' + +[cases.test_rbyd_delete_all_permutations] +defines.N = 'range(1, 7)' +in = 'lfs.c' +if = ''' + BLOCK_SIZE/PROG_SIZE >= 2*N+1 + && BLOCK_SIZE >= 1024 +''' +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 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[6]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // create one consistent block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, j, names[j % 6], 4, + NULL)) => 0; + } + assert(rbyd.weight == N); + + // copy block so we can reset after each delete + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 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) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // restore backup + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // delete each id in permutation order + for (unsigned j = 0; j < N; j++) { + // adjust id based on previous deletions + uint16_t id = perm[j]; + for (unsigned k = 0; k < j; k++) { + if (perm[k] < perm[j]) { + id -= 1; + } + } + + lfs_size_t rbyd_weight_before = rbyd.weight; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0; + assert(rbyd.weight == rbyd_weight_before-1); + } + + // check that all tags are now removed + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // try resuming from all tags being removed + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + assert(rbyd.weight == 1); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 0, buffer, 6) + => 6; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 1, buffer, 6) + => LFS_ERR_NOENT; + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // 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; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + 2*N + 1; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +[cases.test_rbyd_delete_all_range_permutations] +defines.N = 'range(1, 7)' +defines.M = 'range(1, 4)' +in = 'lfs.c' +if = ''' + BLOCK_SIZE/PROG_SIZE >= N+N*M + N + 1+M + && BLOCK_SIZE >= 4096 +''' +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 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[6]; + + // keep track of the worst case log size + lfs_size_t worst_size = 0; + + // create one consistent block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + for (unsigned j = 0; j < N; j++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, j, names[j % 6], 4, + NULL)) => 0; + // note uattrs have a smaller size to help debugging + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), j, names[j % 6], 2, + NULL)) => 0; + } + } + assert(rbyd.weight == N); + + // copy block so we can reset after each delete + lfsr_rbyd_t backup_rbyd = rbyd; + uint8_t backup_block[BLOCK_SIZE]; + lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.off, + rbyd.block, 0, backup_block, rbyd.off) => 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) { + // print permutation to help debugging + printf("--- permutation: ["); + for (unsigned j = 0; j < N; j++) { + if (j > 0) { + printf(", "); + } + printf("%d", perm[j]+1); + } + printf("] ---\n"); + + // restore backup + rbyd = backup_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, + rbyd.block, 0, backup_block, rbyd.off) => 0; + lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0; + + // delete each id in permutation order + for (unsigned j = 0; j < N; j++) { + // adjust id based on previous deletions + uint16_t id = perm[j]; + for (unsigned k = 0; k < j; k++) { + if (perm[k] < perm[j]) { + id -= 1; + } + } + + lfs_size_t rbyd_weight_before = rbyd.weight; + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RM, id, NULL, 0, NULL)) => 0; + assert(rbyd.weight == rbyd_weight_before-1); + } + + // check that all tags are now removed + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 0); + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 0, buffer, 4) + => LFS_ERR_NOENT; + + // try resuming from all tags being removed + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(MKREG, 0, "\xaa\xaa\xaa\xaa\xaa\xaa", 6, + NULL)) => 0; + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u+1), 0, "\xaa\xaa\xaa", 3, + NULL)) => 0; + } + assert(rbyd.weight == 1); + + lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.block, cfg->block_size, NULL) => 0; + assert(rbyd.weight == 1); + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 0, buffer, 6) + => 6; + assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); + for (unsigned u = 0; u < M; u++) { + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(u+1), 0, buffer, 6) + => 3; + assert(memcmp(buffer, "\xaa\xaa\xaa", 3) == 0); + } + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, 1, buffer, 6) + => LFS_ERR_NOENT; + lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(1), 1, buffer, 6) + => LFS_ERR_NOENT; + + // keep track of the worst size + worst_size = lfs_max(worst_size, rbyd.off); + + // 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; + } + } + + // test that tree is self-balancing, we should be strictly bounded + // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes + lfs_size_t n = 1 + N+N*M + N + 1+M; + printf("worst size: %u B (N=%u, estimate=%u)\n", + worst_size, n, 12*n*(2*lfs_nlog2(n)+1)); + printf("avg height: %u B (N=%u, estimate=%u)\n", + worst_size / n, n, 12*(2*lfs_nlog2(n)+1)); + // note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)); + } +''' + +# TODO rm me #[cases.test_rbyd_delete_create_permutations] #defines.N = 'range(1, 6)' #in = 'lfs.c'