diff --git a/lfs.c b/lfs.c index 8f513dc8..40e8d149 100644 --- a/lfs.c +++ b/lfs.c @@ -3460,11 +3460,28 @@ static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_, break; } + // special handling for slice tags, we dereference these + if (lfsr_tag_key(tag) == LFSR_TAG_SLICE) { + lfsr_data_t slice; + err = lfsr_data_readslice(lfs, &data, &slice); + if (err) { + return err; + } + + err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, + lfsr_tag_mode(tag) | LFSR_TAG_DATA, weight, slice); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + // write the tag - err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data); - if (err) { - LFS_ASSERT(err != LFS_ERR_RANGE); - return err; + } else { + err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } } } @@ -3663,8 +3680,20 @@ static lfs_ssize_t lfsr_rbyd_estimate_(lfs_t *lfs, const lfsr_rbyd_t *rbyd, rid = rid__; weight += weight_; + // special handling for slice tags, we dereference these + if (lfsr_tag_key(tag) == LFSR_TAG_SLICE) { + lfsr_data_t slice; + err = lfsr_data_readslice(lfs, &data, &slice); + if (err) { + return err; + } + + dsize += LFSR_ATTR_ESTIMATE + lfsr_data_size(&slice); + // include the cost of this tag - dsize += LFSR_ATTR_ESTIMATE + lfsr_data_size(&data); + } else { + dsize += LFSR_ATTR_ESTIMATE + lfsr_data_size(&data); + } } if (rid_) { @@ -5905,12 +5934,22 @@ static lfs_ssize_t lfsr_mdir_estimate_(lfs_t *lfs, const lfsr_mdir_t *mdir, break; } + // special handling for slice tags, we dereference these + if (lfsr_tag_key(tag) == LFSR_TAG_SLICE) { + lfsr_data_t slice; + err = lfsr_data_readslice(lfs, &data, &slice); + if (err) { + return err; + } + + dsize += LFSR_ATTR_ESTIMATE + lfsr_data_size(&slice); + // special handling for shrub trunks, we need to include the compacted // cost of the shrub in our estimate // // this is what would make lfsr_rbyd_estimate recursive, and why we // need a second function... - if (tag == LFSR_TAG_TRUNK) { + } else if (tag == LFSR_TAG_TRUNK) { lfsr_rbyd_t shrub; err = lfsr_data_readtrunk(lfs, &data, &shrub); if (err) { @@ -9448,10 +9487,6 @@ static lfs_ssize_t lfsr_btree_buildcarve(lfs_t *lfs, const lfsr_btree_t *btree, // and keep track of how our changes will affect our size estimate lfs_off_t rm = 0; lfs_soff_t estimate = 0; - // if we're creating a new btree, add any inlined data to our estimate - if (lfsr_btree_isinlined(btree)) { - estimate += lfsr_btree_weight(btree); - } // try to carve any existing data lfs_off_t pos_ = pos; @@ -9526,20 +9561,25 @@ static lfs_ssize_t lfsr_btree_buildcarve(lfs_t *lfs, const lfsr_btree_t *btree, | LFSR_TAG_SLICE), +(weight_ - overlap_), FROMSLICE(&slice__, &buffer[buffer_off])); buffer += LFSR_SLICE_DSIZE; + + // update our estimate + estimate += lfsr_data_size(&slice__); + } else { attrs[attr_count++] = LFSR_ATTR(bid_, TAG(lfsr_tag_mode(tag) | LFSR_TAG_GROW(WIDE(SLICE))), -overlap_, FROMSLICE(&slice__, &buffer[buffer_off])); buffer += LFSR_SLICE_DSIZE; - } - // update our estimate - estimate -= lfsr_data_size(&slice_) - lfsr_data_size(&slice__); - // we may remove a hole here - if (lfsr_data_size(&slice_) < weight_ - && lfsr_data_size(&slice__) == weight_ - overlap_) { - estimate -= LFSR_ATTR_ESTIMATE; + // update our estimate + estimate -= (lfsr_data_size(&slice_) + - lfsr_data_size(&slice__)); + // we may remove a hole here + if (lfsr_data_size(&slice_) < weight_ + && lfsr_data_size(&slice__) == weight_ - overlap_) { + estimate -= LFSR_ATTR_ESTIMATE; + } } // carve block pointer? @@ -9602,6 +9642,13 @@ static lfs_ssize_t lfsr_btree_buildcarve(lfs_t *lfs, const lfsr_btree_t *btree, FROMSLICE(&slice__, &buffer[buffer_off])); buffer += LFSR_SLICE_DSIZE; + // update our estimate + estimate += lfsr_data_size(&slice__); + // if we split, we may actually create a hole + if (lfsr_data_size(&slice__) < weight_ - overlap_) { + estimate += LFSR_ATTR_ESTIMATE; + } + } else { // we need to account for changes to left sibling here attrs[attr_count++] = LFSR_ATTR(pos+rm+weight_-1, @@ -9609,14 +9656,10 @@ static lfs_ssize_t lfsr_btree_buildcarve(lfs_t *lfs, const lfsr_btree_t *btree, | LFSR_TAG_GROW(WIDE(SLICE))), -overlap_, FROMSLICE(&slice__, &buffer[buffer_off])); buffer += LFSR_SLICE_DSIZE; - } - // update our estimate - estimate -= lfsr_data_size(&slice_) - lfsr_data_size(&slice__); - // if we split, we may actually create a hole - if (lfsr_data_size(&slice_) < weight_ - && overlap_ > weight) { - estimate += LFSR_ATTR_ESTIMATE; + // update our estimate + estimate -= (lfsr_data_size(&slice_) + - lfsr_data_size(&slice__)); } // carve block pointer? @@ -9673,6 +9716,8 @@ static lfs_ssize_t lfsr_btree_buildcarve(lfs_t *lfs, const lfsr_btree_t *btree, | LFSR_TAG_DATA), +lfs_max32(pos, lfsr_btree_weight(btree)), DATA(*(const lfsr_data_t*)btree)); + // update our estimate + estimate += lfsr_data_size((const lfsr_data_t*)btree); // we may be making a hole here if (pos > lfsr_btree_weight(btree)) { estimate += LFSR_ATTR_ESTIMATE; diff --git a/tests/test_files.toml b/tests/test_files.toml index 37085926..9bbb2d0c 100644 --- a/tests/test_files.toml +++ b/tests/test_files.toml @@ -647,17 +647,15 @@ code = ''' } // write first chunk? - if (ORDER == 0) { - if (MASK & 0x1) { + if (MASK & 0x1) { + if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } else { - if (MASK & 0x4) { + } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -665,23 +663,23 @@ code = ''' lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } - } - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } } // write second chunk? - if (MASK & 2) { + if (MASK & 0x2) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -689,33 +687,31 @@ code = ''' lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) => SIZE/2 - CHUNK/2; lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - } - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } } // write third chunk? - if (ORDER == 0) { - if (MASK & 0x4) { + if (MASK & 0x4) { + if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - } else { - if (MASK & 0x1) { + } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -804,9 +800,9 @@ code = ''' memset(sim, 0, SIZE); // we may not write the entire file lfs_off_t size - = (MASK & 0x4) ? SIZE - : (MASK & 0x2) ? SIZE/2 + (CHUNK+2-1)/2 - : (MASK & 0x1) ? CHUNK + = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE + : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 + : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK : 0; // sync? @@ -823,17 +819,15 @@ code = ''' } // write first chunk? - if (ORDER == 0) { - if (MASK & 0x1) { + if (MASK & 0x1) { + if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; - } - } else { - if (MASK & 0x4) { + } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -841,23 +835,23 @@ code = ''' lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; } - } - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } } // write second chunk? - if (MASK & 2) { + if (MASK & 0x2) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -865,33 +859,31 @@ code = ''' lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) => SIZE/2 - CHUNK/2; lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; - } - // sync? - if (SYNC) { - lfsr_file_sync(&lfs, &file) => 0; - } + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } - // remount? - if (REMOUNT) { - lfsr_file_close(&lfs, &file) => 0; - lfsr_unmount(&lfs) => 0; - lfsr_mount(&lfs, CFG) => 0; - lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } } // write third chunk? - if (ORDER == 0) { - if (MASK & 0x4) { + if (MASK & 0x4) { + if (ORDER == 0) { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; - } - } else { - if (MASK & 0x1) { + } else { for (lfs_size_t i = 0; i < CHUNK; i++) { sim[i] = 'a' + (TEST_PRNG(&prng) % 26); } @@ -1496,6 +1488,365 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# these are like the overwrite/hole tests, but with enough rewrites to +# trigger compaction +[cases.test_files_overwrite_compaction] +defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] +defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +# writing this many times guarantees a compaction +defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' +# TODO is setting PROG_SIZE here reasonable? +defines.PROG_SIZE = 64 +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +code = ''' + // format once per test + lfs_t lfs; + int err = lfsr_mount(&lfs, CFG); + if (err) { + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, sim, SIZE) => SIZE; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t w = 0; w < WRITES; w++) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write third chunk? + if (MASK & 0x4) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + // does our file match our simulation? + assert(memcmp(rbuf, sim, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_files_hole_compaction] +defines.SIZE = ['CACHE_SIZE/2', '2*CACHE_SIZE'] +defines.CHUNK = ['CACHE_SIZE/2', '4', '1'] +# bit 0 => first chunk +# bit 1 => middle chunk +# bit 2 => last chunk +defines.MASK = [0, 1, 2, 3, 4, 5, 6, 7] +# 0 => in-order +# 1 => reversed +defines.ORDER = [0, 1] +# writing this many times guarantees a compaction +defines.WRITES = '2*(BLOCK_SIZE/PROG_SIZE)' +# TODO is setting PROG_SIZE here reasonable? +defines.PROG_SIZE = 64 +defines.SYNC = [false, true] +defines.REMOUNT = [false, true] +code = ''' + // format once per test + lfs_t lfs; + int err = lfsr_mount(&lfs, CFG); + if (err) { + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // create a file, truncating in case of powerloss + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; + // simulate our file in ram + uint8_t sim[SIZE]; + uint32_t prng = 42; + memset(sim, 0, SIZE); + // we may not write the entire file + lfs_off_t size + = (MASK & ((ORDER == 0) ? 0x4 : 0x1)) ? SIZE + : (MASK & ((ORDER == 0) ? 0x2 : 0x2)) ? SIZE/2 + (CHUNK+2-1)/2 + : (MASK & ((ORDER == 0) ? 0x1 : 0x4)) ? CHUNK + : 0; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + + // write first chunk? + if (MASK & 0x1) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write second chunk? + if (MASK & 0x2) { + for (lfs_size_t w = 0; w < WRITES; w++) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE/2-CHUNK/2+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE/2 - CHUNK/2, LFS_SEEK_SET) + => SIZE/2 - CHUNK/2; + lfsr_file_write(&lfs, &file, &sim[SIZE/2-CHUNK/2], CHUNK) => CHUNK; + + // sync? + if (SYNC) { + lfsr_file_sync(&lfs, &file) => 0; + } + + // remount? + if (REMOUNT) { + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => 0; + } + } + } + + // write third chunk? + if (MASK & 0x4) { + for (lfs_size_t w = 0; w < WRITES; w++) { + if (ORDER == 0) { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[SIZE-CHUNK+i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, SIZE-CHUNK, LFS_SEEK_SET) => SIZE-CHUNK; + lfsr_file_write(&lfs, &file, &sim[SIZE-CHUNK], CHUNK) => CHUNK; + } else { + for (lfs_size_t i = 0; i < CHUNK; i++) { + sim[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + + lfsr_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0; + lfsr_file_write(&lfs, &file, &sim[0], CHUNK) => CHUNK; + } + } + } + + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our file with stat + struct lfs_info info; + lfsr_stat(&lfs, "hello", &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + + // and with dir read + lfsr_dir_t dir; + lfsr_dir_open(&lfs, &dir, "/") => 0; + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, ".") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "..") == 0); + assert(info.type == LFS_TYPE_DIR); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "hello") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == size); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => size; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => size; + // does our file match our simulation? + assert(memcmp(rbuf, sim, size) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + # fuzz testing [cases.test_files_fuzz_aligned] defines.N = 100