use std::fmt::{Display, Formatter};
use std::io::{Read, Write};
use std::io;
use crate::error::DecodeError;
use crate::ll::bitops::{combine, get_tiny_size, high_nibble_equals};
use crate::ll::bounds::is_in_plus_tiny_int_bound;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Marker {
TinyString(usize),
TinyList(usize),
TinyDictionary(usize),
Structure(usize, u8),
PlusTinyInt(u8),
MinusTinyInt(u8),
Float64,
Int8,
Int16,
Int32,
Int64,
Null,
True,
False,
String8,
String16,
String32,
Bytes8,
Bytes16,
Bytes32,
List8,
List16,
List32,
Dictionary8,
Dictionary16,
Dictionary32,
}
impl Marker {
pub fn high_nibble(&self) -> MarkerHighNibble {
match self {
Marker::Null => MarkerHighNibble::Null,
Marker::True => MarkerHighNibble::True,
Marker::False => MarkerHighNibble::False,
Marker::Float64 => MarkerHighNibble::Float64,
Marker::PlusTinyInt(_) => MarkerHighNibble::PlusTinyInt,
Marker::MinusTinyInt(_) => MarkerHighNibble::MinusTinyInt,
Marker::Int8 => MarkerHighNibble::Int8,
Marker::Int16 => MarkerHighNibble::Int16,
Marker::Int32 => MarkerHighNibble::Int32,
Marker::Int64 => MarkerHighNibble::Int64,
Marker::TinyString(_) => MarkerHighNibble::TinyString,
Marker::String8 => MarkerHighNibble::String8,
Marker::String16 => MarkerHighNibble::String16,
Marker::String32 => MarkerHighNibble::String32,
Marker::TinyList(_) => MarkerHighNibble::TinyList,
Marker::List8 => MarkerHighNibble::List8,
Marker::List16 => MarkerHighNibble::List16,
Marker::List32 => MarkerHighNibble::List32,
Marker::TinyDictionary(_) => MarkerHighNibble::TinyDictionary,
Marker::Dictionary8 => MarkerHighNibble::Dictionary8,
Marker::Dictionary16 => MarkerHighNibble::Dictionary16,
Marker::Dictionary32 => MarkerHighNibble::Dictionary32,
Marker::Bytes8 => MarkerHighNibble::Bytes8,
Marker::Bytes16 => MarkerHighNibble::Bytes16,
Marker::Bytes32 => MarkerHighNibble::Bytes32,
Marker::Structure(_, _) => MarkerHighNibble::Structure,
}
}
pub fn encode<T: Write>(self, into: &mut T) -> io::Result<usize> {
use Marker::*;
match self {
TinyString(size) =>
into.write(&[combine(MarkerHighNibble::TinyString as u8, size as u8)]),
TinyList(size) =>
into.write(&[combine(MarkerHighNibble::TinyList as u8, size as u8)]),
TinyDictionary(size) =>
into.write(&[combine(MarkerHighNibble::TinyDictionary as u8, size as u8)]),
Structure(size, tag) =>
into.write(&[combine(MarkerHighNibble::Structure as u8, size as u8), tag]),
PlusTinyInt(value) => into.write(&[value]),
MinusTinyInt(value) => into.write(&[value]),
p => into.write(&[p.high_nibble() as u8]),
}
}
pub fn decode<T: Read>(reader: &mut T) -> Result<Marker, DecodeError> {
let mut buf = [0; 1];
reader.read_exact(&mut buf)?;
let from = buf[0];
if is_in_plus_tiny_int_bound(from as i64) {
Ok(Marker::PlusTinyInt(from))
} else if MarkerHighNibble::MinusTinyInt.is_contained_in(from) {
Ok(Marker::MinusTinyInt(from))
} else if MarkerHighNibble::TinyString.is_contained_in(from) {
Ok(Marker::TinyString(get_tiny_size(from)))
} else if MarkerHighNibble::TinyList.is_contained_in(from) {
Ok(Marker::TinyList(get_tiny_size(from)))
} else if MarkerHighNibble::TinyDictionary.is_contained_in(from) {
Ok(Marker::TinyDictionary(get_tiny_size(from)))
} else if MarkerHighNibble::Structure.is_contained_in(from) {
let mut buf = [0; 1];
reader.read_exact(&mut buf)?;
Ok(Marker::Structure(get_tiny_size(from), buf[0]))
} else {
match from {
0xC0 => Ok(Marker::Null),
0xC1 => Ok(Marker::Float64),
0xC2 => Ok(Marker::True),
0xC3 => Ok(Marker::False),
0xC8 => Ok(Marker::Int8),
0xC9 => Ok(Marker::Int16),
0xCA => Ok(Marker::Int32),
0xCB => Ok(Marker::Int64),
0xCC => Ok(Marker::Bytes8),
0xCD => Ok(Marker::Bytes16),
0xCE => Ok(Marker::Bytes32),
0xD0 => Ok(Marker::String8),
0xD1 => Ok(Marker::String16),
0xD2 => Ok(Marker::String32),
0xD4 => Ok(Marker::List8),
0xD5 => Ok(Marker::List16),
0xD6 => Ok(Marker::List32),
0xD8 => Ok(Marker::Dictionary8),
0xD9 => Ok(Marker::Dictionary16),
0xDA => Ok(Marker::Dictionary32),
_ => Err(DecodeError::UnknownMarkerByte(from))
}
}
}
}
impl Display for Marker {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MarkerHighNibble {
TinyString = 0x80,
TinyList = 0x90,
TinyDictionary = 0xA0,
Structure = 0xB0,
PlusTinyInt = 0x00,
MinusTinyInt = 0xF0,
Float64 = 0xC1,
Int8 = 0xC8,
Int16 = 0xC9,
Int32 = 0xCA,
Int64 = 0xCB,
Null = 0xC0,
True = 0xC2,
False = 0xC3,
String8 = 0xD0,
String16 = 0xD1,
String32 = 0xD2,
Bytes8 = 0xCC,
Bytes16 = 0xCD,
Bytes32 = 0xCE,
List8 = 0xD4,
List16 = 0xD5,
List32 = 0xD6,
Dictionary8 = 0xD8,
Dictionary16 = 0xD9,
Dictionary32 = 0xDA,
}
impl MarkerHighNibble {
pub fn is_contained_in(self, marker_byte: u8) -> bool {
high_nibble_equals(self as u8, marker_byte & 0xF0)
}
}
#[cfg(test)]
pub mod test {
use crate::ll::marker::Marker;
pub fn marker_from_bytes_test(marker: Marker, mut bytes: &[u8]) {
let m =
Marker::decode(&mut bytes)
.expect(&format!("Decoding error on bytes {:X?} trying to read out marker {:?}", bytes, marker));
assert_eq!(marker, m);
}
#[test]
fn from_u8_plus_tiny_int_marker() {
marker_from_bytes_test(Marker::PlusTinyInt(0x0F), &[0x0F]);
}
#[test]
fn from_u8_minus_tiny_int_marker() {
for i in 0u8..0x10 {
marker_from_bytes_test(Marker::MinusTinyInt(0xF0 | i), &[(0xF0 | i)]);
}
}
#[test]
fn from_u8_tiny_sized_marker() {
let r: Vec<fn(usize) -> Marker> = vec! {
Marker::TinyString,
Marker::TinyList,
Marker::TinyDictionary,
};
for m in r {
for i in 0u8..0x10 {
marker_from_bytes_test(m(i as usize), &[(m(0).high_nibble() as u8 | i)]);
}
}
}
#[test]
fn from_high_nibble() {
let r = vec! {
Marker::Null,
Marker::Float64,
Marker::False,
Marker::True,
Marker::MinusTinyInt(0xF0),
Marker::PlusTinyInt(0x00),
Marker::Int8,
Marker::Int16,
Marker::Int32,
Marker::Int64,
Marker::TinyString(0),
Marker::String8,
Marker::String16,
Marker::String32,
Marker::TinyList(0),
Marker::List8,
Marker::List16,
Marker::List32,
Marker::TinyDictionary(0),
Marker::Dictionary8,
Marker::Dictionary16,
Marker::Dictionary32,
Marker::Bytes8,
Marker::Bytes16,
Marker::Bytes32,
};
for m in r {
marker_from_bytes_test(m, &[m.high_nibble() as u8]);
}
}
}