# Test the low-level rbyd data-structure after = 'test_bd' # test with a number of different erase values defines.ERASE_VALUE = [0xff, 0x00, 0x1b] # set block_size to the full size of disk so we can test arbitrarily # large rbyd trees, we don't really care about block sizes at this # abstraction level # # ok not quite full disk size (we do use the full disk size in bench_rbyd), # but a bit less since erasing the full disk takes time and we don't want to # waste time when testing defines.BLOCK_SIZE = 32768 # TODO we should eventually replace these with lfsr_rbyd_lookup # some internal helpers in = 'lfs.c' code = ''' static lfs_ssize_t lfsr_rbyd_get(lfs_t *lfs, const lfsr_rbyd_t *rbyd, lfs_ssize_t rid, lfsr_tag_t tag, void *buffer, lfs_size_t size) { lfsr_data_t data; int err = lfsr_rbyd_lookup(lfs, rbyd, rid, tag, &data); if (err) { return err; } return lfsr_data_read(lfs, &data, buffer, size); } ''' [cases.test_rbyd_commit] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; ''' [cases.test_rbyd_multi_commit] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; ''' [cases.test_rbyd_commit_fetch_commit] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; // commit with the second attribute lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; ''' # [cases.test_rbyd_fetchmatch] # [cases.test_rbyd_multi_fetchmatch] # TODO we really need to test dense keys... [cases.test_rbyd_lookup] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_lookup] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_get] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => LFS_ERR_NOENT; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; // commit with two attributes, in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_get] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // commit with one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => LFS_ERR_NOENT; // commit with two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; // commit with two attributes, in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), buffer, 4) => 4; lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), buffer, 4) => LFS_ERR_NOENT; ''' [cases.test_rbyd_bifoliate] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // create a split in the leaves // .-'| // 1 1 2 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split the other direction // >b // => .-'| // 2 2 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_bflips] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // ignore a black edge // .----'| // 1 2 1 2 2 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a black edge // b // .-'| => .-'| // 1 2 1 2 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_trifoliate] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // ignore a black edge // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a black edge // >r // .-'| // | >b // .-'| .--|-'| // 2 3 2 3 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_rflips] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // ignore a red edge and black edge // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // ignore a red edge, flip a black edge // | >b // | .-'| | .-'| // 1 2 3 1 2 3 2 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a red edge and black edge // r // .----'| .-'| // | | >b // | .-'| .--|-'| // 1 2 3 1 2 3 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a red edge, ignore a black edge // r // .-'| .-------'| // | >b => | >b // .--|-'| | .-'| // 3 1 2 3 1 2 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_quadrifoliate] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // ignore a red edge and black edge // | .----'| // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // ignore a red edge, flip a black edge // y // .-------'| .-'| // r | >r // .----'| => | .-'| => .--|-'| // | b | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a red edge and black edge // >y // .-'| // b // .----'| => .--|-'| // | b // | .-'| .--|--|-'| // 2 3 4 2 3 4 1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // flip a red edge, ignore a black edge // >y // .-------'| // r // .-'| => | .-'| // | >b | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_rotations] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // all three the same // | .----'| // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // yellow and red alt the same // | .----'| // | b // | .-'| | | .-'| // 1 2 4 1 2 4 3 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // yellow and black alt the same // r | | .----'| => | .-'| // | >b | | b // | .-'| | | .-'| | .--|-'| // 1 4 2 1 4 2 3 1 4 2 3 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // red and black alt the same // >y r | | .----'| => | .-'| // | b // | .-'| | | .-'| .--|--|-'| // 4 1 2 4 1 2 3 4 1 2 3 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_ysplits] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // split a yellow triple, not taking any alt // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the black alt // | b // | | .-'| | | .-'| // 1 2 3 4 1 2 3 4 3 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the red alt // b // .-------'| .-'| // | .--------|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the yellow alt // b // .-------'| .-'| // | b // | .----'| => .-----|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_quintifoliate] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // split a yellow triple, not taking any alt // | .----' | // | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the black alt // | .----' | // | r // | .----'| | | .-'| // | | b // | | .-'| | | .--|-'| // 1 2 4 5 1 2 4 5 3 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the red alt // >b // .-'| // b // | .----'| => | .-----|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // split a yellow triple, taking the yellow alt // >b // .-'| // r // .-------'| .-----|-'| // | b // | .----'| => .--|-----|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_prunes] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // don't prune // | .----' | // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // prune by taking a red alt // b // .-------'| | .-'| // | .-----------|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // prune by taking a yellow alt (this needs to prune during the rflip) // b // .-------'| | .-'| // | b // | .----' | => .--------|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_sextifoliate] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // don't prune // | | .-------'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(6), 0, BUF("\xff\xff\xff\xff", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(6), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(6)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // prune by taking a red alt // b // .-'| .-'| // b // | .----' | => | .--------|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(6), 0, BUF("\xff\xff\xff\xff", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(6), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(6)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); // prune by taking a yellow alt (this needs to prune during the rflip) // b // .-'| .-'| // r // .-------'| | .--------|-'| // | b // | .----' | => .--|--------|-'| // | | 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(-1, UATTR(6), 0, BUF("\xff\xff\xff\xff", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(5)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(6), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(6)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_permutations] defines.N = 'range(1, 8)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[N]; for (unsigned j = 0; j < N; j++) { attrs[j] = LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)); } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("--- summary --\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_multi_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("--- summary --\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_traverse_permutations] defines.N = 'range(1, 8)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[N]; for (unsigned j = 0; j < N; j++) { attrs[j] = LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)); } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' [cases.test_rbyd_multi_traverse_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // 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); // 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("] ---\n"); // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_update_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(j+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // 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); // 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("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // update each tag in permutation order for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa\xaa\xaa", 6)))) => 0; } // check that all tags have been updated lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 6); } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N; printf("--- summary --\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_large] in = 'lfs.c' # ORDER: # 0 = in-order # 1 = reverse-order # 2 = random-order defines.ORDER = [0, 1, 2] code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // create the rbyd tree rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; // keep appending tags until we run out of space // // note, this will likely repeat tags, but that's ok // lfs_size_t count = 0; uint32_t prng = 42; for (lfs_size_t i = 0;; i++) { lfs_size_t x = (ORDER == 0) ? i : (ORDER == 1) ? (((lfs_size_t)-1) - i) : TEST_PRNG(&prng); int err = lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(x & 0x7f), 0, BUF("\xaa\xaa\xaa\xaa", 4)))); if (err == LFS_ERR_RANGE) { break; } assert(err == 0); count = i; } // check that we can still lookup all the tags prng = 42; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (lfs_size_t i = 0; i < count; i++) { lfs_size_t x = (ORDER == 0) ? i : (ORDER == 1) ? (((lfs_size_t)-1) - i) : TEST_PRNG(&prng); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(x & 0x7f), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(x & 0x7f)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } ''' ### Removal testing ### [cases.test_rbyd_remove] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // add and remove one attribute rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, remove the first one rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, remove the second one rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(2)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N; j++) { // print what we are removing to help debugging printf("--- remove: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(j+1)), 0, NULL()))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; for (unsigned k = 0; k < N; k++) { int err = lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(k+1), &rid_, &tag_, NULL, &data_); assert(!err || err == LFS_ERR_NOENT); if (k == j) { if (j == N-1) { assert(err == LFS_ERR_NOENT); } else { assert(!err); assert(tag_ == LFSR_TAG_UATTR(j+1+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } } else { assert(tag_ == LFSR_TAG_UATTR(k+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } } // try appending the tag back to make sure things still work printf("--- append: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(j+1), 0, BUF("\xaa\xaa\xaa\xaa\xaa\xaa", 6)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(k+1), &rid_, &tag_, NULL, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(k+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 6); } else { assert(tag_ == LFSR_TAG_UATTR(k+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + 2; printf("--- summary --\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_traverse_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N; j++) { // print what we are removing to help debugging printf("--- remove: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(j+1)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; tag_ = 0; rid_ = -1; for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; if (k >= j) { assert(tag_ == LFSR_TAG_UATTR(k+1+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } else { assert(tag_ == LFSR_TAG_UATTR(k+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } // cleanup free(backup_block); } ''' [cases.test_rbyd_remove_missing] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // create a tree two attributes rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // try to remove tags that aren't there, this should do nothing lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(3)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(5)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // one last fetch to make sure nothing was broken lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_remove_again] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // create a tree rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(-1, UATTR(3), 0, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(-1, UATTR(4), 0, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(-1, UATTR(5), 0, BUF("\xee\xee\xee\xee", 4)))) => 0; // remove several attributes lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()), LFSR_ATTR(-1, RM(UATTR(3)), 0, NULL()), LFSR_ATTR(-1, RM(UATTR(5)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // try to remove tags that aren't there, this should do nothing lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(3)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(5)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // try to remove the tags again, just to make sure (keep in mind // these removes still commit to the rbyd) lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(3)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(5)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // one last fetch to make sure nothing was broken lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(4), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(4)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(5), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_remove_all] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // commit with one attribute, remove it rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, remove both rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()), LFSR_ATTR(-1, RM(UATTR(2)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // commit with two attributes, remove both in the other order rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(-1, UATTR(2), 0, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(2)), 0, NULL()), LFSR_ATTR(-1, RM(UATTR(1)), 0, NULL()))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(2), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_all_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(j+1), 0, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // 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); // 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("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // remove each tag in permutation order for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(perm[j]+1)), 0, NULL()))) => 0; } // check that all tags are now removed lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } // try resuming from all tags being removed lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(1), 0, BUF("\xaa\xaa\xaa\xaa\xaa\xaa", 6)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 6); for (unsigned j = 1; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N + 1; printf("--- summary --\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # the main purpose of this test is to try to fuzz for failures in the # balancing algorithm [cases.test_rbyd_fuzz_append_removes] defines.N = 'range(1, 33)' defines.SEED = 'range(1000)' # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const char *alpha = "abcdefghijklmnopqrstuvwxyz"; uint8_t buffer[4]; 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.blocks[0]) => 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 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(attr), 0, BUF(&alpha[i % 26], 1)))) => 0; } else { // update our sim sim[attr] = '\0'; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, RM(UATTR(attr)), 0, NULL()))) => 0; } } // 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_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(attr), 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_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(attr), buffer, 4); if (sim[attr]) { assert(size == 1); assert(memcmp(&sim[attr], buffer, 1) == 0); } else { assert(size == LFS_ERR_NOENT); } } // cleanup free(sim); ''' ### Insertion testing ### [cases.test_rbyd_create] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to create one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); // try to create two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); // try to create two in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); // create a third to the right rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); // create a third to the left rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); // create a third in the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); ''' [cases.test_rbyd_multi_create] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to create one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); // try to create two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); // try to create two in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); // create a third to the right rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); // create a third to the left rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); // create a third in the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); ''' [cases.test_rbyd_create_permutations] defines.N = 'range(1, 8)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[N]; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } attrs[j] = LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)); } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_multi_create_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_create_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; uint8_t buffer[4]; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_create_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; uint8_t buffer[4]; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_create_traverse_permutations] defines.N = 'range(1, 8)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[N]; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } attrs[j] = LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)); } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' [cases.test_rbyd_multi_create_traverse_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' [cases.test_rbyd_create_large] in = 'lfs.c' # ORDER: # 0 = in-order # 1 = reverse-order # 2 = random-order defines.ORDER = [0, 1, 2] code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // create the rbyd tree rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; // keep inserting tags until we run out of space // // note, the ids we create this way are both sparse and sometimes // repeated, so we need to mod our current rbyd size to avoid invalid // insertions // uint32_t prng = 42; for (lfs_size_t i = 0;; i++) { uint16_t x = (ORDER == 0) ? (uint16_t)i : (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i) : (uint16_t)TEST_PRNG(&prng); x = x % (rbyd.weight+1); int err = lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(x, REG, +1, BUF(names[x % 6], 4)))); if (err == LFS_ERR_RANGE) { break; } assert(err == 0); } // check that we can at least lookup all the tags // // note with random order we can't check that stored values reliably lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (uint16_t x = 0; x < rbyd.weight; x++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, x, LFSR_TAG_REG, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == x); assert(lfsr_data_size(&data_) == 4); } ''' ### Mixed create and attr testing ### [cases.test_rbyd_mixed] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to create one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); // try to create two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); // try to create two in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); // create a third to the right rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); // create a third to the left rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xcc\xcc", 2)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); // create a third in the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xcc\xcc", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); ''' [cases.test_rbyd_multi_mixed] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to create one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); // try to create two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); // try to create two in the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); // create a third to the right rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); // create a third to the left rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); // create a third in the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 3); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); ''' [cases.test_rbyd_mixed_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[(1+M)*N]; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } attrs[(1+M)*j] = LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)); // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { attrs[(1+M)*j+1+u] = LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)); } } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, (1+M)*N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_UATTR(u+1), buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_multi_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_UATTR(u+1), buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_mixed_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; uint8_t buffer[4]; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_multi_mixed_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; uint8_t buffer[4]; // traverse requires correct biasing of the weights in the rbyd tree // so that lookups return strictly the tag greater than or equal to // the tag requested rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // also try the other direction rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 1); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_mixed_traverse_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // build the attribute list for the current permutation struct lfsr_attr attrs[(1+M)*N]; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } attrs[(1+M)*j] = LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)); // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { attrs[(1+M)*j+1+u] = LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)); } } // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, attrs, (1+M)*N) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == j); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' [cases.test_rbyd_multi_mixed_traverse_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == j); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_mixed_update_permutations] defines.N = 'range(1, 4)' defines.M = 'range(1, 3)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, UATTR(u+1), 0, BUF(names[j % 6], 2)))) => 0; } } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // test all permutations of a given size size_t perm_count = TEST_FACTORIAL(N*M); for (size_t i = 0; i < (PERMUTATION == -1 ? perm_count : 1); i++) { uint32_t perm[N*M]; size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; TEST_PERMUTATION(perm_i, perm, N*M); // print permutation to help debugging printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N*M; j++) { if (j > 0) { printf(", "); } printf("%d", perm[j]); } printf("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // update each tag in permutation order for (unsigned j = 0; j < N*M; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(perm[j]/M, UATTR(perm[j]%M+1), 0, BUF(names[(perm[j]/M) % 6], 3)))) => 0; } // check that all tags have been updated lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_UATTR(u+1), buffer, 4) => 3; assert(memcmp(buffer, names[j % 6], 3) == 0); } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M + N*M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_mixed_remove_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // create given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N*M; j++) { // print what we are removing to help debugging printf("--- remove: rid%jd, %jd ---\n", j/M, (j%M)+1); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j/M, RM(UATTR((j%M)+1)), 0, NULL()))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { int err = lfsr_rbyd_lookupnext(&lfs, &rbyd, k, LFSR_TAG_UATTR(u+1), &rid_, &tag_, NULL, &data_); assert(!err || err == LFS_ERR_NOENT); if (k == j/M && u == j%M) { if (u == M-1 && k == N-1) { assert(err == LFS_ERR_NOENT); } else if (u == M-1) { assert(!err); assert(tag_ == LFSR_TAG_REG); assert(rid_ == k+1); assert(lfsr_data_size(&data_) == 4); } else { assert(!err); assert(tag_ == LFSR_TAG_UATTR(u+1+1)); assert(rid_ == k); assert(lfsr_data_size(&data_) == 2); } } else { assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == k); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, names[k % 6], 2) == 0); } } } // try append the tag back to make sure things still work printf("--- append: rid%jd, %jd ---\n", j/M, (j%M)+1); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j/M, UATTR((j%M)+1), 0, BUF(names[(j/M)%6], 3)))) => 0; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k, LFSR_TAG_UATTR(u+1), &rid_, &tag_, NULL, &data_) => 0; if (k == j/M && u == j%M) { assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == k); assert(lfsr_data_size(&data_) == 3); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 3; assert(memcmp(buffer, names[k % 6], 3) == 0); } else { assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == k); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, names[k % 6], 2) == 0); } } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + 2; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_mixed_remove_all_permutations] defines.N = 'range(1, 4)' defines.M = 'range(1, 3)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, UATTR(u+1), 0, BUF(names[j % 6], 2)))) => 0; } } // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // test all permutations of a given size size_t perm_count = TEST_FACTORIAL(N*M); for (size_t i = 0; i < (PERMUTATION == -1 ? perm_count : 1); i++) { uint32_t perm[N*M]; size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; TEST_PERMUTATION(perm_i, perm, N*M); // print permutation to help debugging printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N*M; j++) { if (j > 0) { printf(", "); } printf("%d", perm[j]); } printf("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // remove each tag in permutation order for (unsigned j = 0; j < N*M; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(perm[j]/M, RM(UATTR(perm[j]%M+1)), 0, NULL()))) => 0; } // check that all tags have been removed lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_UATTR(u+1), buffer, 4) => LFS_ERR_NOENT; } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M + N*M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_mixed_large] in = 'lfs.c' # ORDER: # 0 = in-order # 1 = reverse-order # 2 = random-order defines.ORDER = [0, 1, 2] defines.M = 'range(1, 4)' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // create the rbyd tree rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; // keep inserting tags until we run out of space // // note, the ids we create this way are both sparse and sometimes // repeated, so we need to mod our current rbyd size to avoid invalid // insertions // uint32_t prng = 42; for (lfs_size_t i = 0;; i++) { uint16_t x = (ORDER == 0) ? (uint16_t)i : (ORDER == 1) ? (uint16_t)(((lfs_size_t)-1) - i) : (uint16_t)TEST_PRNG(&prng); x = x % (rbyd.weight+1); // build a single attribute list with all attributes, if this fails // it should fail atomically struct lfsr_attr attrs[1+M]; attrs[0] = LFSR_ATTR(x, REG, +1, BUF(names[x % 6], 4)); for (unsigned u = 0; u < M; u++) { attrs[1+u] = LFSR_ATTR(x, UATTR(u+1), 0, BUF(names[x % 6], 2)); } int err = lfsr_rbyd_commit(&lfs, &rbyd, attrs, 1+M); if (err == LFS_ERR_RANGE) { break; } assert(err == 0); } // check that we can at least lookup all the tags // // note with random order we can't check that stored values reliably lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; for (uint16_t x = 0; x < rbyd.weight; x++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, x, LFSR_TAG_REG, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == x); assert(lfsr_data_size(&data_) == 4); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, x, LFSR_TAG_UATTR(u+1), &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == x); assert(lfsr_data_size(&data_) == 2); } } ''' ### Test unrelated no-rid tags ### [cases.test_rbyd_unrelated_create_permutations] defines.N = 'range(1, 8)' # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // note the data size differences here lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF(names[perm[j] % 6], 1)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; } // try looking up each tag lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), buffer, 4) => 1; assert(memcmp(buffer, names[j % 6], 1) == 0); } for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } // try traversing tags lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfsr_data_t data_; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 1); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 1; assert(memcmp(buffer, names[j % 6], 1) == 0); } for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' [cases.test_rbyd_unrelated_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[4]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // test the given permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // note the data size differences here lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(perm[j]+1), 0, BUF(names[perm[j] % 6], 1)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } // try looking up each tag lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, -1, LFSR_TAG_UATTR(j+1), buffer, 4) => 1; assert(memcmp(buffer, names[j % 6], 1) == 0); } for (unsigned j = 0; j < N; j++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, j, LFSR_TAG_UATTR(u+1), buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } // try traversing tags lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfsr_data_t data_; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(j+1)); assert(rid_ == -1); assert(lfsr_data_size(&data_) == 1); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 1; assert(memcmp(buffer, names[j % 6], 1) == 0); } for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == j); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; assert(memcmp(buffer, names[j % 6], 2) == 0); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' ### Deletion testing ### [cases.test_rbyd_delete] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to delete one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try to delete the other rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try to delete the largest of three rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try to delete the smallest of three rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try to delete the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; ''' [cases.test_rbyd_delete_range] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // try to delete one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; // try to delete the other rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; // try to delete the largest of three rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; // try to delete the smallest of three rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xbb\xbb\xbb\xbb", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xbb\xbb", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; // try to delete the middle rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()))) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 2); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xaa\xaa", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, "\xcc\xcc\xcc\xcc", 4) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 4) => 2; assert(memcmp(buffer, "\xcc\xcc", 2) == 0); lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try deleting each rid for (unsigned j = 0; j < N; j++) { // print what we are deleting to help debugging printf("--- delete: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM, -1, NULL()))) => 0; assert(rbyd.weight == N-1); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N-1); for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 4) => 4; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); } else { assert(memcmp(buffer, names[k % 6], 4) == 0); } } lfsr_rbyd_get(&lfs, &rbyd, N-1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try recreating the rid to make sure things still work printf("--- create: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); } else { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + 2; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_range_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try deleting each rid for (unsigned j = 0; j < N; j++) { // print what we are deleting to help debugging printf("--- delete: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM, -1, NULL()))) => 0; assert(rbyd.weight == N-1); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N-1); for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 4) => 4; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); } else { assert(memcmp(buffer, names[k % 6], 4) == 0); } for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_UATTR(u+1), buffer, 4) => 2; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 2) == 0); } else { assert(memcmp(buffer, names[k % 6], 2) == 0); } } } lfsr_rbyd_get(&lfs, &rbyd, N-1, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, N-1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; // try recreating the rid to make sure things still work printf("--- create: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 6)))) => 0; for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, UATTR(u+1), 0, BUF(names[j % 6], 3)))) => 0; } assert(rbyd.weight == N); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_UATTR(u+1), buffer, 6) => 3; assert(memcmp(buffer, names[k % 6], 3) == 0); } } else { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_REG, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, k, LFSR_TAG_UATTR(u+1), buffer, 6) => 2; assert(memcmp(buffer, names[k % 6], 2) == 0); } } } // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + 1 + 1+M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_traverse_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try deleting each rid for (unsigned j = 0; j < N; j++) { // print what we are deleting to help debugging printf("--- delete: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM, -1, NULL()))) => 0; assert(rbyd.weight == N-1); // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N-1); tag_ = 0; rid_ = -1; for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); } else { assert(memcmp(buffer, names[k % 6], 4) == 0); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } // cleanup free(backup_block); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_traverse_range_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfsr_data_t data_; 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u+1), 0, BUF(names[perm[j] % 6], 2)))) => 0; } } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try deleting each rid for (unsigned j = 0; j < N; j++) { // print what we are deleting to help debugging printf("--- delete: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM, -1, NULL()))) => 0; assert(rbyd.weight == N-1); // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N-1); tag_ = 0; rid_ = -1; for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); } else { assert(memcmp(buffer, names[k % 6], 4) == 0); } for (unsigned u = 0; u < M; u++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(u+1)); assert(rid_ == k); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 2; if (k >= j) { assert(memcmp(buffer, names[(k+1) % 6], 2) == 0); } else { assert(memcmp(buffer, names[k % 6], 2) == 0); } } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT; } // cleanup free(backup_block); } ''' # Note, "delete_all" is a weird state for rbyd trees to be in, since they # don't really have a trunk at this point [cases.test_rbyd_delete_all] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // create and delete one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete two ids in the other order rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete three ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete three ids in the other order rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, RM, -1, NULL()), LFSR_ATTR(1, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; ''' [cases.test_rbyd_delete_all_range] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; uint8_t buffer[4]; // create and delete one rid rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete two ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete two ids in the other order rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete three ids rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // create and delete three ids in the other order rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF( "\xaa\xaa", 2)), LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF( "\xbb\xbb", 2)), LFSR_ATTR(2, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(2, UATTR(1), 0, BUF( "\xcc\xcc", 2)))) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(2, RM, -1, NULL()), LFSR_ATTR(1, RM, -1, NULL()), LFSR_ATTR(0, RM, -1, NULL()))) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_all_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 4)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // 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); // 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("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // delete each rid in permutation order for (unsigned j = 0; j < N; j++) { // adjust rid based on previous deletions uint16_t rid = perm[j]; for (unsigned k = 0; k < j; k++) { if (perm[k] < perm[j]) { rid -= 1; } } lfs_size_t rbyd_weight_before = rbyd.weight; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, RM, -1, NULL()))) => 0; assert(rbyd.weight == rbyd_weight_before-1); } // check that all tags are now removed lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_REG, 0, buffer, 4) => LFS_ERR_NOENT; // try resuming from all tags being removed lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa\xaa\xaa", 6)))) => 0; assert(rbyd.weight == 1); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 6) => 6; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 6) => LFS_ERR_NOENT; // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + 2*N + 1; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_all_range_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; uint8_t buffer[6]; // keep track of the worst case log size lfs_size_t worst_size = 0; size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, REG, +1, BUF(names[j % 6], 4)))) => 0; // note uattrs have a smaller size to help debugging for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, UATTR(u+1), 0, BUF(names[j % 6], 2)))) => 0; } } assert(rbyd.weight == N); // copy block so we can reset after each delete lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // 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); // 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("] ---\n"); // restore backup rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // delete each rid in permutation order for (unsigned j = 0; j < N; j++) { // adjust rid based on previous deletions uint16_t rid = perm[j]; for (unsigned k = 0; k < j; k++) { if (perm[k] < perm[j]) { rid -= 1; } } lfs_size_t rbyd_weight_before = rbyd.weight; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, RM, -1, NULL()))) => 0; assert(rbyd.weight == rbyd_weight_before-1); } // check that all tags are now removed lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 0); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 4) => LFS_ERR_NOENT; // try resuming from all tags being removed lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa\xaa\xaa", 6)))) => 0; for (unsigned u = 0; u < M; u++) { lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, UATTR(u+1), 0, BUF("\xaa\xaa\xaa", 3)))) => 0; } assert(rbyd.weight == 1); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == 1); lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_REG, buffer, 6) => 6; assert(memcmp(buffer, "\xaa\xaa\xaa\xaa\xaa\xaa", 6) == 0); for (unsigned u = 0; u < M; u++) { lfsr_rbyd_get(&lfs, &rbyd, 0, LFSR_TAG_UATTR(u+1), buffer, 6) => 3; assert(memcmp(buffer, "\xaa\xaa\xaa", 3) == 0); } lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_REG, buffer, 6) => LFS_ERR_NOENT; lfsr_rbyd_get(&lfs, &rbyd, 1, LFSR_TAG_UATTR(1), buffer, 6) => LFS_ERR_NOENT; // keep track of the worst size if (rbyd.eoff > worst_size) { worst_size = rbyd.eoff; worst_perm_i = perm_i; } } // cleanup free(backup_block); // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + N + 1+M; printf("--- summary ---\n"); printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { assert(worst_size / n <= 12*(2*lfs_nlog2(n)+1)+4); } ''' # the main purpose of this test is to try to fuzz for failures in the # balancing algorithm [cases.test_rbyd_fuzz_create_deletes] defines.N = 'range(1, 33)' defines.SEED = 'range(1000)' # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const char *alpha = "abcdefghijklmnopqrstuvwxyz"; uint8_t buffer[4]; printf("perm: ["); uint32_t prng = SEED; lfs_size_t count = 0; for (unsigned i = 0; i < N; i++) { // choose an rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose create or delete if (rid == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { printf("c%d=%c", rid, alpha[i % 26]); count += 1; } else { printf("d%d", rid); 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.blocks[0]) => 0; prng = SEED; count = 0; for (unsigned i = 0; i < N; i++) { // choose an rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose create or delete if (rid == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { // update our sim memmove(sim+rid+1, sim+rid, count-rid); sim[rid] = alpha[i % 26]; count += 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(&alpha[i % 26], 1)))) => 0; } else { // update our sim memmove(sim+rid, sim+rid+1, count-rid-1); count -= 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, RM, -1, NULL()))) => 0; } } // compare rbyd vs simulation printf("expd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { printf("%c", sim[rid]); if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); printf("rbyd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)rbyd.weight; rid++) { lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, rid, LFSR_TAG_REG, buffer, 4); if (size >= 0) { printf("%.*s", size, buffer); } else { printf("?"); } if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); assert(count == rbyd.weight); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { lfsr_rbyd_get(&lfs, &rbyd, rid, LFSR_TAG_REG, buffer, 4) => 1; assert(memcmp(&sim[rid], buffer, 1) == 0); } // cleanup free(sim); ''' # Test rbyd weights [cases.test_rbyd_sparse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; // make id0 with weight w1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); // make id2 with weight w2 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +2, BUF("\xbb\xbb\xbb\xbb", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); // make id5 with weight w3 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(3, REG, +3, BUF("\xcc\xcc\xcc\xcc", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); // make id9 with weight w4 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(6, REG, +4, BUF("\xdd\xdd\xdd\xdd", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); // make id14 with weight w5 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(10, REG, +5, BUF("\xee\xee\xee\xee", 4)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); ''' [cases.test_rbyd_sparse_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( // make id0 with weight w1 LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), // make id2 with weight w2 LFSR_ATTR(1, REG, +2, BUF("\xbb\xbb\xbb\xbb", 4)), // make id5 with weight w3 LFSR_ATTR(3, REG, +3, BUF("\xcc\xcc\xcc\xcc", 4)), // make id9 with weight w4 LFSR_ATTR(6, REG, +4, BUF("\xdd\xdd\xdd\xdd", 4)), // make id14 with weight w5 LFSR_ATTR(10, REG, +5, BUF("\xee\xee\xee\xee", 4)))) => 0; // traverse, finding tags and weights lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_sparse_permutations] defines.N = 'range(1, 8)' 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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N*W); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, j*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, j*W+W-1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } } ''' [cases.test_rbyd_sparse_traverse_permutations] defines.N = 'range(1, 8)' 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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N*W); // try traversing all tags tag_ = 0; rid_ = -1; for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; } ''' # Weights mixed with attributes [cases.test_rbyd_sparse_mixed] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; // make id0 with weight w1 rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(3), 0, BUF("unrelated", 9)), LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF("\xaa\xaa", 2)), LFSR_ATTR(0, UATTR(2), 0, BUF("\xaa\xaa", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); // make id2 with weight w2 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF("\xbb\xbb", 2)), LFSR_ATTR(1, RM, +1, NULL()), LFSR_ATTR(2, UATTR(2), 0, BUF("\xbb\xbb", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); // make id5 with weight w3 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(3, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(3, UATTR(1), 0, BUF("\xcc\xcc", 2)), LFSR_ATTR(3, RM, +2, NULL()), LFSR_ATTR(5, UATTR(2), 0, BUF("\xcc\xcc", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); // make id9 with weight w4 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(6, REG, +1, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(6, UATTR(1), 0, BUF("\xdd\xdd", 2)), LFSR_ATTR(6, RM, +3, NULL()), LFSR_ATTR(9, UATTR(2), 0, BUF("\xdd\xdd", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); // make id14 with weight w5 lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(10, REG, +1, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(10, UATTR(1), 0, BUF("\xee\xee", 2)), LFSR_ATTR(10, RM, +4, NULL()), LFSR_ATTR(14, UATTR(2), 0, BUF("\xee\xee", 2)))) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 0, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 2, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 5, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 9, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, 14, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); ''' [cases.test_rbyd_sparse_mixed_traverse] in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(3), 0, BUF("unrelated", 9)), // make id0 with weight w1 LFSR_ATTR(0, REG, +1, BUF("\xaa\xaa\xaa\xaa", 4)), LFSR_ATTR(0, UATTR(1), 0, BUF("\xaa\xaa", 2)), LFSR_ATTR(0, UATTR(2), 0, BUF("\xaa\xaa", 2)), // make id2 with weight w2 LFSR_ATTR(1, REG, +1, BUF("\xbb\xbb\xbb\xbb", 4)), LFSR_ATTR(1, UATTR(1), 0, BUF("\xbb\xbb", 2)), LFSR_ATTR(1, RM, +1, NULL()), LFSR_ATTR(2, UATTR(2), 0, BUF("\xbb\xbb", 2)), // make id5 with weight w3 LFSR_ATTR(3, REG, +1, BUF("\xcc\xcc\xcc\xcc", 4)), LFSR_ATTR(3, UATTR(1), 0, BUF("\xcc\xcc", 2)), LFSR_ATTR(3, RM, +2, NULL()), LFSR_ATTR(5, UATTR(2), 0, BUF("\xcc\xcc", 2)), // make id9 with weight w4 LFSR_ATTR(6, REG, +1, BUF("\xdd\xdd\xdd\xdd", 4)), LFSR_ATTR(6, UATTR(1), 0, BUF("\xdd\xdd", 2)), LFSR_ATTR(6, RM, +3, NULL()), LFSR_ATTR(9, UATTR(2), 0, BUF("\xdd\xdd", 2)), // make id14 with weight w5 LFSR_ATTR(10, REG, +1, BUF("\xee\xee\xee\xee", 4)), LFSR_ATTR(10, UATTR(1), 0, BUF("\xee\xee", 2)), LFSR_ATTR(10, RM, +4, NULL()), LFSR_ATTR(14, UATTR(2), 0, BUF("\xee\xee", 2)))) => 0; // traverse, finding tags and weights lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, 0, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 0); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 0); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 2); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 2); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 5); assert(weight_ == 3); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 5); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 9); assert(weight_ == 4); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 9); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == 14); assert(weight_ == 5); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == 14); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; ''' [cases.test_rbyd_sparse_mixed_permutations] defines.N = 'range(1, 8)' 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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(3), 0, BUF("unrelated", 9)))) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid*W, UATTR(1), 0, BUF(names[perm[j] % 6], 2)), LFSR_ATTR(rid*W, RM, +W-1, NULL()), LFSR_ATTR(rid*W+W-1, UATTR(2), 0, BUF(names[perm[j] % 6], 2)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N*W); lfsr_rbyd_lookupnext(&lfs, &rbyd, -1, LFSR_TAG_UATTR(3), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, j*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, j*W+W-1, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == j*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, j*W+W-1, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == j*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_get(&lfs, &rbyd, j*W+W-1, LFSR_TAG_REG, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } } ''' [cases.test_rbyd_sparse_mixed_traverse_permutations] defines.N = 'range(1, 8)' 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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // test the given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(-1, UATTR(3), 0, BUF("unrelated", 9)))) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid*W, UATTR(1), 0, BUF(names[perm[j] % 6], 2)), LFSR_ATTR(rid*W, RM, +W-1, NULL()), LFSR_ATTR(rid*W+W-1, UATTR(2), 0, BUF(names[perm[j] % 6], 2)))) => 0; } lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N*W); // try traversing all tags tag_ = 0; rid_ = -1; lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(3)); assert(rid_ == -1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 9); for (unsigned j = 0; j < N; j++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == j*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == j*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == j*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; } ''' # other sparse testing, various grow/shrink corner cases [cases.test_rbyd_sparse_grow_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try growing each rid for (unsigned j = 0; j < N; j++) { // print what we are growing to help debugging printf("--- growing: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, RM, +D, NULL()))) => 0; assert(rbyd.weight == N*W+D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W+D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1+D); assert(weight_ == W+D); assert(lfsr_data_size(&data_) == 4); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1+D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); } lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } ''' [cases.test_rbyd_sparse_grupdate_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; uint8_t buffer[6]; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try growing each rid for (unsigned j = 0; j < N; j++) { // print what we are growing to help debugging printf("--- growing: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, GROW(REG), +D, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N*W+D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W+D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1+D); assert(weight_ == W+D); assert(lfsr_data_size(&data_) == 6); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1+D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } } ''' # I don't know if this actually happens in littlefs, but this tests a specific # code path in lfsr_rbyd_append (split altgt + shrinking) [cases.test_rbyd_sparse_grappend_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; uint8_t buffer[6]; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, UATTR(2), +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try growing each rid for (unsigned j = 0; j < N; j++) { // print what we are growing to help debugging printf("--- growing: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, GROW(UATTR(1)), +D, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N*W+D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W+D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == k*W+W-1+D); assert(weight_ == W+D); assert(lfsr_data_size(&data_) == 6); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1+D); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1+D, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1+D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } } ''' [cases.test_rbyd_sparse_shrink_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try shrinking each rid for (unsigned j = 0; j < N; j++) { // print what we are shrinking to help debugging printf("--- shrinking: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, GROW, -D, NULL()))) => 0; assert(rbyd.weight == N*W-D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W-D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1-D); assert(weight_ == W-D); assert(lfsr_data_size(&data_) == 4); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1-D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); } lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } ''' [cases.test_rbyd_sparse_shrupdate_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; uint8_t buffer[6]; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try shrinking each rid for (unsigned j = 0; j < N; j++) { // print what we are shrinking to help debugging printf("--- shrinking: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, GROW(REG), -D, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N*W-D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W-D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1-D); assert(weight_ == W-D); assert(lfsr_data_size(&data_) == 6); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1-D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } } ''' # I don't know if this actually happens in littlefs, but this tests a specific # code path in lfsr_rbyd_append (split altgt + shrinking) [cases.test_rbyd_sparse_shrappend_permutations] defines.N = 'range(1, 7)' defines.W = 5 defines.D = [1, 2] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; uint8_t buffer[6]; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, UATTR(2), +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try shrinking each rid for (unsigned j = 0; j < N; j++) { // print what we are shrinking to help debugging printf("--- shrinking: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, GROW(UATTR(1)), -D, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N*W-D); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W-D); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == k*W+W-1-D); assert(weight_ == W-D); assert(lfsr_data_size(&data_) == 6); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1-D); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else if (k > j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1-D, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1-D); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_UATTR(2), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(2)); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } } ''' [cases.test_rbyd_sparse_delete_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; uint8_t buffer[6]; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try deleting each rid for (unsigned j = 0; j < N; j++) { // print what we are deleting to help debugging printf("--- deleting: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, RM, -W, NULL()))) => 0; assert(rbyd.weight == (N-1)*W); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == (N-1)*W); for (unsigned k = 0; k < N-1; k++) { if (k >= j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[(k+1) % 6], 4) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } // try recreating the rid to make sure things still work printf("--- create: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W, REG, +W, BUF(names[j % 6], 6)))) => 0; assert(rbyd.weight == N*W); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W); for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 6); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 6; assert(memcmp(buffer, names[k % 6], 6) == 0); } else { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 6) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); } } } } ''' [cases.test_rbyd_sparse_attr_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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid*W, REG, +W, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N*W); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try appending an attr to each rid, this should not affect // weights at all! for (unsigned j = 0; j < N; j++) { // print what we are appending to help debugging printf("--- appending: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, UATTR(1), 0, BUF(names[j % 6], 2)))) => 0; assert(rbyd.weight == N*W); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W); for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == k*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } } // now try removing the attr printf("--- removing: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, RM(UATTR(1)), 0, NULL()))) => 0; assert(rbyd.weight == N*W); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W); for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); if (k == j) { lfsr_rbyd_get(&lfs, &rbyd, k*W+W-1, LFSR_TAG_UATTR(1), buffer, 4) => LFS_ERR_NOENT; } } // and try putting the attr back just for good measure printf("--- appending: %d ---\n", j); lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j*W+W-1, UATTR(1), 0, BUF(names[j % 6], 2)))) => 0; assert(rbyd.weight == N*W); lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; assert(rbyd.weight == N*W); for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_REG); assert(rid_ == k*W+W-1); assert(weight_ == W); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 4; assert(memcmp(buffer, names[k % 6], 4) == 0); if (k == j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, k*W+W-1, LFSR_TAG_UATTR(1), &rid_, &tag_, &weight_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR(1)); assert(rid_ == k*W+W-1); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } } } } ''' # Some more fuzzish testing [cases.test_rbyd_fuzz_mixed] defines.N = 'range(1, 33)' defines.M = 3 defines.SEED = 'range(1000)' # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const char *alpha = "abcdefghijklmnopqrstuvwxyz"; uint8_t buffer[4]; 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 rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose an attr uint8_t u = TEST_PRNG(&prng) % M; if (rid == (lfs_ssize_t)count || op == 0) { printf("c%d=%c", rid, alpha[i % 26]); count += 1; } else if (op == 1) { printf("d%d", rid); count -= 1; } else if (op == 2) { printf("a%d,%d=%c", rid, u, alpha[i % 26]); } else if (op == 3) { printf("r%d,%d", rid, 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.blocks[0]) => 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 rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose an attr uint8_t u = TEST_PRNG(&prng) % M; if (rid == (lfs_ssize_t)count || op == 0) { // update our sim memmove(sim+(rid+1)*(M+1), sim+rid*(M+1), (count-rid)*(M+1)); memset(&sim[rid*(M+1)], 0, M+1); sim[rid*(M+1)] = alpha[i % 26]; count += 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(&alpha[i % 26], 1)))) => 0; } else if (op == 1) { // update our sim memmove(sim+rid*(M+1), sim+(rid+1)*(M+1), (count-rid-1)*(M+1)); count -= 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, RM, -1, NULL()))) => 0; } else if (op == 2) { // update our sim sim[rid*(M+1) + u+1] = alpha[i % 26]; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR(u), 0, BUF(&alpha[i % 26], 1)))) => 0; } else if (op == 3) { // update our sim sim[rid*(M+1) + u+1] = '\0'; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, RM(UATTR(u)), 0, NULL()))) => 0; } } // compare rbyd vs simulation printf("expd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { printf("%c", sim[rid*(M+1)]); for (uint8_t u = 0; u < M; u++) { if (sim[rid*(M+1) + u+1]) { printf("%c", sim[rid*(M+1) + u+1]); } else { printf("_"); } } if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); printf("rbyd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)rbyd.weight; rid++) { lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, rid, LFSR_TAG_REG, buffer, 4); if (size >= 0) { printf("%.*s", size, buffer); } else { printf("?"); } for (uint8_t u = 0; u < M; u++) { lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, rid, LFSR_TAG_UATTR(u), buffer, 4); if (size >= 0) { printf("%.*s", size, buffer); } else { printf("_"); } } if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); assert(count == rbyd.weight); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { lfsr_rbyd_get(&lfs, &rbyd, rid, LFSR_TAG_REG, buffer, 4) => 1; assert(memcmp(&sim[rid*(M+1)], buffer, 1) == 0); } // cleanup free(sim); ''' [cases.test_rbyd_fuzz_sparse] defines.N = 'range(1, 33)' defines.W = 5 defines.SEED = 'range(1000)' # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; lfsr_tag_t tag_; lfs_ssize_t rid_; lfs_size_t weight_; lfsr_data_t data_; const char *alpha = "abcdefghijklmnopqrstuvwxyz"; uint8_t buffer[4]; 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 rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose a weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); if (rid == (lfs_ssize_t)count || op == 0) { printf("c%dw%d=%c", rid, weight, alpha[i % 26]); count += 1; } else if (op == 1) { printf("d%d", rid); count -= 1; } else if (op == 2) { printf("g%dw%d", rid, weight); } else if (op == 3) { printf("s%dw%d", rid, 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.blocks[0]) => 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 rid lfs_ssize_t rid = TEST_PRNG(&prng) % (count+1); // choose a weight lfs_size_t weight = 1 + (TEST_PRNG(&prng) % W); // calculate actual rid in rbyd space lfs_ssize_t weighted_rid = 0; for (lfs_ssize_t j = 0; j < rid; j++) { weighted_rid += sim_weights[j]; } if (rid == (lfs_ssize_t)count || op == 0) { // update our sim memmove(sim+rid+1, sim+rid, count-rid); memmove(sim_weights+rid+1, sim_weights+rid, (count-rid)*sizeof(lfs_size_t)); sim[rid] = alpha[i % 26]; sim_weights[rid] = weight; count += 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(weighted_rid, REG, +weight, BUF(&alpha[i % 26], 1)))) => 0; } else if (op == 1) { // get the correct weight from the sim weight_ = sim_weights[rid]; // update our sim memmove(sim+rid, sim+rid+1, count-rid-1); memmove(sim_weights+rid, sim_weights+rid+1, (count-rid-1)*sizeof(lfs_size_t)); count -= 1; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(weighted_rid+weight_-1, RM, -weight_, NULL()))) => 0; } else if (op == 2) { // get the correct weight from the sim weight_ = sim_weights[rid]; // update our sim sim_weights[rid] += weight; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(weighted_rid+weight_-1, GROW, +weight, NULL()))) => 0; } else if (op == 3) { // get the correct weight from the sim weight_ = sim_weights[rid]; // don't let shrink go to zero here! this is already hard enough // to simulate weight = lfs_min(weight, weight_-1); // update our sim sim_weights[rid] -= weight; // update our rbyd lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(weighted_rid+weight_-1, GROW, -weight, NULL()))) => 0; } } // compare rbyd vs simulation printf("expd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { printf("%cw%d", sim[rid], sim_weights[rid]); if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); printf("rbyd: ["); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { // calculate actual rid in rbyd space lfs_ssize_t weighted_rid = 0; for (lfs_ssize_t j = 0; j < rid; j++) { weighted_rid += sim_weights[j]; } int err = lfsr_rbyd_lookupnext(&lfs, &rbyd, weighted_rid, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_); if (!err) { lfs_ssize_t size = lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4); if (size >= 0) { printf("%.*sw%d", size, buffer, weight_); } else { printf("?"); } } else { printf("?"); } if (rid < (lfs_ssize_t)count-1) { printf(", "); } } printf("]\n"); // calculate total weight lfs_size_t total_weight = 0; for (lfs_ssize_t j = 0; j < (lfs_ssize_t)count; j++) { total_weight += sim_weights[j]; } assert(total_weight == rbyd.weight); for (lfs_ssize_t rid = 0; rid < (lfs_ssize_t)count; rid++) { // calculate actual rid in rbyd space lfs_ssize_t weighted_rid = 0; for (lfs_ssize_t j = 0; j < rid; j++) { weighted_rid += sim_weights[j]; } lfsr_rbyd_lookupnext(&lfs, &rbyd, weighted_rid, LFSR_TAG_REG, &rid_, &tag_, &weight_, &data_) => 0; lfsr_rbyd_get(&lfs, &rbyd, rid_, tag_, buffer, 4) => 1; assert(memcmp(&sim[rid], buffer, 1) == 0); } // cleanup free(sim); free(sim_weights); ''' ### Wide-tag things ### [cases.test_rbyd_wide_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // build the attribute list for the current permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)))) => 0; } assert(rbyd.weight == N); // test that we can lookup each attr with a wide lookup lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N; j++) { // print what we are removing to help debugging printf("--- remove: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_REG); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); if (k != j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } } // cleanup free(backup_block); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try replacing each tag for (unsigned j = 0; j < N; j++) { // print what we are replacing to help debugging printf("--- replace: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 3)))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_REG); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; if (k == j) { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 3); } else { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 3); } else { assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } } // cleanup free(backup_block); } ''' [cases.test_rbyd_wide_mixed_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // build the attribute list for the current permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)), LFSR_ATTR(rid, SATTR(0), 0, BUF(names[perm[j] % 6], 1)))) => 0; } assert(rbyd.weight == N); // test that we can lookup each attr with a wide lookup lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_mixed_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)), LFSR_ATTR(rid, SATTR(0), 0, BUF(names[perm[j] % 6], 1)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N; j++) { // print what we are removing to help debugging printf("--- remove: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_REG); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); if (k != j) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_SATTR(0)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 1); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } } // cleanup free(backup_block); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_mixed_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, REG, +1, BUF(names[perm[j] % 6], 4)), LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), 0, BUF(names[perm[j] % 6], 2)), LFSR_ATTR(rid, SATTR(0), 0, BUF(names[perm[j] % 6], 1)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try replacing each tag for (unsigned j = 0; j < N; j++) { // print what we are replacing to help debugging printf("--- replace: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 3)))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_REG); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; if (k == j) { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 3); } else { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 2); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; assert(rid_ == k); assert(tag_ == LFSR_TAG_SATTR(0)); assert(weight_ == 0); assert(lfsr_data_size(&data_) == 1); } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 3); } else { assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 2); } } } // cleanup free(backup_block); } ''' [cases.test_rbyd_wide_weighted_lookup_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -1 => exhaust all permutations # n => reproduce a specific permutation defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; lfs_init(&lfs, CFG) => 0; lfsr_rbyd_t init_rbyd = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // build the attribute list for the current permutation rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), +1, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N); // test that we can lookup each attr with a wide lookup lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], 0) => 0; assert(rbyd.weight == N); for (unsigned j = 0; j < N; j++) { lfsr_tag_t tag_; lfsr_data_t data_; lfsr_rbyd_lookupwide(&lfs, &rbyd, j, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((j + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 4); } } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_weighted_remove_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][4] = { "\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee", "\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), +1, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try removing each tag for (unsigned j = 0; j < N; j++) { // print what we are removing to help debugging printf("--- remove: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // remove with a wide tag lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, RM(WIDE(UATTR)), 0, NULL()))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N-1; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; if (k == j) { assert(rid_ == k+1); assert(tag_ == LFSR_TAG_UATTR((k+1 + SHIFT) & 0x7f)); assert(weight_ == 2); assert(lfsr_data_size(&data_) == 4); } else if (k > j) { assert(rid_ == k+1); assert(tag_ == LFSR_TAG_UATTR((k+1 + SHIFT) & 0x7f)); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); } else { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { if (k == j) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => LFS_ERR_NOENT; } else { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 4); } } } // cleanup free(backup_block); } ''' # NOTE if we separate physical/logical block sizes we may be able to # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_wide_weighted_replace_permutations] defines.N = 'range(1, 7)' defines.SHIFT = [0, 3, -3] # -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 = { .blocks[0] = 0, .eoff = 0, .cksum = 0, .trunk = 0, .weight = 0, }; lfsr_rbyd_t rbyd; const uint8_t names[6][6] = { "\xaa\xaa\xaa\xaa\xaa\xaa", "\xbb\xbb\xbb\xbb\xbb\xbb", "\xcc\xcc\xcc\xcc\xcc\xcc", "\xdd\xdd\xdd\xdd\xdd\xdd", "\xee\xee\xee\xee\xee\xee", "\xff\xff\xff\xff\xff\xff", }; // 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); // 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("] ---\n"); // create given permutation with multiple commits rbyd = init_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; for (unsigned j = 0; j < N; j++) { // adjust rid based on future insertions uint16_t rid = perm[j]; for (unsigned k = j+1; k < N; k++) { if (perm[j] > perm[k]) { rid -= 1; } } // give each attr a subtype based on its rid + SHIFT lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(rid, UATTR((perm[j] + SHIFT) & 0x7f), +1, BUF(names[perm[j] % 6], 4)))) => 0; } assert(rbyd.weight == N); // copy block so we can reset after each remove lfsr_rbyd_t backup_rbyd = rbyd; uint8_t *backup_block = malloc(rbyd.eoff); lfs_bd_read(&lfs, NULL, &lfs.rcache, rbyd.eoff, rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0; // try replacing each tag for (unsigned j = 0; j < N; j++) { // print what we are replacing to help debugging printf("--- replace: %d ---\n", j); rbyd = backup_rbyd; lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0; lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false, rbyd.blocks[0], 0, backup_block, rbyd.eoff, NULL) => 0; lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false, NULL) => 0; // replace with bitwise inverse lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS( LFSR_ATTR(j, WIDE(UATTR(~(j + SHIFT) & 0x7f)), 0, BUF(names[j % 6], 6)))) => 0; // try traversing over the tags lfsr_rbyd_fetch(&lfs, &rbyd, rbyd.blocks[0], CFG->block_size) => 0; lfsr_tag_t tag_ = 0; lfs_ssize_t rid_ = -1; lfs_size_t weight_; lfsr_data_t data_; for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => 0; if (k == j) { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 6); } else { assert(rid_ == k); assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(weight_ == 1); assert(lfsr_data_size(&data_) == 4); } } lfsr_rbyd_lookupnext(&lfs, &rbyd, rid_, tag_+1, &rid_, &tag_, &weight_, &data_) => LFS_ERR_NOENT; // also test that we can lookup each tag with a wide lookup for (unsigned k = 0; k < N; k++) { lfsr_rbyd_lookupwide(&lfs, &rbyd, k, LFSR_TAG_UATTR, &tag_, &data_) => 0; if (k == j) { assert(tag_ == LFSR_TAG_UATTR(~(k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 6); } else { assert(tag_ == LFSR_TAG_UATTR((k + SHIFT) & 0x7f)); assert(lfsr_data_size(&data_) == 4); } } } // cleanup free(backup_block); } '''