Limited block_size and in-block types to 28-bits

One downside of leb128 encoding is that the worst case encoded size is
not that well aligned due to a relatively underutilized last byte:

  0xffffffff => 0xff 0xff 0xff 0xff 0x0f

This normally doesn't really matter, the whole point of leb128 is that
larger encodings are statistically less likely. But in littlefs we need
to allocate the worst-case buffer size in order to encode/decode
leb128s, and these buffers need to stick around on the stack during
metadata commit calls, which are also the point of highest stack usage
in the system.

But 32-bits is somewhat arbitrary, it just happens to be our register
size. In fact, we're not really using 32-bits, but instead only 31-bits
to take advantage of the sign bit for ad-hoc sum types:

  0x7fffffff => 0xff 0xff 0xff 0xff 0x07

In theory, if we limit this further to 28-bits, we could save some stack
space:

  0x7fffffff => 0xff 0xff 0xff 0xff 0x07
  0x0fffffff => 0xff 0xff 0xff 0x7f

This may seem like a small amount of savings, but it also restores
alignment to the encoding, and should result in less wasted padding
around buffers.

Though it's important to note these are the most valuable bits, as the
range grows exponentially with each bit added. Reducing 31-bits to
28-bits reduces the range from ~2GiB to ~256MiB:

  0x7fffffff => 2,147,483,647
  0x0fffffff =>   268,435,455

---

At the moment I'm hesistant to reduce _all_ on-disk leb128s to 28-bits.

The signed-32-bit limit of ~2GiB is fairly well understood in this
space, mainly thanks to FAT, and reducing this to ~256MiB risks quite a
surprise to users (it's also a regression from the current littlefs
version).

But one type where this limit is pretty reasonable is our block_size.

I don't think we'll see devices with erase blocks >256MiB for a while,
and at the very least those devices will probably need a 64-bit
filesystem for other reasons anyways...

And limiting block_size to <=256MiB has a surprising number of knock-on
effects:

- The tag size/jump field never exceeds 28-bits, reducing worst-case tag
  dsize from 12 bytes -> 11 bytes.

  The also reduces our worst-case attr-estimate from 40 bytes ->
  37 bytes

- rbyd/btree trunks never exceed 28-bits, saving space in shrub/branch/
  btree encodings.

- The bptr encoding is reduced from 24 bytes -> 21 bytes, since several
  of its fields are in-block (size, off, cksize).

- The commit checksum encoding is reduced by a byte for every commit,
  from 12 bytes -> 11 bytes.

  This is due to needing to expand the cksum tag's size field to the
  worst possible leb128 encoding due to a catch-22 situation.

Unfortunately the actual stack savings is a bit underwhelming:

            code          stack
  before:  33688           2808
  after:   33700 (+0.0%)   2800 (-0.3%)

This may be because, by adopting 28-bits in only some fields, most
buffers still end up unaligned and the on-stack size doesn't change due
to padding. Or it could just be that I'm overestimating the cost of our
on-stack buffers.

Still, I think the change is worth keeping if only for the reducing
attr-estimate and saved byte on every on-disk commit.

In the future it would be interesting to explore additional
configurations, e.g. a 28-bit flavor of littlefs to compliment this
31-bit flavor. You could imagine the fitting into other register sized
flavors for different capacity/code cost/device compat tradeoffs:

  flavor               register  leb128   size-limit
  14-bit littlefs  =>  16-bit    2 bytes  ~16KiB
  15-bit littlefs  =>  16-bit    3 bytes  ~32KiB
  28-bit littlefs  =>  32-bit    4 bytes  ~256MiB
  31-bit littlefs  =>  32-bit    5 bytes  ~2GiB
  56-bit littlefs  =>  64-bit    8 bytes  ~64PiB
  63-bit littlefs  =>  64-bit    9 bytes  ~8ExiB

This is where the on-disk size-limit attr would really shine.

---

Note we don't need an additional on-disk limit attr for the block_size.
We already store the block_size in the superblock, so we just need to
error if attempting to mount a filesystem with block_size >256MiB.
This commit is contained in:
Christopher Haster
2024-02-10 16:10:46 -06:00
parent 6439650a0e
commit 42ec282a03
+136 -81
View File
@@ -891,11 +891,11 @@ static inline void lfsr_tag_trim2(
// support for encoding/decoding tags on disk
// tag encoding:
// .---+---+---+- -+- -+- -+- -+---+- -+- -+- -+- -. tag: 2 bytes
// | tag | weight | size | weight: <=5 bytes
// '---+---+---+- -+- -+- -+- -+---+- -+- -+- -+- -' size: <=5 bytes
// total: <=12 bytes
#define LFSR_TAG_DSIZE (2+5+5)
// .---+---+---+- -+- -+- -+- -+---+- -+- -+- -. tag: 2 bytes
// | tag | weight | size | weight: <=5 bytes
// '---+---+---+- -+- -+- -+- -+---+- -+- -+- -' size: <=4 bytes
// total: <=11 bytes
#define LFSR_TAG_DSIZE (2+5+4)
static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
lfs_block_t block, lfs_size_t off, lfs_size_t hint,
@@ -936,6 +936,8 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
if (d_ < 0) {
return d_;
}
// weights should be limited to 31-bits
LFS_ASSERT(weight <= 0x7fffffff);
d += d_;
lfs_size_t size;
@@ -943,6 +945,8 @@ static lfs_ssize_t lfsr_bd_readtag(lfs_t *lfs,
if (d_ < 0) {
return d_;
}
// sizes should be limited to 28-bits
LFS_ASSERT(size <= 0x0fffffff);
d += d_;
// optional checksum
@@ -962,11 +966,12 @@ 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,
uint32_t *cksum_) {
// check for underflow issues
LFS_ASSERT(weight < 0x80000000);
LFS_ASSERT(size < 0x80000000);
// bit 7 is reserved for future subtype extensions
LFS_ASSERT(!(tag & 0x80));
// weight should not exceed 31-bits
LFS_ASSERT(weight <= 0x7fffffff);
// 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;
@@ -975,15 +980,15 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
uint8_t tag_buf[LFSR_TAG_DSIZE];
tag_buf[0] = (uint8_t)(tag >> 8);
tag_buf[1] = (uint8_t)(tag >> 0);
lfs_ssize_t d = 2;
lfs_ssize_t d_ = lfs_toleb128(weight, &tag_buf[d], 5);
if (d_ < 0) {
return d_;
}
d += d_;
d_ = lfs_toleb128(size, &tag_buf[d], 5);
d_ = lfs_toleb128(size, &tag_buf[d], 4);
if (d_ < 0) {
return d_;
}
@@ -1237,6 +1242,7 @@ static int lfsr_data_readle32(lfs_t *lfs, lfsr_data_t *data,
return 0;
}
// note all leb128s in our system reserve the sign bit
static int lfsr_data_readleb128(lfs_t *lfs, lfsr_data_t *data,
uint32_t *word_) {
// note we make sure not to update our data offset until after leb128
@@ -1254,11 +1260,30 @@ static int lfsr_data_readleb128(lfs_t *lfs, lfsr_data_t *data,
if (d < 0) {
return d;
}
// all leb128s in our system reserve the sign bit
LFS_ASSERT(*word_ <= 0x7fffffff);
*data = lfsr_data_slice(*data, d, -1);
return 0;
}
// a little-leb128 in our system is truncated to align nicely
//
// for 32-bit words, little-leb128s are truncated to 28-bits, so the
// resulting leb128 encoding fits nicely in 4-bytes
static inline int lfsr_data_readlleb128(lfs_t *lfs, lfsr_data_t *data,
uint32_t *word_) {
// just call readleb128 here
int err = lfsr_data_readleb128(lfs, data, word_);
if (err) {
return err;
}
// little-leb128s should be limited to 28-bits
LFS_ASSERT(*word_ <= 0x0fffffff);
return 0;
}
static lfs_scmp_t lfsr_data_cmp(lfs_t *lfs, const lfsr_data_t *data,
const void *buffer, lfs_size_t size) {
// limit our size to data range
@@ -1619,22 +1644,26 @@ static int lfsr_ecksum_validate(lfs_t *lfs, const lfsr_ecksum_t *ecksum,
// erased-state checksum on-disk encoding
// ecksum encoding:
// .---+- -+- -+- -+- -.
// | cksize | cksize: <=5 bytes
// +---+- -+- -+- -+- -'
// | cksum | cksum: 4 bytes
// '---+---+---+---' total: <=9 bytes
// .---+- -+- -+- -.
// | cksize | cksize: <=4 bytes
// +---+- -+- -+- -+
// | cksum | cksum: 4 bytes
// '---+---+---+---' total: <=8 bytes
//
#define LFSR_ECKSUM_DSIZE (5+4)
#define LFSR_ECKSUM_DSIZE (4+4)
#define LFSR_DATA_FROMECKSUM(_ecksum, _buffer) \
lfsr_data_fromecksum(_ecksum, _buffer)
static lfsr_data_t lfsr_data_fromecksum(const lfsr_ecksum_t *ecksum,
uint8_t buffer[static LFSR_ECKSUM_DSIZE]) {
// you shouldn't try to encode a not-ecksum, that doesn't make sense
LFS_ASSERT(ecksum->cksize != -1);
// cksize should not exceed 28-bits
LFS_ASSERT((uint32_t)ecksum->cksize <= 0x0fffffff);
lfs_ssize_t d = 0;
lfs_ssize_t d_ = lfs_toleb128(ecksum->cksize, &buffer[d], 5);
lfs_ssize_t d_ = lfs_toleb128(ecksum->cksize, &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -1646,7 +1675,7 @@ static lfsr_data_t lfsr_data_fromecksum(const lfsr_ecksum_t *ecksum,
static int lfsr_data_readecksum(lfs_t *lfs, lfsr_data_t *data,
lfsr_ecksum_t *ecksum) {
int err = lfsr_data_readleb128(lfs, data, (uint32_t*)&ecksum->cksize);
int err = lfsr_data_readlleb128(lfs, data, (uint32_t*)&ecksum->cksize);
if (err) {
return err;
}
@@ -1663,29 +1692,37 @@ static int lfsr_data_readecksum(lfs_t *lfs, lfsr_data_t *data,
// block pointer things
// bptr encoding:
// .---+- -+- -+- -+- -.
// | size | size: <=5 bytes
// +---+- -+- -+- -+- -+
// .---+- -+- -+- -.
// | size | size: <=4 bytes
// +---+- -+- -+- -+- -.
// | block | block: <=5 bytes
// +---+- -+- -+- -+- -+
// | off | off: <=5 bytes
// +---+- -+- -+- -+- -+
// | cksize | cksize: <=5 bytes
// +---+- -+- -+- -+- -'
// | off | off: <=4 bytes
// +---+- -+- -+- -+
// | cksize | cksize: <=4 bytes
// +---+- -+- -+- -+
// | cksum | cksum: 4 bytes
// '---+---+---+---' total: <=24 bytes
// '---+---+---+---' total: <=21 bytes
//
#define LFSR_BPTR_DSIZE (5+5+5+5+4)
#define LFSR_BPTR_DSIZE (4+5+4+4+4)
#define LFSR_DATA_FROMBPTR(_bptr, _buffer) \
lfsr_data_frombptr(_bptr, _buffer)
static lfsr_data_t lfsr_data_frombptr(const lfsr_bptr_t *bptr,
uint8_t buffer[static LFSR_BPTR_DSIZE]) {
// size should not exceed 28-bits
LFS_ASSERT(lfsr_data_size(&bptr->data) <= 0x0fffffff);
// block should not exceed 31-bits
LFS_ASSERT(bptr->data.u.disk.block <= 0x7fffffff);
// off should not exceed 28-bits
LFS_ASSERT(bptr->data.u.disk.off <= 0x0fffffff);
// cksize should not exceed 28-bits
LFS_ASSERT(bptr->cksize <= 0x0fffffff);
lfs_ssize_t d = 0;
// write the block, offset, size
lfs_ssize_t d_ = lfs_toleb128(lfsr_data_size(&bptr->data), &buffer[d], 5);
lfs_ssize_t d_ = lfs_toleb128(lfsr_data_size(&bptr->data), &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -1693,12 +1730,12 @@ static lfsr_data_t lfsr_data_frombptr(const lfsr_bptr_t *bptr,
LFS_ASSERT(d_ >= 0);
d += d_;
d_ = lfs_toleb128(bptr->data.u.disk.off, &buffer[d], 5);
d_ = lfs_toleb128(bptr->data.u.disk.off, &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
// write the cksize, cksum
d_ = lfs_toleb128(bptr->cksize, &buffer[d], 5);
d_ = lfs_toleb128(bptr->cksize, &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -1711,7 +1748,7 @@ static lfsr_data_t lfsr_data_frombptr(const lfsr_bptr_t *bptr,
static int lfsr_data_readbptr(lfs_t *lfs, lfsr_data_t *data,
lfsr_bptr_t *bptr) {
// read the block, offset, size
int err = lfsr_data_readleb128(lfs, data,
int err = lfsr_data_readlleb128(lfs, data,
(uint32_t*)&bptr->data.u.disk.size);
if (err) {
return err;
@@ -1722,13 +1759,13 @@ static int lfsr_data_readbptr(lfs_t *lfs, lfsr_data_t *data,
return err;
}
err = lfsr_data_readleb128(lfs, data, &bptr->data.u.disk.off);
err = lfsr_data_readlleb128(lfs, data, &bptr->data.u.disk.off);
if (err) {
return err;
}
// read the cksize, cksum
err = lfsr_data_readleb128(lfs, data, &bptr->cksize);
err = lfsr_data_readlleb128(lfs, data, &bptr->cksize);
if (err) {
return err;
}
@@ -1925,11 +1962,12 @@ static int lfsr_data_readgrm(lfs_t *lfs, lfsr_data_t *data,
grm->rms[1] = -1;
// first read the mode field
lfs_size_t mode;
int err = lfsr_data_readleb128(lfs, data, &mode);
if (err) {
return err;
uint8_t mode;
lfs_ssize_t d = lfsr_data_read(lfs, data, &mode, 1);
if (d < 0) {
return d;
}
LFS_ASSERT(d == 1);
// unknown mode? return an error, we may be able to mount read-only
if (mode > 2) {
@@ -1937,12 +1975,11 @@ static int lfsr_data_readgrm(lfs_t *lfs, lfsr_data_t *data,
}
for (uint8_t i = 0; i < mode; i++) {
err = lfsr_data_readleb128(lfs, data, (lfsr_mid_t*)&grm->rms[i]);
int err = lfsr_data_readleb128(lfs, data, (uint32_t*)&grm->rms[i]);
if (err) {
return err;
}
LFS_ASSERT(grm->rms[i] < lfs_smax32(
LFS_ASSERT((uint32_t)grm->rms[i] < lfs_max32(
lfsr_mtree_weight(&lfs->mtree),
lfsr_mleafweight(lfs)));
}
@@ -3067,27 +3104,27 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// this gets a bit complicated as we have two types of cksums:
//
// - 9-word cksum with ecksum to check following prog (middle of block):
// .---+---+---+---. ecksum tag: 2 bytes
// | etag | 0 |esz| ecksum weight: 1 byte
// +---+---+---+---+- -. ecksum size: 1 byte
// | ecksize | ecksum cksize: <=5 bytes
// +---+- -+- -+- -+- -' ecksum cksum: 4 bytes
// .---+---+---+---. ecksum tag: 2 bytes
// | etag | 0 |esz| ecksum weight: 1 byte
// +---+---+---+---+ ecksum size: 1 byte
// | ecksize | ecksum cksize: <=4 bytes
// +---+- -+- -+- -+ ecksum cksum: 4 bytes
// | ecksum |
// +---+---+---+---+- -+- -+- -+- -. cksum tag: 2 bytes
// | tag | 0 | size | cksum weight: 1 byte
// +---+---+---+---+- -+- -+- -+- -' cksum size: <=5 bytes
// | cksum | cksum cksum: 4 bytes
// '---+---+---+---' total: <=25 bytes
// +---+---+---+---+- -+- -+- -. cksum tag: 2 bytes
// | tag | 0 | size | cksum weight: 1 byte
// +---+---+---+---+- -+- -+- -' cksum size: <=4 bytes
// | cksum | cksum cksum: 4 bytes
// '---+---+---+---' total: <=23 bytes
//
// - 4-word cksum with no following prog (end of block):
// .---+---+---+---+- -+- -+- -+- -. cksum tag: 2 bytes
// | tag | 0 | size | cksum weight: 1 byte
// +---+---+---+---+- -+- -+- -+- -' cksum size: <=5 bytes
// | cksum | cksum cksum: 4 bytes
// '---+---+---+---' total: <=12 bytes
// .---+---+---+---+- -+- -+- -. cksum tag: 2 bytes
// | tag | 0 | size | cksum weight: 1 byte
// +---+---+---+---+- -+- -+- -' cksum size: <=4 bytes
// | cksum | cksum cksum: 4 bytes
// '---+---+---+---' total: <=11 bytes
//
lfs_size_t aligned_eoff = lfs_alignup(
rbyd->eoff + 2+1+1+5+4 + 2+1+5+4,
rbyd->eoff + 2+1+1+4+4 + 2+1+4+4,
lfs->cfg->prog_size);
// space for ecksum?
@@ -3131,7 +3168,7 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
rbyd->eoff += lfsr_data_size(&ecksum_data);
// at least space for a cksum?
} else if (rbyd->eoff + 2+1+5+4 <= lfs->cfg->block_size) {
} else if (rbyd->eoff + 2+1+4+4 <= lfs->cfg->block_size) {
// note this implicitly marks the rbyd as unerased
aligned_eoff = lfs->cfg->block_size;
@@ -3145,36 +3182,35 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
// note padding-size depends on leb-encoding depends on padding-size, to
// get around this catch-22 we just always write a fully-expanded leb128
// encoding
uint8_t cksum_buf[2+1+5+4];
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[2] = 0;
lfs_size_t padding = aligned_eoff - (rbyd->eoff + 2+1+5);
lfs_size_t padding = aligned_eoff - (rbyd->eoff + 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] = 0x80 | (0x7f & (padding >> 21));
cksum_buf[7] = 0x00 | (0x7f & (padding >> 28));
cksum_buf[6] = 0x00 | (0x7f & (padding >> 21));
rbyd->cksum = lfs_crc32c(rbyd->cksum, cksum_buf, 2+1+5);
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)) {
cksum_buf[1] ^= 0x01;
rbyd->cksum ^= 0x68032cc8; // note crc(a ^ b) == crc(a) ^ crc(b)
rbyd->cksum ^= 0xef306b19; // note crc(a ^ b) == crc(a) ^ crc(b)
}
lfs_tole32_(rbyd->cksum, &cksum_buf[2+1+5]);
lfs_tole32_(rbyd->cksum, &cksum_buf[2+1+4]);
int err = lfsr_bd_prog(lfs, rbyd->blocks[0], rbyd->eoff,
cksum_buf, 2+1+5+4,
cksum_buf, 2+1+4+4,
NULL, NULL);
if (err) {
return err;
}
rbyd->eoff += 2+1+5+4;
rbyd->eoff += 2+1+4+4;
// flush our caches, finalizing the commit on-disk
err = lfsr_bd_sync(lfs);
@@ -3762,26 +3798,30 @@ static inline int lfsr_btree_cmp(
// branch encoding:
// .---+- -+- -+- -+- -.
// | block | block: <=5 bytes
// +---+- -+- -+- -+- -+
// | trunk | trunk: <=5 bytes
// +---+- -+- -+- -+- -'
// | trunk | trunk: <=4 bytes
// +---+- -+- -+- -+
// | cksum | cksum: 4 bytes
// '---+---+---+---' total: <=14 bytes
// '---+---+---+---' total: <=13 bytes
//
#define LFSR_BRANCH_DSIZE (5+5+4)
#define LFSR_BRANCH_DSIZE (5+4+4)
#define LFSR_DATA_FROMBRANCH(_branch, _buffer) \
lfsr_data_frombranch(_branch, _buffer)
static lfsr_data_t lfsr_data_frombranch(const lfsr_rbyd_t *branch,
uint8_t buffer[static LFSR_BRANCH_DSIZE]) {
// block should not exceed 31-bits
LFS_ASSERT(branch->blocks[0] <= 0x7fffffff);
// trunk should not exceed 28-bits
LFS_ASSERT(lfsr_rbyd_trunk(branch) <= 0x0fffffff);
lfs_ssize_t d = 0;
lfs_ssize_t d_ = lfs_toleb128(branch->blocks[0], &buffer[d], 5);
LFS_ASSERT(d_ >= 0);
d += d_;
d_ = lfs_toleb128(lfsr_rbyd_trunk(branch), &buffer[d], 5);
d_ = lfs_toleb128(lfsr_rbyd_trunk(branch), &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -3804,7 +3844,7 @@ static int lfsr_data_readbranch(lfs_t *lfs, lfsr_data_t *data,
return err;
}
err = lfsr_data_readleb128(lfs, data, (uint32_t*)&branch->trunk);
err = lfsr_data_readlleb128(lfs, data, (uint32_t*)&branch->trunk);
if (err) {
return err;
}
@@ -3828,11 +3868,11 @@ static int lfsr_data_readbranch(lfs_t *lfs, lfsr_data_t *data,
// | weight | weight: <=5 bytes
// +---+- -+- -+- -+- -+
// | block | block: <=5 bytes
// +---+- -+- -+- -+- -+
// | trunk | trunk: <=5 bytes
// +---+- -+- -+- -+- -'
// | trunk | trunk: <=4 bytes
// +---+- -+- -+- -+
// | cksum | cksum: 4 bytes
// '---+---+---+---' total: <=19 bytes
// '---+---+---+---' total: <=18 bytes
//
#define LFSR_BTREE_DSIZE (5+LFSR_BRANCH_DSIZE)
@@ -3841,7 +3881,10 @@ static int lfsr_data_readbranch(lfs_t *lfs, lfsr_data_t *data,
static lfsr_data_t lfsr_data_frombtree(const lfsr_btree_t *btree,
uint8_t buffer[static LFSR_BTREE_DSIZE]) {
// weight should not exceed 31-bits
LFS_ASSERT(btree->weight <= 0x7fffffff);
lfs_ssize_t d = 0;
lfs_ssize_t d_ = lfs_toleb128(btree->weight, &buffer[d], 5);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -4886,11 +4929,11 @@ static inline int lfsr_shrub_cmp(
// shrub encoding:
// .---+- -+- -+- -+- -.
// | weight | weight: <=5 bytes
// +---+- -+- -+- -+- -+
// | trunk | trunk: <=5 bytes
// '---+- -+- -+- -+- -' total: <=10 bytes
// +---+- -+- -+- -+- -'
// | trunk | trunk: <=4 bytes
// '---+- -+- -+- -' total: <=9 bytes
//
#define LFSR_SHRUB_DSIZE (5+5)
#define LFSR_SHRUB_DSIZE (5+4)
#define LFSR_DATA_FROMSHRUB(_rbyd, _buffer) \
lfsr_data_fromtrunk(_rbyd, _buffer)
@@ -4899,6 +4942,10 @@ static lfsr_data_t lfsr_data_fromshrub(const lfsr_shrub_t *shrub,
uint8_t buffer[static LFSR_SHRUB_DSIZE]) {
// shrub trunks should never be null
LFS_ASSERT(lfsr_shrub_trunk(shrub) != 0);
// weight should not exceed 31-bits
LFS_ASSERT(shrub->weight <= 0x7fffffff);
// trunk should not exceed 28-bits
LFS_ASSERT(lfsr_shrub_trunk(shrub) <= 0x0fffffff);
lfs_ssize_t d = 0;
// just write the trunk and weight, the rest of the rbyd is contextual
@@ -4906,7 +4953,7 @@ static lfsr_data_t lfsr_data_fromshrub(const lfsr_shrub_t *shrub,
LFS_ASSERT(d_ >= 0);
d += d_;
d_ = lfs_toleb128(lfsr_shrub_trunk(shrub), &buffer[d], 5);
d_ = lfs_toleb128(lfsr_shrub_trunk(shrub), &buffer[d], 4);
LFS_ASSERT(d_ >= 0);
d += d_;
@@ -4926,7 +4973,7 @@ static int lfsr_data_readshrub(lfs_t *lfs, lfsr_data_t *data,
return err;
}
err = lfsr_data_readleb128(lfs, data, (uint32_t*)&shrub->trunk);
err = lfsr_data_readlleb128(lfs, data, (uint32_t*)&shrub->trunk);
if (err) {
return err;
}
@@ -5093,6 +5140,10 @@ static inline bool lfsr_mptr_ismrootanchor(const lfsr_mptr_t *mptr) {
static lfsr_data_t lfsr_data_frommptr(const lfsr_mptr_t *mptr,
uint8_t buffer[static LFSR_MPTR_DSIZE]) {
// blocks should not exceed 31-bits
LFS_ASSERT(mptr->blocks[0] <= 0x7fffffff);
LFS_ASSERT(mptr->blocks[1] <= 0x7fffffff);
lfs_ssize_t d = 0;
for (int i = 0; i < 2; i++) {
lfs_ssize_t d_ = lfs_toleb128(mptr->blocks[i], &buffer[d], 5);
@@ -14767,6 +14818,7 @@ failed:;
/// Filesystem operations ///
static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
// TODO this all needs to be cleaned up
lfs->cfg = cfg;
int err = 0;
@@ -14782,6 +14834,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0);
LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0);
// block_size is currently limited to 28-bits
LFS_ASSERT(lfs->cfg->block_size <= 0x0fffffff);
// check that the block size is large enough to fit ctz pointers
LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
<= lfs->cfg->block_size);