From 361dfb0625a50e6a82564d62e4d9d5f09bdc7707 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 1 Mar 2023 14:20:15 -0600 Subject: [PATCH] Added more rbyd fuzz testing - Added test_rbyd_fuzz_mixed/test_rbyd_fuzz_sparse - Added test_rbyd_unwritten_mixed_fuzz/test_rbyd_unwritten_sparse_fuzz - Also renamed "random" tests to "fuzz", this describes their purpose a bit better These were a bit tricky to add since they need to simulate rbyd weights, but they should give significant coverage over complicated rbyd corner cases I may have not thought about. Also fixed a miscalculation in lfsr_rbyd_pendinglookup when finding a id that grew. Finding this bug is a good sign these tests are working. --- lfs.c | 2 +- tests/test_rbyd.toml | 1088 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1065 insertions(+), 25 deletions(-) diff --git a/lfs.c b/lfs.c index 7bcf94b6..b812088a 100644 --- a/lfs.c +++ b/lfs.c @@ -1663,9 +1663,9 @@ again:; // follow the upper edge of the grow } else if (attr->id <= id__ && attr->id+(lfs_ssize_t)attr->size > id__) { + id += attr->id+attr->size - id__; id__ = attr->id; tag = 0x10; - id += id__-attr->id+1; // adjust ids } else if (attr->id <= id__) { diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index 84b63524..3db4cc57 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -3798,7 +3798,7 @@ code = ''' # the main purpose of this test is to try to fuzz for failures in the # balancing algorithm -[cases.test_rbyd_random_append_removes] +[cases.test_rbyd_fuzz_append_removes] defines.N = 'range(1, 33)' defines.ITER = 1000 # large progs take too long for now @@ -8746,7 +8746,7 @@ code = ''' # the main purpose of this test is to try to fuzz for failures in the # balancing algorithm -[cases.test_rbyd_random_create_deletes] +[cases.test_rbyd_fuzz_create_deletes] defines.N = 'range(1, 33)' defines.ITER = 1000 # large progs take too long for now @@ -11392,6 +11392,585 @@ code = ''' ''' +# Some more fuzzish testing + +[cases.test_rbyd_fuzz_mixed] +defines.N = 'range(1, 33)' +defines.M = 3 +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]; + + // keep track of the worst case size and seed + lfs_size_t worst_size = 0; + uint32_t worst_seed = 0; + + // iterate through seeds so we can reproduce easily + for (uint32_t seed = 1; seed < ITER+1; seed++) { + printf("--- seed: %d ---\n", seed); + printf("perm: ["); + uint32_t prng = seed; + lfs_size_t count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%d=%c", id, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("a%d,%d=%c", id, u, alpha[i % 26]); + } else if (op == 3) { + printf("r%d,%d", id, u); + } + 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*(M+1)); + memset(sim, 0, N*(M+1)); + + // set up rbyd block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + prng = seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + // update our sim + memmove(sim+(id+1)*(M+1), sim+id*(M+1), (count-id)*(M+1)); + memset(&sim[id*(M+1)], 0, M+1); + sim[id*(M+1)] = alpha[i % 26]; + count += 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id, NULL, 1, + LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, + NULL))) => 0; + } else if (op == 1) { + // update our sim + memmove(sim+id*(M+1), sim+(id+1)*(M+1), (count-id-1)*(M+1)); + count -= 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id, NULL, 1, + NULL)) => 0; + } else if (op == 2) { + // update our sim + sim[id*(M+1) + u+1] = alpha[i % 26]; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1, + NULL)) => 0; + + } else if (op == 3) { + // update our sim + sim[id*(M+1) + u+1] = '\0'; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(u), id, NULL, 0, + NULL)) => 0; + } + } + + // compare rbyd vs simulation + printf("expd: ["); + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + printf("%c", sim[id*(M+1)]); + for (uint8_t u = 0; u < M; u++) { + if (sim[id*(M+1) + u+1]) { + printf("%c", sim[id*(M+1) + u+1]); + } else { + printf("_"); + } + } + if (id < (lfs_ssize_t)count-1) { + printf(", "); + } + } + printf("]\n"); + printf("rbyd: ["); + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)rbyd.weight; id++) { + lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_MKREG, id, buffer, 4); + if (size >= 0) { + printf("%.*s", size, buffer); + } else { + printf("?"); + } + for (uint8_t u = 0; u < M; u++) { + lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, + LFSR_TAG_UATTR(u), id, buffer, 4); + if (size >= 0) { + printf("%.*s", size, buffer); + } else { + printf("_"); + } + } + if (id < (lfs_ssize_t)count-1) { + printf(", "); + } + } + printf("]\n"); + + assert(count == rbyd.weight); + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, id, buffer, 4) => 1; + assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0); + } + + // keep track of the worst permutation + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_seed = seed; + } + + // cleanup + free(sim); + } + + // print the worst seed + size, and rerun it so it's left on the disk + // if used with -ddisk + printf("--- worst ---\n"); + printf("worst_seed: %d\n", worst_seed); + printf("worst_size: %d\n", worst_size); + printf("worst_perm: ["); + uint32_t prng = worst_seed; + lfs_size_t count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%d=%c", id, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("a%d,%d=%c", id, u, alpha[i % 26]); + } else if (op == 3) { + printf("r%d,%d", id, u); + } + if (i < N-1) { + printf(", "); + } + } + printf("]\n"); + + // set up rbyd block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + prng = worst_seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + count += 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id, NULL, 1, + LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, + NULL))) => 0; + } else if (op == 1) { + count -= 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id, NULL, 1, + NULL)) => 0; + } else if (op == 2) { + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1, + NULL)) => 0; + + } else if (op == 3) { + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(u), id, NULL, 0, + NULL)) => 0; + } + } + + // our tree should be strictly <= 2*log(n)+1, assume tags are strictly + // <=12 bytes, note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1); + } +''' + +[cases.test_rbyd_fuzz_sparse] +defines.N = 'range(1, 33)' +defines.W = 5 +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; + lfsr_tag_t tag_; + lfs_ssize_t id_; + lfs_size_t weight_; + lfs_off_t off_; + lfs_size_t size_; + const char *alpha = "abcdefghijklmnopqrstuvwxyz"; + uint8_t buffer[4]; + + // keep track of the worst case size and seed + lfs_size_t worst_size = 0; + uint32_t worst_seed = 0; + + // iterate through seeds so we can reproduce easily + for (uint32_t seed = 1; seed < ITER+1; seed++) { + printf("--- seed: %d ---\n", seed); + printf("perm: ["); + uint32_t prng = seed; + lfs_size_t count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%dw%d=%c", id, weight, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("g%dw%d", id, weight); + } else if (op == 3) { + printf("s%dw%d", id, weight); + } + 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); + lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); + memset(sim, 0, N); + memset(sim_weights, 0, N*sizeof(lfs_size_t)); + + // set up rbyd block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + prng = seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + if (id == (lfs_ssize_t)count || op == 0) { + // update our sim + memmove(sim+id+1, sim+id, count-id); + memmove(sim_weights+id+1, sim_weights+id, + (count-id)*sizeof(lfs_size_t)); + sim[id] = alpha[i % 26]; + sim_weights[id] = weight; + count += 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + LFSR_ATTR(MKREG, id__+weight-1, &alpha[i % 26], 1, + NULL))) => 0; + } else if (op == 1) { + // get the correct weight from the sim + weight = sim_weights[id]; + // update our sim + memmove(sim+id, sim+id+1, count-id-1); + memmove(sim_weights+id, sim_weights+id+1, + (count-id-1)*sizeof(lfs_size_t)); + count -= 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } else if (op == 2) { + // update our sim + sim_weights[id] += weight; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + NULL)) => 0; + } else if (op == 3) { + // don't let shrink go to zero here! this is already hard enough + // to simulate + weight = lfs_min(weight, sim_weights[id]-1); + // update our sim + sim_weights[id] -= weight; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } + } + + // compare rbyd vs simulation + printf("expd: ["); + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + printf("%cw%d", sim[id], sim_weights[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++) { + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + int err = lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_MKREG, id__, + &tag_, &id_, &weight_, &off_, &size_); + if (!err) { + lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, + tag_, id_, buffer, 4); + if (size >= 0) { + printf("%.*sw%d", size, buffer, weight_); + } else { + printf("?"); + } + } else { + printf("?"); + } + if (id < (lfs_ssize_t)count-1) { + printf(", "); + } + } + printf("]\n"); + + // calculate total weight + lfs_size_t total_weight = 0; + for (lfs_ssize_t j = 0; j < (lfs_ssize_t)count; j++) { + total_weight += sim_weights[j]; + } + assert(total_weight == rbyd.weight); + + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + lfsr_rbyd_lookup(&lfs, &rbyd, + LFSR_TAG_MKREG, id__, + &tag_, &id_, &weight_, &off_, &size_) => 0; + lfsr_rbyd_get(&lfs, &rbyd, tag_, id_, buffer, 4) => 1; + assert(memcmp(&sim[id], buffer, 1) == 0); + } + + // keep track of the worst permutation + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_seed = seed; + } + + // cleanup + free(sim); + free(sim_weights); + } + + // print the worst seed + size, and rerun it so it's left on the disk + // if used with -ddisk + printf("--- worst ---\n"); + printf("worst_seed: %d\n", worst_seed); + printf("worst_size: %d\n", worst_size); + printf("worst_perm: ["); + uint32_t prng = worst_seed; + lfs_size_t count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%dw%d=%c", id, weight, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("g%dw%d", id, weight); + } else if (op == 3) { + printf("s%dw%d", id, weight); + } + if (i < N-1) { + printf(", "); + } + } + printf("]\n"); + + // note we still need sim here, since we use it to calculate shrink limits + + // set up a simulation to compare against, fun fact this performs + // worst than our actual rbyd block! + char *sim = malloc(N); + lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); + memset(sim, 0, N); + memset(sim_weights, 0, N*sizeof(lfs_size_t)); + + // set up rbyd block + rbyd = init_rbyd; + lfs_bd_erase(&lfs, rbyd.block) => 0; + + prng = worst_seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + if (id == (lfs_ssize_t)count || op == 0) { + // update our sim + memmove(sim+id+1, sim+id, count-id); + memmove(sim_weights+id+1, sim_weights+id, + (count-id)*sizeof(lfs_size_t)); + sim[id] = alpha[i % 26]; + sim_weights[id] = weight; + count += 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + LFSR_ATTR(MKREG, id__+weight-1, &alpha[i % 26], 1, + NULL))) => 0; + } else if (op == 1) { + // get the correct weight from the sim + weight = sim_weights[id]; + // update our sim + memmove(sim+id, sim+id+1, count-id-1); + memmove(sim_weights+id, sim_weights+id+1, + (count-id-1)*sizeof(lfs_size_t)); + count -= 1; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } else if (op == 2) { + // update our sim + sim_weights[id] += weight; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + NULL)) => 0; + } else if (op == 3) { + // don't let shrink go to zero here! this is already hard enough + // to simulate + weight = lfs_min(weight, sim_weights[id]-1); + // update our sim + sim_weights[id] -= weight; + // update our rbyd + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } + } + + // cleanup + free(sim); + free(sim_weights); + + // our tree should be strictly <= 2*log(n)+1, assume tags are strictly + // <=12 bytes, note this only holds true with byte-level progs + if (PROG_SIZE == 1) { + assert(worst_size <= 2*N*12*(2*lfs_nlog2(N)+1)+1); + } +''' + + + ## Test unwritten attributes [cases.test_rbyd_unwritten_permutations] @@ -11504,7 +12083,7 @@ code = ''' } ''' -[cases.test_rbyd_unwritten_random] +[cases.test_rbyd_unwritten_fuzz] defines.N = 'range(1, 13)' defines.ITER = 1000 # large progs take too long for now @@ -11560,7 +12139,7 @@ code = ''' // set up our unwritten attr list struct lfsr_attr *attrs = malloc(N*sizeof(struct lfsr_attr)); - unsigned j = 0; + unsigned a = 0; prng = seed; for (unsigned i = 0; i < N; i++) { @@ -11577,13 +12156,13 @@ code = ''' NULL)) => 0; // append to our unwritten attrs } else { - if (j > 0) { - attrs[j-1].next = &attrs[j]; + if (a > 0) { + attrs[a-1].next = &attrs[a]; } - attrs[j] = *LFSR_ATTR( + attrs[a] = *LFSR_ATTR( UATTR(attr), -1, &alpha[i % 26], 1, NULL); - j += 1; + a += 1; } } else { // update our sim @@ -11595,13 +12174,13 @@ code = ''' NULL)) => 0; // append to our unwritten attrs } else { - if (j > 0) { - attrs[j-1].next = &attrs[j]; + if (a > 0) { + attrs[a-1].next = &attrs[a]; } - attrs[j] = *LFSR_ATTR( + attrs[a] = *LFSR_ATTR( RMUATTR(attr), -1, NULL, 0, NULL); - j += 1; + a += 1; } } } @@ -11781,7 +12360,7 @@ code = ''' } ''' -[cases.test_rbyd_unwritten_create_random] +[cases.test_rbyd_unwritten_create_fuzz] defines.N = 'range(1, 13)' defines.ITER = 1000 # large progs take too long for now @@ -11841,7 +12420,7 @@ code = ''' // set up attr list struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr)); - unsigned j = 0; + unsigned a = 0; prng = seed; count = 0; @@ -11861,16 +12440,16 @@ code = ''' LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, NULL))) => 0; } else { - if (j > 0) { - attrs[j-1].next = &attrs[j]; + if (a > 0) { + attrs[a-1].next = &attrs[a]; } - attrs[j] = *LFSR_ATTR( + attrs[a] = *LFSR_ATTR( GROW, id, NULL, 1, - &attrs[j+1]); - attrs[j+1] = *LFSR_ATTR( + &attrs[a+1]); + attrs[a+1] = *LFSR_ATTR( MKREG, id, &alpha[i % 26], 1, NULL); - j += 2; + a += 2; } } else { // update our sim @@ -11882,13 +12461,13 @@ code = ''' LFSR_ATTR(SHRINK, id, NULL, 1, NULL)) => 0; } else { - if (j > 0) { - attrs[j-1].next = &attrs[j]; + if (a > 0) { + attrs[a-1].next = &attrs[a]; } - attrs[j] = *LFSR_ATTR( + attrs[a] = *LFSR_ATTR( SHRINK, id, NULL, 1, NULL); - j += 1; + a += 1; } } } @@ -12105,6 +12684,222 @@ code = ''' } ''' +[cases.test_rbyd_unwritten_mixed_fuzz] +defines.N = 'range(1, 13)' +defines.M = 3 +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 create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%d=%c", id, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("a%d,%d=%c", id, u, alpha[i % 26]); + } else if (op == 3) { + printf("r%d,%d", id, u); + } + 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*(M+1)); + memset(sim, 0, N*(M+1)); + + // 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 a = 0; + + prng = seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete or attr append/remove + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose an attr + uint8_t u = TEST_PRNG(&prng) % M; + + if (id == (lfs_ssize_t)count || op == 0) { + // update our sim + memmove(sim+(id+1)*(M+1), sim+id*(M+1), (count-id)*(M+1)); + memset(&sim[id*(M+1)], 0, M+1); + sim[id*(M+1)] = 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 (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + GROW, id, NULL, 1, + &attrs[a+1]); + attrs[a+1] = *LFSR_ATTR( + MKREG, id, &alpha[i % 26], 1, + NULL); + a += 2; + } + } else if (op == 1) { + // update our sim + memmove(sim+id*(M+1), sim+(id+1)*(M+1), (count-id-1)*(M+1)); + count -= 1; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id, NULL, 1, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + SHRINK, id, NULL, 1, + NULL); + a += 1; + } + } else if (op == 2) { + // update our sim + sim[id*(M+1) + u+1] = alpha[i % 26]; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + UATTR(u), id, &alpha[i % 26], 1, + NULL); + a += 1; + } + } else if (op == 3) { + // update our sim + sim[id*(M+1) + u+1] = '\0'; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(RMUATTR(u), id, NULL, 0, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + RMUATTR(u), id, NULL, 0, + NULL); + a += 1; + } + } + } + const struct lfsr_attr *unwritten = w < N ? attrs : NULL; + + // compare rbyd vs simulation + printf("expd: ["); + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + printf("%c", sim[id*(M+1)]); + for (uint8_t u = 0; u < M; u++) { + if (sim[id*(M+1) + u+1]) { + printf("%c", sim[id*(M+1) + u+1]); + } else { + printf("_"); + } + } + 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, unwritten, + LFSR_TAG_MKREG, id, buffer, 4); + if (size >= 0) { + printf("%.*s", size, buffer); + } else { + printf("?"); + } + for (uint8_t u = 0; u < M; u++) { + lfs_ssize_t size = lfsr_rbyd_pendingget( + &lfs, &rbyd, unwritten, + LFSR_TAG_UATTR(u), 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, unwritten, + LFSR_TAG_MKREG, id, buffer, 4) => 1; + assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0); + } + + // cleanup + free(sim); + free(attrs); + } + } +''' + [cases.test_rbyd_unwritten_sparse_permutations] defines.N = 'range(1, 7)' defines.W = 5 @@ -12233,3 +13028,248 @@ code = ''' } } ''' + +[cases.test_rbyd_unwritten_sparse_fuzz] +defines.N = 'range(1, 13)' +defines.W = 5 +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; + lfsr_tag_t tag_; + lfs_ssize_t id_; + lfsr_data_t data_; + 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 create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + if (id == (lfs_ssize_t)count || op == 0) { + printf("c%dw%d=%c", id, weight, alpha[i % 26]); + count += 1; + } else if (op == 1) { + printf("d%d", id); + count -= 1; + } else if (op == 2) { + printf("g%dw%d", id, weight); + } else if (op == 3) { + printf("s%dw%d", id, weight); + } + 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); + lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); + memset(sim, 0, N); + memset(sim_weights, 0, N*sizeof(lfs_size_t)); + + // 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 a = 0; + + prng = seed; + count = 0; + for (unsigned i = 0; i < N; i++) { + // choose create/delete/grow/shrink + uint8_t op = TEST_PRNG(&prng) % 4; + // choose an id + lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); + // choose a weight + lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); + + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + if (id == (lfs_ssize_t)count || op == 0) { + // update our sim + memmove(sim+id+1, sim+id, count-id); + memmove(sim_weights+id+1, sim_weights+id, + (count-id)*sizeof(lfs_size_t)); + sim[id] = alpha[i % 26]; + sim_weights[id] = weight; + count += 1; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + LFSR_ATTR(MKREG, id__+weight-1, + &alpha[i % 26], 1, + NULL))) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + GROW, id__, NULL, weight, + &attrs[a+1]); + attrs[a+1] = *LFSR_ATTR( + MKREG, id__+weight-1, &alpha[i % 26], 1, + NULL); + a += 2; + } + } else if (op == 1) { + // get the correct weight from the sim + weight = sim_weights[id]; + // update our sim + memmove(sim+id, sim+id+1, count-id-1); + memmove(sim_weights+id, sim_weights+id+1, + (count-id-1)*sizeof(lfs_size_t)); + count -= 1; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + SHRINK, id__, NULL, weight, + NULL); + a += 1; + } + } else if (op == 2) { + // update our sim + sim_weights[id] += weight; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(GROW, id__, NULL, weight, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + GROW, id__, NULL, weight, + NULL); + a += 1; + } + } else if (op == 3) { + // don't let shrink go to zero here! this is already hard + // enough to simulate + weight = lfs_min(weight, sim_weights[id]-1); + // update our sim + sim_weights[id] -= weight; + // update our rbyd + if (i < w) { + lfsr_rbyd_commit(&lfs, &rbyd, + LFSR_ATTR(SHRINK, id__, NULL, weight, + NULL)) => 0; + } else { + if (a > 0) { + attrs[a-1].next = &attrs[a]; + } + attrs[a] = *LFSR_ATTR( + SHRINK, id__, NULL, weight, + NULL); + a += 1; + } + } + } + const struct lfsr_attr *unwritten = w < N ? attrs : NULL; + + + // 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++) { + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + int err = lfsr_rbyd_pendinglookup( + &lfs, &rbyd, unwritten, + LFSR_TAG_MKREG, id__, + &tag_, &id_, &data_); + if (!err) { + lfs_ssize_t size = lfsr_rbyd_pendingget( + &lfs, &rbyd, unwritten, + tag_, id_, buffer, 4); + if (size >= 0) { + printf("%.*s", size, buffer); + } else { + printf("?"); + } + } else { + printf("?"); + } + if (id < (lfs_ssize_t)count-1) { + printf(", "); + } + } + printf("]\n"); + + for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { + // calculate actual id in rbyd space + lfs_ssize_t id__ = 0; + for (lfs_ssize_t j = 0; j < id; j++) { + id__ += sim_weights[j]; + } + + lfsr_rbyd_pendinglookup(&lfs, &rbyd, unwritten, + LFSR_TAG_MKREG, id__, + &tag_, &id_, &data_) => 0; + lfsr_rbyd_pendingget(&lfs, &rbyd, unwritten, + tag_, id_, buffer, 4) => 1; + assert(memcmp(&sim[id], buffer, 1) == 0); + } + + // cleanup + free(sim); + free(sim_weights); + free(attrs); + } + } +'''