diff --git a/lfs.c b/lfs.c index 9608852f..2ba86462 100644 --- a/lfs.c +++ b/lfs.c @@ -944,59 +944,6 @@ static lfs_ssize_t lfsr_fcrc_fromdisk( return sizeof(uint32_t) + delta; } -//// btree on-disk encoding -//#define LFSR_BTREE_DSIZE 14 -// -//static lfs_ssize_t lfsr_btree_todisk( -// const lfsr_btree_t *btree, -// uint8_t buf[static LFSR_BTREE_DSIZE]) { -// lfs_ssize_t delta = 0; -// lfs_ssize_t delta_ = lfs_toleb128(btree->block, &buf[delta], 5); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// delta_ = lfs_toleb128(btree->limit, &buf[delta], 4); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// delta_ = lfs_toleb128(btree->weight, &buf[delta], 5); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// return delta; -//} -// -//static lfs_ssize_t lfsr_btree_fromdisk( -// lfsr_btree_t *btree, -// const uint8_t buf[static LFSR_BTREE_DSIZE]) { -// lfs_ssize_t delta = 0; -// lfs_ssize_t delta_ = lfs_fromleb128(&btree->block, &buf[delta], 5); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// delta_ = lfs_fromleb128(&btree->limit, &buf[delta], 4); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// delta_ = lfs_fromleb128(&btree->weight, &buf[delta], 5); -// if (delta_ < 0) { -// return delta_; -// } -// delta += delta_; -// -// return delta; -//} - // other endianness operations static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { ctz->head = lfs_fromle32(ctz->head); @@ -1652,239 +1599,6 @@ static lfs_ssize_t lfsr_rbyd_get(lfs_t *lfs, const lfsr_rbyd_t *rbyd, return size_; } -// TODO should we merge this into lfsr_rbyd_lookup? -static int lfsr_rbyd_predictedlookup(lfs_t *lfs, - const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs, - lfsr_tag_t tag, lfs_ssize_t id, - lfsr_tag_t *tag_, lfs_ssize_t *id_, lfsr_data_t *data_) { - // For this lookup to work it requires a lot of caveats: - // - // 1. Finding the weight is much more difficult when attrs aren't on disk - // so we don't do this. Note that weight can still be derived during - // traversal by diffing ids. - // - // 2. Our grow/shrink checks expect the grow/shrink ids to always be on the - // lowest id. This is notably different from our rbyd bias. Fortunately - // this is only a requirement of in-flight attrs so this isn't a - // requirement on-disk. - -again:; - // tag must be non-zero! zero tags may deceptively look like they work but - // fail when the tree contains a deleted id0 - LFS_ASSERT(tag != 0); - - // keep track of best id/tag and upper/lower bounds to determine weight - lfs_ssize_t id__ = id; - lfs_ssize_t best_id = -2; - lfsr_tag_t best_tag = 0; - lfsr_data_t best_data = LFSR_DATA_NULL; - - // search through our tags backwards to figure out the best tag/id - // TODO hmm, reverse iteration over a linked-list? this is a bad design - unsigned attr_count = 0; - for (const struct lfsr_attr *attr = attrs; attr; attr = attr->next) { - attr_count += 1; - } - for (unsigned i = 0; i < attr_count; i++) { - const struct lfsr_attr *attr = attrs; - for (unsigned j = 0; j < attr_count-1-i; j++) { - attr = attr->next; - } - - if (attr->tag == LFSR_TAG_GROW) { - // found grow which includes both target and best ids? this - // must be the source of the best tag - if (attr->id <= id__ - && best_id != -2 - && attr->id+(lfs_ssize_t)attr->size > best_id) { - goto found; - - // found grow which only includes target id? this must be a - // weight-changing grow so we can just adjust our target id to - // follow the upper edge of the grow - } else if (attr->id <= id__ - && attr->id+(lfs_ssize_t)attr->size > id__) { - id += attr->id+attr->size - id__; - id__ = attr->id; - tag = 0x10; - - // adjust ids - } else if (attr->id <= id__) { - id__ -= attr->size; - if (best_id != -2) { - best_id -= attr->size; - } - - // use grow as upper bound - } else if (best_id == -2 || attr->id <= best_id) { - best_id = attr->id-1; - best_tag = 0; - } - } else if (attr->tag == LFSR_TAG_SHRINK) { - // adjust ids - if (attr->id <= id__) { - id__ += attr->size; - if (best_id != -2) { - best_id += attr->size; - } - - // use shrink as upper bound - } else if (best_id == -2 || attr->id <= best_id) { - best_id = attr->id-1; - best_tag = 0; - } - - } else if (attr->tag == LFSR_TAG_FROM) { - // TODO - LFS_ASSERT(false); - - } else { - // found better tag? - if ((attr->id > id__ - || (attr->id == id__ - && lfsr_tag_key(attr->tag) - >= lfsr_tag_key(tag))) - && (best_id == -2 - || attr->id < best_id - || (attr->id == best_id - && (!best_tag - || lfsr_tag_key(attr->tag) - < lfsr_tag_key(best_tag))))) { - best_id = attr->id; - best_tag = attr->tag; - best_data = LFSR_DATA_BUF(attr->buffer, attr->size); - } - } - } - - // try to found our id/tag on disk - lfsr_tag_t rbyd_tag; - lfs_ssize_t rbyd_id; - lfs_off_t rbyd_off; - lfs_size_t rbyd_size; - int err = lfsr_rbyd_lookup(lfs, rbyd, tag, id__, - &rbyd_tag, &rbyd_id, NULL, &rbyd_off, &rbyd_size); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err != LFS_ERR_NOENT) { - // found a better tag? - if (best_id == -2 - || rbyd_id < best_id - || (rbyd_id == best_id - && (!best_tag - || lfsr_tag_key(rbyd_tag) - < lfsr_tag_key(best_tag)))) { - best_id = rbyd_id; - best_tag = rbyd_tag; - best_data = LFSR_DATA_DISK(rbyd->block, rbyd_off, rbyd_size); - } - } - -found:; - // no better id found - if (best_id == -2) { - return LFS_ERR_NOENT; - } - - // no tag found? increase id - if (!best_tag || lfsr_tag_isrm(best_tag)) { - tag = best_tag + 0x10; - id = best_id+(id-id__) + 1; - goto again; - } - - // found an id/tag - // TODO how many of these should be conditional? - if (tag_) { - *tag_ = best_tag; - } - if (id_) { - *id_ = best_id+(id-id__); - } - if (data_) { - *data_ = best_data; - } - - return 0; -} - -// TODO do we need this function? -static lfs_ssize_t lfsr_rbyd_predictedget(lfs_t *lfs, - const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs, - lfsr_tag_t tag, lfs_ssize_t id, void *buffer, lfs_size_t size) { - lfsr_tag_t tag_; - lfs_ssize_t id_; - lfsr_data_t data_; - int err = lfsr_rbyd_predictedlookup(lfs, rbyd, attrs, tag, id, - &tag_, &id_, &data_); - if (err) { - return err; - } - - // lookup finds the next-smallest tag, for get, we need to fail - // if it's not an exact match - if (id_ != id || tag_ != tag) { - return LFS_ERR_NOENT; - } - - // TODO should this be its own lfsr_data_ function? - lfs_size_t delta = lfs_min(size, lfsr_data_len(data_)); - if (!lfsr_data_ondisk(data_)) { - memcpy(buffer, data_.u.buf, delta); - } else { - err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, delta, - data_.u.disk.block, data_.u.disk.off, buffer, delta); - if (err) { - return err; - } - } - - return lfsr_data_len(data_); -} - -static lfs_ssize_t lfsr_rbyd_predictedsize( - lfs_t *lfs, const lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs, - lfs_ssize_t start, lfs_ssize_t stop) { - // find the strict upper bound on the amount of disk taken by a range of - // tags immediately after compaction (when the tags should be perfectly - // rbyd balanced), this is used for a number of heuristics - - // find the size/count of tags - lfsr_tag_t tag = 0; - lfs_ssize_t id = start; - lfs_size_t count = 0; - lfs_size_t dsize = 0; - while (true) { - lfsr_data_t data; - int err = lfsr_rbyd_predictedlookup(lfs, rbyd, attrs, - lfsr_tag_next(tag), id, - &tag, &id, &data); - if (err < 0 && err != LFS_ERR_NOENT) { - return err; - } - - if (err == LFS_ERR_NOENT || id >= stop) { - break; - } - - count += 1; - dsize += lfsr_data_len(data); - } - - // make sure to account for both tag and alt metametadata, and assume the - // worst-case leb128 encoding for tags. Note that since we assume this is - // immediately after compaciton, the tree should be perfectly rbyd - // balanced, which puts a strict upper bound of 2*log2(n)+1 on the space - // overhead per tag -// printf("predictedsize: %d + %d*(12 + 2*log2(%d)+1)*12 = %d\n", -// dsize, count, count, -// dsize + count*(12 + (2*lfs_nlog2(count)+1)*12)); - return dsize + count*(12 + (2*lfs_nlog2(count)+1)*12); -} - static lfs_ssize_t lfsr_rbyd_bisect(lfs_t *lfs, const lfsr_rbyd_t *rbyd) { // find the best id to split an rbyd evenly // @@ -2638,172 +2352,6 @@ leaf:; return 0; } -//static int lfsr_rbyd_appendrev(lfs_t *lfs, lfsr_rbyd_t *rbyd) { -// LFS_ASSERT(rbyd->erased); -// LFS_ASSERT(rbyd->off == 0); -// -// // append revision count, this should start every rbyd -// uint32_t rev; -// lfs_tole32_(rbyd->rev, &rev); -// int err = lfsr_rbyd_prog(lfs, rbyd, -// &rev, sizeof(uint32_t), &rbyd->crc); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// -// return 0; -//} - -//static int lfsr_rbyd_appendcrc(lfs_t *lfs, lfsr_rbyd_t *rbyd, -// lfs_off_t off, uint32_t crc) { -// LFS_ASSERT(rbyd->erased); -// -// // align to the next prog unit -// // -// // this gets a bit complicated as we have two types of crcs: -// // -// // - 9-word crc with fcrc to check following prog (middle of block) -// // - fcrc tag type => 1 byte leb128 -// // - fcrc tag id => 1 byte leb128 -// // - fcrc tag size => 1 byte leb128 (worst case) -// // - fcrc crc => 4 byte le32 -// // - fcrc size => 5 byte leb128 (worst case) -// // - crc tag type => 1 byte leb128 -// // - crc tag id => 1 byte leb128 -// // - crc tag size => 5 byte leb128 (worst case) -// // - crc crc => 4 byte le32 -// // => 23 bytes total -// // -// // - 4-word crc with no following prog (end of block) -// // - crc tag type => 1 byte leb128 -// // - crc tag id => 1 byte leb128 -// // - crc tag size => 5 byte leb128 (worst case) -// // - crc crc => 4 byte le32 -// // => 11 bytes total -// // -// lfs_off_t aligned = lfs_alignup( -// rbyd->off + 1+1+1+4+5 + 1+1+5+4, -// lfs->cfg->prog_size); -// -// // space for fcrc? -// uint8_t perturb = 0; -// if (aligned <= lfs->cfg->block_size - lfs->cfg->prog_size) { -// // read the leading byte in case we need to change the expected -// // value of the next tag's valid bit -// int err = lfs_bd_read(lfs, -// &lfs->pcache, &lfs->rcache, lfs->cfg->prog_size, -// rbyd->block, aligned, &perturb, 1); -// if (err && err != LFS_ERR_CORRUPT) { -// rbyd->erased = false; -// return err; -// } -// -// // find the expected fcrc, don't bother avoiding a reread of the -// // perturb byte, as it should still be in our cache -// struct lfsr_fcrc fcrc = {.crc=0, .size=lfs->cfg->prog_size}; -// err = lfs_bd_crc32c(lfs, -// &lfs->pcache, &lfs->rcache, lfs->cfg->prog_size, -// rbyd->block, aligned, fcrc.size, &fcrc.crc); -// if (err && err != LFS_ERR_CORRUPT) { -// rbyd->erased = false; -// return err; -// } -// -// uint8_t fbuf[LFSR_FCRC_DSIZE]; -// lfs_size_t fcrc_delta = lfsr_fcrc_todisk(&fcrc, fbuf); -// err = lfsr_rbyd_progtag(lfs, rbyd, -// LFSR_TAG_FCRC, -1, fcrc_delta, &rbyd->crc); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// -// err = lfsr_rbyd_prog(lfs, rbyd, -// fbuf, fcrc_delta, &rbyd->crc); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// } else { -// // recalculate aligned without fcrc -// aligned = lfs_alignup( -// rbyd->off + 1+1+5+4, -// lfs->cfg->prog_size); -// rbyd->erased = false; -// } -// -// // not even space for the crc? -// if (aligned > lfs->cfg->block_size) { -// lfs_cache_zero(lfs, &lfs->pcache); -// rbyd->erased = false; -// return LFS_ERR_RANGE; -// } -// -// // build end-of-commit crc -// // -// // note padding-size depends on leb-encoding depends on padding-size, to -// // get around this catch-22 we just always write a fully-expanded leb128 -// // encoding -// uint8_t buffer[1+1+5+4]; -// buffer[0] = LFSR_TAG_CRC | (lfs_popc(rbyd->crc) & 1); -// buffer[1] = 0; -// -// lfs_off_t padding = aligned - (rbyd->off + 1+1+5); -// buffer[2] = 0x80 | (0x7f & (padding >> 0)); -// buffer[3] = 0x80 | (0x7f & (padding >> 7)); -// buffer[4] = 0x80 | (0x7f & (padding >> 14)); -// buffer[5] = 0x80 | (0x7f & (padding >> 21)); -// buffer[6] = 0x00 | (0x7f & (padding >> 28)); -// -// rbyd->crc = lfs_crc32c(rbyd->crc, buffer, 1+1+5); -// // we can't let the next tag appear as valid, so intentionally perturb the -// // commit if this happens, note parity(crc(m)) == parity(m) with crc32c, -// // so we can really change any bit to make this happen, we've reserved a bit -// // in crc tags just for this purpose -// if ((lfs_popc(rbyd->crc) & 1) == (perturb & 1)) { -// buffer[0] ^= 0x10; -// rbyd->crc ^= 0x9c5bfaa6; // note crc(a ^ b) == crc(a) ^ crc(b) -// } -// lfs_tole32_(rbyd->crc, &buffer[1+1+5]); -// -// int err = lfsr_rbyd_prog(lfs, rbyd, buffer, 1+1+5+4, NULL); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// -// // flush our caches, finalizing the commit on-disk -// err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// -// // succesful commit, check checksum to make sure -// uint32_t crc_ = crc; -// err = lfs_bd_crc32c(lfs, -// NULL, &lfs->rcache, rbyd->off-4, -// rbyd->block, off, rbyd->off-4 - off, &crc_); -// if (err) { -// rbyd->erased = false; -// return err; -// } -// -// if (rbyd->crc != crc_) { -// // oh no, something went wrong -// LFS_ERROR("Rbyd corrupted during commit " -// "(block=0x%"PRIx32", 0x%08"PRIx32" != 0x%08"PRIx32")", -// rbyd->block, rbyd->crc, crc_); -// rbyd->erased = false; -// return LFS_ERR_CORRUPT; -// } -// -// // ok, everything is good, save what we've committed -// rbyd->off = aligned; -// return 0; -//} - static int lfsr_rbyd_commit(lfs_t *lfs, lfsr_rbyd_t *rbyd, const struct lfsr_attr *attrs) { // we can't do anything if we're not erased @@ -2829,83 +2377,11 @@ static int lfsr_rbyd_commit(lfs_t *lfs, lfsr_rbyd_t *rbyd, // append each tag to the tree for (const struct lfsr_attr *attr = attrs; attr; attr = attr->next) { - // TODO can FROM/COMPACT share more logic? - // indirect set of attributes from another rbyd - if (attr->tag == LFSR_TAG_FROM) { - LFS_ASSERT(false); - - const struct lfsr_attr_from *from = attr->buffer; - -// // first create one grow for the entire from range -// // TODO should LFSR_DATA_BUF be used this way? smells fishy -// int err = lfsr_rbyd_append(lfs, &rbyd_, -// LFSR_TAG_GROW, attr->id, LFSR_DATA_BUF(NULL, attr->size)); -// if (err) { -// return err; -// } - - // now copy over ids - lfsr_tag_t tag = 0; - lfs_ssize_t id = from->start; - while (true) { - lfsr_tag_t tag_; - lfs_ssize_t id_; - lfsr_data_t data_; - int err = lfsr_rbyd_predictedlookup( - lfs, from->rbyd, from->attrs, - lfsr_tag_next(tag), id, - &tag_, &id_, &data_); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err == LFS_ERR_NOENT || id_-from->start >= attr->size) { - break; - } - - // TODO this is really wasteful and throws off our predicted - // size, can we combine grows into the tag append in the rbyd - // somehow? - // create grows as necessary - lfs_size_t weight_ = id_-id + (tag == 0 ? 1 : 0); -// printf("from %d %x w%d\n", id_, tag_, weight_); - if (weight_ > 0) { - int err = lfsr_rbyd_append(lfs, &rbyd_, - LFSR_TAG_GROW, - attr->id+(id_-from->start)-(weight_-1), - // TODO also this is a weird way to use lfsr_data_t - LFSR_DATA_BUF(NULL, weight_)); - if (err) { - return err; - } - } - - // append the attr - err = lfsr_rbyd_append(lfs, &rbyd_, - tag_, attr->id+(id_-from->start), data_); - if (err) { - return err; - } - - tag = tag_; - id = id_; - } - -// // attempting to compact an rbyd, this is the same as LFSR_TAG_FROM, -// // except we may abort if we exceed our compaction threshold -// // (1/2 block_size) -// } else if (attr->tag == LFSR_TAG_COMPACT) { -// -// -// - // a normal attribute - } else { - int err = lfsr_rbyd_append(lfs, &rbyd_, - attr->tag, attr->id, - LFSR_DATA_BUF(attr->buffer, attr->size)); - if (err) { - return err; - } + int err = lfsr_rbyd_append(lfs, &rbyd_, + attr->tag, attr->id, + LFSR_DATA_BUF(attr->buffer, attr->size)); + if (err) { + return err; } } @@ -3177,8 +2653,6 @@ static lfs_ssize_t lfsr_btree_lookup(lfs_t *lfs, return err; } -// printf("lookup %x#%x %d => %d %x %d\n", branch.block, branch.limit, rid, rid__, tag__, size_); - // found another branch if (tag__ == LFSR_TAG_BRANCH) { // adjust rid with subtree's weight @@ -3199,8 +2673,6 @@ static lfs_ssize_t lfsr_btree_lookup(lfs_t *lfs, return delta; } -// printf("branch %x#%x\n", branch.block, branch.limit); - // found our id } else { // TODO how many of these should be conditional? @@ -3347,12 +2819,10 @@ static int lfsr_btree_commit(lfs_t *lfs, pid = -1; } lfs_size_t pweight = rbyd->weight; -// printf("parent: %x#%x %dw%d\n", parent.block, parent.off, pid, pweight); // is rbyd erased? can we sneak our commit into any remaining // erased bytes? note that the btree limit prevents this from mutating // other references to the rbyd -// printf("committing to %x+%x...\n", rbyd->block, rbyd->off); err = lfsr_rbyd_commit(lfs, rbyd, attrs); if (err && err != LFS_ERR_RANGE) { // TODO wait should we also move if there is corruption here? @@ -3400,76 +2870,6 @@ static int lfsr_btree_commit(lfs_t *lfs, // no? try to compact compact:; -// // wait are we root? is our root+attrs degenerate? we might be able to -// // inline/collapse our root -// // -// // we really need to figure this out before allocation/erasing, but -// // determining if our root+attrs is degenerate without writing to the -// // rbyd is a bit difficult -// if (pid == -1) { -// -// } -// -// goddammit this doesn't work if we have attrs, needs more thought -// -// // wait, are we root and a single branch? -// if (pid == -1) { -// lfs_size_t weight; -// err = lfsr_rbyd_lookup(lfs, rbyd, LFSR_TAG_MK, 0, -// NULL, NULL, &weight, NULL, NULL); -// if (err) { -// assert(!err); -// return err; -// } -// -// if (weight == rbyd->weight) { -// lfsr_tag_t tag; -// lfs_off_t off; -// lfs_size_t size; -// err = lfsr_rbyd_lookup(lfs, rbyd, LFSR_TAG_STRUCT, weight-1, -// &tag, NULL, NULL, &off, &size); -// if (err) { -// assert(!err); -// return err; -// } -// -// // TODO we can probably use the inlined buf in both branches -// // TODO deduplicate? -// if (tag == LFSR_TAG_BRANCH) { -// uint8_t buf[LFSR_BRANCH_DSIZE]; -// // TODO wait, why min and not an assert? -// lfs_ssize_t delta = lfs_min(LFSR_BRANCH_DSIZE, size); -// err = lfs_bd_read(lfs, -// &lfs->pcache, &lfs->rcache, delta, -// rbyd->block, off, buf, delta); -// if (err) { -// assert(!err); -// return err; -// } -// -// delta = lfsr_branch_fromdisk(&btree->u.trunk, buf); -// if (delta < 0) { -// assert(!delta); -// return delta; -// } -// -// return 0; -// } else { -// LFS_ASSERT(size <= LFSR_BTREE_INLINE_SIZE); -// err = lfs_bd_read(lfs, -// &lfs->pcache, &lfs->rcache, size, -// rbyd->block, off, btree->u.inlined.buf, size); -// if (err) { -// assert(!err); -// return err; -// } -// -// btree->tag = tag; -// btree->u.inlined.size = size; -// } -// } -// } - // TODO were we doing something funky with rev? // first allocate a new rbyd lfsr_rbyd_t rbyd_; @@ -4293,9 +3693,6 @@ static int lfsr_btree_split(lfs_t *lfs, lfsr_btree_t *btree, } - - - /// Metadata pair operations /// diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index 26098e34..95405760 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -11384,1266 +11384,3 @@ code = ''' } ''' - - -## Test unwritten attributes - -[cases.test_rbyd_unwritten_permutations] -defines.N = 'range(1, 7)' -# -1 => exhaust all permutations -# n => reproduce a specific permutation -defines.PERMUTATION = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_ = 0; - lfs_ssize_t id_ = -1; - lfsr_data_t data_ = LFSR_DATA_NULL; - - // test all permutations of a given size - size_t perm_count = TEST_FACTORIAL(N); - for (size_t i = 0; - i < (PERMUTATION == -1 ? perm_count : 1); - i++) { - uint32_t perm[N]; - size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; - TEST_PERMUTATION(perm_i, perm, N); - - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - // print permutation to help debugging - printf("--- permutation: %zd [", perm_i); - for (unsigned j = 0; j < N; j++) { - if (j > 0) { - printf(", "); - } - printf("%d", perm[j]); - } - printf("], written: %d/%jd ---\n", w, N); - - // build the attribute lists for the current permutation - struct lfsr_attr attrs[N]; - for (unsigned j = 0; j < N; j++) { - attrs[j] = *LFSR_ATTR( - UATTR(perm[j]+1), -1, "\xaa\xaa\xaa\xaa", 4, - (j+1 < N && j+1 != w) ? &attrs[j+1] : NULL); - } - const struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL; - const struct lfsr_attr *unwritten = w < N ? &attrs[w] : NULL; - - // create rbyd with written attr - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, written) => 0; - - lfsr_rbyd_fetch(&lfs, &rbyd, - rbyd.block, cfg->block_size, NULL) => 0; - - // test lookup both written/unwritten - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(j+1), -1, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_UATTR(j+1)); - assert(id_ == -1); - assert(lfsr_data_len(data_) == 4); - } - - // test traverse both written/unwritten - tag_ = 0; - id_ = -1; - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_UATTR(j+1)); - assert(id_ == -1); - assert(lfsr_data_len(data_) == 4); - } - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => LFS_ERR_NOENT; - } - } -''' - -[cases.test_rbyd_unwritten_fuzz] -defines.N = 'range(1, 13)' -defines.SAMPLES = 1000 -# -1 => all pseudo-random seeds -# n => reproduce a specific seed -defines.SEED = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - const char *alpha = "abcdefghijklmnopqrstuvwxyz"; - uint8_t buffer[4]; - - // iterate through seeds so we can reproduce easily - for (uint32_t seed = (SEED == -1 ? 1 : SEED); - (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); - seed++) { - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N); - printf("perm: ["); - uint32_t prng = seed; - for (unsigned i = 0; i < N; i++) { - // choose an attr - uint8_t attr = TEST_PRNG(&prng) % N; - // choose append or remove - if (TEST_PRNG(&prng) & 1) { - printf("a0x%02x=%c", attr, alpha[i % 26]); - } else { - printf("r0x%02x", attr); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up a simulation to compare against - char *sim = malloc(N); - memset(sim, 0, N); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - // set up our unwritten attr list - struct lfsr_attr *attrs = malloc(N*sizeof(struct lfsr_attr)); - unsigned a = 0; - - prng = seed; - for (unsigned i = 0; i < N; i++) { - // choose an attr - uint8_t attr = TEST_PRNG(&prng) % N; - // choose append or remove - if (TEST_PRNG(&prng) & 1) { - // update our sim - sim[attr] = alpha[i % 26]; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1, - NULL)) => 0; - // append to our unwritten attrs - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - UATTR(attr), -1, &alpha[i % 26], 1, - NULL); - a += 1; - } - } else { - // update our sim - sim[attr] = '\0'; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(RMUATTR(attr), -1, NULL, 0, - NULL)) => 0; - // append to our unwritten attrs - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - RMUATTR(attr), -1, NULL, 0, - NULL); - a += 1; - } - } - } - const struct lfsr_attr *unwritten = w < N ? attrs : NULL; - - // compare rbyd vs simulation - printf("expd: ["); - bool first = true; - for (unsigned attr = 0; attr < N; attr++) { - if (sim[attr]) { - if (!first) { - printf(", "); - } - first = false; - printf("0x%02x=%c", attr, sim[attr]); - } - } - printf("]\n"); - printf("rbyd: ["); - first = true; - for (unsigned attr = 0; attr < N; attr++) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(attr), -1, buffer, 4); - if (size >= 0) { - if (!first) { - printf(", "); - } - first = false; - printf("0x%02x=%.*s", attr, size, buffer); - } - } - printf("]\n"); - - for (unsigned attr = 0; attr < N; attr++) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(attr), -1, buffer, 4); - if (sim[attr]) { - assert(size == 1); - assert(memcmp(&sim[attr], buffer, 1) == 0); - } else { - assert(size == LFS_ERR_NOENT); - } - } - - // cleanup - free(sim); - free(attrs); - } - } -''' - -[cases.test_rbyd_unwritten_create_permutations] -defines.N = 'range(1, 7)' -# -1 => exhaust all permutations -# n => reproduce a specific permutation -defines.PERMUTATION = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_ = 0; - lfs_ssize_t id_ = -1; - lfsr_data_t data_ = LFSR_DATA_NULL; - const uint8_t names[6][4] = { - "\xaa\xaa\xaa\xaa", - "\xbb\xbb\xbb\xbb", - "\xcc\xcc\xcc\xcc", - "\xdd\xdd\xdd\xdd", - "\xee\xee\xee\xee", - "\xff\xff\xff\xff", - }; - uint8_t buffer[4]; - - // test all permutations of a given size - size_t perm_count = TEST_FACTORIAL(N); - for (size_t i = 0; - i < (PERMUTATION == -1 ? perm_count : 1); - i++) { - uint32_t perm[N]; - size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; - TEST_PERMUTATION(perm_i, perm, N); - - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - // print permutation to help debugging - printf("--- permutation: %zd [", perm_i); - for (unsigned j = 0; j < N; j++) { - if (j > 0) { - printf(", "); - } - printf("%d", perm[j]); - } - printf("], written: %d/%jd ---\n", w, N); - - // build the attribute lists for the current permutation - struct lfsr_attr attrs[2*N]; - for (unsigned j = 0; j < N; j++) { - // adjust id based on future insertions - uint16_t id = perm[j]; - for (unsigned k = j+1; k < N; k++) { - if (perm[j] > perm[k]) { - id -= 1; - } - } - - attrs[2*j+0] = *LFSR_ATTR( - GROW, id, NULL, 1, - &attrs[2*j+1]); - attrs[2*j+1] = *LFSR_ATTR( - MKREG, id, names[perm[j] % 6], 4, - (j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL); - } - const struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL; - const struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL; - - // create rbyd with written attr - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, written) => 0; - - lfsr_rbyd_fetch(&lfs, &rbyd, - rbyd.block, cfg->block_size, NULL) => 0; - assert(rbyd.weight == w); - - // test lookup both written/unwritten - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, j, buffer, 4) => 4; - assert(memcmp(buffer, names[j % 6], 4) == 0); - } - - // test traverse both written/unwritten - tag_ = 0; - id_ = -1; - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_MKREG); - assert(id_ == j); - assert(lfsr_data_len(data_) == 4); - } - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => LFS_ERR_NOENT; - } - } -''' - -[cases.test_rbyd_unwritten_create_fuzz] -defines.N = 'range(1, 13)' -defines.SAMPLES = 1000 -# -1 => all pseudo-random seeds -# n => reproduce a specific seed -defines.SEED = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - const char *alpha = "abcdefghijklmnopqrstuvwxyz"; - uint8_t buffer[4]; - - // iterate through seeds so we can reproduce easily - for (uint32_t seed = (SEED == -1 ? 1 : SEED); - (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); - seed++) { - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N); - printf("perm: ["); - uint32_t prng = seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose create or delete - if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { - printf("c%d=%c", id, alpha[i % 26]); - count += 1; - } else { - printf("d%d", id); - count -= 1; - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up a simulation to compare against, fun fact this performs - // worst than our actual rbyd block! - char *sim = malloc(N); - memset(sim, 0, N); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - // set up attr list - struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr)); - unsigned a = 0; - - prng = seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose create or delete - if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { - // update our sim - memmove(sim+id+1, sim+id, count-id); - sim[id] = alpha[i % 26]; - count += 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, id, NULL, 1, - LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, - NULL))) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - GROW, id, NULL, 1, - &attrs[a+1]); - attrs[a+1] = *LFSR_ATTR( - MKREG, id, &alpha[i % 26], 1, - NULL); - a += 2; - } - } else { - // update our sim - memmove(sim+id, sim+id+1, count-id-1); - count -= 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, id, NULL, 1, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - SHRINK, id, NULL, 1, - NULL); - a += 1; - } - } - } - const struct lfsr_attr *unwritten = w < N ? attrs : NULL; - - // compare rbyd vs simulation - printf("expd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - printf("%c", sim[id]); - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - printf("rbyd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, id, buffer, 4); - if (size >= 0) { - printf("%.*s", size, buffer); - } else { - printf("?"); - } - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, id, buffer, 4) => 1; - assert(memcmp(&sim[id], buffer, 1) == 0); - } - - // cleanup - free(sim); - free(attrs); - } - } -''' - -[cases.test_rbyd_unwritten_mixed_permutations] -defines.N = 'range(1, 7)' -defines.M = 'range(1, 4)' -# -1 => exhaust all permutations -# n => reproduce a specific permutation -defines.PERMUTATION = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_ = 0; - lfs_ssize_t id_ = -1; - lfsr_data_t data_ = LFSR_DATA_NULL; - const uint8_t names[6][4] = { - "\xaa\xaa\xaa\xaa", - "\xbb\xbb\xbb\xbb", - "\xcc\xcc\xcc\xcc", - "\xdd\xdd\xdd\xdd", - "\xee\xee\xee\xee", - "\xff\xff\xff\xff", - }; - uint8_t buffer[4]; - - // test all permutations of a given size - size_t perm_count = TEST_FACTORIAL(N); - for (size_t i = 0; - i < (PERMUTATION == -1 ? perm_count : 1); - i++) { - uint32_t perm[N]; - size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; - TEST_PERMUTATION(perm_i, perm, N); - - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (signed w = -1; w <= N; w++) { - // print permutation to help debugging - printf("--- permutation: %zd [", perm_i); - for (unsigned j = 0; j < N; j++) { - if (j > 0) { - printf(", "); - } - printf("%d", perm[j]); - } - printf("], written: %d/%jd ---\n", w, N); - - // build the attribute lists for the current permutation - struct lfsr_attr attrs[1+(2+M)*N]; - attrs[0] = *LFSR_ATTR(UATTR(3), -1, "unrelated", 9, &attrs[1]); - - for (unsigned j = 0; j < N; j++) { - // adjust id based on future insertions - uint16_t id = perm[j]; - for (unsigned k = j+1; k < N; k++) { - if (perm[j] > perm[k]) { - id -= 1; - } - } - - attrs[1+(2+M)*j+0] = *LFSR_ATTR( - GROW, id, NULL, 1, - &attrs[1+(2+M)*j+1]); - attrs[1+(2+M)*j+1] = *LFSR_ATTR( - MKREG, id, names[perm[j] % 6], 4, - &attrs[1+(2+M)*j+2]); - for (unsigned u = 0; u < M; u++) { - attrs[1+(2+M)*j+2+u] = *LFSR_ATTR( - UATTR(u+1), id, names[perm[j] % 6], 2, - &attrs[1+(2+M)*j+2+u+1]); - } - } - if (w >= 0) { - attrs[1+(2+M)*w-1].next = NULL; - } - attrs[1+(2+M)*N-1].next = NULL; - const struct lfsr_attr *written - = w >= 0 ? &attrs[0] - : NULL; - const struct lfsr_attr *unwritten - = w < 0 ? &attrs[0] - : w < N ? &attrs[1+(2+M)*w] - : NULL; - - // create rbyd with written attr - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, written) => 0; - - lfsr_rbyd_fetch(&lfs, &rbyd, - rbyd.block, cfg->block_size, NULL) => 0; - assert(rbyd.weight == (w >= 0 ? w : 0)); - - // test lookup both written/unwritten - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(3), -1, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_UATTR(3)); - assert(id_ == -1); - assert(lfsr_data_len(data_) == 9); - - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, j, buffer, 4) => 4; - assert(memcmp(buffer, names[j % 6], 4) == 0); - - for (unsigned u = 0; u < M; u++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(u+1), j, buffer, 4) => 2; - assert(memcmp(buffer, names[j % 6], 2) == 0); - } - } - - // test traverse both written/unwritten - tag_ = 0; - id_ = -1; - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_UATTR(3)); - assert(id_ == -1); - assert(lfsr_data_len(data_) == 9); - - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_MKREG); - assert(id_ == j); - assert(lfsr_data_len(data_) == 4); - - for (unsigned u = 0; u < M; u++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_UATTR(u+1)); - assert(id_ == j); - assert(lfsr_data_len(data_) == 2); - } - } - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => LFS_ERR_NOENT; - } - } -''' - -[cases.test_rbyd_unwritten_mixed_fuzz] -defines.N = 'range(1, 13)' -defines.M = 3 -defines.SAMPLES = 1000 -# -1 => all pseudo-random seeds -# n => reproduce a specific seed -defines.SEED = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - const char *alpha = "abcdefghijklmnopqrstuvwxyz"; - uint8_t buffer[4]; - - // iterate through seeds so we can reproduce easily - for (uint32_t seed = (SEED == -1 ? 1 : SEED); - (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); - seed++) { - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N); - printf("perm: ["); - uint32_t prng = seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete or attr append/remove - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose an attr - uint8_t u = TEST_PRNG(&prng) % M; - - if (id == (lfs_ssize_t)count || op == 0) { - printf("c%d=%c", id, alpha[i % 26]); - count += 1; - } else if (op == 1) { - printf("d%d", id); - count -= 1; - } else if (op == 2) { - printf("a%d,%d=%c", id, u, alpha[i % 26]); - } else if (op == 3) { - printf("r%d,%d", id, u); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up a simulation to compare against, fun fact this performs - // worst than our actual rbyd block! - char *sim = malloc(N*(M+1)); - memset(sim, 0, N*(M+1)); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - // set up attr list - struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr)); - unsigned a = 0; - - prng = seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete or attr append/remove - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose an attr - uint8_t u = TEST_PRNG(&prng) % M; - - if (id == (lfs_ssize_t)count || op == 0) { - // update our sim - memmove(sim+(id+1)*(M+1), sim+id*(M+1), (count-id)*(M+1)); - memset(&sim[id*(M+1)], 0, M+1); - sim[id*(M+1)] = alpha[i % 26]; - count += 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, id, NULL, 1, - LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, - NULL))) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - GROW, id, NULL, 1, - &attrs[a+1]); - attrs[a+1] = *LFSR_ATTR( - MKREG, id, &alpha[i % 26], 1, - NULL); - a += 2; - } - } else if (op == 1) { - // update our sim - memmove(sim+id*(M+1), sim+(id+1)*(M+1), (count-id-1)*(M+1)); - count -= 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, id, NULL, 1, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - SHRINK, id, NULL, 1, - NULL); - a += 1; - } - } else if (op == 2) { - // update our sim - sim[id*(M+1) + u+1] = alpha[i % 26]; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - UATTR(u), id, &alpha[i % 26], 1, - NULL); - a += 1; - } - } else if (op == 3) { - // update our sim - sim[id*(M+1) + u+1] = '\0'; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(RMUATTR(u), id, NULL, 0, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - RMUATTR(u), id, NULL, 0, - NULL); - a += 1; - } - } - } - const struct lfsr_attr *unwritten = w < N ? attrs : NULL; - - // compare rbyd vs simulation - printf("expd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - printf("%c", sim[id*(M+1)]); - for (uint8_t u = 0; u < M; u++) { - if (sim[id*(M+1) + u+1]) { - printf("%c", sim[id*(M+1) + u+1]); - } else { - printf("_"); - } - } - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - printf("rbyd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, id, buffer, 4); - if (size >= 0) { - printf("%.*s", size, buffer); - } else { - printf("?"); - } - for (uint8_t u = 0; u < M; u++) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - LFSR_TAG_UATTR(u), id, buffer, 4); - if (size >= 0) { - printf("%.*s", size, buffer); - } else { - printf("_"); - } - } - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, id, buffer, 4) => 1; - assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0); - } - - // cleanup - free(sim); - free(attrs); - } - } -''' - -[cases.test_rbyd_unwritten_sparse_permutations] -defines.N = 'range(1, 7)' -defines.W = 5 -# -1 => exhaust all permutations -# n => reproduce a specific permutation -defines.PERMUTATION = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_ = 0; - lfs_ssize_t id_ = -1; - lfsr_data_t data_ = LFSR_DATA_NULL; - const uint8_t names[6][4] = { - "\xaa\xaa\xaa\xaa", - "\xbb\xbb\xbb\xbb", - "\xcc\xcc\xcc\xcc", - "\xdd\xdd\xdd\xdd", - "\xee\xee\xee\xee", - "\xff\xff\xff\xff", - }; - uint8_t buffer[4]; - - // test all permutations of a given size - size_t perm_count = TEST_FACTORIAL(N); - for (size_t i = 0; - i < (PERMUTATION == -1 ? perm_count : 1); - i++) { - uint32_t perm[N]; - size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; - TEST_PERMUTATION(perm_i, perm, N); - - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - // print permutation to help debugging - printf("--- permutation: %zd [", perm_i); - for (unsigned j = 0; j < N; j++) { - if (j > 0) { - printf(", "); - } - printf("%d", perm[j]); - } - printf("], written: %d/%jd ---\n", w, N); - - // build the attribute lists for the current permutation - struct lfsr_attr attrs[2*N]; - for (unsigned j = 0; j < N; j++) { - // adjust id based on future insertions - uint16_t id = perm[j]; - for (unsigned k = j+1; k < N; k++) { - if (perm[j] > perm[k]) { - id -= 1; - } - } - - attrs[2*j+0] = *LFSR_ATTR( - GROW, id*W, NULL, W, - &attrs[2*j+1]); - attrs[2*j+1] = *LFSR_ATTR( - MKREG, id*W+W-1, names[perm[j] % 6], 4, - (j+1 < N && j+1 != w) ? &attrs[2*j+2] : NULL); - } - const struct lfsr_attr *written = w > 0 ? &attrs[0] : NULL; - const struct lfsr_attr *unwritten = w < N ? &attrs[2*w] : NULL; - - // create rbyd with written attr - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - lfsr_rbyd_commit(&lfs, &rbyd, written) => 0; - - lfsr_rbyd_fetch(&lfs, &rbyd, - rbyd.block, cfg->block_size, NULL) => 0; - assert(rbyd.weight == w*W); - - // test lookup both written/unwritten - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4; - assert(memcmp(buffer, names[j % 6], 4) == 0); - } - - // test traverse both written/unwritten - tag_ = 0; - id_ = -1; - for (unsigned j = 0; j < N; j++) { - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => 0; - assert(tag_ == LFSR_TAG_MKREG); - assert(id_ == j*W+W-1); - assert(lfsr_data_len(data_) == 4); - } - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - lfsr_tag_next(tag_), id_, - &tag_, &id_, &data_) => LFS_ERR_NOENT; - } - } -''' - -[cases.test_rbyd_unwritten_sparse_fuzz] -defines.N = 'range(1, 13)' -defines.W = 5 -defines.SAMPLES = 1000 -# -1 => all pseudo-random seeds -# n => reproduce a specific seed -defines.SEED = -1 -# large progs take too long for now -if = 'PROG_SIZE < 512' -in = 'lfs.c' -code = ''' - lfs_t lfs; - lfs_init(&lfs, cfg) => 0; - - lfsr_rbyd_t init_rbyd = { - .block = 0, - .rev = 1, - .off = 0, - .crc = 0, - .trunk = 0, - .weight = 0, - .erased = true, - }; - lfsr_rbyd_t rbyd; - lfsr_tag_t tag_; - lfs_ssize_t id_; - lfsr_data_t data_; - const char *alpha = "abcdefghijklmnopqrstuvwxyz"; - uint8_t buffer[4]; - - // iterate through seeds so we can reproduce easily - for (uint32_t seed = (SEED == -1 ? 1 : SEED); - (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); - seed++) { - // test each number of written/unwritten tags, this gives us a quick - // way to test several unwritten situations - for (unsigned w = 0; w <= N; w++) { - printf("--- seed: %d, written: %d/%jd ---\n", seed, w, N); - printf("perm: ["); - uint32_t prng = seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete/grow/shrink - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose a weight - lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); - - if (id == (lfs_ssize_t)count || op == 0) { - printf("c%dw%d=%c", id, weight, alpha[i % 26]); - count += 1; - } else if (op == 1) { - printf("d%d", id); - count -= 1; - } else if (op == 2) { - printf("g%dw%d", id, weight); - } else if (op == 3) { - printf("s%dw%d", id, weight); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up a simulation to compare against, fun fact this performs - // worst than our actual rbyd block! - char *sim = malloc(N); - lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); - memset(sim, 0, N); - memset(sim_weights, 0, N*sizeof(lfs_size_t)); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - // set up attr list - struct lfsr_attr *attrs = malloc(2*N*sizeof(struct lfsr_attr)); - unsigned a = 0; - - prng = seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete/grow/shrink - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose a weight - lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); - - // calculate actual id in rbyd space - lfs_ssize_t weighted_id = 0; - for (lfs_ssize_t j = 0; j < id; j++) { - weighted_id += sim_weights[j]; - } - - if (id == (lfs_ssize_t)count || op == 0) { - // update our sim - memmove(sim+id+1, sim+id, count-id); - memmove(sim_weights+id+1, sim_weights+id, - (count-id)*sizeof(lfs_size_t)); - sim[id] = alpha[i % 26]; - sim_weights[id] = weight; - count += 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, weighted_id, NULL, weight, - LFSR_ATTR(MKREG, weighted_id+weight-1, - &alpha[i % 26], 1, - NULL))) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - GROW, weighted_id, NULL, weight, - &attrs[a+1]); - attrs[a+1] = *LFSR_ATTR( - MKREG, weighted_id+weight-1, &alpha[i % 26], 1, - NULL); - a += 2; - } - } else if (op == 1) { - // get the correct weight from the sim - weight = sim_weights[id]; - // update our sim - memmove(sim+id, sim+id+1, count-id-1); - memmove(sim_weights+id, sim_weights+id+1, - (count-id-1)*sizeof(lfs_size_t)); - count -= 1; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, weighted_id, NULL, weight, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - SHRINK, weighted_id, NULL, weight, - NULL); - a += 1; - } - } else if (op == 2) { - // update our sim - sim_weights[id] += weight; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, weighted_id, NULL, weight, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - GROW, weighted_id, NULL, weight, - NULL); - a += 1; - } - } else if (op == 3) { - // don't let shrink go to zero here! this is already hard - // enough to simulate - weight = lfs_min(weight, sim_weights[id]-1); - // update our sim - sim_weights[id] -= weight; - // update our rbyd - if (i < w) { - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, weighted_id, NULL, weight, - NULL)) => 0; - } else { - if (a > 0) { - attrs[a-1].next = &attrs[a]; - } - attrs[a] = *LFSR_ATTR( - SHRINK, weighted_id, NULL, weight, - NULL); - a += 1; - } - } - } - const struct lfsr_attr *unwritten = w < N ? attrs : NULL; - - - // compare rbyd vs simulation - printf("expd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - printf("%c", sim[id]); - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - printf("rbyd: ["); - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - // calculate actual id in rbyd space - lfs_ssize_t weighted_id = 0; - for (lfs_ssize_t j = 0; j < id; j++) { - weighted_id += sim_weights[j]; - } - - int err = lfsr_rbyd_predictedlookup( - &lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, weighted_id, - &tag_, &id_, &data_); - if (!err) { - lfs_ssize_t size = lfsr_rbyd_predictedget( - &lfs, &rbyd, unwritten, - tag_, id_, buffer, 4); - if (size >= 0) { - printf("%.*s", size, buffer); - } else { - printf("?"); - } - } else { - printf("?"); - } - if (id < (lfs_ssize_t)count-1) { - printf(", "); - } - } - printf("]\n"); - - for (lfs_ssize_t id = 0; id < (lfs_ssize_t)count; id++) { - // calculate actual id in rbyd space - lfs_ssize_t weighted_id = 0; - for (lfs_ssize_t j = 0; j < id; j++) { - weighted_id += sim_weights[j]; - } - - lfsr_rbyd_predictedlookup(&lfs, &rbyd, unwritten, - LFSR_TAG_MKREG, weighted_id, - &tag_, &id_, &data_) => 0; - lfsr_rbyd_predictedget(&lfs, &rbyd, unwritten, - tag_, id_, buffer, 4) => 1; - assert(memcmp(&sim[id], buffer, 1) == 0); - } - - // cleanup - free(sim); - free(sim_weights); - free(attrs); - } - } -'''