# maximize lookahead buffer, we don't actually gc so we only get one pass # of the disk for these tests defines.LOOKAHEAD_SIZE = 'lfs_alignup(BLOCK_COUNT / 8, 8)' # test an empty tree [cases.test_btree_zero] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create an empty tree lfsr_btree_t btree = LFSR_BTREE_NULL; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 0); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' # test an inlined tree [cases.test_btree_one] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' # test a single-rbyd tree [cases.test_btree_two] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_two_backwards] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' # still a single-rbyd tree, just making sure it works [cases.test_btree_three] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("c", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "c", 1) == 0); lfsr_btree_get(&lfs, &btree, 3, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_three_backwards] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("c", 1)) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "c", 1) == 0); lfsr_btree_get(&lfs, &btree, 3, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' # try larger trees, when exactly a tree splits depends on the disk geometry, so # we don't really have a better way of testing multi-rbyd trees [cases.test_btree_push] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_size_t n = 0; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, n, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_push_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_size_t n = 0; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[(N-1-i) % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_get(&lfs, &btree, n-1-i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[(N-1-i) % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, n, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_push_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t sim_size = 0; memset(sim, 0, N); uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1); // add to btree int err = lfsr_btree_push(&lfs, &btree, id, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // add to sim memmove(&sim[id+1], &sim[id], sim_size-id); sim[id] = alphas[i % 26]; sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%c", sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, sim_size, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); lfs_deinit(&lfs) => 0; } ''' [cases.test_btree_push_sparse] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_size_t n = 0; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n*W); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_get(&lfs, &btree, i*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, n*W, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == i*W+W-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_push_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 0; memset(sim, 0, N); memset(sim_weights, 0, N*sizeof(lfs_size_t)); uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1); // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // add to btree int err = lfsr_btree_push(&lfs, &btree, weighted_id, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // add to sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_weights[id+1], &sim_weights[id], (sim_size-id)*sizeof(lfs_size_t)); sim[id] = alphas[i % 26]; sim_weights[id] = weight; sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%dw%d=%c", weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, total_weight, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == weighted_id+sim_weights[i]-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' # test btree updates # try some small trees for easy corner cases first [cases.test_btree_update_one] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; // update the tree lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("A", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "A", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_update_two] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; // update the tree lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("A", 1)) => 0; lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("B", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "A", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "B", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_update_three] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("c", 1)) => 0; // update the tree lfsr_btree_update(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("A", 1)) => 0; lfsr_btree_update(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("B", 1)) => 0; lfsr_btree_update(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("C", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "A", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "B", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "C", 1) == 0); lfsr_btree_get(&lfs, &btree, 3, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_update] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // update the tree for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_update(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == N); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < N; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &uppers[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, N, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_update_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); for (lfs_size_t i = 0; i < N; i++) { sim[i] = alphas[i % 26]; } uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % N; // update btree int err = lfsr_btree_update(&lfs, &btree, id, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = uppers[i % 26]; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < N; i++) { if (!first) { printf(", "); } first = false; printf("%c", sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == N); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < N; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, N, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' [cases.test_btree_update_sparse] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // update the tree for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_update(&lfs, &btree, i*W+W-1, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == N*W); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < N; i++) { lfsr_btree_get(&lfs, &btree, i*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, &uppers[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, N*W, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < N; i++) { lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == i*W+W-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &uppers[i % 26], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_update_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); for (lfs_size_t i = 0; i < N; i++) { sim[i] = alphas[i % 26]; sim_weights[i] = W; } uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % N; // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // update btree int err = lfsr_btree_update(&lfs, &btree, weighted_id+sim_weights[id]-1, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = uppers[i % 26]; sim_weights[id] = weight; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < N; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%dw%d=%c", weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < N; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < N; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, total_weight, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < N; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == weighted_id+sim_weights[i]-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' # test btree pops # try some corner cases first, these are actually pretty tricky since we # need to recognize when to collapse back into an inlined tree [cases.test_btree_pop_one] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; // pop! lfsr_btree_pop(&lfs, &btree, 0) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 0); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try to putting it back to see if things still work lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("A", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try looking up tags lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "A", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_two] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; // pop! lfsr_btree_pop(&lfs, &btree, 1) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try to putting it back to see if things still work lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("B", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "B", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_two_other] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; // pop! lfsr_btree_pop(&lfs, &btree, 0) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try to putting it back to see if things still work lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("A", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "A", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_three] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("a", 1)) => 0; lfsr_btree_push(&lfs, &btree, 1, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("b", 1)) => 0; lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("c", 1)) => 0; // pop! lfsr_btree_pop(&lfs, &btree, 2) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try looking up tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try to putting it back to see if things still work lfsr_btree_push(&lfs, &btree, 2, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("C", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try looking up tags lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "a", 1) == 0); lfsr_btree_get(&lfs, &btree, 1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "b", 1) == 0); lfsr_btree_get(&lfs, &btree, 2, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "C", 1) == 0); lfsr_btree_get(&lfs, &btree, 3, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.REMAINING = [64, 2, 1, 0] defines.VALIDATE = [1, 0] if = 'N > REMAINING' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // drain the tree for (lfs_size_t i = 0; i < N-REMAINING; i++) { int err = lfsr_btree_pop(&lfs, &btree, N-1-i); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == REMAINING); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, REMAINING, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try recovering lfsr_btree_push(&lfs, &btree, REMAINING, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("R", 1)) => 0; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } lfsr_btree_get(&lfs, &btree, REMAINING, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "R", 1) == 0); lfsr_btree_get(&lfs, &btree, REMAINING+1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.REMAINING = [64, 2, 1, 0] defines.VALIDATE = [1, 0] if = 'N > REMAINING' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // drain the tree for (lfs_size_t i = 0; i < N-REMAINING; i++) { int err = lfsr_btree_pop(&lfs, &btree, 0); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == REMAINING); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[(i+(N-REMAINING)) % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, REMAINING, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try recovering lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("R", 1)) => 0; lfsr_btree_get(&lfs, &btree, 0, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, "R", 1) == 0); for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i+1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[(i+(N-REMAINING)) % 26], 1) == 0); } lfsr_btree_get(&lfs, &btree, REMAINING+1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMAINING = [64, 2, 1, 0] defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 if = 'N > REMAINING' in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t sim_size = N; for (lfs_size_t i = 0; i < N; i++) { sim[i] = alphas[i % 26]; } uint32_t prng = seed; for (lfs_size_t i = 0; i < (N-REMAINING); i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % sim_size; // remove from btree int err = lfsr_btree_pop(&lfs, &btree, id); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // remove from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); sim_size -= 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%c", sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, sim_size, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' [cases.test_btree_pop_sparse] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.REMAINING = [64, 2, 1, 0] defines.VALIDATE = [1, 0] if = 'N > REMAINING' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; for (lfs_size_t i = 0; i < N; i++) { int err = lfsr_btree_push(&lfs, &btree, i*W, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } // drain the tree for (lfs_size_t i = 0; i < N-REMAINING; i++) { int err = lfsr_btree_pop(&lfs, &btree, (N-1-i)*W+W-1); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == REMAINING*W); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, REMAINING*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // try recovering lfsr_btree_push(&lfs, &btree, REMAINING*W, LFSR_TAG_INLINED, W, LFSR_DATA_BUF("R", 1)) => 0; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_get(&lfs, &btree, i*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } lfsr_btree_get(&lfs, &btree, REMAINING*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, "R", 1) == 0); lfsr_btree_get(&lfs, &btree, (REMAINING+1)*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < REMAINING; i++) { lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == i*W+W-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == REMAINING*W+W-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "R", 1) == 0); lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_pop_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 defines.REMAINING = [64, 2, 1, 0] defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 if = 'N > REMAINING' in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 0; // set up simulation and btree with pseudo-random weights uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } int err = lfsr_btree_push(&lfs, &btree, weighted_id, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); return; } assert(err == 0); sim[i] = alphas[i % 26]; sim_weights[i] = weight; sim_size += 1; } for (lfs_size_t i = 0; i < (N-REMAINING); i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % sim_size; // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // remove from btree int err = lfsr_btree_pop(&lfs, &btree, weighted_id+sim_weights[id]-1); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // remove from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); memmove(&sim_weights[id], &sim_weights[id+1], (sim_size-(id+1))*sizeof(lfs_size_t)); sim_size -= 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%dw%d=%c", weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, total_weight, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == weighted_id+sim_weights[i]-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' # test btree splits [cases.test_btree_split] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[0 % 26], 1)) => 0; lfs_size_t n = 1; for (lfs_size_t i = 1; i < N; i++) { int err = lfsr_btree_split(&lfs, &btree, i-1, LFSR_DATA_NULL, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[(i-1) % 26], 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[(i-0) % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, n, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_split_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t sim_size = 1; memset(sim, 0, N); sim[0] = '_'; uint32_t prng = seed; for (lfs_size_t i = 1; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % sim_size; // split btree int err = lfsr_btree_split(&lfs, &btree, id, LFSR_DATA_NULL, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // split sim memmove(&sim[id+1], &sim[id], sim_size-id); sim[id+0] = alphas[i % 26]; sim[id+1] = uppers[i % 26]; sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%c", sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, sim_size, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); lfs_deinit(&lfs) => 0; } ''' [cases.test_btree_split_sparse] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.VALIDATE = [1, 0] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[0 % 26], 1)) => 0; lfs_size_t n = 1; for (lfs_size_t i = 1; i < N; i++) { int err = lfsr_btree_split(&lfs, &btree, (i-1)*W+W-1, LFSR_DATA_NULL, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[(i-1) % 26], 1), LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&alphas[(i-0) % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n*W); // check that the elements are in the tree uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < n; i++) { lfsr_btree_get(&lfs, &btree, i*W+W-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == W); assert(memcmp(buffer, &alphas[i % 26], 1) == 0); } // and check that we can't lookup elements that aren't in the tree lfsr_btree_get(&lfs, &btree, n*W, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; ''' [cases.test_btree_split_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 defines.VALIDATE = [1, 0] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, W, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 1; memset(sim, 0, N); memset(sim_weights, 0, N*sizeof(lfs_size_t)); sim[0] = '_'; sim_weights[0] = W; uint32_t prng = seed; for (lfs_size_t i = 1; i < N; i++) { // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % sim_size; // choose pseudo-random weights lfs_size_t weight1 = 1 + (TEST_PRNG(&prng) % W); lfs_size_t weight2 = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // split btree int err = lfsr_btree_split(&lfs, &btree, weighted_id+sim_weights[id]-1, LFSR_DATA_NULL, LFSR_TAG_INLINED, weight1, LFSR_DATA_BUF(&alphas[i % 26], 1), LFSR_TAG_INLINED, weight2, LFSR_DATA_BUF(&uppers[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // add to sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_weights[id+1], &sim_weights[id], (sim_size-id)*sizeof(lfs_size_t)); sim[id+0] = alphas[i % 26]; sim[id+1] = uppers[i % 26]; sim_weights[id+0] = weight1; sim_weights[id+1] = weight2; sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%dw%d=%c", weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, total_weight, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == weighted_id+sim_weights[i]-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' # Some more general fuzz testing [cases.test_btree_general_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.VALIDATE = [1, 0] defines.SAMPLES = 100 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t sim_size = 0; memset(sim, 0, N); uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random op uint8_t op = TEST_PRNG(&prng) % 3; // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1); if (op == 0 || id == sim_size) { // push to btree int err = lfsr_btree_push(&lfs, &btree, id, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // push to sim memmove(&sim[id+1], &sim[id], sim_size-id); sim[id] = alphas[i % 26]; sim_size += 1; } else if (op == 1) { // update btree int err = lfsr_btree_update(&lfs, &btree, id, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = alphas[i % 26]; } else { // pop from btree int err = lfsr_btree_pop(&lfs, &btree, id); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // pop from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); sim_size -= 1; } } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%c", sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_get(&lfs, &btree, i, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == 1); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, sim_size, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' [cases.test_btree_general_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.VALIDATE = [1, 0] defines.SAMPLES = 100 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 0; memset(sim, 0, N); memset(sim_weights, 0, N*sizeof(lfs_size_t)); uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random op uint8_t op = TEST_PRNG(&prng) % 3; // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size+1); // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } if (op == 0 || id == sim_size) { // push to btree int err = lfsr_btree_push(&lfs, &btree, weighted_id, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // push to sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_weights[id+1], &sim_weights[id], (sim_size-id)*sizeof(lfs_size_t)); sim[id] = alphas[i % 26]; sim_weights[id] = weight; sim_size += 1; } else if (op == 1) { // update btree int err = lfsr_btree_update(&lfs, &btree, weighted_id+sim_weights[id]-1, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&alphas[i % 26], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = alphas[i % 26]; sim_weights[id] = weight; } else { // remove from btree int err = lfsr_btree_pop(&lfs, &btree, weighted_id+sim_weights[id]-1); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // remove from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); memmove(&sim_weights[id], &sim_weights[id+1], (sim_size-(id+1))*sizeof(lfs_size_t)); sim_size -= 1; } } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%dw%d=%c", weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t weight_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_get(&lfs, &btree, weighted_id+sim_weights[i]-1, &tag_, &weight_, buffer, 4, VALIDATE) => 1; assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); assert(memcmp(buffer, &sim[i], 1) == 0); } // and no extra elements lfsr_btree_get(&lfs, &btree, total_weight, &tag_, &weight_, buffer, 4, VALIDATE) => LFS_ERR_NOENT; // also test that we can traverse the tree without prior knowledge lfs_size_t id_ = -1; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => 0; assert(id_ == weighted_id+sim_weights[i]-1); assert(tag_ == LFSR_TAG_INLINED); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } lfsr_btree_lookupnext(&lfs, &btree, id_+1, &id_, &tag_, &weight_, &data_, VALIDATE) => LFS_ERR_NOENT; // clean up sim free(sim); } ''' # test key-value btrees [cases.test_btree_find_zero] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a zero-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 0); // try to find tags lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; lfsr_btree_namelookupnext(&lfs, &btree, "aaa", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => LFS_ERR_NOENT; ''' [cases.test_btree_find_one] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a single-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 1); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; lfsr_btree_namelookupnext(&lfs, &btree, "aaa", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 0); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "0", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aab", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 0); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "0", 1) == 0); ''' [cases.test_btree_find_two] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1)) => 0; lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_BUF("aab", 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("1", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 2); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; lfsr_btree_namelookupnext(&lfs, &btree, "aaa", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 0); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "0", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aab", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 1); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "1", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aac", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 1); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "1", 1) == 0); ''' [cases.test_btree_find_three] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1)) => 0; lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_BUF("aab", 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("1", 1)) => 0; lfsr_btree_split(&lfs, &btree, 1, LFSR_DATA_BUF("aac", 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("1", 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("2", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; lfsr_btree_namelookupnext(&lfs, &btree, "aaa", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 0); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "0", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aab", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 1); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "1", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aac", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 2); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "2", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aad", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 2); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "2", 1) == 0); ''' [cases.test_btree_find_three_backwards] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a two-entry tree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1)) => 0; lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_BUF("aac", 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("1", 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("2", 1)) => 0; lfsr_btree_split(&lfs, &btree, 0, LFSR_DATA_BUF("aab", 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("0", 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("1", 1)) => 0; printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == 3); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; lfsr_btree_namelookupnext(&lfs, &btree, "aaa", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 0); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "0", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aab", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 1); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "1", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aac", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 2); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "2", 1) == 0); lfsr_btree_namelookupnext(&lfs, &btree, "aad", 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == 2); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, "2", 1) == 0); ''' [cases.test_btree_find] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[0 % 10], 1)) => 0; lfs_size_t n = 1; for (lfs_size_t i = 1; i < N; i++) { char name[3] = { alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26] }; int err = lfsr_btree_split(&lfs, &btree, i-1, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[(i-1) % 10], 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[(i-0) % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < n; i++) { char name[3] = { alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26] }; lfsr_btree_namelookupnext(&lfs, &btree, name, 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == i); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &nums[i % 10], 1) == 0); } ''' [cases.test_btree_find_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); char (*sim_names)[3] = malloc(N*3); lfs_size_t sim_size = 1; memset(sim, 0, N); memset(sim_names, 0, N*3); sim[0] = '_'; memcpy(&sim_names[0], "___", 3); uint32_t prng = seed; for (lfs_size_t i = 1; i < N; i++) { // choose a pseudo-random name lfs_size_t x = TEST_PRNG(&prng) % (26*26*26); char name[3] = { alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26] }; // find where to split lfs_size_t id = 0; while (id+1 < sim_size && memcmp(sim_names[id+1], name, 3) <= 0) { id += 1; } // just skip exact matches for now if (memcmp(sim_names[id], name, 3) == 0) { continue; } // split btree int err = lfsr_btree_split(&lfs, &btree, id, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[i % 10], 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // split sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_names[id+1], &sim_names[id], (sim_size-id)*3); sim[id+0] = nums[i % 10]; sim[id+1] = nums[i % 10]; memcpy(&sim_names[id+1], name, 3); sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%.3s=%c", sim_names[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_namelookupnext(&lfs, &btree, sim_names[i], 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == i); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } // clean up sim free(sim); free(sim_names); lfs_deinit(&lfs) => 0; } ''' [cases.test_btree_find_sparse] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a tree with N elements lfsr_btree_t btree = LFSR_BTREE_NULL; const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&nums[0 % 10], 1)) => 0; lfs_size_t n = 1; for (lfs_size_t i = 1; i < N; i++) { char name[3] = { alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26] }; int err = lfsr_btree_split(&lfs, &btree, (i-1)*W+W-1, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&nums[(i-1) % 10], 1), LFSR_TAG_INLINED, W, LFSR_DATA_BUF(&nums[(i-0) % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); n += 1; } printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == n*W); // try to find tags uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < n; i++) { char name[3] = { alphas[(i/26/26) % 26], alphas[(i/26) % 26], alphas[i % 26] }; lfsr_btree_namelookupnext(&lfs, &btree, name, 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == i*W+W-1); assert(weight_ == W); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &nums[i % 10], 1) == 0); } ''' [cases.test_btree_find_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 defines.SAMPLES = 10 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, W, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); char (*sim_names)[3] = malloc(N*3); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 1; memset(sim, 0, N); memset(sim_names, 0, N*3); memset(sim_weights, 0, N*sizeof(lfs_size_t)); sim[0] = '_'; memcpy(&sim_names[0], "___", 3); sim_weights[0] = W; uint32_t prng = seed; for (lfs_size_t i = 1; i < N; i++) { // choose a pseudo-random name lfs_size_t x = TEST_PRNG(&prng) % (26*26*26); char name[3] = { alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26] }; // choose pseudo-random weights lfs_size_t weight1 = 1 + (TEST_PRNG(&prng) % W); lfs_size_t weight2 = 1 + (TEST_PRNG(&prng) % W); // find where to split lfs_size_t id = 0; while (id+1 < sim_size && memcmp(sim_names[id+1], name, 3) <= 0) { id += 1; } // just skip exact matches for now if (memcmp(sim_names[id], name, 3) == 0) { continue; } // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // split btree int err = lfsr_btree_split(&lfs, &btree, weighted_id+sim_weights[id]-1, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, weight1, LFSR_DATA_BUF(&nums[i % 10], 1), LFSR_TAG_INLINED, weight2, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // split sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_names[id+1], &sim_names[id], (sim_size-id)*3); memmove(&sim_weights[id+1], &sim_weights[id], (sim_size-id)*sizeof(lfs_size_t)); sim[id+0] = nums[i % 10]; sim[id+1] = nums[i % 10]; memcpy(&sim_names[id+1], name, 3); sim_weights[id+0] = weight1; sim_weights[id+1] = weight2; sim_size += 1; } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%.3sid%dw%d=%c", sim_names[i], weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_namelookupnext(&lfs, &btree, sim_names[i], 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == weighted_id+sim_weights[i]-1); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } // clean up sim free(sim); free(sim_names); free(sim_weights); lfs_deinit(&lfs) => 0; } ''' # make sure we test finds with other operations [cases.test_btree_find_general_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.SAMPLES = 100 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); char (*sim_names)[3] = malloc(N*3); lfs_size_t sim_size = 1; memset(sim, 0, N); memset(sim_names, 0, N*3); sim[0] = '_'; memcpy(&sim_names[0], "___", 3); uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random op uint8_t op = TEST_PRNG(&prng) % 3; // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size == 0 ? 1 : sim_size); // choose a pseudo-random name lfs_size_t x = TEST_PRNG(&prng) % (26*26*26); char name[3] = { alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26] }; // don't let sim drop below one element if (op == 0 || sim_size <= 1) { // find where to split lfs_size_t id = 0; while (id+1 < sim_size && memcmp(sim_names[id+1], name, 3) <= 0) { id += 1; } // just skip exact matches for now if (memcmp(sim_names[id], name, 3) == 0) { continue; } // split btree lfs_size_t split_id; lfsr_data_t split_data; lfsr_btree_namelookupnext(&lfs, &btree, name, 3, &split_id, NULL, NULL, NULL, NULL, &split_data) => 0; uint8_t split_buf[4]; lfsr_data_read(&lfs, split_data, 0, split_buf, 4) => 1; if (split_id > id) { int err = lfsr_btree_split(&lfs, &btree, split_id, LFSR_DATA_BUF(sim_names[id+1], 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[i % 10], 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(split_buf, 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); } else { int err = lfsr_btree_split(&lfs, &btree, split_id, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(split_buf, 1), LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); } // split sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_names[id+1], &sim_names[id], (sim_size-id)*3); sim[id+1] = nums[i % 10]; memcpy(&sim_names[id+1], name, 3); sim_size += 1; } else if (op == 1) { // update btree int err = lfsr_btree_update(&lfs, &btree, id, LFSR_TAG_INLINED, 1, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = nums[i % 10]; } else { // pop from btree int err = lfsr_btree_pop(&lfs, &btree, id); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // pop from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); memmove(&sim_names[id], &sim_names[id+1], (sim_size-(id+1))*3); sim_size -= 1; // our B-tree doesn't actually track the name of id0, so we need // mirror this in our sim if (id == 0) { memcpy(&sim_names[0], "___", 3); } } } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { if (!first) { printf(", "); } first = false; printf("%.3s=%c", sim_names[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); assert(lfsr_btree_weight(&btree) == sim_size); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { lfsr_btree_namelookupnext(&lfs, &btree, sim_names[i], 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == i); assert(weight_ == 1); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } // clean up sim free(sim); free(sim_names); } ''' [cases.test_btree_find_general_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 defines.SAMPLES = 100 # -1 => all pseudo-random seeds # n => reproduce a specific seed defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *nums = "0123456789"; // iterate through severals seeds that we can reproduce easily for (uint32_t seed = (SEED == -1 ? 1 : SEED); (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); seed++) { printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; lfs_init(&lfs, cfg) => 0; // create free lookahead memset(lfs.free.buffer, 0, lfs.cfg->lookahead_size); lfs.free.off = 0; lfs.free.size = lfs_min(8*lfs.cfg->lookahead_size, lfs.cfg->block_count); lfs.free.i = 0; lfs_alloc_ack(&lfs); // create a btree lfsr_btree_t btree = LFSR_BTREE_NULL; lfsr_btree_push(&lfs, &btree, 0, LFSR_TAG_INLINED, W, LFSR_DATA_BUF("_", 1)) => 0; // set up a simulation to compare against // // fun fact this is slower than our actual tree! unfun fact this is // starting to be a problem... char *sim = malloc(N); char (*sim_names)[3] = malloc(N*3); lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); lfs_size_t sim_size = 1; memset(sim, 0, N); memset(sim_names, 0, N*3); memset(sim_weights, 0, N*sizeof(lfs_size_t)); sim[0] = '_'; memcpy(&sim_names[0], "___", 3); sim_weights[0] = W; uint32_t prng = seed; for (lfs_size_t i = 0; i < N; i++) { // choose a pseudo-random op uint8_t op = TEST_PRNG(&prng) % 3; // choose a pseudo-random id lfs_size_t id = TEST_PRNG(&prng) % (sim_size == 0 ? 1 : sim_size); // choose a pseudo-random name lfs_size_t x = TEST_PRNG(&prng) % (26*26*26); char name[3] = { alphas[(x/26/26) % 26], alphas[(x/26) % 26], alphas[x % 26] }; // choose a pseudo-random weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // don't let sim drop below one element if (op == 0 || sim_size <= 1) { // find where to split lfs_size_t id = 0; while (id+1 < sim_size && memcmp(sim_names[id+1], name, 3) <= 0) { id += 1; } // just skip exact matches for now if (memcmp(sim_names[id], name, 3) == 0) { continue; } // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < id; j++) { weighted_id += sim_weights[j]; } // split btree lfs_size_t split_id; lfs_size_t split_weight; lfsr_data_t split_data; lfsr_btree_namelookupnext(&lfs, &btree, name, 3, &split_id, NULL, NULL, NULL, &split_weight, &split_data) => 0; uint8_t split_buf[4]; lfsr_data_read(&lfs, split_data, 0, split_buf, 4) => 1; if (split_id > weighted_id+sim_weights[id]-1) { int err = lfsr_btree_split(&lfs, &btree, split_id, LFSR_DATA_BUF(sim_names[id+1], 3), LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&nums[i % 10], 1), LFSR_TAG_INLINED, split_weight, LFSR_DATA_BUF(split_buf, 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); } else { int err = lfsr_btree_split(&lfs, &btree, split_id, LFSR_DATA_BUF(name, 3), LFSR_TAG_INLINED, split_weight, LFSR_DATA_BUF(split_buf, 1), LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); } // split sim memmove(&sim[id+1], &sim[id], sim_size-id); memmove(&sim_names[id+1], &sim_names[id], (sim_size-id)*3); memmove(&sim_weights[id+1], &sim_weights[id], (sim_size-id)*sizeof(lfs_size_t)); sim[id+1] = nums[i % 10]; memcpy(&sim_names[id+1], name, 3); sim_weights[id+1] = weight; sim_size += 1; } else if (op == 1) { // update btree int err = lfsr_btree_update(&lfs, &btree, weighted_id+sim_weights[id]-1, LFSR_TAG_INLINED, weight, LFSR_DATA_BUF(&nums[i % 10], 1)); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // update sim sim[id] = nums[i % 10]; sim_weights[id] = weight; } else { // pop from btree int err = lfsr_btree_pop(&lfs, &btree, weighted_id+sim_weights[id]-1); // ignore space issues if (err == LFS_ERR_NOSPC) { break; } assert(err == 0); // pop from sim memmove(&sim[id], &sim[id+1], sim_size-(id+1)); memmove(&sim_names[id], &sim_names[id+1], (sim_size-(id+1))*3); memmove(&sim_weights[id], &sim_weights[id+1], (sim_size-(id+1))*sizeof(lfs_size_t)); sim_size -= 1; // our B-tree doesn't actually track the name of id0, so we need // mirror this in our sim if (id == 0) { memcpy(&sim_names[0], "___", 3); } } } // check that btree matches sim printf("expd: ["); bool first = true; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } if (!first) { printf(", "); } first = false; printf("%.3sid%dw%d=%c", sim_names[i], weighted_id+sim_weights[i]-1, sim_weights[i], sim[i]); } printf("]\n"); printf("btree: w%d 0x%x.%x\n", btree.weight, btree.root.block, btree.root.trunk); lfs_size_t total_weight = 0; for (lfs_size_t j = 0; j < sim_size; j++) { total_weight += sim_weights[j]; } assert(lfsr_btree_weight(&btree) == total_weight); uint8_t buffer[4]; lfsr_tag_t tag_; lfs_size_t id_; lfs_size_t weight_; lfsr_data_t data_; for (lfs_size_t i = 0; i < sim_size; i++) { // calculate actual id in btree space lfs_size_t weighted_id = 0; for (lfs_size_t j = 0; j < i; j++) { weighted_id += sim_weights[j]; } lfsr_btree_namelookupnext(&lfs, &btree, sim_names[i], 3, &id_, NULL, NULL, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_INLINED); assert(id_ == weighted_id+sim_weights[i]-1); assert(weight_ == sim_weights[i]); lfsr_data_read(&lfs, data_, 0, buffer, 4) => 1; assert(memcmp(buffer, &sim[i], 1) == 0); } // clean up sim free(sim); free(sim_names); free(sim_weights); } '''