use crate::btree::encode_sqlite_varint;
pub enum RecordValue<'a> {
Null,
Int(i64),
Float(f64),
Blob(&'a [u8]),
Text(&'a str),
}
fn serial_type(val: &RecordValue<'_>) -> u64 {
match val {
RecordValue::Null => 0,
RecordValue::Int(v) => smallest_int_serial(*v),
RecordValue::Float(_) => 7,
RecordValue::Blob(b) => (b.len() as u64) * 2 + 12,
RecordValue::Text(s) => (s.len() as u64) * 2 + 13,
}
}
fn smallest_int_serial(v: i64) -> u64 {
if (i8::MIN as i64..=i8::MAX as i64).contains(&v) {
1
} else if (i16::MIN as i64..=i16::MAX as i64).contains(&v) {
2
} else if (-8_388_608..=8_388_607_i64).contains(&v) {
3
} else if (i32::MIN as i64..=i32::MAX as i64).contains(&v) {
4
} else if (-140_737_488_355_328..=140_737_488_355_327_i64).contains(&v) {
5
} else {
6
}
}
fn encode_value_bytes(val: &RecordValue<'_>, buf: &mut Vec<u8>) {
match val {
RecordValue::Null => {}
RecordValue::Int(v) => {
let st = smallest_int_serial(*v);
match st {
1 => buf.push(*v as u8),
2 => buf.extend_from_slice(&(*v as i16).to_be_bytes()),
3 => {
let raw = (*v as i32).to_be_bytes();
buf.push(raw[1]);
buf.push(raw[2]);
buf.push(raw[3]);
}
4 => buf.extend_from_slice(&(*v as i32).to_be_bytes()),
5 => {
let raw = v.to_be_bytes();
buf.extend_from_slice(&raw[2..8]);
}
6 => buf.extend_from_slice(&v.to_be_bytes()),
_ => unreachable!("smallest_int_serial returns 1-6"),
}
}
RecordValue::Float(v) => {
buf.extend_from_slice(&v.to_be_bytes());
}
RecordValue::Blob(b) => {
buf.extend_from_slice(b);
}
RecordValue::Text(s) => {
buf.extend_from_slice(s.as_bytes());
}
}
}
pub fn encode_record(cols: &[RecordValue<'_>]) -> Vec<u8> {
let serial_types: Vec<u64> = cols.iter().map(serial_type).collect();
let type_varints: Vec<Vec<u8>> = serial_types
.iter()
.map(|st| encode_sqlite_varint(*st))
.collect();
let type_varints_total: usize = type_varints.iter().map(|v| v.len()).sum();
let header_len_estimate = 1 + type_varints_total;
let header_len_varint = encode_sqlite_varint(header_len_estimate as u64);
let true_header_len = header_len_varint.len() + type_varints_total;
let true_varint = encode_sqlite_varint(true_header_len as u64);
let final_header_len = if true_varint.len() != header_len_varint.len() {
true_varint.len() + type_varints_total
} else {
true_header_len
};
let final_varint = encode_sqlite_varint(final_header_len as u64);
let mut data_buf = Vec::new();
for col in cols {
encode_value_bytes(col, &mut data_buf);
}
let mut out = Vec::with_capacity(final_varint.len() + type_varints_total + data_buf.len());
out.extend_from_slice(&final_varint);
for tv in &type_varints {
out.extend_from_slice(tv);
}
out.extend_from_slice(&data_buf);
out
}