Made rbyd cksums erased-state agnostic

Long story short, rbyd checksums are now fully reproducible. If you
write the same set of tags to any block, you will end up with the same
checksum.

This is actually a bit tricky with littlefs's constraints.

---

The main problem boils down to erased-state. littlefs has a fairly
flexible model for erased-state, and this brings some challenges. In
littlefs, storage goes through 2 states:

1. Erase - Prepare storage for progging. Reads after an erase may return
   arbitrary, but consistent, values.

2. Prog - Program storage with data. Storage must be erased and no progs
   attempted. Reads after a prog must return the new data.

Note in this model erased-state may not be all 0xffs, though it likely
will be for flash. This allows littlefs to support a wide range of
other storage devices: SD, RAM, NVRAM, encryption, ECC, etc.

But this model also means erased-state may be different from block to
block, and even different on later erases of the same block.

And if that wasn't enough of a challenge, _erased-state can contain
perfectly valid commits_. Usually you can expect arbitrary valid cksums
to be rare, but thanks to SD, RAM, etc, modeling erase as a noop, valid
cksums in erased-state is actually very common.

So how do we manage erased-state in our rbyds?

First we need some way to detect it, since we can't prog if we're not
erased. This is accomplished by the forward-looking erased-state cksum
(ecksum):

  .---+---+---+---.     \
  |     commit    |     |
  |               |     |
  |               |     |
  +---+---+---+---+     +-.
  |     ecksum -------. | | <-- ecksum - cksum of erased state
  +---+---+---+---+   | / |
  |     cksum --------|---' <-- cksum - cksum of commit,
  +---+---+---+---+   |                 including ecksum
  |    padding    |   |
  |               |   |
  +---+---+---+---+ \ |
  |     erased    | +-'
  |               | /
  .               .
  .               .

You may have already noticed the start of our problems. The ecksum
contains the erased-state, which is different per-block, and our rbyd
cksum contains the ecksum. We need to include the ecksum so we know if
it's valid, but this means our rbyd cksum changes block to block.

Solving this is simple enough: Stop the rbyd's canonical cksum before
the ecksum, but include the ecksum in the actual cksum we write to disk.

Future commits will need to start from the canonical cksum, so the old
ecksum won't be included in new commits, but this shouldn't be a
problem:

  .---+---+---+---. . . \ . \ . . . . .---+---+---+---.     \   \
  |     commit    |     |   |         |     commit    |     |   |
  |               |     |   +- rbyd   |               |     |   |
  |               |     |   |  cksum  |               |     |   |
  +---+---+---+---+     +-. /         +---+---+---+---+     |   |
  |     ecksum -------. | |           |     ecksum    |     .   .
  +---+---+---+---+   | / |           +---+---+---+---+     .   .
  |     cksum --------|---'           |     cksum     |     .   .
  +---+---+---+---+   |               +---+---+---+---+     .   .
  |    padding    |   |               |    padding    |     .   .
  |               |   |               |               |     .   .
  +---+---+---+---+ \ | . . . . . . . +---+---+---+---+     |   |
  |     erased    | +-'               |     commit    |     |   |
  |               | /                 |               |     |   +- rbyd
  .               .                   |               |     |   |  cksum
  .               .                   +---+---+---+---+     +-. /
                                      |     ecksum -------. | |
                                      +---+---+---+---+   | / |
                                      |     cksum ------------'
                                      +---+---+---+---+   |
                                      |    padding    |   |
                                      |               |   |
                                      +---+---+---+---+ \ |
                                      |     erased    | +-'
                                      |               | /
                                      .               .
                                      .               .

The second challenge is the pesky possibility of existing valid commits.
We need some way to ensure that erased-state following a commit does not
accidentally contain a valid old commit.

This is where are tag's valid bits come into play: The valid bit of each
tag must match the parity of all preceding tags (equivalent to the
parity of the crc32c), and we can use some perturb bits in the cksum tag
to make sure any tags in our erased-state do _not_ match:

  .---+---+---+---. \ . . . . . .---+---+---+---. \   \   \
  |v|    tag      | |           |v|    tag      | |   |   |
  +---+---+---+---+ |           +---+---+---+---+ |   |   |
  |     commit    | |           |     commit    | |   |   |
  |               | |           |               | |   |   |
  +---+---+---+---+ +-----.     +---+---+---+---+ +-. |   |
  |v|p|  tag      | |     |     |v|p|  tag      | | | |   |
  +---+---+---+---+ /     |     +---+---+---+---+ / | |   |
  |     cksum     |       |     |     cksum     |   | .   .
  +---+---+---+---+       |     +---+---+---+---+   | .   .
  |    padding    |       |     |    padding    |   | .   .
  |               |       |     |               |   | .   .
  +---+---+---+---+ . . . | . . +---+---+---+---+   | |   |
  |v---------------- != --'     |v------------------' |   |
  |     erased    |             +---+---+---+---+     |   |
  .               .             |     commit    |     |   |
  .               .             |               |     |   |
                                +---+---+---+---+     +-. +-.
                                |v|p|  tag      |     | | | |
                                +---+---+---+---+     / | / |
                                |     cksum ----------------'
                                +---+---+---+---+       |
                                |    padding    |       |
                                |               |       |
                                +---+---+---+---+       |
                                |v---------------- != --'
                                |     erased    |
                                .               .
                                .               .

New problem! The rbyd cksum contains the valid bits, which contain the
perturb bits, which depends on the erased-state!

And you can't just derive the valid bits from the rbyd's canonical
cksum. This avoids erased-state poisoning, sure, but then nothing in the
new commit depends on the perturb bits! The catch-22 here is that we
need the valid bits to both depend on, and ignore, the erased-state
poisoned perturb bits.

As far as I can tell, the only way around this is to make the rybd's
canonical cksum not include the parity bits. Which is annoying, masking
out bits is not great for bulk cksum calculation...

But this does solve our problem:

  .---+---+---+---. \ . . . . . .---+---+---+---. \   \   \   \
  |v|    tag      | |           |v|    tag      | |   |   o   o
  +---+---+---+---+ |           +---+---+---+---+ |   |   |   |
  |     commit    | |           |     commit    | |   |   |   |
  |               | |           |               | |   |   |   |
  +---+---+---+---+ +-----.     +---+---+---+---+ +-. |   |   |
  |v|p|  tag      | |     |     |v|p|  tag      | | | |   .   .
  +---+---+---+---+ /     |     +---+---+---+---+ / | |   .   .
  |     cksum     |       |     |     cksum     |   | .   .   .
  +---+---+---+---+       |     +---+---+---+---+   | .   .   .
  |    padding    |       |     |    padding    |   | .   .   .
  |               |       |     |               |   | .   .   .
  +---+---+---+---+ . . . | . . +---+---+---+---+   | |   |   |
  |v---------------- != --'     |v------------------' |   o   o
  |     erased    |             +---+---+---+---+     |   |   |
  .               .             |     commit    |     |   |   +- rbyd
  .               .             |               |     |   |   |  cksum
                                +---+---+---+---+     +-. +-. /
                                |v|p|  tag      |     | | o |
                                +---+---+---+---+     / | / |
                                |     cksum ----------------'
                                +---+---+---+---+       |
                                |    padding    |       |
                                |               |       |
                                +---+---+---+---+       |
                                |v---------------- != --'
                                |     erased    |
                                .               .
                                .               .

Note that because each commit's cksum derives from the canonical cksum,
the valid bits and commit cksums no longer contain the same data, so our
parity(m) = parity(crc32c(m)) trick no longer works.

However our crc32c still does tell us a bit about each tag's parity, so
with a couple well-placed xors we can at least avoid needing two
parallel calculations:

  cksum' = crc32c(cksum, m)
  valid' = parity(cksum' xor cksum) xor valid

This also means our commit cksums don't include any information about
the valid bits, since we mask these out before cksum calculation. Which
is a bit concerning, but as far as I can tell not a real problem.

---

An alternative design would be to just keep track of two cksums: A
commit cksum and a canonical cksum.

This would be much simpler, but would also require storing two cksums in
RAM in our lfsr_rbyd_t struct. A bit annoying for our 4-byte crc32cs,
and a bit more than a bit annoying for hypothetical 32-byte sha256s.

It's also not entirely clear how you would update both crc32cs
efficiently. There is a way to xor out the initial state before each
tag, but I think it would still require O(n) cycles of crc32c
calculation...

As it is, the extra bit needed to keep track of commit parity is easy
enough to sneak into some unused sign bits in our lfsr_rbyd_t struct.

---

I've also gone ahead and mixed in the current commit parity into our
cksum's perturb bits, so the commit cksum at least contains _some_
information about the previous parity.

But it's not entirely clear this actually adds anything. Our perturb
bits aren't _required_ to reflect the commit parity, so a very unlucky
power-loss could in theory still make a cksum valid for the wrong
parity.

At least this situation will be caught by later valid bits...

I've also carved out a tag encoding, LFSR_TAG_PERTURB, solely for adding
more perturb bits to commit cksums:

  LFSR_TAG_CKSUM          0x3cpp  v-11 cccc -ppp pppp

  LFSR_TAG_CKSUM          0x30pp  v-11 ---- -ppp pppp
  LFSR_TAG_PERTURB        0x3100  v-11 ---1 ---- ----
  LFSR_TAG_ECKSUM         0x3200  v-11 --1- ---- ----
  LFSR_TAG_GCKSUMDELTA+   0x3300  v-11 --11 ---- ----

  + Planned

This allows for more than 7 perturb bits, and could even mix in the
entire previous commit cksum, if we ever think that is worth the RAM
tradeoff.

LFSR_TAG_PERTURB also has the advantage that it is validated by the
cksum tag's valid bit before being included in the commit cksum, which
indirectly includes the current commit parity. We may eventually want to
use this instead of the cksum tag's perturb bits for this reason, but
right now I'm not sure this tiny bit of extra safety is worth the
minimum 5-byte per commit overhead...

Note if you want perturb bits that are also included in the rbyd's
canonical cksum, you can just use an LFSR_TAG_SHRUBDATA tag. Or any
unreferenced shrub tag really.

---

All of these changes required a decent amount of code, I think mostly
just to keep track of the parity bit. But the isolation of rbyd cksums
from erased-state is necessary for several future-planned features:

           code          stack
  before: 33564           2816
  after:  33916 (+1.0%)   2824 (+0.3%)
This commit is contained in:
Christopher Haster
2024-04-30 12:38:17 -05:00
parent c4fcc78814
commit 8a75a68d8b
9 changed files with 664 additions and 451 deletions
+201 -136
View File
@@ -764,7 +764,8 @@ enum lfsr_tag {
// checksum tags
LFSR_TAG_CKSUM = 0x3000,
LFSR_TAG_ECKSUM = 0x3100,
LFSR_TAG_PERTURB = 0x3100,
LFSR_TAG_ECKSUM = 0x3200,
// in-device only tags, these should never get written to disk
LFSR_TAG_INTERNAL = 0x0800,
@@ -1046,40 +1047,28 @@ static inline bool lfsr_tag_diverging2(
// total: <=11 bytes
#define LFSR_TAG_DSIZE (2+5+4)
static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
static lfs_ssize_t lfsr_bd_readtag_(lfs_t *lfs,
lfs_block_t block, lfs_size_t off, lfs_size_t hint,
lfsr_tag_t *tag_, lfsr_rid_t *weight_, lfs_size_t *size_,
uint32_t *cksum_) {
// read the largest possible tag size
uint8_t tag_buf[LFSR_TAG_DSIZE];
lfs_size_t tag_dsize = lfs_min32(LFSR_TAG_DSIZE, lfs->cfg->block_size-off);
if (tag_dsize < 4) {
return LFS_ERR_CORRUPT;
}
int err = lfsr_bd_read(lfs, block, off, hint, &tag_buf, tag_dsize);
if (err) {
LFS_ASSERT(err < 0);
return err;
}
if (tag_dsize < 2) {
return LFS_ERR_CORRUPT;
}
lfsr_tag_t tag
= ((lfsr_tag_t)tag_buf[0] << 8)
| ((lfsr_tag_t)tag_buf[1] << 0);
lfs_ssize_t d = 2;
if (cksum_) {
// on-disk, the tags valid bit must reflect the parity of the
// preceding data, fortunately for crc32c, this is the same as the
// parity of the crc
//
// note we need to do this before leb128 decoding as we may not have
// valid leb128 if we're erased, but we shouldn't treat a truncated
// leb128 here as corruption
if ((tag >> 15) != (lfs_popc(*cksum_) & 1)) {
return LFS_ERR_INVAL;
}
}
lfsr_rid_t weight;
lfs_ssize_t d_ = lfs_fromleb128(&weight, &tag_buf[d], tag_dsize-d);
if (d_ < 0) {
@@ -1098,19 +1087,36 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
LFS_ASSERT(size <= 0x0fffffff);
d += d_;
// optional checksum
// ignore the valid bit when calculating optional checksum
tag_buf[0] &= ~0x80;
if (cksum_) {
*cksum_ = lfs_crc32c(*cksum_, tag_buf, d);
}
// save what we found, clearing the valid bit from the tag, note we
// checked this earlier
*tag_ = tag & 0x7fff;
// save what we found
*tag_ = tag;
*weight_ = weight;
*size_ = size;
return d;
}
// clear the valid bit, since most readtag calls don't care
static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
lfs_block_t block, lfs_size_t off, lfs_size_t hint,
lfsr_tag_t *tag_, lfsr_rid_t *weight_, lfs_size_t *size_,
uint32_t *cksum_) {
lfs_ssize_t d = lfsr_bd_readtag_(lfs, block, off, hint,
tag_, weight_, size_, cksum_);
if (d < 0) {
return d;
}
if (tag_) {
*tag_ &= 0x7fff;
}
return d;
}
static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
lfs_block_t block, lfs_size_t off,
lfsr_tag_t tag, lfsr_rid_t weight, lfs_size_t size,
@@ -1122,9 +1128,6 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
// size should not exceed 28-bits
LFS_ASSERT(size <= 0x0fffffff);
// make sure to include the parity of the current crc
tag |= (lfs_popc(*cksum_) & 1) << 15;
// encode into a be16 and pair of leb128s
uint8_t tag_buf[LFSR_TAG_DSIZE];
tag_buf[0] = (uint8_t)(tag >> 8);
@@ -1144,12 +1147,18 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
d += d_;
int err = lfsr_bd_prog(lfs, block, off, &tag_buf, d,
cksum_);
NULL);
if (err) {
LFS_ASSERT(err < 0);
return err;
}
// ignore the valid bit when calculating optional checksum
tag_buf[0] &= ~0x80;
if (cksum_) {
*cksum_ = lfs_crc32c(*cksum_, tag_buf, d);
}
return d;
}
@@ -1738,30 +1747,6 @@ typedef struct lfsr_tinfo {
//#endif
// erased-state checksum stuff
static int lfsr_ecksum_validate(lfs_t *lfs, const lfsr_ecksum_t *ecksum,
lfs_block_t block, lfs_size_t off) {
LFS_ASSERT(ecksum->cksize != -1);
LFS_ASSERT(off < lfs->cfg->block_size);
// check that erased-state matches our checksum, if this fails
// most likely a write was interrupted
uint32_t cksum_ = 0;
int err = lfsr_bd_cksum(lfs, block, off, 0, ecksum->cksize,
&cksum_);
if (err) {
return err;
}
// ecksum mismatch?
if (cksum_ != ecksum->cksum) {
return LFS_ERR_CORRUPT;
}
return 0;
}
// erased-state checksum on-disk encoding
// ecksum encoding:
@@ -2170,6 +2155,7 @@ static void lfs_alloc_ckpoint(lfs_t *lfs);
/// Red-black-yellow Dhara tree operations ///
#define LFSR_RBYD_ISSHRUB 0x80000000
#define LFSR_RBYD_PARITY 0x80000000
// helper functions
static inline bool lfsr_rbyd_isshrub(const lfsr_rbyd_t *rbyd) {
@@ -2188,6 +2174,14 @@ static inline bool lfsr_rbyd_isfetched(const lfsr_rbyd_t *rbyd) {
return !(lfsr_rbyd_hastrunk(rbyd) && rbyd->eoff == 0);
}
static inline bool lfsr_rbyd_parity(const lfsr_rbyd_t *rbyd) {
return (lfs_size_t)rbyd->eoff >> (8*sizeof(lfs_size_t)-1);
}
static inline lfs_size_t lfsr_rbyd_eoff(const lfsr_rbyd_t *rbyd) {
return rbyd->eoff & ~LFSR_RBYD_PARITY;
}
static inline int lfsr_rbyd_cmp(
const lfsr_rbyd_t *a,
const lfsr_rbyd_t *b) {
@@ -2212,6 +2206,11 @@ static int lfsr_rbyd_alloc(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfs_block_t block, lfs_ssize_t trunk) {
// set up some initial state
rbyd->blocks[0] = block;
rbyd->trunk = (trunk & LFSR_RBYD_ISSHRUB) | 0;
rbyd->eoff = 0;
// ignore the shrub bit here
trunk &= ~LFSR_RBYD_ISSHRUB;
@@ -2223,15 +2222,12 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
return err;
}
rbyd->blocks[0] = block;
rbyd->eoff = 0;
rbyd->trunk = (trunk & LFSR_RBYD_ISSHRUB) | 0;
// temporary state until we validate a cksum
uint32_t cksum_ = cksum;
bool parity_ = lfs_popc(cksum) & 1;
lfs_size_t off = sizeof(uint32_t);
lfs_size_t trunk_ = 0;
lfs_size_t trunk__ = 0;
bool wastrunk = false;
lfsr_rid_t weight = 0;
lfsr_rid_t weight_ = 0;
@@ -2240,41 +2236,50 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
// scan tags, checking valid bits, cksums, etc
while (off < lfs->cfg->block_size
&& (!trunk || rbyd->eoff <= (lfs_size_t)trunk)) {
&& (!trunk || lfsr_rbyd_eoff(rbyd) <= (lfs_size_t)trunk)) {
lfsr_tag_t tag;
lfsr_rid_t weight__;
lfs_size_t size;
lfs_ssize_t d = lfsr_bd_readtag(lfs, block, off, -1,
&tag, &weight__, &size, &cksum);
uint32_t cksum__ = cksum_;
lfs_ssize_t d = lfsr_bd_readtag_(lfs, block, off, -1,
&tag, &weight__, &size, &cksum__);
if (d < 0) {
if (d == LFS_ERR_INVAL || d == LFS_ERR_CORRUPT) {
// if we are breaking for any reason other than the tag's
// valid bit, our ecksum must be invalid
if (d != LFS_ERR_INVAL) {
ecksum.cksize = -1;
}
if (d == LFS_ERR_CORRUPT) {
break;
}
return d;
}
lfs_size_t off_ = off + d;
// parity mismatch?
if ((tag >> 15) != parity_) {
break;
}
tag &= 0x7fff;
parity_ ^= lfs_popc(cksum_ ^ cksum__) & 1;
cksum_ = cksum__;
// tag goes out of range?
if (!lfsr_tag_isalt(tag) && off_ + size > lfs->cfg->block_size) {
break;
}
// take care of cksum
if (!lfsr_tag_isalt(tag)) {
// not an end-of-commit cksum
if (!lfsr_tag_isalt(tag) && lfsr_tag_suptype(tag) != LFSR_TAG_CKSUM) {
if (lfsr_tag_suptype(tag) != LFSR_TAG_CKSUM) {
// cksum the entry, hopefully leaving it in the cache
uint32_t cksum__ = cksum_;
err = lfsr_bd_cksum(lfs, block, off_, -1, size,
&cksum);
&cksum__);
if (err) {
if (err == LFS_ERR_CORRUPT) {
break;
}
return err;
}
parity_ ^= lfs_popc(cksum_ ^ cksum__) & 1;
cksum_ = cksum__;
// found an ecksum? save for later
if (tag == LFSR_TAG_ECKSUM) {
@@ -2294,19 +2299,19 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
}
// is an end-of-commit cksum
} else if (!lfsr_tag_isalt(tag)) {
uint32_t cksum_ = 0;
} else {
uint32_t cksum__ = 0;
err = lfsr_bd_read(lfs, block, off_, -1,
&cksum_, sizeof(uint32_t));
&cksum__, sizeof(uint32_t));
if (err) {
if (err == LFS_ERR_CORRUPT) {
break;
}
return err;
}
cksum_ = lfs_fromle32_(&cksum_);
cksum__ = lfs_fromle32_(&cksum__);
if (cksum != cksum_) {
if (cksum_ != cksum__) {
// uh oh, cksums don't match
break;
}
@@ -2318,18 +2323,23 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfs->seed = lfs_crc32c(lfs->seed, &cksum, sizeof(uint32_t));
// save what we've found so far
rbyd->eoff = off_ + size;
rbyd->eoff
= ((lfs_size_t)parity_ << (8*sizeof(lfs_size_t)-1))
| (off_ + size);
rbyd->cksum = cksum;
rbyd->trunk = (LFSR_RBYD_ISSHRUB & rbyd->trunk) | trunk_;
rbyd->weight = weight;
// revert to data checksum
cksum_ = cksum;
}
}
// found a trunk of a tree?
if (lfsr_tag_istrunk(tag)
&& (!trunk || (lfs_size_t)trunk >= off || wastrunk)) {
&& (!trunk || off <= (lfs_size_t)trunk || trunk__)) {
// start of trunk?
if (!wastrunk) {
wastrunk = true;
if (!trunk__) {
// keep track of trunk's entry point
trunk__ = off;
// reset weight
@@ -2346,13 +2356,14 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
// end of trunk?
if (!lfsr_tag_isalt(tag)) {
wastrunk = false;
// update most recent trunk and weight, unless we are a
// shrub trunk
if (!lfsr_tag_isshrub(tag)) {
// update data checksum
cksum = cksum_;
// update trunk and weight, unless we are a shrub trunk
if (!lfsr_tag_isshrub(tag) || trunk__ == (lfs_size_t)trunk) {
trunk_ = trunk__;
weight = weight_;
}
trunk__ = 0;
}
}
@@ -2369,17 +2380,36 @@ static int lfsr_rbyd_fetch(lfs_t *lfs, lfsr_rbyd_t *rbyd,
return LFS_ERR_CORRUPT;
}
// did we end on a valid commit? we may have an erased state
// did we end on a valid commit? we may have erased-state
bool erased = false;
if (rbyd->eoff < lfs->cfg->block_size
&& rbyd->eoff % lfs->cfg->prog_size == 0
if (lfsr_rbyd_eoff(rbyd) < lfs->cfg->block_size
&& lfsr_rbyd_eoff(rbyd) % lfs->cfg->prog_size == 0
&& ecksum.cksize != -1) {
err = lfsr_ecksum_validate(lfs, &ecksum, rbyd->blocks[0], rbyd->eoff);
// TODO is this correct for erased=corrupt?
uint8_t e = 0;
err = lfsr_bd_read(lfs,
rbyd->blocks[0], lfsr_rbyd_eoff(rbyd), ecksum.cksize,
&e, 1);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
erased = (err != LFS_ERR_CORRUPT);
// the next valid bit must _not_ match, or a commit was attempted
if ((e >> 7) != lfsr_rbyd_parity(rbyd)) {
// check that erased-state matches our checksum, if this fails
// most likely a write was interrupted
uint32_t ecksum_ = lfs_crc32c(0, &e, 1);
int err = lfsr_bd_cksum(lfs,
rbyd->blocks[0], lfsr_rbyd_eoff(rbyd)+1, 0,
ecksum.cksize-1,
&ecksum_);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
// found erased-state?
erased = (ecksum_ == ecksum.cksum);
}
}
if (!erased) {
rbyd->eoff = -1;
@@ -2585,46 +2615,70 @@ static int lfsr_rbyd_suplookup(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
static int lfsr_rbyd_appendrev(lfs_t *lfs, lfsr_rbyd_t *rbyd, uint32_t rev) {
// should only be called before any tags are written
LFS_ASSERT(rbyd->eoff == 0);
LFS_ASSERT(rbyd->cksum == 0);
// revision count stored as le32, we don't use a leb128 encoding as we
// intentionally allow the revision count to overflow
uint8_t rev_buf[sizeof(uint32_t)];
lfs_tole32_(rev, &rev_buf);
int err = lfsr_bd_prog(lfs, rbyd->blocks[0], rbyd->eoff,
uint32_t cksum_ = rbyd->cksum;
int err = lfsr_bd_prog(lfs, rbyd->blocks[0], lfsr_rbyd_eoff(rbyd),
&rev_buf, sizeof(uint32_t),
&rbyd->cksum);
&cksum_);
if (err) {
return err;
}
rbyd->eoff += sizeof(uint32_t);
// update eoff, xor cksum parity
rbyd->eoff
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
<< (8*sizeof(lfs_size_t)-1))
+ sizeof(uint32_t);
rbyd->cksum = cksum_;
return 0;
}
// other low-level appends
static int lfsr_rbyd_appendtag(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfsr_tag_t tag, lfsr_rid_t weight, lfs_size_t size) {
lfs_ssize_t d = lfsr_bd_progtag(lfs, rbyd->blocks[0], rbyd->eoff,
// include the previous tag parity
tag ^= (lfsr_tag_t)lfsr_rbyd_parity(rbyd) << 15;
uint32_t cksum_ = rbyd->cksum;
lfs_ssize_t d = lfsr_bd_progtag(lfs,
rbyd->blocks[0], lfsr_rbyd_eoff(rbyd),
tag, weight, size,
&rbyd->cksum);
&cksum_);
if (d < 0) {
return d;
}
rbyd->eoff += d;
// update eoff, xor cksum parity
rbyd->eoff
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
<< (8*sizeof(lfs_size_t)-1))
+ d;
rbyd->cksum = cksum_;
return 0;
}
static int lfsr_rbyd_appenddata(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfsr_data_t data) {
int err = lfsr_bd_progdata(lfs, rbyd->blocks[0], rbyd->eoff,
uint32_t cksum_ = rbyd->cksum;
int err = lfsr_bd_progdata(lfs, rbyd->blocks[0], lfsr_rbyd_eoff(rbyd),
data,
&rbyd->cksum);
&cksum_);
if (err) {
return err;
}
rbyd->eoff += lfsr_data_size(data);
// update eoff, xor cksum parity
rbyd->eoff
+= ((lfs_popc(rbyd->cksum ^ cksum_) & 1)
<< (8*sizeof(lfs_size_t)-1))
+ lfsr_data_size(data);
rbyd->cksum = cksum_;
return 0;
}
@@ -2650,7 +2704,7 @@ static int lfsr_rbyd_prepareappend(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
LFS_ASSERT(lfsr_rbyd_isfetched(rbyd));
// we can't do anything if we're not erased
if (rbyd->eoff >= lfs->cfg->block_size) {
if (lfsr_rbyd_eoff(rbyd) >= lfs->cfg->block_size) {
return LFS_ERR_RANGE;
}
@@ -2683,7 +2737,7 @@ static int lfsr_rbyd_p_flush(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfsr_tag_t alt = p[3-1-i].alt;
lfsr_rid_t weight = p[3-1-i].weight;
lfs_size_t jump = (p[3-1-i].jump)
? rbyd->eoff - p[3-1-i].jump
? lfsr_rbyd_eoff(rbyd) - p[3-1-i].jump
: 0;
int err = lfsr_rbyd_appendtag(lfs, rbyd, alt, weight, jump);
@@ -2859,7 +2913,7 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
trunk:;
// the new trunk starts here
lfs_size_t trunk_ = rbyd->eoff;
lfs_size_t trunk_ = lfsr_rbyd_eoff(rbyd);
// keep track of bounds as we descend down the tree
//
@@ -3360,6 +3414,9 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
return err;
}
// save the data checksum
uint32_t cksum = rbyd->cksum;
// align to the next prog unit
//
// this gets a bit complicated as we have two types of cksums:
@@ -3384,45 +3441,43 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// | cksum | cksum cksum: 1 le32 4 bytes
// '---+---+---+---' total: <=11 bytes
//
lfs_size_t aligned_eoff = lfs_alignup(
rbyd->eoff + 2+1+1+4+4 + 2+1+4+4,
lfs_size_t off_ = lfs_alignup(
lfsr_rbyd_eoff(rbyd) + 2+1+1+4+4 + 2+1+4+4,
lfs->cfg->prog_size);
// space for ecksum?
uint8_t perturb = 0;
if (aligned_eoff < lfs->cfg->block_size) {
// read the leading byte in case we need to change the expected
// value of the next tag's valid bit
uint8_t e = 0;
if (off_ < lfs->cfg->block_size) {
// read the leading byte in case we need to perturb the next tag
err = lfsr_bd_read(lfs,
rbyd->blocks[0], aligned_eoff, lfs->cfg->prog_size,
&perturb, 1);
rbyd->blocks[0], off_, lfs->cfg->prog_size,
&e, 1);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
// find the expected ecksum, don't bother avoiding a reread of the
// perturb byte, as it should still be in our cache
lfsr_ecksum_t ecksum = {.cksize=lfs->cfg->prog_size};
// calculate the erased-state checksum
lfsr_ecksum_t ecksum;
ecksum.cksize = lfs->cfg->prog_size;
ecksum.cksum = lfs_crc32c(0, &e, 1);
err = lfsr_bd_cksum(lfs,
rbyd->blocks[0], aligned_eoff, ecksum.cksize,
ecksum.cksize,
rbyd->blocks[0], off_+1, ecksum.cksize-1,
ecksum.cksize-1,
&ecksum.cksum);
if (err && err != LFS_ERR_CORRUPT) {
return err;
}
uint8_t ecksum_buf[LFSR_ECKSUM_DSIZE];
lfsr_data_t ecksum_data = lfsr_data_fromecksum(&ecksum, ecksum_buf);
err = lfsr_rbyd_appendattr_(lfs, rbyd,
LFSR_TAG_ECKSUM, 0, ecksum_data);
LFSR_TAG_ECKSUM, 0, LFSR_DATA_FROMECKSUM(&ecksum));
if (err) {
return err;
}
// at least space for a cksum?
} else if (rbyd->eoff + 2+1+4+4 <= lfs->cfg->block_size) {
} else if (lfsr_rbyd_eoff(rbyd) + 2+1+4+4 <= lfs->cfg->block_size) {
// note this implicitly marks the rbyd as unerased
aligned_eoff = lfs->cfg->block_size;
off_ = lfs->cfg->block_size;
// not even space for a cksum? we can't finish the commit
} else {
@@ -3431,38 +3486,43 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// build end-of-commit cksum
//
// note padding-size depends on leb-encoding depends on padding-size, to
// get around this catch-22 we just always write a fully-expanded leb128
// encoding
// note padding-size depends on leb-encoding depends on padding-size
// depends leb-encoding depends on... to get around this catch-22 we
// just always write a fully-expanded leb128 encoding
uint8_t cksum_buf[2+1+4+4];
cksum_buf[0] = (LFSR_TAG_CKSUM >> 8) | ((lfs_popc(rbyd->cksum) & 1) << 7);
cksum_buf[1] = 0;
cksum_buf[0] = (uint8_t)(LFSR_TAG_CKSUM >> 8);
cksum_buf[1] = (uint8_t)(LFSR_TAG_CKSUM >> 0)
// include tag parity in the perturb bits
| ((uint8_t)lfsr_rbyd_parity(rbyd) << 1);
cksum_buf[2] = 0;
lfs_size_t padding = aligned_eoff - (rbyd->eoff + 2+1+4);
lfs_size_t padding = off_ - (lfsr_rbyd_eoff(rbyd) + 2+1+4);
cksum_buf[3] = 0x80 | (0x7f & (padding >> 0));
cksum_buf[4] = 0x80 | (0x7f & (padding >> 7));
cksum_buf[5] = 0x80 | (0x7f & (padding >> 14));
cksum_buf[6] = 0x00 | (0x7f & (padding >> 21));
rbyd->cksum = lfs_crc32c(rbyd->cksum, cksum_buf, 2+1+4);
// we can't let the next tag appear as valid, so intentionally perturb the
// commit if this happens, note parity(crc(m)) == parity(m) with crc32c,
// so we can really change any bit to make this happen, we've reserved a bit
// in cksum tags just for this purpose
if ((lfs_popc(rbyd->cksum) & 1) == (perturb >> 7)) {
// calculate checksum before tag parity
uint32_t cksum_ = lfs_crc32c(rbyd->cksum, cksum_buf, 2+1+4);
// xor in the tag parity
cksum_buf[0] ^= (uint8_t)lfsr_rbyd_parity(rbyd) << 7;
// find the new parity
bool parity_ = lfsr_rbyd_parity(rbyd)
^ (lfs_popc(rbyd->cksum ^ cksum_) & 1);
// and intentionally perturb the commit so the next tag appears invalid
if ((e >> 7) == parity_) {
cksum_buf[1] ^= 0x01;
rbyd->cksum ^= 0xef306b19; // note crc(a ^ b) == crc(a) ^ crc(b)
cksum_ ^= 0xef306b19;
parity_ ^= 0x1;
}
lfs_tole32_(rbyd->cksum, &cksum_buf[2+1+4]);
lfs_tole32_(cksum_, &cksum_buf[2+1+4]);
err = lfsr_bd_prog(lfs, rbyd->blocks[0], rbyd->eoff,
err = lfsr_bd_prog(lfs, rbyd->blocks[0], lfsr_rbyd_eoff(rbyd),
cksum_buf, 2+1+4+4,
NULL);
if (err) {
return err;
}
rbyd->eoff += 2+1+4+4;
// flush our caches, finalizing the commit on-disk
err = lfsr_bd_sync(lfs);
@@ -3470,7 +3530,12 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
return err;
}
rbyd->eoff = aligned_eoff;
// update the eoff and parity
rbyd->eoff
= ((lfs_size_t)parity_ << (8*sizeof(lfs_size_t)-1))
| off_;
// revert to data checksum
rbyd->cksum = cksum;
return 0;
}
@@ -3691,7 +3756,7 @@ static int lfsr_rbyd_appendcompaction(lfs_t *lfs, lfsr_rbyd_t *rbyd,
off = lfs_max32(off, sizeof(uint32_t));
// empty rbyd? write a null tag so our trunk can still point to something
if (rbyd->eoff == off) {
if (lfsr_rbyd_eoff(rbyd) == off) {
err = lfsr_rbyd_appendtag(lfs, rbyd,
// mark as shrub if we are a shrub
(lfsr_rbyd_isshrub(rbyd) ? LFSR_TAG_SHRUB : 0)
@@ -3712,7 +3777,7 @@ static int lfsr_rbyd_appendcompaction(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfs_size_t layer = off;
lfsr_rid_t weight = 0;
while (true) {
lfs_size_t layer_ = rbyd->eoff;
lfs_size_t layer_ = lfsr_rbyd_eoff(rbyd);
off = layer;
while (off < layer_) {
// connect two trunks together with a new binary trunk
@@ -3782,7 +3847,7 @@ static int lfsr_rbyd_appendcompaction(lfs_t *lfs, lfsr_rbyd_t *rbyd,
LFSR_TAG_LE,
tag),
weight,
rbyd->eoff - trunk);
lfsr_rbyd_eoff(rbyd) - trunk);
if (err) {
return err;
}
@@ -3872,7 +3937,7 @@ static int lfsr_rbyd_appendgdelta(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
static int lfsr_rbyd_appendshrub(lfs_t *lfs, lfsr_rbyd_t *rbyd,
const lfsr_shrub_t *shrub) {
// keep track of the start of the new tree
lfs_size_t off = rbyd->eoff;
lfs_size_t off = lfsr_rbyd_eoff(rbyd);
// mark as shrub
rbyd->trunk |= LFSR_RBYD_ISSHRUB;
@@ -10123,7 +10188,7 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_file_t *file, lfsr_bid_t bid,
// does our estimate exceed our shrub_size? need to recalculate an
// accurate estimate
lfs_ssize_t estimate = (alloc)
? (lfs_size_t)-1
? -1
: file->bshrub.u.bshrub.eoff;
// this double condition avoids overflow issues
if ((lfs_size_t)estimate > lfs->cfg->shrub_size
+3 -2
View File
@@ -343,11 +343,12 @@ typedef struct lfsr_rbyd {
lfs_block_t blocks[2];
// sign(trunk)=0 => normal rbyd
// sign(trunk)=1 => shrub rbyd
lfs_ssize_t trunk;
// sign(eoff) => commit parity
// eoff=0, trunk=0 => not yet committed
// eoff=0, trunk>0 => not yet fetched
// eoff>=block_size => rbyd not erased/needs compaction
lfs_ssize_t trunk;
lfs_size_t eoff;
lfs_ssize_t eoff;
uint32_t cksum;
} lfsr_rbyd_t;
+32 -18
View File
@@ -41,7 +41,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -133,6 +134,9 @@ def crc32c(data, crc=0):
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def fromle32(data):
return struct.unpack('<I', data[0:4].ljust(4, b'\0'))[0]
@@ -582,13 +586,14 @@ class Bmap:
# our core rbyd type
class Rbyd:
def __init__(self, block, data, rev, eoff, trunk, weight):
def __init__(self, block, data, rev, eoff, trunk, weight, cksum):
self.block = block
self.data = data
self.rev = rev
self.eoff = eoff
self.trunk = trunk
self.weight = weight
self.cksum = cksum
self.redund_blocks = []
@property
@@ -643,7 +648,10 @@ class Rbyd:
rev = fromle32(data[0:4])
cksum = 0
cksum_ = crc32c(data[0:4])
cksum__ = cksum_
parity__ = parity(cksum_)
eoff = 0
eoff_ = None
j_ = 4
trunk_ = 0
trunk__ = 0
@@ -651,13 +659,14 @@ class Rbyd:
weight = 0
weight_ = 0
weight__ = 0
wastrunk = False
trunkeoff = None
while j_ < len(data) and (not trunk or eoff <= trunk):
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum_) & 1):
if v != parity__:
break
cksum_ = crc32c(data[j_:j_+d], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c([data[j_] & ~0x80], cksum__)
cksum__ = crc32c(data[j_+1:j_+d], cksum__)
parity__ ^= parity(cksum__)
j_ += d
if not tag & TAG_ALT and j_ + size > len(data):
break
@@ -665,24 +674,27 @@ class Rbyd:
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c(data[j_:j_+size], cksum__)
parity__ ^= parity(cksum__)
# found a cksum?
else:
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
cksum___ = fromle32(data[j_:j_+4])
if cksum__ != cksum___:
break
# commit what we have
eoff = trunkeoff if trunkeoff else j_ + size
eoff = eoff_ if eoff_ else j_ + size
cksum = cksum_
trunk_ = trunk__
weight = weight_
# revert to data cksum
cksum__ = cksum_
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM and (
not trunk or trunk >= j_-d or wastrunk):
not trunk or j_-d <= trunk or trunk___):
# new trunk?
if not wastrunk:
wastrunk = True
if not trunk___:
trunk___ = j_-d
weight__ = 0
@@ -691,24 +703,26 @@ class Rbyd:
# end of trunk?
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum_ = cksum__
# update trunk/weight unless we found a shrub or an
# explicit trunk (which may be a shrub) is requested
if not tag & TAG_SHRUB or trunk:
if not tag & TAG_SHRUB or trunk___ == trunk:
trunk__ = trunk___
weight_ = weight__
# keep track of eoff for best matching trunk
if trunk and j_ + size > trunk:
trunkeoff = j_ + size
eoff = trunkeoff
eoff_ = j_ + size
eoff = eoff_
cksum = cksum_
trunk_ = trunk__
weight = weight_
trunk___ = 0
if not tag & TAG_ALT:
j_ += size
return cls(block, data, rev, eoff, trunk_, weight)
return cls(block, data, rev, eoff, trunk_, weight, cksum)
def lookup(self, rid, tag):
if not self:
+39 -20
View File
@@ -39,7 +39,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -116,6 +117,9 @@ def crc32c(data, crc=0):
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def fromle32(data):
return struct.unpack('<I', data[0:4].ljust(4, b'\0'))[0]
@@ -222,6 +226,11 @@ def tagrepr(tag, w=None, size=None, off=None):
tag & 0xff,
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_PERTURB:
return 'perturb%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_ECKSUM:
return 'ecksum%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
@@ -252,13 +261,14 @@ TBranch = co.namedtuple('TBranch', 'a, b, d, c')
# our core rbyd type
class Rbyd:
def __init__(self, block, data, rev, eoff, trunk, weight):
def __init__(self, block, data, rev, eoff, trunk, weight, cksum):
self.block = block
self.data = data
self.rev = rev
self.eoff = eoff
self.trunk = trunk
self.weight = weight
self.cksum = cksum
self.redund_blocks = []
def addr(self):
@@ -309,7 +319,10 @@ class Rbyd:
rev = fromle32(data[0:4])
cksum = 0
cksum_ = crc32c(data[0:4])
cksum__ = cksum_
parity__ = parity(cksum_)
eoff = 0
eoff_ = None
j_ = 4
trunk_ = 0
trunk__ = 0
@@ -317,13 +330,14 @@ class Rbyd:
weight = 0
weight_ = 0
weight__ = 0
wastrunk = False
trunkeoff = None
while j_ < len(data) and (not trunk or eoff <= trunk):
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum_) & 1):
if v != parity__:
break
cksum_ = crc32c(data[j_:j_+d], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c([data[j_] & ~0x80], cksum__)
cksum__ = crc32c(data[j_+1:j_+d], cksum__)
parity__ ^= parity(cksum__)
j_ += d
if not tag & TAG_ALT and j_ + size > len(data):
break
@@ -331,24 +345,27 @@ class Rbyd:
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c(data[j_:j_+size], cksum__)
parity__ ^= parity(cksum__)
# found a cksum?
else:
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
cksum___ = fromle32(data[j_:j_+4])
if cksum__ != cksum___:
break
# commit what we have
eoff = trunkeoff if trunkeoff else j_ + size
eoff = eoff_ if eoff_ else j_ + size
cksum = cksum_
trunk_ = trunk__
weight = weight_
# revert to data cksum
cksum__ = cksum_
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM and (
not trunk or trunk >= j_-d or wastrunk):
not trunk or j_-d <= trunk or trunk___):
# new trunk?
if not wastrunk:
wastrunk = True
if not trunk___:
trunk___ = j_-d
weight__ = 0
@@ -357,24 +374,26 @@ class Rbyd:
# end of trunk?
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum_ = cksum__
# update trunk/weight unless we found a shrub or an
# explicit trunk (which may be a shrub) is requested
if not tag & TAG_SHRUB or trunk:
if not tag & TAG_SHRUB or trunk___ == trunk:
trunk__ = trunk___
weight_ = weight__
# keep track of eoff for best matching trunk
if trunk and j_ + size > trunk:
trunkeoff = j_ + size
eoff = trunkeoff
eoff_ = j_ + size
eoff = eoff_
cksum = cksum_
trunk_ = trunk__
weight = weight_
trunk___ = 0
if not tag & TAG_ALT:
j_ += size
return cls(block, data, rev, eoff, trunk_, weight)
return cls(block, data, rev, eoff, trunk_, weight, cksum)
def lookup(self, rid, tag):
if not self:
@@ -591,8 +610,8 @@ def main(disk, roots=None, *,
# fetch the root
btree = Rbyd.fetch(f, block_size, roots, trunk)
print('btree %s, rev %d, weight %d' % (
btree.addr(), btree.rev, btree.weight))
print('btree %s, rev %d, weight %d, cksum %08x' % (
btree.addr(), btree.rev, btree.weight, btree.cksum))
# look up a bid, while keeping track of the search path
def btree_lookup(bid, *,
+37 -18
View File
@@ -40,7 +40,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -117,6 +118,9 @@ def crc32c(data, crc=0):
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def fromle32(data):
return struct.unpack('<I', data[0:4].ljust(4, b'\0'))[0]
@@ -253,6 +257,11 @@ def tagrepr(tag, w=None, size=None, off=None):
tag & 0xff,
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_PERTURB:
return 'perturb%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_ECKSUM:
return 'ecksum%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
@@ -283,13 +292,14 @@ TBranch = co.namedtuple('TBranch', 'a, b, d, c')
# our core rbyd type
class Rbyd:
def __init__(self, block, data, rev, eoff, trunk, weight):
def __init__(self, block, data, rev, eoff, trunk, weight, cksum):
self.block = block
self.data = data
self.rev = rev
self.eoff = eoff
self.trunk = trunk
self.weight = weight
self.cksum = cksum
self.redund_blocks = []
def addr(self):
@@ -340,7 +350,10 @@ class Rbyd:
rev = fromle32(data[0:4])
cksum = 0
cksum_ = crc32c(data[0:4])
cksum__ = cksum_
parity__ = parity(cksum_)
eoff = 0
eoff_ = None
j_ = 4
trunk_ = 0
trunk__ = 0
@@ -348,13 +361,14 @@ class Rbyd:
weight = 0
weight_ = 0
weight__ = 0
wastrunk = False
trunkeoff = None
while j_ < len(data) and (not trunk or eoff <= trunk):
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum_) & 1):
if v != parity__:
break
cksum_ = crc32c(data[j_:j_+d], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c([data[j_] & ~0x80], cksum__)
cksum__ = crc32c(data[j_+1:j_+d], cksum__)
parity__ ^= parity(cksum__)
j_ += d
if not tag & TAG_ALT and j_ + size > len(data):
break
@@ -362,24 +376,27 @@ class Rbyd:
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c(data[j_:j_+size], cksum__)
parity__ ^= parity(cksum__)
# found a cksum?
else:
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
cksum___ = fromle32(data[j_:j_+4])
if cksum__ != cksum___:
break
# commit what we have
eoff = trunkeoff if trunkeoff else j_ + size
eoff = eoff_ if eoff_ else j_ + size
cksum = cksum_
trunk_ = trunk__
weight = weight_
# revert to data cksum
cksum__ = cksum_
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM and (
not trunk or trunk >= j_-d or wastrunk):
not trunk or j_-d <= trunk or trunk___):
# new trunk?
if not wastrunk:
wastrunk = True
if not trunk___:
trunk___ = j_-d
weight__ = 0
@@ -388,24 +405,26 @@ class Rbyd:
# end of trunk?
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum_ = cksum__
# update trunk/weight unless we found a shrub or an
# explicit trunk (which may be a shrub) is requested
if not tag & TAG_SHRUB or trunk:
if not tag & TAG_SHRUB or trunk___ == trunk:
trunk__ = trunk___
weight_ = weight__
# keep track of eoff for best matching trunk
if trunk and j_ + size > trunk:
trunkeoff = j_ + size
eoff = trunkeoff
eoff_ = j_ + size
eoff = eoff_
cksum = cksum_
trunk_ = trunk__
weight = weight_
trunk___ = 0
if not tag & TAG_ALT:
j_ += size
return cls(block, data, rev, eoff, trunk_, weight)
return cls(block, data, rev, eoff, trunk_, weight, cksum)
def lookup(self, rid, tag):
if not self:
+42 -20
View File
@@ -39,7 +39,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -116,6 +117,9 @@ def crc32c(data, crc=0):
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def fromle32(data):
return struct.unpack('<I', data[0:4].ljust(4, b'\0'))[0]
@@ -237,6 +241,11 @@ def tagrepr(tag, w=None, size=None, off=None):
tag & 0xff,
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_PERTURB:
return 'perturb%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_ECKSUM:
return 'ecksum%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
@@ -267,13 +276,14 @@ TBranch = co.namedtuple('TBranch', 'a, b, d, c')
# our core rbyd type
class Rbyd:
def __init__(self, block, data, rev, eoff, trunk, weight):
def __init__(self, block, data, rev, eoff, trunk, weight, cksum):
self.block = block
self.data = data
self.rev = rev
self.eoff = eoff
self.trunk = trunk
self.weight = weight
self.cksum = cksum
self.redund_blocks = []
def addr(self):
@@ -324,7 +334,10 @@ class Rbyd:
rev = fromle32(data[0:4])
cksum = 0
cksum_ = crc32c(data[0:4])
cksum__ = cksum_
parity__ = parity(cksum_)
eoff = 0
eoff_ = None
j_ = 4
trunk_ = 0
trunk__ = 0
@@ -332,13 +345,14 @@ class Rbyd:
weight = 0
weight_ = 0
weight__ = 0
wastrunk = False
trunkeoff = None
while j_ < len(data) and (not trunk or eoff <= trunk):
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum_) & 1):
if v != parity__:
break
cksum_ = crc32c(data[j_:j_+d], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c([data[j_] & ~0x80], cksum__)
cksum__ = crc32c(data[j_+1:j_+d], cksum__)
parity__ ^= parity(cksum__)
j_ += d
if not tag & TAG_ALT and j_ + size > len(data):
break
@@ -346,24 +360,27 @@ class Rbyd:
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c(data[j_:j_+size], cksum__)
parity__ ^= parity(cksum__)
# found a cksum?
else:
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
cksum___ = fromle32(data[j_:j_+4])
if cksum__ != cksum___:
break
# commit what we have
eoff = trunkeoff if trunkeoff else j_ + size
eoff = eoff_ if eoff_ else j_ + size
cksum = cksum_
trunk_ = trunk__
weight = weight_
# revert to data cksum
cksum__ = cksum_
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM and (
not trunk or trunk >= j_-d or wastrunk):
not trunk or j_-d <= trunk or trunk___):
# new trunk?
if not wastrunk:
wastrunk = True
if not trunk___:
trunk___ = j_-d
weight__ = 0
@@ -372,24 +389,26 @@ class Rbyd:
# end of trunk?
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum_ = cksum__
# update trunk/weight unless we found a shrub or an
# explicit trunk (which may be a shrub) is requested
if not tag & TAG_SHRUB or trunk:
if not tag & TAG_SHRUB or trunk___ == trunk:
trunk__ = trunk___
weight_ = weight__
# keep track of eoff for best matching trunk
if trunk and j_ + size > trunk:
trunkeoff = j_ + size
eoff = trunkeoff
eoff_ = j_ + size
eoff = eoff_
cksum = cksum_
trunk_ = trunk__
weight = weight_
trunk___ = 0
if not tag & TAG_ALT:
j_ += size
return cls(block, data, rev, eoff, trunk_, weight)
return cls(block, data, rev, eoff, trunk_, weight, cksum)
def lookup(self, rid, tag):
if not self:
@@ -1481,8 +1500,11 @@ def main(disk, mroots=None, *,
#### actual debugging begins here
# print some information about the mtree
print('mtree %s, rev %d, weight %d.%d' % (
mroot.addr(), mroot.rev, bweight//mleaf_weight, 1*mleaf_weight))
print('mtree %s, rev %d, weight %d.%d, cksum %08x' % (
mroot.addr(),
mroot.rev,
bweight//mleaf_weight, 1*mleaf_weight,
mroot.cksum))
# dynamically size the id field
w_width = max(
+82 -44
View File
@@ -48,7 +48,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -125,6 +126,12 @@ def crc32c(data, crc=0):
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def parity(x):
return popc(x) & 1
def fromle32(data):
return struct.unpack('<I', data[0:4].ljust(4, b'\0'))[0]
@@ -224,6 +231,11 @@ def tagrepr(tag, w=None, size=None, off=None):
tag & 0xff,
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_PERTURB:
return 'perturb%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_ECKSUM:
return 'ecksum%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
@@ -252,8 +264,6 @@ def tagrepr(tag, w=None, size=None, off=None):
def dbg_log(data, block_size, rev, eoff, weight, *,
color=False,
**args):
cksum = crc32c(data[0:4])
# preprocess jumps
if args.get('jumps'):
jumps = []
@@ -353,7 +363,7 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
lower_, upper_ = 0, 0
weight_ = 0
wastrunk = False
trunk_ = 0
j_ = 4
while j_ < (block_size if args.get('all') else eoff):
j = j_
@@ -364,8 +374,8 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM:
if not wastrunk:
wastrunk = True
if not trunk_:
trunk_ = j_-d
lower_, upper_ = 0, 0
if tag & TAG_ALT and not tag & TAG_GT:
@@ -374,11 +384,11 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
upper_ += w
if not tag & TAG_ALT:
wastrunk = False
# derive the current tag's rid from alt weights
delta = (lower_+upper_) - weight_
weight_ = lower_+upper_
rid = lower_ + w-1
trunk_ = 0
if (tag & 0xf000) != TAG_CKSUM and not tag & TAG_ALT:
# note we ignore out-of-bounds here for debugging
@@ -481,7 +491,7 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
# does not include any shrub trees
weight_ = 0
weight__ = 0
wastrunk = False
trunk_ = 0
j_ = 4
while j_ < (block_size if args.get('all') else eoff):
j = j_
@@ -493,16 +503,16 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM:
if not wastrunk:
wastrunk = True
if not trunk_:
trunk_ = j_-d
weight__ = 0
weight__ += w
if not tag & TAG_ALT:
wastrunk = False
# found new weight?
weight_ = max(weight_, weight__)
trunk_ = 0
w_width = m.ceil(m.log10(max(1, weight_)+1))
@@ -515,34 +525,44 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
next(xxd(data[0:4]))))
# print tags
cksum = crc32c(data[0:4])
cksum_ = cksum
parity_ = parity(cksum)
lower_, upper_ = 0, 0
wastrunk = False
trunk_ = 0
j_ = 4
while j_ < (block_size if args.get('all') else eoff):
notes = []
j = j_
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum) & 1):
notes.append('v!=%x' % (popc(cksum) & 1))
cksum = crc32c(data[j_:j_+d], cksum)
if v != parity_:
notes.append('v!=%x' % parity_)
parity_ ^= parity(cksum_)
cksum_ = crc32c([data[j_] & ~0x80], cksum_)
cksum_ = crc32c(data[j_+1:j_+d], cksum_)
parity_ ^= parity(cksum_)
j_ += d
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum = crc32c(data[j_:j_+size], cksum)
parity_ ^= parity(cksum_)
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity_ ^= parity(cksum_)
# found a cksum?
else:
cksum_ = fromle32(data[j_:j_+4])
if cksum != cksum_:
notes.append('cksum!=%08x' % cksum)
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
notes.append('cksum!=%08x' % cksum_)
# revert to data cksum
cksum_ = cksum
j_ += size
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM:
if not wastrunk:
wastrunk = True
if not trunk_:
trunk_ = j_-d
lower_, upper_ = 0, 0
if tag & TAG_ALT and not tag & TAG_GT:
@@ -551,9 +571,11 @@ def dbg_log(data, block_size, rev, eoff, weight, *,
upper_ += w
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum = cksum_
# derive the current tag's rid from alt weights
rid = lower_ + w-1
trunk_ = 0
# show human-readable tag representation
print('%s%08x:%s %*s%s%*s %-*s%s%s%s' % (
@@ -905,7 +927,10 @@ def main(disk, blocks=None, *,
rev = fromle32(data[0:4])
cksum = 0
cksum_ = crc32c(data[0:4])
cksum__ = cksum_
parity__ = parity(cksum_)
eoff = 0
eoff_ = None
j_ = 4
trunk_ = 0
trunk__ = 0
@@ -913,13 +938,14 @@ def main(disk, blocks=None, *,
weight = 0
weight_ = 0
weight__ = 0
wastrunk = False
trunkeoff = None
while j_ < len(data) and (not trunk or eoff <= trunk):
v, tag, w, size, d = fromtag(data[j_:])
if v != (popc(cksum_) & 1):
if v != parity__:
break
cksum_ = crc32c(data[j_:j_+d], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c([data[j_] & ~0x80], cksum__)
cksum__ = crc32c(data[j_+1:j_+d], cksum__)
parity__ ^= parity(cksum__)
j_ += d
if not tag & TAG_ALT and j_ + size > len(data):
break
@@ -927,24 +953,27 @@ def main(disk, blocks=None, *,
# take care of cksums
if not tag & TAG_ALT:
if (tag & 0xff00) != TAG_CKSUM:
cksum_ = crc32c(data[j_:j_+size], cksum_)
parity__ ^= parity(cksum__)
cksum__ = crc32c(data[j_:j_+size], cksum__)
parity__ ^= parity(cksum__)
# found a cksum?
else:
cksum__ = fromle32(data[j_:j_+4])
if cksum_ != cksum__:
cksum___ = fromle32(data[j_:j_+4])
if cksum__ != cksum___:
break
# commit what we have
eoff = trunkeoff if trunkeoff else j_ + size
eoff = eoff_ if eoff_ else j_ + size
cksum = cksum_
trunk_ = trunk__
weight = weight_
# revert to data cksum
cksum__ = cksum_
# evaluate trunks
if (tag & 0xf000) != TAG_CKSUM and (
not trunk or trunk >= j_-d or wastrunk):
not trunk or j_-d <= trunk or trunk___):
# new trunk?
if not wastrunk:
wastrunk = True
if not trunk___:
trunk___ = j_-d
weight__ = 0
@@ -953,33 +982,36 @@ def main(disk, blocks=None, *,
# end of trunk?
if not tag & TAG_ALT:
wastrunk = False
# update data checksum
cksum_ = cksum__
# update trunk/weight unless we found a shrub or an
# explicit trunk (which may be a shrub) is requested
if not tag & TAG_SHRUB or trunk:
if not tag & TAG_SHRUB or trunk___ == trunk:
trunk__ = trunk___
weight_ = weight__
# keep track of eoff for best matching trunk
if trunk and j_ + size > trunk:
trunkeoff = j_ + size
eoff = trunkeoff
eoff_ = j_ + size
eoff = eoff_
cksum = cksum_
trunk_ = trunk__
weight = weight_
trunk___ = 0
if not tag & TAG_ALT:
j_ += size
return rev, eoff, trunk_, weight
return rev, eoff, trunk_, weight, cksum
revs, eoffs, trunks_, weights = [], [], [], []
revs, eoffs, trunks_, weights, cksums = [], [], [], [], []
i = 0
for i_, (data, trunk_) in enumerate(zip(datas, trunks)):
rev, eoff, trunk_, weight = fetch(data, trunk_)
rev, eoff, trunk_, weight, cksum = fetch(data, trunk_)
revs.append(rev)
eoffs.append(eoff)
trunks_.append(trunk_)
weights.append(weight)
cksums.append(cksum)
# compare with sequence arithmetic
if trunk_ and (
@@ -989,10 +1021,16 @@ def main(disk, blocks=None, *,
i = i_
# print contents of the winning metadata block
block, data, rev, eoff, trunk_, weight = (
blocks[i], datas[i], revs[i], eoffs[i], trunks_[i], weights[i])
block, data, rev, eoff, trunk_, weight, cksum = (
blocks[i],
datas[i],
revs[i],
eoffs[i],
trunks_[i],
weights[i],
cksums[i])
print('rbyd %s, rev %d, size %d, weight %d' % (
print('rbyd %s, rev %d, size %d, weight %d, cksum %08x' % (
'0x%x.%x' % (block, trunk_)
if len(blocks) == 1
else '0x{%x,%s}.%x' % (
@@ -1000,7 +1038,7 @@ def main(disk, blocks=None, *,
','.join('%x' % blocks[(i+1+j) % len(blocks)]
for j in range(len(blocks)-1)),
trunk_),
rev, eoff, weight))
rev, eoff, weight, cksum))
if args.get('log'):
dbg_log(data, block_size, rev, eoff, weight,
+7 -1
View File
@@ -37,7 +37,8 @@ TAG_UATTR = 0x0400
TAG_SATTR = 0x0600
TAG_SHRUB = 0x1000
TAG_CKSUM = 0x3000
TAG_ECKSUM = 0x3100
TAG_PERTURB = 0x3100
TAG_ECKSUM = 0x3200
TAG_ALT = 0x4000
TAG_R = 0x2000
TAG_GT = 0x1000
@@ -182,6 +183,11 @@ def tagrepr(tag, w=None, size=None, off=None):
tag & 0xff,
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_PERTURB:
return 'perturb%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
' w%d' % w if w else '',
' %s' % size if size is not None else '')
elif (tag & 0x7f00) == TAG_ECKSUM:
return 'ecksum%s%s%s' % (
' 0x%02x' % (tag & 0xff) if tag & 0xff else '',
+177 -148
View File
@@ -2326,8 +2326,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -2414,8 +2414,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -2814,9 +2814,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
@@ -2840,7 +2840,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -2864,8 +2865,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -3138,9 +3139,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -3149,7 +3150,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -3205,8 +3207,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -3287,9 +3289,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -3298,7 +3300,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -3901,9 +3904,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
@@ -3927,7 +3930,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -3965,8 +3969,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -4506,8 +4510,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -5540,8 +5544,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -6048,9 +6052,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
@@ -6074,7 +6078,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -6103,8 +6108,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -6214,9 +6219,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N*M; j++) {
@@ -6225,7 +6230,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -6310,8 +6316,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -6393,9 +6399,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N*M);
@@ -6419,7 +6425,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -6446,8 +6453,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -6685,8 +6692,8 @@ code = '''
&rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -6855,8 +6862,8 @@ code = '''
&rid_, &tag_, NULL, &data_) => LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -10606,9 +10613,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try deleting each rid
for (unsigned j = 0; j < N; j++) {
@@ -10617,7 +10624,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -10663,8 +10671,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -10774,9 +10782,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try deleting each rid
for (unsigned j = 0; j < N; j++) {
@@ -10785,7 +10793,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -10864,8 +10873,8 @@ code = '''
}
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -10966,9 +10975,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try deleting each rid
for (unsigned j = 0; j < N; j++) {
@@ -10977,7 +10986,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -11102,9 +11112,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try deleting each rid
for (unsigned j = 0; j < N; j++) {
@@ -11113,7 +11123,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -11534,9 +11545,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
@@ -11560,7 +11571,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -11606,8 +11618,8 @@ code = '''
&data) => LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -11689,9 +11701,9 @@ code = '''
// copy block so we can reset after each delete
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// test all permutations of a given size
size_t perm_count = TEST_FACTORIAL(N);
@@ -11715,7 +11727,8 @@ code = '''
// restore backup
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -11776,8 +11789,8 @@ code = '''
&data) => LFS_ERR_NOENT;
// keep track of the worst size
if (rbyd.eoff > worst_size) {
worst_size = rbyd.eoff;
if (lfsr_rbyd_eoff(&rbyd) > worst_size) {
worst_size = lfsr_rbyd_eoff(&rbyd);
worst_perm_i = perm_i;
}
}
@@ -13792,9 +13805,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try growing each rid
for (unsigned j = 0; j < N; j++) {
@@ -13803,7 +13816,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -13923,9 +13937,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try growing each rid
for (unsigned j = 0; j < N; j++) {
@@ -13934,7 +13948,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14066,9 +14081,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try growing each rid
for (unsigned j = 0; j < N; j++) {
@@ -14077,7 +14092,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14222,9 +14238,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try shrinking each rid
for (unsigned j = 0; j < N; j++) {
@@ -14233,7 +14249,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14353,9 +14370,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try shrinking each rid
for (unsigned j = 0; j < N; j++) {
@@ -14364,7 +14381,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14496,9 +14514,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try shrinking each rid
for (unsigned j = 0; j < N; j++) {
@@ -14507,7 +14525,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14651,9 +14670,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try deleting each rid
for (unsigned j = 0; j < N; j++) {
@@ -14662,7 +14681,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -14814,9 +14834,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try appending an attr to each rid, this should not affect
// weights at all!
@@ -14826,7 +14846,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -15445,9 +15466,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -15456,7 +15477,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -15588,9 +15610,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try replacing each tag
for (unsigned j = 0; j < N; j++) {
@@ -15599,7 +15621,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -15829,9 +15852,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -15840,7 +15863,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -15982,9 +16006,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try replacing each tag
for (unsigned j = 0; j < N; j++) {
@@ -15993,7 +16017,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -16218,9 +16243,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -16229,7 +16254,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -16361,9 +16387,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try replacing each tag
for (unsigned j = 0; j < N; j++) {
@@ -16372,7 +16398,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -16590,9 +16617,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try removing each tag
for (unsigned j = 0; j < N; j++) {
@@ -16601,7 +16628,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;
@@ -16733,9 +16761,9 @@ code = '''
// copy block so we can reset after each remove
lfsr_rbyd_t backup_rbyd = rbyd;
uint8_t *backup_block = malloc(rbyd.eoff);
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, rbyd.eoff,
backup_block, rbyd.eoff) => 0;
uint8_t *backup_block = malloc(lfsr_rbyd_eoff(&rbyd));
lfsr_bd_read(&lfs, rbyd.blocks[0], 0, lfsr_rbyd_eoff(&rbyd),
backup_block, lfsr_rbyd_eoff(&rbyd)) => 0;
// try replacing each tag
for (unsigned j = 0; j < N; j++) {
@@ -16744,7 +16772,8 @@ code = '''
rbyd = backup_rbyd;
lfsr_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0, backup_block, rbyd.eoff,
lfsr_bd_prog(&lfs, rbyd.blocks[0], 0,
backup_block, lfsr_rbyd_eoff(&rbyd),
NULL) => 0;
lfsr_bd_flush(&lfs,
NULL) => 0;