Reworked compat flags a bit

Now with a bit more granularity for possibly-future-optional on-disk
data structures:

  LFSR_RCOMPAT_NONSTANDARD  0x0001  ---- ---- ---- ---1 (reserved)
  LFSR_RCOMPAT_MLEAF        0x0002  ---- ---- ---- --1-
  LFSR_RCOMPAT_MSHRUB       0x0004  ---- ---- ---- -1-- (reserved)
  LFSR_RCOMPAT_MTREE        0x0008  ---- ---- ---- 1---
  LFSR_RCOMPAT_BSPROUT      0x0010  ---- ---- ---1 ----
  LFSR_RCOMPAT_BLEAF        0x0020  ---- ---- --1- ----
  LFSR_RCOMPAT_BSHRUB       0x0040  ---- ---- -1-- ----
  LFSR_RCOMPAT_BTREE        0x0080  ---- ---- 1--- ----
  LFSR_RCOMPAT_GRM          0x0100  ---- ---1 ---- ----

  LFSR_WCOMPAT_NONSTANDARD  0x0001  ---- ---- ---- ---1 (reserved)

  LFSR_OCOMPAT_NONSTANDARD  0x0001  ---- ---- ---- ---1 (reserved)

This adds a couple reserved flags:

- LFSR_*COMPAT_NONSTANDARD - This flag will never be set by a standard
  version of littlefs. The idea is to allow implementations with
  non-standard extensions a way to signal potential compatibility issues
  without worrying about future compat flag conflicts.

  This is limited to a single bit, but hey, it's not like it's possible
  to predict all future extensions.

  If a non-standard extension needs more granularity, reservations of
  standard compat flags can always be requested, even if they don't end
  up implemented in standard littlefs. (Though such reservations will
  need a strong motivation, it's not like these flags are free).

- LFSR_RCOMPAT_MSHRUB - In theory littlefs supports a shrubbed mtree,
  where the root is inlined into the mroot. But in practice this turned
  out to be more complicated than it was worth. Still, a future
  implementation may find an mshrub useful, so preserving a compat flag
  for such a case makes sense.

  That being said, I have no plans to add support for mshrubs even in
  the dbg scripts.

  I would like the expected feature-set for debug tools to be
  well-defined, but also conservative. This gets a bit tricky with
  theoretical features like the mshrubs, but until mshrubs are actually
  implemented in littlefs, I would like to consider them non-standard.

  The implication of this is that, while LFSR_RCOMPAT_MSHRUB is
  currently "reserved", it may be repurposed for some other meaning in
  the future.

These changes also rename *COMPATFLAGS -> *COMPAT, and reorder the tags
by decreasing importance. This ordering seems more valuable than the
original intention of making rcompat/wcompat a single bit flip.

Implementation-wise, it's interesting to note the internal-only
LFSR_*COMPAT_OVERFLOW flag. This gets set when out-of-range bits are set
on-disk, and allows us to detect unrepresentable compat flags without
too much extra complexity.

The extra encoding/decoding overhead does add a bit of cost though:

           code          stack
  before: 33944           2880
  after:  34124 (+0.5%)   2880 (+0.0%)
This commit is contained in:
Christopher Haster
2024-03-16 17:00:30 -05:00
parent 76721638db
commit 130281ac05
6 changed files with 175 additions and 89 deletions
+3 -3
View File
@@ -14,9 +14,9 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_RCOMPAT = 0x0005
TAG_WCOMPAT = 0x0006
TAG_OCOMPAT = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
+6 -6
View File
@@ -12,9 +12,9 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_RCOMPAT = 0x0005
TAG_WCOMPAT = 0x0006
TAG_OCOMPAT = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
@@ -165,9 +165,9 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT
else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT
else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
+24 -24
View File
@@ -13,9 +13,9 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_RCOMPAT = 0x0005
TAG_WCOMPAT = 0x0006
TAG_OCOMPAT = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
@@ -196,9 +196,9 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT
else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT
else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
@@ -1031,25 +1031,25 @@ class Config:
return (None, None)
@ft.cached_property
def ocompatflags(self):
if TAG_OCOMPATFLAGS in self.config:
_, data = self.config[TAG_OCOMPATFLAGS]
def rcompat(self):
if TAG_RCOMPAT in self.config:
_, data = self.config[TAG_RCOMPAT]
return data
else:
return None
@ft.cached_property
def rcompatflags(self):
if TAG_RCOMPATFLAGS in self.config:
_, data = self.config[TAG_RCOMPATFLAGS]
def wcompat(self):
if TAG_WCOMPAT in self.config:
_, data = self.config[TAG_WCOMPAT]
return data
else:
return None
@ft.cached_property
def wcompatflags(self):
if TAG_WCOMPATFLAGS in self.config:
_, data = self.config[TAG_WCOMPATFLAGS]
def ocompat(self):
if TAG_OCOMPAT in self.config:
_, data = self.config[TAG_OCOMPAT]
return data
else:
return None
@@ -1098,15 +1098,15 @@ class Config:
for b in map(chr, self.magic))
elif tag == TAG_VERSION:
return 'version v%d.%d' % self.version
elif tag == TAG_OCOMPATFLAGS:
return 'ocompatflags 0x%s' % ''.join(
'%02x' % f for f in reversed(self.ocompatflags))
elif tag == TAG_RCOMPATFLAGS:
return 'rcompatflags 0x%s' % ''.join(
'%02x' % f for f in reversed(self.rcompatflags))
elif tag == TAG_WCOMPATFLAGS:
return 'wcompatflags 0x%s' % ''.join(
'%02x' % f for f in reversed(self.wcompatflags))
elif tag == TAG_RCOMPAT:
return 'rcompat 0x%s' % ''.join(
'%x' % f for f in reversed(self.rcompat))
elif tag == TAG_WCOMPAT:
return 'wcompat 0x%s' % ''.join(
'%x' % f for f in reversed(self.wcompat))
elif tag == TAG_OCOMPAT:
return 'ocompat 0x%s' % ''.join(
'%x' % f for f in reversed(self.ocompat))
elif tag == TAG_BLOCKSIZE:
return 'blocksize %d' % self.block_size
elif tag == TAG_BLOCKCOUNT:
+6 -6
View File
@@ -12,9 +12,9 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_RCOMPAT = 0x0005
TAG_WCOMPAT = 0x0006
TAG_OCOMPAT = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
@@ -180,9 +180,9 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT
else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT
else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT
+6 -6
View File
@@ -21,9 +21,9 @@ TAG_NULL = 0x0000
TAG_CONFIG = 0x0000
TAG_MAGIC = 0x0003
TAG_VERSION = 0x0004
TAG_OCOMPATFLAGS = 0x0005
TAG_RCOMPATFLAGS = 0x0006
TAG_WCOMPATFLAGS = 0x0007
TAG_RCOMPAT = 0x0005
TAG_WCOMPAT = 0x0006
TAG_OCOMPAT = 0x0007
TAG_BLOCKSIZE = 0x0008
TAG_BLOCKCOUNT = 0x0009
TAG_NAMELIMIT = 0x000a
@@ -167,9 +167,9 @@ def tagrepr(tag, w, size, off=None):
'shrub' if tag & TAG_SHRUB else '',
'magic' if (tag & 0xfff) == TAG_MAGIC
else 'version' if (tag & 0xfff) == TAG_VERSION
else 'ocompatflags' if (tag & 0xfff) == TAG_OCOMPATFLAGS
else 'rcompatflags' if (tag & 0xfff) == TAG_RCOMPATFLAGS
else 'wcompatflags' if (tag & 0xfff) == TAG_WCOMPATFLAGS
else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT
else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT
else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT
else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE
else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT
else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT