use super::NativeError;
use super::lowcard::LowCard;
use crate::schema::typeparse::ChType;
use bytes::{BufMut, BytesMut};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ScalarKind {
Bool,
I8,
I16,
I32,
I64,
I128,
U8,
U16,
U32,
U64,
U128,
F32,
F64,
}
impl ScalarKind {
pub(crate) fn width(self) -> usize {
match self {
ScalarKind::Bool | ScalarKind::I8 | ScalarKind::U8 => 1,
ScalarKind::I16 | ScalarKind::U16 => 2,
ScalarKind::I32 | ScalarKind::U32 | ScalarKind::F32 => 4,
ScalarKind::I64 | ScalarKind::U64 | ScalarKind::F64 => 8,
ScalarKind::I128 | ScalarKind::U128 => 16,
}
}
pub(crate) fn name(self) -> &'static str {
match self {
ScalarKind::Bool => "Bool",
ScalarKind::I8 => "Int8",
ScalarKind::I16 => "Int16",
ScalarKind::I32 => "Int32-width",
ScalarKind::I64 => "Int64-width",
ScalarKind::I128 => "Int128-width",
ScalarKind::U8 => "UInt8",
ScalarKind::U16 => "UInt16-width",
ScalarKind::U32 => "UInt32-width",
ScalarKind::U64 => "UInt64",
ScalarKind::U128 => "UInt128",
ScalarKind::F32 => "Float32",
ScalarKind::F64 => "Float64",
}
}
}
pub(crate) enum ColumnWriter {
Scalar { kind: ScalarKind, buf: BytesMut },
Str(BytesMut),
Fixed { width: usize, buf: BytesMut },
RawExact { width: usize, buf: BytesMut },
Nullable {
nullmap: BytesMut,
inner: Box<ColumnWriter>,
},
Array {
offsets: BytesMut,
cum: u64,
inner: Box<ColumnWriter>,
},
Map {
offsets: BytesMut,
cum: u64,
key: Box<ColumnWriter>,
val: Box<ColumnWriter>,
},
Tuple(Vec<ColumnWriter>),
LowCard(LowCard),
}
impl ColumnWriter {
pub(crate) fn build(ty: &ChType) -> Result<ColumnWriter, String> {
Ok(match ty {
ChType::Named(name) => Self::build_named(name)?,
ChType::Nullable(inner) => ColumnWriter::Nullable {
nullmap: BytesMut::new(),
inner: Box::new(Self::build(inner)?),
},
ChType::LowCardinality(inner) => ColumnWriter::LowCard(LowCard::build(inner)?),
ChType::Array(inner) => ColumnWriter::Array {
offsets: BytesMut::new(),
cum: 0,
inner: Box::new(Self::build(inner)?),
},
ChType::Map(k, v) => ColumnWriter::Map {
offsets: BytesMut::new(),
cum: 0,
key: Box::new(Self::build(k)?),
val: Box::new(Self::build(v)?),
},
ChType::Tuple(elems) => ColumnWriter::Tuple(
elems
.iter()
.map(Self::build)
.collect::<Result<Vec<_>, _>>()?,
),
ChType::FixedString(n) => ColumnWriter::Fixed {
width: *n as usize,
buf: BytesMut::new(),
},
ChType::DateTime { .. } => Self::scalar(ScalarKind::U32),
ChType::DateTime64 { .. } => Self::scalar(ScalarKind::I64),
ChType::Decimal { precision, .. } => match precision {
0..=9 => Self::scalar(ScalarKind::I32),
10..=18 => Self::scalar(ScalarKind::I64),
19..=38 => Self::scalar(ScalarKind::I128),
_ => return Err(Self::unsupported(ty)),
},
ChType::Enum8 => Self::scalar(ScalarKind::I8),
ChType::Enum16 => Self::scalar(ScalarKind::I16),
ChType::Time64 { .. } | ChType::Other(_) => return Err(Self::unsupported(ty)),
})
}
fn build_named(name: &str) -> Result<ColumnWriter, String> {
Ok(match name {
"Bool" => Self::scalar(ScalarKind::Bool),
"Int8" => Self::scalar(ScalarKind::I8),
"Int16" => Self::scalar(ScalarKind::I16),
"Int32" => Self::scalar(ScalarKind::I32),
"Int64" => Self::scalar(ScalarKind::I64),
"Int128" => Self::scalar(ScalarKind::I128),
"UInt8" => Self::scalar(ScalarKind::U8),
"UInt16" => Self::scalar(ScalarKind::U16),
"UInt32" => Self::scalar(ScalarKind::U32),
"UInt64" => Self::scalar(ScalarKind::U64),
"UInt128" => Self::scalar(ScalarKind::U128),
"Float32" => Self::scalar(ScalarKind::F32),
"Float64" => Self::scalar(ScalarKind::F64),
"String" => ColumnWriter::Str(BytesMut::new()),
"IPv4" => Self::scalar(ScalarKind::U32),
"Date" => Self::scalar(ScalarKind::U16),
"Date32" => Self::scalar(ScalarKind::I32),
"UUID" | "IPv6" => ColumnWriter::RawExact {
width: 16,
buf: BytesMut::new(),
},
"Int256" | "UInt256" => ColumnWriter::RawExact {
width: 32,
buf: BytesMut::new(),
},
"Point" => Self::point(),
"Ring" | "LineString" => Self::array(Self::point()),
"Polygon" | "MultiLineString" => Self::array(Self::array(Self::point())),
"MultiPolygon" => Self::array(Self::array(Self::array(Self::point()))),
other => return Err(other.to_string()),
})
}
fn array(inner: ColumnWriter) -> ColumnWriter {
ColumnWriter::Array {
offsets: BytesMut::new(),
cum: 0,
inner: Box::new(inner),
}
}
fn scalar(kind: ScalarKind) -> ColumnWriter {
ColumnWriter::Scalar {
kind,
buf: BytesMut::new(),
}
}
fn point() -> ColumnWriter {
ColumnWriter::Tuple(vec![
Self::scalar(ScalarKind::F64),
Self::scalar(ScalarKind::F64),
])
}
fn unsupported(ty: &ChType) -> String {
format!("{ty:?}")
}
#[inline]
pub(crate) fn append<V: serde::Serialize + ?Sized>(
&mut self,
value: &V,
) -> Result<(), NativeError> {
match self {
ColumnWriter::RawExact { width, buf } => {
let before = buf.len();
value.serialize(super::dispatch::ByteSink { buf })?;
let got = buf.len() - before;
if got != *width {
buf.truncate(before);
return Err(NativeError::RawWidth {
expected: *width,
got,
});
}
Ok(())
}
ColumnWriter::Fixed { width, buf } => {
let before = buf.len();
value.serialize(super::dispatch::ByteSink { buf })?;
let got = buf.len() - before;
if got > *width {
buf.truncate(before);
return Err(NativeError::FixedTooLong { width: *width, got });
}
buf.put_bytes(0, *width - got);
Ok(())
}
_ => value.serialize(super::dispatch::ColumnSerializer { writer: self }),
}
}
pub(crate) fn push_default(&mut self) {
match self {
ColumnWriter::Scalar { kind, buf } => buf.put_bytes(0, kind.width()),
ColumnWriter::Str(buf) => super::leaf::put_leb128(buf, 0),
ColumnWriter::Fixed { width, buf } | ColumnWriter::RawExact { width, buf } => {
buf.put_bytes(0, *width);
}
ColumnWriter::Nullable { nullmap, inner } => {
nullmap.put_u8(1);
inner.push_default();
}
ColumnWriter::Array { offsets, cum, .. } => offsets.put_u64_le(*cum),
ColumnWriter::Map { offsets, cum, .. } => offsets.put_u64_le(*cum),
ColumnWriter::Tuple(elems) => elems.iter_mut().for_each(ColumnWriter::push_default),
ColumnWriter::LowCard(lc) => lc.push_default(),
}
}
pub(crate) fn write_prefix(&self, out: &mut BytesMut) {
match self {
ColumnWriter::Scalar { .. }
| ColumnWriter::Str(_)
| ColumnWriter::Fixed { .. }
| ColumnWriter::RawExact { .. } => {}
ColumnWriter::Nullable { inner, .. } => inner.write_prefix(out),
ColumnWriter::Array { inner, .. } => inner.write_prefix(out),
ColumnWriter::Map { key, val, .. } => {
key.write_prefix(out);
val.write_prefix(out);
}
ColumnWriter::Tuple(elems) => elems.iter().for_each(|e| e.write_prefix(out)),
ColumnWriter::LowCard(lc) => lc.write_prefix(out),
}
}
pub(crate) fn write_data(&self, out: &mut BytesMut) {
match self {
ColumnWriter::Scalar { buf, .. }
| ColumnWriter::Str(buf)
| ColumnWriter::Fixed { buf, .. }
| ColumnWriter::RawExact { buf, .. } => out.put_slice(buf),
ColumnWriter::Nullable { nullmap, inner } => {
out.put_slice(nullmap);
inner.write_data(out);
}
ColumnWriter::Array { offsets, inner, .. } => {
out.put_slice(offsets);
inner.write_data(out);
}
ColumnWriter::Map {
offsets, key, val, ..
} => {
out.put_slice(offsets);
key.write_data(out);
val.write_data(out);
}
ColumnWriter::Tuple(elems) => elems.iter().for_each(|e| e.write_data(out)),
ColumnWriter::LowCard(lc) => lc.write_data(out),
}
}
pub(crate) fn byte_len(&self) -> usize {
match self {
ColumnWriter::Scalar { buf, .. }
| ColumnWriter::Str(buf)
| ColumnWriter::Fixed { buf, .. }
| ColumnWriter::RawExact { buf, .. } => buf.len(),
ColumnWriter::Nullable { nullmap, inner } => nullmap.len() + inner.byte_len(),
ColumnWriter::Array { offsets, inner, .. } => offsets.len() + inner.byte_len(),
ColumnWriter::Map {
offsets, key, val, ..
} => offsets.len() + key.byte_len() + val.byte_len(),
ColumnWriter::Tuple(elems) => elems.iter().map(ColumnWriter::byte_len).sum(),
ColumnWriter::LowCard(lc) => lc.byte_len(),
}
}
pub(crate) fn reset(&mut self) {
match self {
ColumnWriter::Scalar { buf, .. }
| ColumnWriter::Str(buf)
| ColumnWriter::Fixed { buf, .. }
| ColumnWriter::RawExact { buf, .. } => buf.clear(),
ColumnWriter::Nullable { nullmap, inner } => {
nullmap.clear();
inner.reset();
}
ColumnWriter::Array {
offsets,
cum,
inner,
} => {
offsets.clear();
*cum = 0;
inner.reset();
}
ColumnWriter::Map {
offsets,
cum,
key,
val,
} => {
offsets.clear();
*cum = 0;
key.reset();
val.reset();
}
ColumnWriter::Tuple(elems) => elems.iter_mut().for_each(ColumnWriter::reset),
ColumnWriter::LowCard(lc) => lc.reset(),
}
}
}