use std::borrow::Cow;
use anyhow::{Result, bail, ensure};
#[derive(Debug)]
pub struct VariantMetadata<'a> {
pub(crate) header: VariantHeader,
pub(crate) offsets: Vec<u32>,
pub(crate) strings: Vec<&'a str>,
}
impl<'a> VariantMetadata<'a> {
pub fn find_by_field_id(&self, field_id: u32) -> Option<&'a str> {
match self.header.layout {
DictionaryLayout::SortedUnique => todo!(),
DictionaryLayout::Naive => {
if field_id >= self.dictionary_len() as u32 {
return None;
}
let field_id = field_id as usize;
self.strings.get(field_id).map(|v| &**v)
}
}
}
pub fn find_by_field_name(&self, field_name: &str) -> Option<&'a str> {
match self.header.layout {
DictionaryLayout::SortedUnique => self.search_sorted_unique(field_name),
DictionaryLayout::Naive => self.search_naive(field_name),
}
}
pub const fn dictionary_len(&self) -> usize {
self.offsets.len() - 1
}
pub const fn is_empty(&self) -> bool {
self.dictionary_len() == 0
}
pub fn dictionary_strings(&self) -> &[&str] {
&self.strings
}
fn search_sorted_unique(&self, field_name: &str) -> Option<&'a str> {
if self.dictionary_len() < 6 {
return self.search_naive(field_name);
}
self.strings
.binary_search(&field_name)
.map(|i| self.strings[i])
.ok()
}
fn search_naive(&self, field_name: &str) -> Option<&'a str> {
self.strings
.iter()
.find(|&&n| n == field_name)
.map(|v| &**v)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DictionaryLayout {
SortedUnique,
Naive,
}
#[derive(Debug, Clone, Copy)]
pub struct VariantHeader {
pub layout: DictionaryLayout,
pub offset_width: OffsetWidth,
}
impl VariantHeader {
pub const VERSION: u8 = 1;
}
impl TryFrom<u8> for VariantHeader {
type Error = anyhow::Error;
fn try_from(b: u8) -> Result<Self, Self::Error> {
ensure!(b & 0b1111 == 1, "version must be 1");
Ok(Self {
layout: if ((b >> 4) & 0b1) == 1 {
DictionaryLayout::SortedUnique
} else {
DictionaryLayout::Naive
},
offset_width: OffsetWidth::try_from(b >> 6)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OffsetWidth {
U8 = 0,
U16 = 1,
U24 = 2,
U32 = 3,
}
impl OffsetWidth {
pub const fn find_offset(n: u32) -> Self {
if n <= u8::MAX as u32 {
return Self::U8;
}
if n <= u16::MAX as u32 {
return Self::U16;
}
if n <= 0xFFFFFF_u32 {
return Self::U24;
}
Self::U32
}
}
impl TryFrom<u8> for OffsetWidth {
type Error = anyhow::Error;
fn try_from(b: u8) -> Result<Self> {
let w = match b {
0 => Self::U8,
1 => Self::U16,
2 => Self::U24,
3 => Self::U32,
_ => bail!("Offset width can not be larger than 3"),
};
Ok(w)
}
}
#[derive(Debug, PartialEq)]
pub enum Variant<'a> {
Null, BooleanTrue, BooleanFalse, Int8(i8), Int16(i16), Int32(i32), Int64(i64), Double(f64), Decimal4(u8, i32), Decimal8(u8, i64), Decimal16(u8, i128), Date(u32), TimestampMicros(u64), TimestampNTZMicros(u64), Float(f32), Binary(Cow<'a, [u8]>), String(Cow<'a, str>), TimeNTZMicros(u64), TimestampNanos(u64), TimestampNTZNanos(u64), UUID(u128),
Int16Leb128(i16), Int32Leb128(i32), Int64Leb128(i64), Int128Leb128(i128),
ShortString(ShortString<'a>),
Object(VariantObject<'a>),
Array(VariantArray<'a>),
}
impl From<()> for Variant<'_> {
fn from(_: ()) -> Self {
Self::Null
}
}
impl From<bool> for Variant<'_> {
fn from(value: bool) -> Self {
if value {
Self::BooleanTrue
} else {
Self::BooleanFalse
}
}
}
impl From<String> for Variant<'_> {
fn from(value: String) -> Self {
if value.len() < 64 {
Self::ShortString(ShortString(Cow::Owned(value)))
} else {
Self::String(Cow::Owned(value))
}
}
}
impl<'a> From<&'a str> for Variant<'a> {
fn from(value: &'a str) -> Self {
if value.len() < 64 {
Self::ShortString(ShortString(Cow::Borrowed(value)))
} else {
Self::String(Cow::Borrowed(value))
}
}
}
impl From<Vec<u8>> for Variant<'_> {
fn from(value: Vec<u8>) -> Self {
Self::Binary(Cow::Owned(value))
}
}
impl<'a> From<&'a [u8]> for Variant<'a> {
fn from(value: &'a [u8]) -> Self {
Self::Binary(Cow::Borrowed(value))
}
}
impl From<i8> for Variant<'_> {
fn from(value: i8) -> Self {
Self::Int8(value)
}
}
impl From<i16> for Variant<'_> {
fn from(value: i16) -> Self {
Self::Int16(value)
}
}
impl From<i32> for Variant<'_> {
fn from(value: i32) -> Self {
Self::Int32(value)
}
}
impl From<i64> for Variant<'_> {
fn from(value: i64) -> Self {
Self::Int64(value)
}
}
impl From<f64> for Variant<'_> {
fn from(value: f64) -> Self {
Self::Double(value)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ShortString<'a>(Cow<'a, str>);
impl<'a> ShortString<'a> {
pub const fn new(str: &'a str) -> Self {
Self(Cow::Borrowed(str))
}
pub fn inner(&self) -> &str {
&self.0
}
pub fn len(&self) -> u8 {
self.0.len() as u8
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a> TryFrom<&'a str> for ShortString<'a> {
type Error = anyhow::Error;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
ensure!(s.len() < 64, "short string must be less than 64 bytes");
Ok(Self(Cow::Borrowed(s)))
}
}
impl<'a> TryFrom<String> for ShortString<'a> {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
ensure!(s.len() < 64, "short string must be less than 64 bytes");
Ok(Self(Cow::Owned(s)))
}
}
#[derive(Debug, PartialEq)]
pub struct VariantArray<'a> {
pub(crate) field_offsets: Vec<u32>,
pub(crate) values: Vec<Variant<'a>>,
}
impl<'a> VariantArray<'a> {
pub const fn len(&self) -> u32 {
self.values.len() as u32
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, PartialEq)]
pub struct VariantObject<'a> {
pub(crate) field_ids: Vec<u32>,
pub(crate) field_offsets: Vec<u32>,
pub(crate) values: Vec<Variant<'a>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offset_width() {
assert_eq!(OffsetWidth::try_from(0).unwrap(), OffsetWidth::U8);
assert_eq!(OffsetWidth::try_from(1).unwrap(), OffsetWidth::U16);
assert_eq!(OffsetWidth::try_from(2).unwrap(), OffsetWidth::U24);
assert_eq!(OffsetWidth::try_from(3).unwrap(), OffsetWidth::U32);
assert!(OffsetWidth::try_from(4).is_err());
assert!(OffsetWidth::try_from(std::u8::MAX).is_err());
}
#[test]
fn test_sorted_header() {
let b = 0b1101_0001; let header = VariantHeader::try_from(b).unwrap();
assert_eq!(header.layout, DictionaryLayout::SortedUnique);
assert_eq!(header.offset_width, OffsetWidth::U32);
}
}