use crate::{
ast::{DataType, Value},
catalog::objects::ColumnEntry,
common::interner::Interner,
storage::error::StorageError,
};
pub fn serialize_tuple(
schema: &[ColumnEntry],
values: &[Value],
interner: &Interner,
) -> Result<Vec<u8>, StorageError> {
if values.len() != schema.len() {
return Err(StorageError::TupleError(format!(
"expected {} values for {} columns",
schema.len(),
values.len(),
)));
}
let col_count = schema.len();
let bitmap_bytes = bitmap_size(col_count);
let mut buf: Vec<u8> = vec![0u8; bitmap_bytes];
for (i, (col, val)) in schema.iter().zip(values.iter()).enumerate() {
match val {
Value::Null => {
if !col.nullable {
return Err(StorageError::TupleError(format!(
"column {:?} is NOT NULL but received NULL",
i
)));
}
set_null_bit(&mut buf, i);
}
_ => {
encode_value(&col.data_type, val, interner, &mut buf)?;
}
}
}
Ok(buf)
}
pub fn deserialize_tuple(
schema: &[ColumnEntry],
data: &[u8],
interner: &Interner,
) -> Result<Vec<Value>, StorageError> {
let col_count = schema.len();
let bitmap_bytes = bitmap_size(col_count);
if data.len() < bitmap_bytes {
return Err(StorageError::TupleError(
"tuple data too short to contain NULL bitmap".into(),
));
}
let mut cursor = bitmap_bytes; let mut values = Vec::with_capacity(col_count);
for (i, col) in schema.iter().enumerate() {
if is_null(data, i) {
values.push(Value::Null);
} else {
let (val, bytes_read) = decode_value(&col.data_type, &data[cursor..], interner)?;
cursor += bytes_read;
values.push(val);
}
}
Ok(values)
}
fn bitmap_size(col_count: usize) -> usize {
(col_count + 7) / 8
}
fn set_null_bit(buf: &mut [u8], col_idx: usize) {
let byte = col_idx / 8;
let bit = col_idx % 8;
buf[byte] |= 1 << bit;
}
fn is_null(data: &[u8], col_idx: usize) -> bool {
let byte = col_idx / 8;
let bit = col_idx % 8;
(data[byte] >> bit) & 1 == 1
}
fn encode_value(
data_type: &DataType,
val: &Value,
interner: &Interner,
buf: &mut Vec<u8>,
) -> Result<(), StorageError> {
match (data_type, val) {
(DataType::SmallInt, Value::Int(n)) => {
let n16 = i16::try_from(*n).map_err(|_| {
StorageError::TupleError(format!("value {} out of range for SMALLINT", n))
})?;
buf.extend_from_slice(&n16.to_le_bytes());
}
(DataType::Int, Value::Int(n)) => {
buf.extend_from_slice(&n.to_le_bytes());
}
(DataType::BigInt, Value::Int(n)) => {
buf.extend_from_slice(&n.to_le_bytes());
}
(DataType::Boolean, Value::Boolean(b)) => {
buf.push(if *b { 1 } else { 0 });
}
(DataType::Float, Value::Float(f)) => {
buf.extend_from_slice(&(*f as f32).to_bits().to_le_bytes());
}
(DataType::Double, Value::Float(f)) => {
buf.extend_from_slice(&f.to_bits().to_le_bytes());
}
(DataType::Float, Value::Int(n)) => {
buf.extend_from_slice(&(*n as f32).to_bits().to_le_bytes());
}
(DataType::Double, Value::Int(n)) => {
buf.extend_from_slice(&(*n as f64).to_bits().to_le_bytes());
}
(DataType::VarChar(_) | DataType::Char(_) | DataType::Text, Value::String(sym)) => {
let s = interner.resolve(*sym);
let bytes = s.as_bytes();
let len = bytes.len() as u32;
buf.extend_from_slice(&len.to_le_bytes()); buf.extend_from_slice(bytes); }
(expected, actual) => {
return Err(StorageError::TupleError(format!(
"type mismatch: column declared as {:?} but got {:?}",
expected, actual
)));
}
}
Ok(())
}
fn decode_value(
data_type: &DataType,
data: &[u8],
interner: &Interner,
) -> Result<(Value, usize), StorageError> {
match data_type {
DataType::SmallInt => {
need(data, 2, "SMALLINT")?;
let n = i16::from_le_bytes(data[0..2].try_into().unwrap());
Ok((Value::Int(n as i64), 2))
}
DataType::Int | DataType::BigInt => {
need(data, 8, "INT/BIGINT")?;
let n = i64::from_le_bytes(data[0..8].try_into().unwrap());
Ok((Value::Int(n), 8))
}
DataType::Boolean => {
need(data, 1, "BOOLEAN")?;
Ok((Value::Boolean(data[0] != 0), 1))
}
DataType::Float => {
need(data, 4, "FLOAT")?;
let bits = u32::from_le_bytes(data[0..4].try_into().unwrap());
Ok((Value::Float(f32::from_bits(bits) as f64), 4))
}
DataType::Double => {
need(data, 8, "DOUBLE")?;
let bits = u64::from_le_bytes(data[0..8].try_into().unwrap());
Ok((Value::Float(f64::from_bits(bits)), 8))
}
DataType::VarChar(_) | DataType::Char(_) | DataType::Text => {
need(data, 4, "string length prefix")?;
let len = u32::from_le_bytes(data[0..4].try_into().unwrap()) as usize;
need(&data[4..], len, "string payload")?;
let s = str::from_utf8(&data[4..4 + len])
.map_err(|_| StorageError::TupleError("invalid UTF-8 in stored string".into()))?;
let sym = interner.intern(s);
Ok((Value::String(sym), 4 + len))
}
other => Err(StorageError::TupleError(format!(
"unsupported data type for deserialization: {:?}",
other
))),
}
}
fn need(data: &[u8], n: usize, ctx: &str) -> Result<(), StorageError> {
if data.len() < n {
Err(StorageError::TupleError(format!(
"unexpected end of tuple data reading {}: need {} bytes, have {}",
ctx,
n,
data.len()
)))
} else {
Ok(())
}
}