use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
pub const VERSION: u8 = 3;
pub const MAX_GROUP_ROWS: u32 = crate::config::MAX_BLOCK_SIZE;
const VAR_U16_MAX_LEN: usize = 3;
const VAR_U32_MAX_LEN: usize = 5;
const PART_LEN: usize = 2 + 1;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ZoneBlock {
pub column_id: u16,
pub length: u32,
}
pub const ZONE_BOUND_LEN: usize = 64;
const ZONE_MAX_UNBOUNDED: u8 = 1;
const ZONE_MIN_LEN: usize = 1 + 1 + 2;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct ZoneSlot {
null_count: u32,
min: (u32, u8),
max: Option<(u32, u8)>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Zone<'a> {
pub null_count: u32,
pub min: &'a [u8],
pub max: Option<&'a [u8]>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct PageZones {
columns: Vec<u16>,
bounds: Vec<u8>,
slots: Vec<ZoneSlot>,
}
impl PageZones {
#[must_use]
pub fn new(columns: Vec<u16>) -> Self {
Self {
columns,
bounds: Vec::new(),
slots: Vec::new(),
}
}
pub fn push(&mut self, null_count: u32, range: Option<(&[u8], &[u8])>) {
let Some((min, max)) = range else {
self.push_bounds(null_count, &[], Some(&[]));
return;
};
let min = min.get(..ZONE_BOUND_LEN).unwrap_or(min);
if max.len() <= ZONE_BOUND_LEN {
self.push_bounds(null_count, min, Some(max));
return;
}
let prefix = max.get(..ZONE_BOUND_LEN).unwrap_or(max);
match prefix.iter().rposition(|&b| b != u8::MAX) {
Some(last) => {
let mut bound = prefix.get(..=last).unwrap_or(prefix).to_vec();
if let Some(byte) = bound.last_mut() {
*byte += 1;
}
self.push_bounds(null_count, min, Some(&bound));
}
None => self.push_bounds(null_count, min, None),
}
}
fn push_bounds(&mut self, null_count: u32, min: &[u8], max: Option<&[u8]>) {
let mut place = |bound: &[u8]| {
#[expect(
clippy::cast_possible_truncation,
reason = "a directory block is far below 4 GiB and a bound below 256 bytes"
)]
let at = (self.bounds.len() as u32, bound.len() as u8);
self.bounds.extend_from_slice(bound);
at
};
let min = place(min);
let max = max.map(&mut place);
self.slots.push(ZoneSlot {
null_count,
min,
max,
});
}
#[must_use]
pub fn describes(&self, column_id: u16) -> bool {
self.columns.contains(&column_id)
}
#[must_use]
pub fn columns(&self) -> &[u16] {
&self.columns
}
#[must_use]
pub fn only(&self, keep: impl Fn(u16) -> bool) -> Self {
let kept: Vec<usize> = (0..self.columns.len())
.filter(|&i| self.columns.get(i).is_some_and(|&c| keep(c)))
.collect();
let mut out = Self::new(
kept.iter()
.filter_map(|&i| self.columns.get(i).copied())
.collect(),
);
let width = self.columns.len();
if width == 0 {
return out;
}
for row_page in 0..self.slots.len() / width {
for &column in &kept {
if let Some(zone) = self.get(row_page * width + column) {
out.push_bounds(zone.null_count, zone.min, zone.max);
}
}
}
out
}
#[must_use]
pub fn zone(&self, row_page: u16, column_id: u16) -> Option<Zone<'_>> {
let column = self.columns.iter().position(|&c| c == column_id)?;
let index = usize::from(row_page)
.checked_mul(self.columns.len())?
.checked_add(column)?;
self.get(index)
}
#[must_use]
fn get(&self, index: usize) -> Option<Zone<'_>> {
let slot = self.slots.get(index)?;
let bound = |(start, len): (u32, u8)| {
let start = start as usize;
self.bounds.get(start..start + usize::from(len))
};
Some(Zone {
null_count: slot.null_count,
min: bound(slot.min)?,
max: match slot.max {
Some(max) => Some(bound(max)?),
None => None,
},
})
}
fn min_reference(&self, index: usize) -> &[u8] {
index
.checked_sub(self.columns.len())
.and_then(|before| self.get(before))
.map_or(&[], |zone| zone.max.unwrap_or(zone.min))
}
pub fn encode_into(&self, out: &mut Vec<u8>) {
out.reserve(
VAR_U16_MAX_LEN
+ self.columns.len() * 2
+ self.slots.len() * (VAR_U32_MAX_LEN + 1 + 4)
+ self.bounds.len(),
);
put_varint(out, self.columns.len() as u64);
for column_id in &self.columns {
out.extend_from_slice(&column_id.to_le_bytes());
}
for index in 0..self.slots.len() {
let Some(zone) = self.get(index) else {
continue;
};
put_varint(out, u64::from(zone.null_count));
out.push(if zone.max.is_none() {
ZONE_MAX_UNBOUNDED
} else {
0
});
put_bound(out, self.min_reference(index), zone.min);
if let Some(max) = zone.max {
put_bound(out, zone.min, max);
}
}
}
fn decode_from(rest: &mut &[u8], row_page_count: usize) -> Result<Self> {
const ERR: Error = Error::InvalidHeader("ColumnZones");
let Some(column_count) = take_var_u16(rest).map(usize::from) else {
return Err(ERR);
};
let mut columns = Vec::with_capacity(column_count.min(rest.len() / 2));
for _ in 0..column_count {
let Some(column_id) = take(rest).map(u16::from_le_bytes) else {
return Err(ERR);
};
columns.push(column_id);
}
let Some(count) = row_page_count.checked_mul(column_count) else {
return Err(ERR);
};
let mut zones = Self::new(columns);
zones.slots.reserve(count.min(rest.len() / ZONE_MIN_LEN));
let mut min_buf = [0u8; ZONE_BOUND_LEN];
let mut max_buf = [0u8; ZONE_BOUND_LEN];
for index in 0..count {
let (Some(null_count), Some([flags])) = (take_var_u32(rest), take::<1>(rest)) else {
return Err(ERR);
};
let Some(min) = take_bound(rest, zones.min_reference(index), &mut min_buf)
.and_then(|len| min_buf.get(..len))
else {
return Err(ERR);
};
let max = match flags {
0 => {
let Some(max) =
take_bound(rest, min, &mut max_buf).and_then(|len| max_buf.get(..len))
else {
return Err(ERR);
};
Some(max)
}
ZONE_MAX_UNBOUNDED => None,
_ => return Err(Error::InvalidHeader("ColumnZones: reserved zone flag set")),
};
zones.push_bounds(null_count, min, max);
}
Ok(zones)
}
}
fn put_bound(out: &mut Vec<u8>, reference: &[u8], bound: &[u8]) {
let shared = reference
.iter()
.zip(bound)
.take_while(|(a, b)| a == b)
.count();
let suffix = bound.get(shared..).unwrap_or_default();
#[expect(
clippy::cast_possible_truncation,
reason = "a zone bound is below 256 bytes"
)]
out.extend_from_slice(&[shared as u8, suffix.len() as u8]);
out.extend_from_slice(suffix);
}
fn take_bound(rest: &mut &[u8], reference: &[u8], out: &mut [u8; ZONE_BOUND_LEN]) -> Option<usize> {
let [shared, suffix_len] = take::<2>(rest)?;
let (shared, suffix_len) = (usize::from(shared), usize::from(suffix_len));
let prefix = reference.get(..shared)?;
let suffix = take_slice(rest, suffix_len)?;
let len = shared + suffix_len;
let bound = out.get_mut(..len)?;
let (head, tail) = bound.split_at_mut(shared);
head.copy_from_slice(prefix);
tail.copy_from_slice(suffix);
Some(len)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct PageId {
pub column_id: u16,
pub part: u8,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PageEntry {
pub offset: u32,
pub length: u32,
pub id: PageId,
pub row_page: u16,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PageStamp {
pub group_tag: u64,
pub id: PageId,
pub row_page: u16,
}
impl PageStamp {
pub const LEN: usize = 8 + 2 + 1 + 2;
pub fn encode_into(self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.group_tag.to_le_bytes());
out.extend_from_slice(&self.id.column_id.to_le_bytes());
out.push(self.id.part);
out.extend_from_slice(&self.row_page.to_le_bytes());
}
#[must_use]
pub fn decode(bytes: [u8; Self::LEN]) -> Self {
let [t0, t1, t2, t3, t4, t5, t6, t7, c0, c1, part, r0, r1] = bytes;
Self {
group_tag: u64::from_le_bytes([t0, t1, t2, t3, t4, t5, t6, t7]),
id: PageId {
column_id: u16::from_le_bytes([c0, c1]),
part,
},
row_page: u16::from_le_bytes([r0, r1]),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct PageDirectory {
row_count: u32,
group_tag: u64,
row_pages: Vec<u32>,
row_page_starts: Vec<u32>,
entries: Vec<PageEntry>,
sorted_parts: Vec<PageId>,
head_zone_block: Option<ZoneBlock>,
head_zones: PageZones,
zone_blocks: Vec<ZoneBlock>,
zones_len: u32,
}
impl PageDirectory {
pub fn new(
row_count: u32,
group_tag: u64,
row_pages: Vec<u32>,
entries: Vec<PageEntry>,
head_zone_block: Option<ZoneBlock>,
zone_blocks: Vec<ZoneBlock>,
) -> Result<Self> {
if entries.len() > usize::from(u16::MAX) {
return Err(Error::InvalidHeader(
"column page: page count exceeds the u16 directory field",
));
}
if row_count > MAX_GROUP_ROWS {
return Err(Error::InvalidHeader(
"column page: a row group of more rows than a writer cuts",
));
}
if row_pages.len() > usize::from(u16::MAX) {
return Err(Error::InvalidHeader(
"column page: row page count exceeds the u16 directory field",
));
}
let mut row_page_starts = Vec::with_capacity(row_pages.len());
let mut rows: u32 = 0;
for &page_rows in &row_pages {
if page_rows == 0 {
return Err(Error::InvalidHeader("column page: an empty row page"));
}
row_page_starts.push(rows);
rows = rows.checked_add(page_rows).ok_or(Error::InvalidHeader(
"column page: row pages overflow the row count",
))?;
}
if rows != row_count {
return Err(Error::InvalidHeader(
"column page: row pages do not sum to the group's rows",
));
}
let row_page_count = row_pages.len();
let mut end_of_previous: u32 = 0;
for (index, entry) in entries.iter().enumerate() {
if entry.offset != end_of_previous {
return Err(Error::InvalidHeader(
"column page: a page does not start where the one before it ends",
));
}
end_of_previous = entry
.offset
.checked_add(entry.length)
.ok_or(Error::InvalidHeader("column page: extent overflows u32"))?;
if usize::from(entry.row_page) >= row_page_count {
return Err(Error::InvalidHeader(
"column page: a page names a row page that does not exist",
));
}
let run_start = index - index % row_page_count;
let part = entries.get(run_start).map(|first| first.id);
if usize::from(entry.row_page) != index % row_page_count || part != Some(entry.id) {
return Err(Error::InvalidHeader(
"column page: a column part is missing a row page",
));
}
}
if !entries.len().is_multiple_of(row_page_count) {
return Err(Error::InvalidHeader(
"column page: a column part is missing a row page",
));
}
let mut sorted_parts: Vec<PageId> = entries
.iter()
.step_by(row_page_count.max(1))
.map(|e| e.id)
.collect();
sorted_parts.sort_unstable();
if sorted_parts
.windows(2)
.any(|pair| pair.first() == pair.last())
{
return Err(Error::InvalidHeader(
"column page: two pages claim the same column part and row page",
));
}
let zones_len = Self::check_zone_blocks(head_zone_block, &zone_blocks, &sorted_parts)?;
Ok(Self {
row_count,
group_tag,
row_pages,
row_page_starts,
entries,
sorted_parts,
head_zone_block,
head_zones: PageZones::default(),
zone_blocks,
zones_len,
})
}
fn check_zone_blocks(
head: Option<ZoneBlock>,
zone_blocks: &[ZoneBlock],
parts: &[PageId],
) -> Result<u32> {
let bad = |what| Err(Error::InvalidHeader(what));
if zone_blocks.len() > usize::from(u16::MAX) {
return bad("column page: zone block count exceeds the u16 directory field");
}
let mut columns: Vec<u16> = head
.iter()
.chain(zone_blocks)
.map(|b| b.column_id)
.collect();
columns.sort_unstable();
if columns.windows(2).any(|pair| pair.first() == pair.last()) {
return bad("column page: a column has two zone blocks");
}
let mut total: u32 = 0;
for (block, after_pages) in head
.iter()
.map(|b| (b, false))
.chain(zone_blocks.iter().map(|b| (b, true)))
{
let has_pages = parts
.binary_search_by(|id| id.column_id.cmp(&block.column_id))
.is_ok();
if !has_pages {
return bad("column page: a zone block names a column the group does not have");
}
if block.length == 0 {
return bad("column page: an empty zone block");
}
if after_pages {
total = total.checked_add(block.length).ok_or(Error::InvalidHeader(
"column page: zone blocks overflow u32",
))?;
}
}
Ok(total)
}
pub fn with_head_zones(mut self, zones: PageZones) -> Result<Self> {
let Some(head) = self.head_zone_block else {
return Err(Error::InvalidHeader(
"column page: head zones for a group without a head zone block",
));
};
if zones.columns != [head.column_id] {
return Err(Error::InvalidHeader(
"ColumnZones: a zone block holds zones of another column than its own",
));
}
Self::check_zones(&zones, &self.row_pages, &self.sorted_parts)?;
self.head_zones = zones;
Ok(self)
}
pub fn encode_zone_block(group_tag: u64, zones: &PageZones, out: &mut Vec<u8>) {
out.extend_from_slice(&group_tag.to_le_bytes());
zones.encode_into(out);
}
pub fn decode_zone_block(&self, column_id: u16, bytes: &[u8]) -> Result<PageZones> {
let Some((tag, mut rest)) = bytes.split_first_chunk::<8>() else {
return Err(Error::InvalidHeader(
"ColumnZones: payload shorter than its group tag",
));
};
if u64::from_le_bytes(*tag) != self.group_tag {
return Err(Error::InvalidHeader(
"ColumnZones: the zone block belongs to another row group",
));
}
let zones = PageZones::decode_from(&mut rest, self.row_pages.len())?;
if !rest.is_empty() {
return Err(Error::InvalidHeader(
"ColumnZones: trailing bytes after the declared zones",
));
}
if zones.columns != [column_id] {
return Err(Error::InvalidHeader(
"ColumnZones: a zone block holds zones of another column than its own",
));
}
Self::check_zones(&zones, &self.row_pages, &self.sorted_parts)?;
Ok(zones)
}
fn check_zones(zones: &PageZones, row_pages: &[u32], parts: &[PageId]) -> Result<()> {
let bad = |what| Err(Error::InvalidHeader(what));
if zones.columns.len() > usize::from(u16::MAX) {
return bad("column page: zone column count exceeds the u16 directory field");
}
let mut columns = zones.columns.clone();
columns.sort_unstable();
if columns.windows(2).any(|pair| pair.first() == pair.last()) {
return bad("column page: a column has two zones");
}
for column_id in &columns {
let has_pages = parts
.binary_search_by(|id| id.column_id.cmp(column_id))
.is_ok();
if !has_pages {
return bad("column page: a zone names a column the group does not have");
}
}
if Some(zones.slots.len()) != row_pages.len().checked_mul(zones.columns.len()) {
return bad("column page: zones do not cover every row page and zone column");
}
for (index, rows) in row_pages
.iter()
.flat_map(|&rows| core::iter::repeat_n(rows, zones.columns.len()))
.enumerate()
{
let Some(zone) = zones.get(index) else {
return bad("column page: a zone's bounds lie outside the directory");
};
if zone.min.len() > ZONE_BOUND_LEN || zone.max.is_some_and(|m| m.len() > ZONE_BOUND_LEN)
{
return bad("column page: a zone bound is longer than a zone keeps");
}
if zone.null_count > rows {
return bad("column page: a zone counts more nulls than its row page has rows");
}
let empty = zone.min.is_empty() && zone.max == Some(&[][..]);
if zone.null_count == rows && !empty {
return bad("column page: an all-null zone records a range");
}
if zone.max.is_some_and(|max| zone.min > max) {
return bad("column page: a zone's lower bound is above its upper bound");
}
}
Ok(())
}
pub fn contiguous(
row_count: u32,
group_tag: u64,
row_pages: Vec<u32>,
pages: impl IntoIterator<Item = (PageId, u16, u32)>,
head_zone_block: Option<ZoneBlock>,
zone_blocks: Vec<ZoneBlock>,
) -> Result<Self> {
let mut offset: u32 = 0;
let mut entries = Vec::new();
for (id, row_page, length) in pages {
entries.push(PageEntry {
offset,
length,
id,
row_page,
});
offset = offset.checked_add(length).ok_or(Error::InvalidHeader(
"column page: group length overflows u32",
))?;
}
Self::new(
row_count,
group_tag,
row_pages,
entries,
head_zone_block,
zone_blocks,
)
}
#[must_use]
pub fn head_zone_block(&self) -> Option<ZoneBlock> {
self.head_zone_block
}
#[must_use]
pub fn zone_blocks(&self) -> &[ZoneBlock] {
&self.zone_blocks
}
#[must_use]
pub fn pages_start(&self) -> u32 {
self.head_zone_block.map_or(0, |head| head.length)
}
#[must_use]
pub fn zone_block(&self, column_id: u16) -> Option<(u32, u32)> {
if let Some(head) = self.head_zone_block
&& head.column_id == column_id
{
return Some((0, head.length));
}
let mut start = self.pages_start().wrapping_add(self.pages_len());
for block in &self.zone_blocks {
if block.column_id == column_id {
return Some((start, block.length));
}
start = start.wrapping_add(block.length);
}
None
}
#[must_use]
pub fn group_len(&self, directory_len: u32) -> Option<u32> {
directory_len
.checked_add(self.pages_start())?
.checked_add(self.pages_len())?
.checked_add(self.zones_len)
}
#[must_use]
pub fn pages_len(&self) -> u32 {
self.entries
.last()
.map_or(0, |e| e.offset.wrapping_add(e.length))
}
#[must_use]
pub fn row_count(&self) -> u32 {
self.row_count
}
#[must_use]
pub fn row_pages(&self) -> &[u32] {
&self.row_pages
}
#[must_use]
pub fn row_page_start(&self, row_page: u16) -> Option<u32> {
self.row_page_starts.get(usize::from(row_page)).copied()
}
#[must_use]
pub fn row_page_rows(&self, row_page: u16) -> Option<u32> {
self.row_pages.get(usize::from(row_page)).copied()
}
#[must_use]
pub fn group_tag(&self) -> u64 {
self.group_tag
}
#[must_use]
pub fn stamp_for(&self, entry: &PageEntry) -> PageStamp {
PageStamp {
group_tag: self.group_tag,
id: entry.id,
row_page: entry.row_page,
}
}
#[must_use]
pub fn entries(&self) -> &[PageEntry] {
&self.entries
}
#[must_use]
pub fn head_zones(&self) -> &PageZones {
&self.head_zones
}
#[must_use]
pub fn lacks_head_zones_for(&self, column_id: u16) -> bool {
self.head_zone_block
.is_some_and(|head| head.column_id == column_id)
&& self.head_zones.columns.is_empty()
}
#[must_use]
pub fn heap_size(&self) -> usize {
use core::mem::size_of;
(self.row_pages.len() + self.row_page_starts.len()) * size_of::<u32>()
+ (self.entries.len() * size_of::<PageEntry>())
+ (self.sorted_parts.len() * size_of::<PageId>())
+ self.zone_blocks.len() * size_of::<ZoneBlock>()
+ self.head_zones.columns.len() * size_of::<u16>()
+ self.head_zones.slots.len() * size_of::<ZoneSlot>()
+ self.head_zones.bounds.len()
}
pub fn encode_into(&self, out: &mut Vec<u8>) {
let row_page_count = self.row_pages.len();
let parts = self.entries.iter().step_by(row_page_count.max(1));
let part_count = parts.len();
out.reserve(
1 + 8
+ 3 * VAR_U16_MAX_LEN
+ part_count * PART_LEN
+ (row_page_count + self.entries.len()) * VAR_U32_MAX_LEN
+ self.zone_blocks.len() * (2 + VAR_U32_MAX_LEN),
);
out.push(VERSION);
out.extend_from_slice(&self.group_tag.to_le_bytes());
put_varint(out, row_page_count as u64);
put_varint(out, part_count as u64);
put_varint(out, self.zone_blocks.len() as u64);
put_varint(out, u64::from(self.pages_start()));
if let Some(head) = self.head_zone_block {
out.extend_from_slice(&head.column_id.to_le_bytes());
}
for entry in parts {
out.extend_from_slice(&entry.id.column_id.to_le_bytes());
out.push(entry.id.part);
}
for &rows in &self.row_pages {
put_varint(out, u64::from(rows));
}
for entry in &self.entries {
put_varint(out, u64::from(entry.length));
}
for block in &self.zone_blocks {
out.extend_from_slice(&block.column_id.to_le_bytes());
put_varint(out, u64::from(block.length));
}
}
pub fn decode(bytes: &[u8]) -> Result<Self> {
const ERR: Error = Error::InvalidHeader("ColumnPageDirectory");
let mut rest = bytes;
let Some([version]) = take::<1>(&mut rest) else {
return Err(ERR);
};
if version != VERSION {
return Err(Error::InvalidHeader(
"ColumnPageDirectory: unknown directory version",
));
}
let (Some(group_tag), Some(row_page_count), Some(part_count), Some(zone_block_count)) = (
take(&mut rest).map(u64::from_le_bytes),
take_var_u16(&mut rest),
take_var_u16(&mut rest),
take_var_u16(&mut rest),
) else {
return Err(ERR);
};
let (row_page_count, part_count, zone_block_count) = (
usize::from(row_page_count),
usize::from(part_count),
usize::from(zone_block_count),
);
let Some(head_len) = take_var_u32(&mut rest) else {
return Err(ERR);
};
let head_zone_block = if head_len == 0 {
None
} else {
let Some(column_id) = take(&mut rest).map(u16::from_le_bytes) else {
return Err(ERR);
};
Some(ZoneBlock {
column_id,
length: head_len,
})
};
let page_count = part_count * row_page_count;
if page_count > usize::from(u16::MAX) {
return Err(Error::InvalidHeader(
"column page: page count exceeds the u16 directory field",
));
}
let mut parts = Vec::with_capacity(part_count.min(rest.len() / PART_LEN));
for _ in 0..part_count {
let (Some(column_id), Some([part])) = (
take(&mut rest).map(u16::from_le_bytes),
take::<1>(&mut rest),
) else {
return Err(ERR);
};
parts.push(PageId { column_id, part });
}
let mut row_pages = Vec::with_capacity(row_page_count.min(rest.len()));
let mut row_count: u32 = 0;
for _ in 0..row_page_count {
let Some(rows) = take_var_u32(&mut rest) else {
return Err(ERR);
};
let Some(sum) = row_count.checked_add(rows) else {
return Err(Error::InvalidHeader(
"column page: row pages overflow the row count",
));
};
row_count = sum;
row_pages.push(rows);
}
let mut entries = Vec::with_capacity(page_count.min(rest.len()));
let mut offset: u32 = 0;
for id in &parts {
for row_page in (0u16..).take(row_page_count) {
let Some(length) = take_var_u32(&mut rest) else {
return Err(ERR);
};
entries.push(PageEntry {
offset,
length,
id: *id,
row_page,
});
let Some(end) = offset.checked_add(length) else {
return Err(Error::InvalidHeader("column page: extent overflows u32"));
};
offset = end;
}
}
let mut zone_blocks = Vec::with_capacity(zone_block_count.min(rest.len() / 3));
for _ in 0..zone_block_count {
let (Some(column_id), Some(length)) = (
take(&mut rest).map(u16::from_le_bytes),
take_var_u32(&mut rest),
) else {
return Err(ERR);
};
zone_blocks.push(ZoneBlock { column_id, length });
}
if !rest.is_empty() {
return Err(Error::InvalidHeader(
"ColumnPageDirectory: trailing bytes after the declared zone blocks",
));
}
Self::new(
row_count,
group_tag,
row_pages,
entries,
head_zone_block,
zone_blocks,
)
}
}
pub fn put_varint(out: &mut Vec<u8>, mut value: u64) {
loop {
let low = (value & 0x7f) as u8;
value >>= 7;
if value == 0 {
out.push(low);
return;
}
out.push(low | 0x80);
}
}
pub const VAR_U64_MAX_LEN: usize = 10;
pub fn take_varint(bytes: &mut &[u8], max_len: usize) -> Option<u64> {
let mut value = 0u64;
for (index, &byte) in bytes.iter().take(max_len.min(VAR_U64_MAX_LEN)).enumerate() {
if index == VAR_U64_MAX_LEN - 1 && byte > 1 {
return None;
}
value |= u64::from(byte & 0x7f) << (7 * index);
if byte & 0x80 == 0 {
*bytes = bytes.get(index + 1..)?;
return Some(value);
}
}
None
}
fn take_var_u16(bytes: &mut &[u8]) -> Option<u16> {
u16::try_from(take_varint(bytes, VAR_U16_MAX_LEN)?).ok()
}
fn take_var_u32(bytes: &mut &[u8]) -> Option<u32> {
u32::try_from(take_varint(bytes, VAR_U32_MAX_LEN)?).ok()
}
pub fn take<const N: usize>(bytes: &mut &[u8]) -> Option<[u8; N]> {
let (head, tail) = bytes.split_first_chunk::<N>()?;
*bytes = tail;
Some(*head)
}
pub fn take_slice<'a>(bytes: &mut &'a [u8], len: usize) -> Option<&'a [u8]> {
let (head, tail) = bytes.split_at_checked(len)?;
*bytes = tail;
Some(head)
}
#[cfg(test)]
#[expect(
clippy::expect_used,
clippy::indexing_slicing,
reason = "test code: a failed expectation is the assertion"
)]
mod tests;