Files
cubelinux-kernel/drivers/cube/cube_format.rs
T
surface-camera-build a111d7e8b2 cubelinux: v4 — a 16-bit class mask in the index and the log entry
The first half of the flag substrate (DESIGN-flag-vocabularies.md): the shared
format file now defines VERSION_V4, whose index entry is `key | flags(u16) |
value_off | value_len`, and WAL version 2, whose entry carries the same mask
before its length. The mask is a raw u16 — its bits are a vocabulary's business,
never the format's.

Backward compatible, and pinned as such: a v3 index entry and a v1 log entry read
as a zero mask ("no class"), which a scan treats as matching nothing, so a store
folded before the flag existed degrades to "unclassified" rather than "matches
everything". `V3::decode` accepts both versions and `index_stride()` names the
one that differs; `wal_entry` keys its stride off the log's own version byte.

The readers and writers that actually move bytes (the driver's serialize and
append, and `cube-store-raw`) are separate and are the next commit; this is the
shared definition and the arithmetic a reader derives from it.
2026-09-21 22:38:43 -04:00

645 lines
24 KiB
Rust

// The CUBE store's format, in one file, used by both sides.
//
// `cube-store-raw` (and through it `cube-cli`, `cube-image`) and this kernel's driver have to agree
// about the store's bytes to the byte: the digest line is diffed between a kernel-written image and
// a userspace-written one, a listing taken through `cube(2)` is compared with one taken by
// userspace, and a store the kernel folds has to be a store userspace can read. They were two
// implementations of one format, and when v3 landed the kernel moved and userspace did not — an
// hour of "unsupported-version" and a README's worth of careful duplication.
//
// Two build systems cannot share a crate: the kernel's Rust build compiles what is in its own
// module tree, and a cargo crate is not that. So they share a *file*, instead: this one, which the
// driver declares as a module and `crates/cube-format` includes by path. There is no copy to drift
// from, which is the only arrangement that cannot go stale.
//
// Everything here is a pure function over slices: no allocation, no I/O, no `std`, no logging. The
// kernel reads into buffers with its own allocator and userspace already has the whole image; what
// they must agree on is the layout, and that is what lives here.
//
// The constants are `pub` because both sides need them (a reader computing a stride, a writer
// sizing an image), and `#[allow(dead_code)]` is applied per item where one side does not use it —
// an unused `pub` constant in a kernel module is a warning, and a warning-free build is how the
// gates stay readable.
/// Every image starts with these four bytes.
pub const MAGIC: &[u8; 4] = b"CUBE";
/// The original format: magic, version, curve, then records until zero padding.
pub const VERSION_V1: u8 = 1;
/// The packed format: the same, plus the image's byte extent and record count.
pub const VERSION_V2: u8 = 2;
/// The addressed format: the same, plus a space table, a fixed-size index, and packed values.
pub const VERSION_V3: u8 = 3;
/// v3, plus a 16-bit class mask in each index entry, so a scan by flag is a seek rather than a
/// walk. The mask is the flag substrate (DESIGN-flag-vocabularies.md): a raw `u16` whose bits are a
/// vocabulary's business, written at put time and read by `CUBE_OP_FLAG_SCAN`.
pub const VERSION_V4: u8 = 4;
pub const HEADER_LEN_V1: usize = 6;
pub const HEADER_LEN_V2: usize = 6 + 8 + 8;
/// magic(4) version(1) curve(1) image_bytes(8) record_count(8) space_count(8) index_off(8)
/// values_off(8).
pub const HEADER_LEN_V3: usize = 4 + 1 + 1 + 8 + 8 + 8 + 8 + 8;
pub const SPACE_ID_LEN: usize = 32;
pub const RAW_KEY_LEN: usize = 24;
/// The class mask's width: one `u16` per record.
pub const FLAGS_LEN: usize = 2;
/// A packed record's fixed part: `space | key | value_len(u64)`, with the value behind it.
pub const RECORD_FIXED: usize = SPACE_ID_LEN + RAW_KEY_LEN + 8;
/// A v3 index entry: `key | value_off(u64) | value_len(u64)`.
pub const INDEX_ENTRY: usize = RAW_KEY_LEN + 8 + 8;
/// A v4 index entry: `key | flags(u16) | value_off(u64) | value_len(u64)`.
pub const INDEX_ENTRY_V4: usize = RAW_KEY_LEN + FLAGS_LEN + 8 + 8;
/// A v3 space-table row: `space | first index(u64) | records(u64)`.
pub const SPACE_ENTRY: usize = SPACE_ID_LEN + 8 + 8;
/// The log's framing, which the fold and the readers both parse.
pub const WAL_MAGIC: &[u8; 4] = b"CUBW";
/// The original log entry: no class mask.
pub const WAL_VERSION: u8 = 1;
/// The flagged log entry: the entry carries a `u16` class mask before its length.
pub const WAL_VERSION_V2: u8 = 2;
pub const WAL_HEADER_LEN: usize = 6;
/// `op(1) | crc(4) | space(32) | key(24) | len(4)`, with the value behind it.
pub const ENTRY_FIXED: usize = 1 + 4 + SPACE_ID_LEN + RAW_KEY_LEN + 4;
/// v2's entry: the same, with a `flags(2)` field before the length.
pub const ENTRY_FIXED_V2: usize = 1 + 4 + SPACE_ID_LEN + RAW_KEY_LEN + FLAGS_LEN + 4;
/// `CUBE_OP_PUT`: an entry that stores a value.
pub const WAL_OP_WRITE: u8 = 1;
/// `CUBE_OP_DEL`: an entry that removes one.
pub const WAL_OP_DELETE: u8 = 2;
/// What an image's header says, in any version.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Header {
pub version: u8,
pub curve: u8,
/// The image's byte length, header included. `None` in v1, which states no extent.
pub image_bytes: Option<u64>,
/// How many records follow the header. `None` in v1.
pub record_count: Option<u64>,
}
impl Header {
/// Where the records start.
pub fn records_off(&self) -> usize {
if self.version == VERSION_V1 {
HEADER_LEN_V1
} else if self.version == VERSION_V3 || self.version == VERSION_V4 {
HEADER_LEN_V3
} else {
HEADER_LEN_V2
}
}
/// The image's extent within `bytes`: what the header promises, or all of it for v1.
pub fn extent(&self, bytes_len: usize) -> usize {
match self.image_bytes {
Some(n) => core::cmp::min(n as usize, bytes_len),
None => bytes_len,
}
}
}
/// Why an image was refused. A reader that guesses at an extent can read somebody else's bytes, so
/// these are all refusals rather than defaults.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Bad {
/// Not a CUBE image at all.
Magic,
/// A version this build does not know.
Version(u8),
/// A header that does not fit, or an extent that does not cover its own header.
Extent,
/// A v3 table that does not sit where the header says it does.
Tables,
}
/// Read and validate an image header.
pub fn parse_header(bytes: &[u8]) -> Result<Header, Bad> {
if bytes.len() < 5 || &bytes[0..4] != MAGIC {
return Err(Bad::Magic);
}
let version = bytes[4];
let curve = bytes[5];
match version {
VERSION_V1 => Ok(Header {
version,
curve,
image_bytes: None,
record_count: None,
}),
VERSION_V2 => {
if bytes.len() < HEADER_LEN_V2 {
return Err(Bad::Magic);
}
let image_bytes = le_u64(bytes, 6);
let record_count = le_u64(bytes, 14);
if (image_bytes as usize) < HEADER_LEN_V2 {
return Err(Bad::Extent);
}
Ok(Header {
version,
curve,
image_bytes: Some(image_bytes),
record_count: Some(record_count),
})
}
VERSION_V3 | VERSION_V4 => {
if bytes.len() < HEADER_LEN_V3 {
return Err(Bad::Magic);
}
let geometry = V3::decode(bytes)?;
Ok(Header {
version,
curve,
image_bytes: Some(le_u64(bytes, 6)),
record_count: Some(geometry.record_count),
})
}
other => Err(Bad::Version(other)),
}
}
/// The v3 tables' geometry: where the index is, where the values start, and how many of each.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct V3 {
/// The version this geometry belongs to: `VERSION_V3` or `VERSION_V4`, which differ only in
/// the index entry's stride (the class mask adds two bytes).
pub version: u8,
pub record_count: u64,
pub space_count: u64,
pub index_off: u64,
pub values_off: u64,
}
impl V3 {
/// The width of one index entry for this geometry's version.
pub fn index_stride(&self) -> usize {
if self.version == VERSION_V4 {
INDEX_ENTRY_V4
} else {
INDEX_ENTRY
}
}
/// Read the tables' geometry, refusing anything that does not add up.
///
/// The three equalities here are the whole point of the format: a reader computes a record's
/// place as `index_off + i * stride`, and that is only a place if the index really starts
/// there and really is that wide.
pub fn decode(bytes: &[u8]) -> Result<Self, Bad> {
let version = bytes.get(4).copied().ok_or(Bad::Magic)?;
if bytes.len() < HEADER_LEN_V3 || &bytes[0..4] != MAGIC || (version != VERSION_V3 && version != VERSION_V4) {
return Err(Bad::Magic);
}
let geometry = V3 {
version,
record_count: le_u64(bytes, 14),
space_count: le_u64(bytes, 22),
index_off: le_u64(bytes, 30),
values_off: le_u64(bytes, 38),
};
let image_bytes = le_u64(bytes, 6);
if image_bytes < HEADER_LEN_V3 as u64 {
return Err(Bad::Extent);
}
let table_end = HEADER_LEN_V3 as u64 + geometry.space_count * SPACE_ENTRY as u64;
let index_end = geometry.index_off + geometry.record_count * geometry.index_stride() as u64;
if geometry.index_off != table_end
|| geometry.values_off != index_end
|| geometry.values_off > image_bytes
{
return Err(Bad::Tables);
}
Ok(geometry)
}
/// Write the header this geometry describes. `image_bytes` is filled in by the caller's
/// arithmetic, since only it knows how long the values are.
pub fn encode(&self, out: &mut [u8], curve: u8, image_bytes: u64) -> Result<usize, Bad> {
if out.len() < HEADER_LEN_V3 {
return Err(Bad::Extent);
}
out[0..4].copy_from_slice(MAGIC);
out[4] = VERSION_V3;
out[5] = curve;
out[6..14].copy_from_slice(&image_bytes.to_le_bytes());
out[14..22].copy_from_slice(&self.record_count.to_le_bytes());
out[22..30].copy_from_slice(&self.space_count.to_le_bytes());
out[30..38].copy_from_slice(&self.index_off.to_le_bytes());
out[38..46].copy_from_slice(&self.values_off.to_le_bytes());
Ok(HEADER_LEN_V3)
}
/// One space-table row: the space, where its records start in the index, and how many.
pub fn space_row<'a>(&self, bytes: &'a [u8], row: u64) -> Option<(&'a [u8; SPACE_ID_LEN], u64, u64)> {
if row >= self.space_count {
return None;
}
let at = HEADER_LEN_V3 + row as usize * SPACE_ENTRY;
if at + SPACE_ENTRY > bytes.len() {
return None;
}
let space: &[u8; SPACE_ID_LEN] = bytes[at..at + SPACE_ID_LEN].try_into().ok()?;
Some((
space,
le_u64(bytes, at + SPACE_ID_LEN),
le_u64(bytes, at + SPACE_ID_LEN + 8),
))
}
/// One index entry, by its position in the index.
pub fn index_entry<'a>(&self, bytes: &'a [u8], at: u64) -> Option<IndexEntry<'a>> {
if at >= self.record_count {
return None;
}
let stride = self.index_stride();
let off = self.index_off as usize + at as usize * stride;
if off + stride > bytes.len() {
return None;
}
// The flag field exists only in v4; a v3 entry reads as a zero mask, which is honest —
// "no class" — and matches nothing in a scan.
let flags = if self.version == VERSION_V4 {
le_u16(bytes, off + RAW_KEY_LEN)
} else {
0
};
let vo = off + RAW_KEY_LEN + if self.version == VERSION_V4 { FLAGS_LEN } else { 0 };
let entry = IndexEntry {
key: bytes[off..off + RAW_KEY_LEN].try_into().ok()?,
flags,
value_off: le_u64(bytes, vo),
value_len: le_u64(bytes, vo + 8),
};
// A value that is not inside the image is a truncated image, not an empty one.
if entry.value_off < self.values_off
|| entry.value_off + entry.value_len > bytes.len() as u64
{
return None;
}
Some(entry)
}
/// The value an index entry points at.
pub fn value<'a>(&self, bytes: &'a [u8], entry: &IndexEntry<'_>) -> Option<&'a [u8]> {
let at = entry.value_off as usize;
let len = entry.value_len as usize;
if at < self.values_off as usize || at + len > bytes.len() {
return None;
}
Some(&bytes[at..at + len])
}
}
/// An addressed index entry: the key, its class mask, and where its value lies.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndexEntry<'a> {
pub key: &'a [u8; RAW_KEY_LEN],
/// The class mask (v4), or zero (v3, where no mask was written).
pub flags: u16,
pub value_off: u64,
pub value_len: u64,
}
/// One packed record, and where the next one starts.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PackedRecord<'a> {
pub space: &'a [u8; SPACE_ID_LEN],
pub key: &'a [u8; RAW_KEY_LEN],
pub value: &'a [u8],
/// The offset of the record after this one.
pub next: usize,
}
/// Read one packed record at `off`, if there is a whole one before `end`.
///
/// This is the v1/v2 layout's only parsing rule, and it is shared for the same reason the constants
/// are: a reader that disagrees about a frame's length reads every following record wrong.
pub fn packed_record(bytes: &[u8], off: usize, end: usize) -> Option<PackedRecord<'_>> {
if off + RECORD_FIXED > end || end > bytes.len() {
return None;
}
let frame = &bytes[off..off + RECORD_FIXED];
let value_len = le_u64(frame, SPACE_ID_LEN + RAW_KEY_LEN) as usize;
let value_at = off + RECORD_FIXED;
if value_at + value_len > end {
return None;
}
Some(PackedRecord {
space: frame[..SPACE_ID_LEN].try_into().ok()?,
key: frame[SPACE_ID_LEN..SPACE_ID_LEN + RAW_KEY_LEN].try_into().ok()?,
value: &bytes[value_at..value_at + value_len],
next: value_at + value_len,
})
}
/// One log entry, and where the next one starts.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WalEntry<'a> {
pub space: &'a [u8; SPACE_ID_LEN],
pub key: &'a [u8; RAW_KEY_LEN],
pub op: u8,
/// The class mask (WAL version 2), or zero (version 1, where no mask was written).
pub flags: u16,
pub value: &'a [u8],
pub next: usize,
}
/// Read one log entry at `off`. The log slice includes its header, so the version at `log[4]`
/// decides the entry's stride: version 2 carries a two-byte class mask before the length.
///
/// The checksum covers space, key, the mask, length and value, so a torn tail is stopped at rather
/// than applied — the rule the fold and every reader share.
pub fn wal_entry(log: &[u8], off: usize) -> Option<WalEntry<'_>> {
let version = log.get(4).copied().unwrap_or(WAL_VERSION);
let (fixed, len_at) = if version == WAL_VERSION_V2 {
(ENTRY_FIXED_V2, RAW_KEY_LEN + FLAGS_LEN + SPACE_ID_LEN + 5)
} else {
(ENTRY_FIXED, 61)
};
if off + fixed > log.len() {
return None;
}
let op = log[off];
if op != WAL_OP_WRITE && op != WAL_OP_DELETE {
return None;
}
let crc = le_u32(log, off + 1);
let len = le_u32(log, off + len_at) as usize;
let frame_end = off + fixed + len;
if frame_end > log.len() {
return None;
}
if crc32(&log[off + 5..frame_end]) != crc {
return None;
}
let flags = if version == WAL_VERSION_V2 {
le_u16(log, off + SPACE_ID_LEN + RAW_KEY_LEN + 5)
} else {
0
};
Some(WalEntry {
space: log[off + 5..off + 37].try_into().ok()?,
key: log[off + 37..off + 61].try_into().ok()?,
op,
flags,
value: &log[off + fixed..frame_end],
next: frame_end,
})
}
/// A little-endian `u64` at `at`, or zero if it does not fit — callers bound-check first, and the
/// alternative is a panic in a reader that exists to refuse rather than guess.
pub fn le_u64(bytes: &[u8], at: usize) -> u64 {
let mut w = [0u8; 8];
if at + 8 <= bytes.len() {
w.copy_from_slice(&bytes[at..at + 8]);
}
u64::from_le_bytes(w)
}
/// A little-endian `u32` at `at`, with the same rule.
pub fn le_u32(bytes: &[u8], at: usize) -> u32 {
let mut w = [0u8; 4];
if at + 4 <= bytes.len() {
w.copy_from_slice(&bytes[at..at + 4]);
}
u32::from_le_bytes(w)
}
/// A little-endian `u16` at `at`, with the same rule.
pub fn le_u16(bytes: &[u8], at: usize) -> u16 {
let mut w = [0u8; 2];
if at + 2 <= bytes.len() {
w.copy_from_slice(&bytes[at..at + 2]);
}
u16::from_le_bytes(w)
}
/// CRC-32 (IEEE 802.3), bitwise.
///
/// A corruption check, not a security check: it catches a torn write or a flipped bit, and says
/// nothing about whether anyone tampered with the bytes — that is `cube-crypt`'s job, and it belongs
/// on the record rather than on the framing.
pub fn crc32(bytes: &[u8]) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for byte in bytes {
crc ^= *byte as u32;
for _ in 0..8 {
let mask = (crc & 1).wrapping_neg();
crc = (crc >> 1) ^ (0xEDB8_8320 & mask);
}
}
!crc
}
/// The store's digest hash: FNV-1a, 64-bit.
pub const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
/// Fold more bytes into an FNV-1a hash.
pub fn fnv1a64(bytes: &[u8], mut h: u64) -> u64 {
for byte in bytes {
h ^= *byte as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
/// The digest line, as both sides print it.
///
/// `bytes` is the records' logical size — the same number whatever layout holds them — because this
/// line is what a kernel-written store and a userspace-written one are compared by, and a figure
/// that changed with the layout would make one store look like two.
pub struct Digest {
pub version: u8,
pub curve: u8,
pub records: u64,
pub value_bytes: u64,
pub hash: u64,
pub errors: u64,
}
impl Digest {
/// The logical size of the store these records describe.
pub fn logical_bytes(records: u64, value_bytes: u64) -> u64 {
records * RECORD_FIXED as u64 + value_bytes
}
/// Fold one record in.
pub fn add(&mut self, space: &[u8], key: &[u8], value: &[u8]) {
self.hash = fnv1a64(space, self.hash);
self.hash = fnv1a64(key, self.hash);
self.hash = fnv1a64(&(value.len() as u64).to_le_bytes(), self.hash);
self.hash = fnv1a64(value, self.hash);
self.records += 1;
self.value_bytes += value.len() as u64;
}
pub fn new(version: u8, curve: u8) -> Self {
Digest {
version,
curve,
records: 0,
value_bytes: 0,
hash: FNV_OFFSET,
errors: 0,
}
}
}
// ── Where a coordinate's key sits, and what span a box covers ──────────────────────────
//
// The key is a three-axis bit interleave: axis `x` owns bit `3i`, `y` owns `3i+1`, `z` owns
// `3i+2`, and the 24-byte key is that 192-bit number written big-endian — so comparing two keys
// byte-wise from the front is comparing the interleaved integers, and `key_cmp` is the comparison
// a sorted index performs.
//
// This lives in the shared file rather than in the driver because it is the one piece of the
// addressing that the two sides must agree on byte for byte: the driver computes a key to look a
// record up, and userspace computes the same key to lay an image out. It was written twice before
// (`morton_encode` in `cubelinux_store.rs` and the curve in `cube-core`) with the agreement held
// by the gates rather than by the compiler; now the kernel's copy is this one.
/// The key a coordinate has.
///
/// `x` at bit `3i`, `y` at `3i+1`, `z` at `3i+2`, for `i` in `0..64`, big-endian into 24 bytes.
/// The correspondence is monotone in every axis — if `a <= a'` on all three then `key(a) <= key(a')`
/// — which is what makes [`key_span`] a sound bound rather than a guess.
pub fn morton_key(x: u64, y: u64, z: u64) -> [u8; RAW_KEY_LEN] {
let mut k = [0u8; RAW_KEY_LEN];
let mut i = 0;
while i < 64 {
let mut axis = 0;
while axis < 3 {
let value = match axis {
0 => x,
1 => y,
_ => z,
};
if (value >> i) & 1 != 0 {
let n = 3 * i + axis;
k[RAW_KEY_LEN - 1 - (n / 8)] |= 1 << (n % 8);
}
axis += 1;
}
i += 1;
}
k
}
/// The point a key encodes — the inverse of [`morton_key`].
///
/// Bit `n` of the key is bit `n / 3` of axis `n % 3`, which is the interleave read backwards.
///
/// Why the kernel needs it: a region walk is handed a **key span**, not a set of keys. A span is a
/// bound, not the set — keys of points *outside* the box also fall inside it (Z-order
/// amplification) — so every candidate found in the span has to be turned back into a point and
/// tested for membership. Without this, the walk would return records that are outside the region
/// the caller asked for, which is exactly the mistake the span's own doc warns about.
pub fn morton_decode(k: &[u8; RAW_KEY_LEN]) -> [u64; 3] {
let mut out = [0u64; 3];
let mut n = 0;
while n < RAW_KEY_LEN * 8 {
if k[RAW_KEY_LEN - 1 - (n / 8)] & (1 << (n % 8)) != 0 {
out[n % 3] |= 1u64 << (n / 3);
}
n += 1;
}
out
}
/// Compare two keys as the 192-bit numbers they are. `a < b` means `a` sorts first.
pub fn key_cmp(a: &[u8; RAW_KEY_LEN], b: &[u8; RAW_KEY_LEN]) -> core::cmp::Ordering { let mut i = 0;
while i < RAW_KEY_LEN {
if a[i] != b[i] {
return if a[i] < b[i] {
core::cmp::Ordering::Less
} else {
core::cmp::Ordering::Greater
};
}
i += 1;
}
core::cmp::Ordering::Equal
}
/// The key span a box covers: `(key(lo), key(hi))`, inclusive.
///
/// **This is the bound a seek needs, and it needs no decomposition.** Because the interleave is
/// monotone, every point in the box has a key between these two — so a sorted index can be
/// binary-searched for the lower one, scanned forward while the key stays under the upper one, and
/// each candidate tested for membership. Records may be found in that span that are *not* in the
/// box (that is the classic Z-order amplification, and how much depends on the box), but no record
/// in the box is outside the span. What is *not* true — and was believed here until it was checked
/// — is that the box is one contiguous run: see [`aligned_box_is_one_run`]. The span being sound
/// does not make it tight, and confusing the two is how an aligned box of three runs gets
/// documented as one.
pub fn key_span(lo: [u64; 3], hi: [u64; 3]) -> ([u8; RAW_KEY_LEN], [u8; RAW_KEY_LEN]) {
(
morton_key(lo[0], lo[1], lo[2]),
morton_key(hi[0], hi[1], hi[2]),
)
}
/// Whether a power-of-two-aligned box with these per-axis sizes is exactly **one** contiguous run
/// of the key.
///
/// The condition, derived and then checked against the keys themselves on every properly-aligned
/// box with sizes in `{1,2,4,8,16}` per axis and origins swept to 16 — 4,805 boxes, no
/// disagreement (pinned in `crates/cube-format`'s tests and in `cube-store`'s):
///
/// > `max(k) - min(k) <= 1` **and** the axes at the top level form a prefix of `(x, y, z)` in that
/// > order, where `2^k` is the size on an axis.
///
/// The free bit positions of such a box are `{3i + a : i < k_a}`, and that set is a prefix of
/// `{0,1,2,…}` only when the levels are complete except possibly the last — so a cube qualifies, a
/// cube doubled along `x` qualifies, a cube doubled along `x` and `y` qualifies, and **doubling `z`
/// alone, or `x` and `z` together, does not**, because `z` cannot be free at a level where `y` is
/// not. The asymmetry belongs to the interleave; it is why the property looked plausible.
///
/// Sizes are taken as given: a size that is not a power of two is not an aligned box's size, and is
/// answered `false` rather than rounded, because a caller that passes one has a bug and the useful
/// answer is "no", not a guess.
pub fn aligned_box_is_one_run(sx: u64, sy: u64, sz: u64) -> bool {
fn k_of(s: u64) -> Option<u32> {
if s == 0 || !s.is_power_of_two() {
return None;
}
Some(s.trailing_zeros())
}
let (kx, ky, kz) = match (k_of(sx), k_of(sy), k_of(sz)) {
(Some(a), Some(b), Some(c)) => (a, b, c),
_ => return false,
};
let (lo, hi) = (
kx.min(ky).min(kz),
kx.max(ky).max(kz),
);
if hi - lo > 1 {
return false;
}
// The axes at the top level, in the order the interleave gives them: x, then y, then z.
let mut top = [false; 3];
if kx == hi {
top[0] = true;
}
if ky == hi {
top[1] = true;
}
if kz == hi {
top[2] = true;
}
// A prefix: x before y before z, with no gaps.
if top[2] && !top[1] {
return false;
}
if top[1] && !top[0] {
return false;
}
top[0]
}