use crate::bin_table::Value;
use crate::header::TableColumnFormat;
pub(crate) fn encode(value: &Value, format: TableColumnFormat, out: &mut Vec<u8>) {
let start = out.len();
let width = format.bytes_len();
let value = &coerce(value, format);
match value {
Value::Null => {}
Value::String(text) => out.extend_from_slice(text.as_bytes()),
Value::StringArray(values) => {
for text in values {
out.extend_from_slice(text.as_bytes());
}
}
Value::Boolean(values) => {
out.extend(values.iter().map(|value| if *value { b'T' } else { b'F' }));
}
Value::U8(values) => out.extend_from_slice(values),
Value::Bit { bytes, .. } => out.extend_from_slice(bytes),
Value::I8(values) => out.extend(values.iter().map(|value| *value as u8)),
Value::U16(values) => extend(out, values, u16::to_be_bytes),
Value::I16(values) => extend(out, values, i16::to_be_bytes),
Value::U32(values) => extend(out, values, u32::to_be_bytes),
Value::I32(values) => extend(out, values, i32::to_be_bytes),
Value::I64(values) => extend(out, values, i64::to_be_bytes),
Value::U64(values) => extend(out, values, u64::to_be_bytes),
Value::F32(values) => extend(out, values, f32::to_be_bytes),
Value::F64(values) => extend(out, values, f64::to_be_bytes),
Value::C32(values) => {
for (real, imaginary) in values {
out.extend_from_slice(&real.to_be_bytes());
out.extend_from_slice(&imaginary.to_be_bytes());
}
}
Value::M64(values) => {
for (real, imaginary) in values {
out.extend_from_slice(&real.to_be_bytes());
out.extend_from_slice(&imaginary.to_be_bytes());
}
}
}
out.truncate(start + width);
let padding = match format {
TableColumnFormat::String(_) | TableColumnFormat::StringArray(..) => b' ',
_ => 0,
};
out.resize(start + width, padding);
}
fn coerce(value: &Value, format: TableColumnFormat) -> Value {
let Some(integers) = as_integers(value) else {
return match (value, format) {
(value, TableColumnFormat::F32(_)) => match as_floats(value) {
Some(floats) => Value::F32(floats.into_iter().map(|v| v as f32).collect()),
None => value.clone(),
},
(value, TableColumnFormat::F64(_)) => match as_floats(value) {
Some(floats) => Value::F64(floats),
None => value.clone(),
},
(value, _) => value.clone(),
};
};
match format {
TableColumnFormat::U8(_) => Value::U8(integers.iter().map(|v| *v as u8).collect()),
TableColumnFormat::I8(_) => Value::I8(integers.iter().map(|v| *v as i8).collect()),
TableColumnFormat::U16(_) => Value::U16(integers.iter().map(|v| *v as u16).collect()),
TableColumnFormat::I16(_) => Value::I16(integers.iter().map(|v| *v as i16).collect()),
TableColumnFormat::U32(_) => Value::U32(integers.iter().map(|v| *v as u32).collect()),
TableColumnFormat::I32(_) => Value::I32(integers.iter().map(|v| *v as i32).collect()),
TableColumnFormat::I64(_) => Value::I64(integers.iter().map(|v| *v as i64).collect()),
TableColumnFormat::F32(_) => Value::F32(integers.iter().map(|v| *v as f32).collect()),
TableColumnFormat::F64(_) => Value::F64(integers.iter().map(|v| *v as f64).collect()),
_ => value.clone(),
}
}
fn as_integers(value: &Value) -> Option<Vec<i128>> {
Some(match value {
Value::U8(values) => values.iter().map(|v| *v as i128).collect(),
Value::I8(values) => values.iter().map(|v| *v as i128).collect(),
Value::U16(values) => values.iter().map(|v| *v as i128).collect(),
Value::I16(values) => values.iter().map(|v| *v as i128).collect(),
Value::U32(values) => values.iter().map(|v| *v as i128).collect(),
Value::I32(values) => values.iter().map(|v| *v as i128).collect(),
Value::I64(values) => values.iter().map(|v| *v as i128).collect(),
Value::U64(values) => values.iter().map(|v| *v as i128).collect(),
_ => return None,
})
}
fn as_floats(value: &Value) -> Option<Vec<f64>> {
match value {
Value::F32(values) => Some(values.iter().map(|v| *v as f64).collect()),
Value::F64(values) => Some(values.clone()),
other => as_integers(other).map(|values| values.into_iter().map(|v| v as f64).collect()),
}
}
fn extend<T: Copy, const N: usize>(
out: &mut Vec<u8>,
values: &[T],
to_be_bytes: impl Fn(T) -> [u8; N],
) {
for value in values {
out.extend_from_slice(&to_be_bytes(*value));
}
}