use alloc::vec::Vec;
use super::handle::BlobIndirection;
use crate::coding::{Decode, Encode};
pub use crate::table::column_type::TypeTag;
use crate::{Error, UserKey, vlog::BlobFileId};
#[derive(Clone, Copy, Debug)]
pub struct BlobRef<'a> {
pub(crate) indirection: BlobIndirection,
pub(crate) key: &'a [u8],
pub(crate) tree: crate::TreeId,
pub(crate) source: u64,
}
impl BlobRef<'_> {
#[must_use]
pub fn blob_file_id(&self) -> BlobFileId {
self.indirection.vhandle.blob_file_id
}
#[must_use]
pub fn size(&self) -> u32 {
self.indirection.size
}
}
impl PartialEq for BlobRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.tree == other.tree && self.indirection.vhandle == other.indirection.vhandle
}
}
impl Eq for BlobRef<'_> {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cell<'a> {
Value(&'a [u8]),
Ref(BlobRef<'a>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Field<'a> {
pub column: u16,
pub tag: TypeTag,
pub cell: Cell<'a>,
}
impl<'a> Field<'a> {
#[must_use]
pub const fn bytes(column: u16, bytes: &'a [u8]) -> Self {
Self {
column,
tag: TypeTag::Bytes,
cell: Cell::Value(bytes),
}
}
}
pub struct RowCells {
pub(crate) key: UserKey,
pub(crate) row: crate::Slice,
pub(crate) read: alloc::sync::Arc<CellRead>,
}
pub(crate) struct CellRead {
pub(crate) tree: crate::TreeId,
pub(crate) version: crate::version::SuperVersion,
pub(crate) source: super::BlobSource,
pub(crate) token: super::released::ReaderToken,
}
impl core::fmt::Debug for RowCells {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RowCells")
.field("key", &self.key)
.field("tree", &self.read.tree)
.field("version", &self.read.token.version())
.finish_non_exhaustive()
}
}
impl RowCells {
#[must_use]
pub fn key(&self) -> &UserKey {
&self.key
}
pub fn fields(&self) -> crate::Result<Vec<Field<'_>>> {
Ok(decode_row(&self.row)?
.into_iter()
.map(|field| Field {
column: field.column,
tag: field.tag,
cell: match field.cell {
RowCell::Value(bytes) => Cell::Value(bytes),
RowCell::Ref { indirection, .. } => Cell::Ref(BlobRef {
indirection,
key: &self.key,
tree: self.read.tree,
source: self.read.token.version(),
}),
},
})
.collect())
}
pub fn resolve(&self, column: u16) -> crate::Result<Option<crate::Slice>> {
let fields = decode_row(&self.row)?;
let Some(field) = fields.iter().find(|field| field.column == column) else {
return Ok(None);
};
match &field.cell {
RowCell::Value(bytes) => Ok(Some(crate::Slice::from(*bytes))),
RowCell::Ref { indirection, .. } => {
let object =
self.read
.source
.object(&self.read.version.version, &self.key, indirection)?;
if object.len() != indirection.size as usize {
return Err(Error::InvalidHeader(
"field row: referenced object differs from its recorded size",
));
}
Ok(Some(object))
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum RowCell<'a> {
Value(&'a [u8]),
Ref {
indirection: BlobIndirection,
owner: bool,
},
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct RowField<'a> {
pub(crate) column: u16,
pub(crate) tag: TypeTag,
pub(crate) cell: RowCell<'a>,
}
#[cfg(test)]
impl<'a> RowField<'a> {
pub(crate) const fn bytes(column: u16, cell: RowCell<'a>) -> Self {
Self {
column,
tag: TypeTag::Bytes,
cell,
}
}
}
const CORRUPT: Error = Error::InvalidHeader("field row: malformed cell row");
const DESCRIPTOR_LEN: usize = 4;
pub const FIRST_FIELD_COLUMN: u16 = 3;
#[cfg(feature = "columnar")]
pub(crate) const WHOLE_VALUE_COLUMN: u16 = u16::MAX;
pub(crate) const CELL_REFS_COLUMN: u16 = u16::MAX - 1;
pub const RESERVED_COLUMNS: u16 = CELL_REFS_COLUMN;
pub(crate) const fn is_field_column(column: u16) -> bool {
column >= FIRST_FIELD_COLUMN && column < RESERVED_COLUMNS
}
pub(crate) fn order_fields(fields: &mut [RowField<'_>]) -> crate::Result<()> {
fields.sort_by_key(|field| field.column);
let mut previous = None;
for field in fields.iter() {
if !is_field_column(field.column) {
return Err(Error::CellRow("a field's column is not a field id"));
}
if previous == Some(field.column) {
return Err(Error::CellRow("one column in two fields"));
}
previous = Some(field.column);
let (tag, width) = field.tag.to_wire();
if TypeTag::from_wire(tag, width).is_err() {
return Err(Error::CellRow("a field's type has no wire form"));
}
if let Some(width) = field.tag.fixed_width() {
let len = match field.cell {
RowCell::Value(bytes) => bytes.len(),
RowCell::Ref { indirection, .. } => indirection.size as usize,
};
if len != usize::from(width) {
return Err(Error::CellRow("a field is not its type's width"));
}
}
}
Ok(())
}
pub(crate) fn encode_row(fields: &[RowField<'_>]) -> crate::Result<Vec<u8>> {
let count = u16::try_from(fields.len())
.map_err(|_| Error::InvalidHeader("field row: more than u16::MAX cells"))?;
let bitmap_len = fields.len().div_ceil(8);
let mut out = Vec::with_capacity(2 + 2 * bitmap_len + fields.len() * (DESCRIPTOR_LEN + 8));
out.extend_from_slice(&count.to_le_bytes());
let refs_at = out.len();
let owners_at = refs_at + bitmap_len;
out.resize(owners_at + bitmap_len, 0);
for field in fields {
let (tag, width) = field.tag.to_wire();
out.extend_from_slice(&field.column.to_le_bytes());
out.push(tag);
out.push(width);
}
let mut encoded = Vec::new();
for (i, field) in fields.iter().enumerate() {
let bytes: &[u8] = match &field.cell {
RowCell::Value(bytes) => bytes,
RowCell::Ref { indirection, owner } => {
set_bit(&mut out, refs_at, i);
if *owner {
set_bit(&mut out, owners_at, i);
}
encoded.clear();
indirection.encode_into(&mut encoded)?;
&encoded
}
};
let len = u32::try_from(bytes.len())
.map_err(|_| Error::InvalidHeader("field row: cell exceeds u32"))?;
out.extend_from_slice(&len.to_le_bytes());
out.extend_from_slice(bytes);
}
Ok(out)
}
fn set_bit(out: &mut [u8], bitmap_at: usize, i: usize) {
if let Some(byte) = out.get_mut(bitmap_at + i / 8) {
*byte |= 1 << (i % 8);
}
}
fn bit(bitmap: &[u8], i: usize) -> bool {
bitmap.get(i / 8).is_some_and(|b| b >> (i % 8) & 1 == 1)
}
pub(crate) fn decode_row(row: &[u8]) -> crate::Result<Vec<RowField<'_>>> {
let count = row
.first_chunk::<2>()
.map(|b| usize::from(u16::from_le_bytes(*b)))
.ok_or(CORRUPT)?;
let bitmap_len = count.div_ceil(8);
let refs = row.get(2..2 + bitmap_len).ok_or(CORRUPT)?;
let owners = row.get(2 + bitmap_len..2 + 2 * bitmap_len).ok_or(CORRUPT)?;
let descriptors_at = 2 + 2 * bitmap_len;
let descriptors = row
.get(descriptors_at..descriptors_at + count * DESCRIPTOR_LEN)
.ok_or(CORRUPT)?;
let mut pos = descriptors_at + count * DESCRIPTOR_LEN;
let mut fields = Vec::with_capacity(count);
let mut previous = None;
for (i, descriptor) in descriptors.chunks_exact(DESCRIPTOR_LEN).enumerate() {
let [c0, c1, tag, width] =
<[u8; DESCRIPTOR_LEN]>::try_from(descriptor).map_err(|_| CORRUPT)?;
let column = u16::from_le_bytes([c0, c1]);
if !is_field_column(column) || previous.is_some_and(|previous| column <= previous) {
return Err(CORRUPT);
}
previous = Some(column);
let tag = TypeTag::from_wire(tag, width).map_err(|_| CORRUPT)?;
let len = row
.get(pos..)
.and_then(<[u8]>::first_chunk::<4>)
.map(|b| u32::from_le_bytes(*b) as usize)
.ok_or(CORRUPT)?;
let start = pos + 4;
let end = start.checked_add(len).ok_or(CORRUPT)?;
let bytes = row.get(start..end).ok_or(CORRUPT)?;
let owner = bit(owners, i);
let cell = if bit(refs, i) {
let mut reader = bytes;
let indirection = BlobIndirection::decode_from(&mut reader)?;
if !reader.is_empty() {
return Err(CORRUPT);
}
RowCell::Ref { indirection, owner }
} else if owner {
return Err(CORRUPT);
} else {
RowCell::Value(bytes)
};
if let Some(width) = tag.fixed_width() {
let len = match &cell {
RowCell::Value(bytes) => bytes.len(),
RowCell::Ref { indirection, .. } => indirection.size as usize,
};
if len != usize::from(width) {
return Err(CORRUPT);
}
}
fields.push(RowField { column, tag, cell });
pos = end;
}
if pos != row.len() {
return Err(CORRUPT);
}
Ok(fields)
}
pub(crate) fn row_refs(row: &[u8]) -> crate::Result<Vec<(BlobIndirection, bool)>> {
Ok(decode_row(row)?
.into_iter()
.filter_map(|field| match field.cell {
RowCell::Ref { indirection, owner } => Some((indirection, owner)),
RowCell::Value(_) => None,
})
.collect())
}
fn frame_overhead(tag: TypeTag) -> u32 {
if tag.fixed_width().is_some() { 0 } else { 4 }
}
pub(crate) fn logical_len(row: &[u8]) -> crate::Result<u32> {
let mut len = 0u32;
for field in decode_row(row)? {
let cell_len = match field.cell {
RowCell::Value(bytes) => u32::try_from(bytes.len()).map_err(|_| CORRUPT)?,
RowCell::Ref { indirection, .. } => indirection.size,
};
len = len
.checked_add(frame_overhead(field.tag))
.and_then(|len| len.checked_add(cell_len))
.ok_or(Error::InvalidHeader(
"field row: logical value exceeds u32::MAX bytes",
))?;
}
Ok(len)
}
pub(crate) fn separate_row(
row: &[u8],
threshold: impl Fn(u16) -> u32,
mut write: impl FnMut(&[u8]) -> crate::Result<crate::vlog::ValueHandle>,
) -> crate::Result<Option<Vec<u8>>> {
let mut fields = decode_row(row)?;
let mut separated = false;
for field in &mut fields {
if let RowCell::Value(bytes) = field.cell {
let size = u32::try_from(bytes.len()).map_err(|_| CORRUPT)?;
if size >= threshold(field.column) {
field.cell = RowCell::Ref {
indirection: BlobIndirection {
vhandle: write(bytes)?,
size,
},
owner: true,
};
separated = true;
}
}
}
if separated {
encode_row(&fields).map(Some)
} else {
Ok(None)
}
}
pub(crate) fn resolve_row(
row: &[u8],
mut fetch: impl FnMut(&BlobIndirection) -> crate::Result<crate::Slice>,
) -> crate::Result<Vec<u8>> {
let fields = decode_row(row)?;
let mut out = Vec::new();
for field in fields {
let prefixed = frame_overhead(field.tag) > 0;
match field.cell {
RowCell::Value(bytes) => {
if prefixed {
let len = u32::try_from(bytes.len()).map_err(|_| CORRUPT)?;
out.extend_from_slice(&len.to_le_bytes());
}
out.extend_from_slice(bytes);
}
RowCell::Ref { indirection, .. } => {
let object = fetch(&indirection)?;
if object.len() != indirection.size as usize {
return Err(Error::InvalidHeader(
"field row: referenced object differs from its recorded size",
));
}
if prefixed {
out.extend_from_slice(&indirection.size.to_le_bytes());
}
out.extend_from_slice(&object);
}
}
}
Ok(out)
}
#[cfg(test)]
#[expect(clippy::unwrap_used, clippy::indexing_slicing, reason = "test code")]
mod tests;