use std::{
cmp::Ordering,
io::{Read, Write},
};
use crate::{
errors::{ParquetError, Result},
write_thrift_field,
};
use std::io::Error;
use std::str::Utf8Error;
#[derive(Debug)]
pub(crate) enum ThriftProtocolError {
Eof,
IO(Error),
InvalidFieldType(u8),
InvalidElementType(u8),
FieldDeltaOverflow { field_delta: u8, last_field_id: i16 },
InvalidBoolean(u8),
Utf8Error,
SkipDepth(FieldType),
SkipUnsupportedType(FieldType),
}
impl From<ThriftProtocolError> for ParquetError {
#[inline(never)]
fn from(e: ThriftProtocolError) -> Self {
match e {
ThriftProtocolError::Eof => eof_err!("Unexpected EOF"),
ThriftProtocolError::IO(e) => e.into(),
ThriftProtocolError::InvalidFieldType(value) => {
general_err!("Unexpected struct field type {}", value)
}
ThriftProtocolError::InvalidElementType(value) => {
general_err!("Unexpected list/set element type {}", value)
}
ThriftProtocolError::FieldDeltaOverflow {
field_delta,
last_field_id,
} => general_err!("cannot add {} to {}", field_delta, last_field_id),
ThriftProtocolError::InvalidBoolean(value) => {
general_err!("cannot convert {} into bool", value)
}
ThriftProtocolError::Utf8Error => general_err!("invalid utf8"),
ThriftProtocolError::SkipDepth(field_type) => {
general_err!("cannot parse past {:?}", field_type)
}
ThriftProtocolError::SkipUnsupportedType(field_type) => {
general_err!("cannot skip field type {:?}", field_type)
}
}
}
}
impl From<Utf8Error> for ThriftProtocolError {
fn from(_: Utf8Error) -> Self {
Self::Utf8Error
}
}
impl From<Error> for ThriftProtocolError {
fn from(e: Error) -> Self {
Self::IO(e)
}
}
pub type ThriftProtocolResult<T> = Result<T, ThriftProtocolError>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OrderedF64(f64);
impl From<f64> for OrderedF64 {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<OrderedF64> for f64 {
fn from(value: OrderedF64) -> Self {
value.0
}
}
impl Eq for OrderedF64 {}
impl Ord for OrderedF64 {
fn cmp(&self, other: &Self) -> Ordering {
self.0.total_cmp(&other.0)
}
}
impl PartialOrd for OrderedF64 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum FieldType {
Stop = 0,
BooleanTrue = 1,
BooleanFalse = 2,
Byte = 3,
I16 = 4,
I32 = 5,
I64 = 6,
Double = 7,
Binary = 8,
List = 9,
Set = 10,
Map = 11,
Struct = 12,
}
impl TryFrom<u8> for FieldType {
type Error = ThriftProtocolError;
fn try_from(value: u8) -> ThriftProtocolResult<Self> {
match value {
0 => Ok(Self::Stop),
1 => Ok(Self::BooleanTrue),
2 => Ok(Self::BooleanFalse),
3 => Ok(Self::Byte),
4 => Ok(Self::I16),
5 => Ok(Self::I32),
6 => Ok(Self::I64),
7 => Ok(Self::Double),
8 => Ok(Self::Binary),
9 => Ok(Self::List),
10 => Ok(Self::Set),
11 => Ok(Self::Map),
12 => Ok(Self::Struct),
_ => Err(ThriftProtocolError::InvalidFieldType(value)),
}
}
}
impl TryFrom<ElementType> for FieldType {
type Error = ThriftProtocolError;
fn try_from(value: ElementType) -> std::result::Result<Self, Self::Error> {
match value {
ElementType::Bool => Ok(Self::BooleanTrue),
ElementType::Byte => Ok(Self::Byte),
ElementType::I16 => Ok(Self::I16),
ElementType::I32 => Ok(Self::I32),
ElementType::I64 => Ok(Self::I64),
ElementType::Double => Ok(Self::Double),
ElementType::Binary => Ok(Self::Binary),
ElementType::List => Ok(Self::List),
ElementType::Struct => Ok(Self::Struct),
_ => Err(ThriftProtocolError::InvalidFieldType(value as u8)),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ElementType {
Bool = 2,
Byte = 3,
I16 = 4,
I32 = 5,
I64 = 6,
Double = 7,
Binary = 8,
List = 9,
Set = 10,
Map = 11,
Struct = 12,
}
impl TryFrom<u8> for ElementType {
type Error = ThriftProtocolError;
fn try_from(value: u8) -> ThriftProtocolResult<Self> {
match value {
1 | 2 => Ok(Self::Bool),
3 => Ok(Self::Byte),
4 => Ok(Self::I16),
5 => Ok(Self::I32),
6 => Ok(Self::I64),
7 => Ok(Self::Double),
8 => Ok(Self::Binary),
9 => Ok(Self::List),
10 => Ok(Self::Set),
11 => Ok(Self::Map),
12 => Ok(Self::Struct),
_ => Err(ThriftProtocolError::InvalidElementType(value)),
}
}
}
pub(crate) struct FieldIdentifier {
pub(crate) field_type: FieldType,
pub(crate) id: i16,
pub(crate) bool_val: Option<bool>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct ListIdentifier {
pub(crate) element_type: ElementType,
pub(crate) size: i32,
}
pub(crate) trait ThriftCompactInputProtocol<'a> {
fn read_byte(&mut self) -> ThriftProtocolResult<u8>;
fn read_bytes(&mut self) -> ThriftProtocolResult<&'a [u8]>;
fn read_bytes_owned(&mut self) -> ThriftProtocolResult<Vec<u8>>;
fn skip_bytes(&mut self, n: usize) -> ThriftProtocolResult<()>;
fn read_vlq(&mut self) -> ThriftProtocolResult<u64> {
let byte = self.read_byte()?;
if byte & 0x80 == 0 {
return Ok(byte as u64);
}
let mut in_progress = (byte & 0x7f) as u64;
let mut shift = 7;
loop {
let byte = self.read_byte()?;
in_progress |= ((byte & 0x7F) as u64).wrapping_shl(shift);
if byte & 0x80 == 0 {
return Ok(in_progress);
}
shift += 7;
}
}
fn read_zig_zag(&mut self) -> ThriftProtocolResult<i64> {
let val = self.read_vlq()?;
Ok((val >> 1) as i64 ^ -((val & 1) as i64))
}
fn read_list_begin(&mut self) -> ThriftProtocolResult<ListIdentifier> {
let header = self.read_byte()?;
if header == 0 {
return Ok(ListIdentifier {
element_type: ElementType::Byte,
size: 0,
});
}
let element_type = ElementType::try_from(header & 0x0f)?;
let possible_element_count = (header & 0xF0) >> 4;
let element_count = if possible_element_count != 15 {
possible_element_count as i32
} else {
self.read_vlq()? as _
};
Ok(ListIdentifier {
element_type,
size: element_count,
})
}
#[cold]
fn read_full_field_id(&mut self) -> ThriftProtocolResult<i16> {
self.read_i16()
}
fn read_field_begin(&mut self, last_field_id: i16) -> ThriftProtocolResult<FieldIdentifier> {
let field_type = self.read_byte()?;
let field_delta = (field_type & 0xf0) >> 4;
let field_type = FieldType::try_from(field_type & 0xf)?;
let mut bool_val: Option<bool> = None;
match field_type {
FieldType::Stop => Ok(FieldIdentifier {
field_type: FieldType::Stop,
id: 0,
bool_val,
}),
_ => {
if field_type == FieldType::BooleanFalse {
bool_val = Some(false);
} else if field_type == FieldType::BooleanTrue {
bool_val = Some(true);
}
let field_id = if field_delta != 0 {
last_field_id.checked_add(field_delta as i16).ok_or(
ThriftProtocolError::FieldDeltaOverflow {
field_delta,
last_field_id,
},
)?
} else {
self.read_full_field_id()?
};
Ok(FieldIdentifier {
field_type,
id: field_id,
bool_val,
})
}
}
}
fn read_field_header(&mut self) -> ThriftProtocolResult<(u8, u8)> {
let field_type = self.read_byte()?;
let field_delta = (field_type & 0xf0) >> 4;
let field_type = field_type & 0xf;
Ok((field_type, field_delta))
}
fn read_bool(&mut self) -> ThriftProtocolResult<bool> {
let b = self.read_byte()?;
match b {
0x01 => Ok(true),
0x00 | 0x02 => Ok(false),
_ => Err(ThriftProtocolError::InvalidBoolean(b)),
}
}
fn read_string(&mut self) -> ThriftProtocolResult<&'a str> {
let slice = self.read_bytes()?;
Ok(std::str::from_utf8(slice)?)
}
fn read_i8(&mut self) -> ThriftProtocolResult<i8> {
Ok(self.read_byte()? as _)
}
fn read_i16(&mut self) -> ThriftProtocolResult<i16> {
Ok(self.read_zig_zag()? as _)
}
fn read_i32(&mut self) -> ThriftProtocolResult<i32> {
Ok(self.read_zig_zag()? as _)
}
fn read_i64(&mut self) -> ThriftProtocolResult<i64> {
self.read_zig_zag()
}
fn read_double(&mut self) -> ThriftProtocolResult<f64>;
fn skip_vlq(&mut self) -> ThriftProtocolResult<()> {
loop {
let byte = self.read_byte()?;
if byte & 0x80 == 0 {
return Ok(());
}
}
}
fn skip_binary(&mut self) -> ThriftProtocolResult<()> {
let len = self.read_vlq()? as usize;
self.skip_bytes(len)
}
fn skip(&mut self, field_type: FieldType) -> ThriftProtocolResult<()> {
const DEFAULT_SKIP_DEPTH: i8 = 64;
self.skip_till_depth(field_type, DEFAULT_SKIP_DEPTH)
}
fn skip_empty_struct(&mut self) -> Result<()> {
let b = self.read_byte()?;
if b != 0 {
Err(general_err!("Empty struct has fields"))
} else {
Ok(())
}
}
fn skip_till_depth(&mut self, field_type: FieldType, depth: i8) -> ThriftProtocolResult<()> {
if depth == 0 {
return Err(ThriftProtocolError::SkipDepth(field_type));
}
match field_type {
FieldType::BooleanFalse | FieldType::BooleanTrue => Ok(()),
FieldType::Byte => self.read_i8().map(|_| ()),
FieldType::I16 => self.skip_vlq().map(|_| ()),
FieldType::I32 => self.skip_vlq().map(|_| ()),
FieldType::I64 => self.skip_vlq().map(|_| ()),
FieldType::Double => self.skip_bytes(8).map(|_| ()),
FieldType::Binary => self.skip_binary().map(|_| ()),
FieldType::Struct => {
let mut last_field_id = 0i16;
loop {
let field_ident = self.read_field_begin(last_field_id)?;
if field_ident.field_type == FieldType::Stop {
break;
}
self.skip_till_depth(field_ident.field_type, depth - 1)?;
last_field_id = field_ident.id;
}
Ok(())
}
FieldType::List => {
let list_ident = self.read_list_begin()?;
for _ in 0..list_ident.size {
let element_type = FieldType::try_from(list_ident.element_type)?;
self.skip_till_depth(element_type, depth - 1)?;
}
Ok(())
}
_ => Err(ThriftProtocolError::SkipUnsupportedType(field_type)),
}
}
}
pub(crate) struct ThriftSliceInputProtocol<'a> {
buf: &'a [u8],
}
impl<'a> ThriftSliceInputProtocol<'a> {
pub fn new(buf: &'a [u8]) -> Self {
Self { buf }
}
pub fn as_slice(&self) -> &'a [u8] {
self.buf
}
}
impl<'b, 'a: 'b> ThriftCompactInputProtocol<'b> for ThriftSliceInputProtocol<'a> {
#[inline]
fn read_byte(&mut self) -> ThriftProtocolResult<u8> {
let ret = *self.buf.first().ok_or(ThriftProtocolError::Eof)?;
self.buf = &self.buf[1..];
Ok(ret)
}
fn read_bytes(&mut self) -> ThriftProtocolResult<&'b [u8]> {
let len = self.read_vlq()? as usize;
let ret = self.buf.get(..len).ok_or(ThriftProtocolError::Eof)?;
self.buf = &self.buf[len..];
Ok(ret)
}
fn read_bytes_owned(&mut self) -> ThriftProtocolResult<Vec<u8>> {
Ok(self.read_bytes()?.to_vec())
}
#[inline]
fn skip_bytes(&mut self, n: usize) -> ThriftProtocolResult<()> {
self.buf.get(..n).ok_or(ThriftProtocolError::Eof)?;
self.buf = &self.buf[n..];
Ok(())
}
fn read_double(&mut self) -> ThriftProtocolResult<f64> {
let slice = self.buf.get(..8).ok_or(ThriftProtocolError::Eof)?;
self.buf = &self.buf[8..];
match slice.try_into() {
Ok(slice) => Ok(f64::from_le_bytes(slice)),
Err(_) => unreachable!(),
}
}
}
pub(crate) struct ThriftReadInputProtocol<R: Read> {
reader: R,
}
impl<R: Read> ThriftReadInputProtocol<R> {
pub(crate) fn new(reader: R) -> Self {
Self { reader }
}
}
impl<'a, R: Read> ThriftCompactInputProtocol<'a> for ThriftReadInputProtocol<R> {
#[inline]
fn read_byte(&mut self) -> ThriftProtocolResult<u8> {
let mut buf = [0_u8; 1];
self.reader.read_exact(&mut buf)?;
Ok(buf[0])
}
fn read_bytes(&mut self) -> ThriftProtocolResult<&'a [u8]> {
unimplemented!()
}
fn read_bytes_owned(&mut self) -> ThriftProtocolResult<Vec<u8>> {
let len = self.read_vlq()? as usize;
let mut v = Vec::with_capacity(len);
std::io::copy(&mut self.reader.by_ref().take(len as u64), &mut v)?;
Ok(v)
}
fn skip_bytes(&mut self, n: usize) -> ThriftProtocolResult<()> {
std::io::copy(
&mut self.reader.by_ref().take(n as u64),
&mut std::io::sink(),
)?;
Ok(())
}
fn read_double(&mut self) -> ThriftProtocolResult<f64> {
let mut buf = [0_u8; 8];
self.reader.read_exact(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
}
pub(crate) trait ReadThrift<'a, R: ThriftCompactInputProtocol<'a>> {
fn read_thrift(prot: &mut R) -> Result<Self>
where
Self: Sized;
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for bool {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_bool()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for i8 {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_i8()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for i16 {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_i16()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for i32 {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_i32()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for i64 {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_i64()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for OrderedF64 {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(OrderedF64(prot.read_double()?))
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for &'a str {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_string()?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for String {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(String::from_utf8(prot.read_bytes_owned()?)?)
}
}
impl<'a, R: ThriftCompactInputProtocol<'a>> ReadThrift<'a, R> for &'a [u8] {
fn read_thrift(prot: &mut R) -> Result<Self> {
Ok(prot.read_bytes()?)
}
}
pub(crate) fn read_thrift_vec<'a, T, R>(prot: &mut R) -> Result<Vec<T>>
where
R: ThriftCompactInputProtocol<'a>,
T: ReadThrift<'a, R>,
{
let list_ident = prot.read_list_begin()?;
let mut res = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let val = T::read_thrift(prot)?;
res.push(val);
}
Ok(res)
}
pub(crate) struct ThriftCompactOutputProtocol<W: Write> {
writer: W,
}
impl<W: Write> ThriftCompactOutputProtocol<W> {
pub(crate) fn new(writer: W) -> Self {
Self { writer }
}
fn write_byte(&mut self, b: u8) -> Result<()> {
self.writer.write_all(&[b])?;
Ok(())
}
fn write_vlq(&mut self, val: u64) -> Result<()> {
let mut v = val;
while v > 0x7f {
self.write_byte(v as u8 | 0x80)?;
v >>= 7;
}
self.write_byte(v as u8)
}
fn write_zig_zag(&mut self, val: i64) -> Result<()> {
let s = (val < 0) as i64;
self.write_vlq((((val ^ -s) << 1) + s) as u64)
}
pub(crate) fn write_field_begin(
&mut self,
field_type: FieldType,
field_id: i16,
last_field_id: i16,
) -> Result<()> {
let delta = field_id.wrapping_sub(last_field_id);
if delta > 0 && delta <= 0xf {
self.write_byte((delta as u8) << 4 | field_type as u8)
} else {
self.write_byte(field_type as u8)?;
self.write_i16(field_id)
}
}
pub(crate) fn write_list_begin(&mut self, element_type: ElementType, len: usize) -> Result<()> {
if len < 15 {
self.write_byte((len as u8) << 4 | element_type as u8)
} else {
self.write_byte(0xf0u8 | element_type as u8)?;
self.write_vlq(len as _)
}
}
pub(crate) fn write_struct_end(&mut self) -> Result<()> {
self.write_byte(0)
}
pub(crate) fn write_bytes(&mut self, val: &[u8]) -> Result<()> {
self.write_vlq(val.len() as u64)?;
self.writer.write_all(val)?;
Ok(())
}
pub(crate) fn write_empty_struct(&mut self, field_id: i16, last_field_id: i16) -> Result<i16> {
self.write_field_begin(FieldType::Struct, field_id, last_field_id)?;
self.write_struct_end()?;
Ok(last_field_id)
}
pub(crate) fn write_bool(&mut self, val: bool) -> Result<()> {
match val {
true => self.write_byte(1),
false => self.write_byte(2),
}
}
pub(crate) fn write_i8(&mut self, val: i8) -> Result<()> {
self.write_byte(val as u8)
}
pub(crate) fn write_i16(&mut self, val: i16) -> Result<()> {
self.write_zig_zag(val as _)
}
pub(crate) fn write_i32(&mut self, val: i32) -> Result<()> {
self.write_zig_zag(val as _)
}
pub(crate) fn write_i64(&mut self, val: i64) -> Result<()> {
self.write_zig_zag(val as _)
}
pub(crate) fn write_double(&mut self, val: f64) -> Result<()> {
self.writer.write_all(&val.to_le_bytes())?;
Ok(())
}
}
pub(crate) trait WriteThrift {
const ELEMENT_TYPE: ElementType;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()>;
}
impl<T> WriteThrift for Vec<T>
where
T: WriteThrift,
{
const ELEMENT_TYPE: ElementType = ElementType::List;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_list_begin(T::ELEMENT_TYPE, self.len())?;
for item in self {
item.write_thrift(writer)?;
}
Ok(())
}
}
impl WriteThrift for bool {
const ELEMENT_TYPE: ElementType = ElementType::Bool;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_bool(*self)
}
}
impl WriteThrift for i8 {
const ELEMENT_TYPE: ElementType = ElementType::Byte;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_i8(*self)
}
}
impl WriteThrift for i16 {
const ELEMENT_TYPE: ElementType = ElementType::I16;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_i16(*self)
}
}
impl WriteThrift for i32 {
const ELEMENT_TYPE: ElementType = ElementType::I32;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_i32(*self)
}
}
impl WriteThrift for i64 {
const ELEMENT_TYPE: ElementType = ElementType::I64;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_i64(*self)
}
}
impl WriteThrift for OrderedF64 {
const ELEMENT_TYPE: ElementType = ElementType::Double;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_double(self.0)
}
}
impl WriteThrift for f64 {
const ELEMENT_TYPE: ElementType = ElementType::Double;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_double(*self)
}
}
impl WriteThrift for &[u8] {
const ELEMENT_TYPE: ElementType = ElementType::Binary;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_bytes(self)
}
}
impl WriteThrift for &str {
const ELEMENT_TYPE: ElementType = ElementType::Binary;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_bytes(self.as_bytes())
}
}
impl WriteThrift for String {
const ELEMENT_TYPE: ElementType = ElementType::Binary;
fn write_thrift<W: Write>(&self, writer: &mut ThriftCompactOutputProtocol<W>) -> Result<()> {
writer.write_bytes(self.as_bytes())
}
}
pub(crate) trait WriteThriftField {
fn write_thrift_field<W: Write>(
&self,
writer: &mut ThriftCompactOutputProtocol<W>,
field_id: i16,
last_field_id: i16,
) -> Result<i16>;
}
impl WriteThriftField for bool {
fn write_thrift_field<W: Write>(
&self,
writer: &mut ThriftCompactOutputProtocol<W>,
field_id: i16,
last_field_id: i16,
) -> Result<i16> {
match *self {
true => writer.write_field_begin(FieldType::BooleanTrue, field_id, last_field_id)?,
false => writer.write_field_begin(FieldType::BooleanFalse, field_id, last_field_id)?,
}
Ok(field_id)
}
}
write_thrift_field!(i8, FieldType::Byte);
write_thrift_field!(i16, FieldType::I16);
write_thrift_field!(i32, FieldType::I32);
write_thrift_field!(i64, FieldType::I64);
write_thrift_field!(OrderedF64, FieldType::Double);
write_thrift_field!(f64, FieldType::Double);
write_thrift_field!(String, FieldType::Binary);
impl WriteThriftField for &[u8] {
fn write_thrift_field<W: Write>(
&self,
writer: &mut ThriftCompactOutputProtocol<W>,
field_id: i16,
last_field_id: i16,
) -> Result<i16> {
writer.write_field_begin(FieldType::Binary, field_id, last_field_id)?;
writer.write_bytes(self)?;
Ok(field_id)
}
}
impl WriteThriftField for &str {
fn write_thrift_field<W: Write>(
&self,
writer: &mut ThriftCompactOutputProtocol<W>,
field_id: i16,
last_field_id: i16,
) -> Result<i16> {
writer.write_field_begin(FieldType::Binary, field_id, last_field_id)?;
writer.write_bytes(self.as_bytes())?;
Ok(field_id)
}
}
impl<T> WriteThriftField for Vec<T>
where
T: WriteThrift,
{
fn write_thrift_field<W: Write>(
&self,
writer: &mut ThriftCompactOutputProtocol<W>,
field_id: i16,
last_field_id: i16,
) -> Result<i16> {
writer.write_field_begin(FieldType::List, field_id, last_field_id)?;
self.write_thrift(writer)?;
Ok(field_id)
}
}
#[cfg(test)]
pub(crate) mod tests {
use crate::basic::{TimeUnit, Type};
use super::*;
use std::fmt::Debug;
pub(crate) fn test_roundtrip<T>(val: T)
where
T: for<'a> ReadThrift<'a, ThriftSliceInputProtocol<'a>> + WriteThrift + PartialEq + Debug,
{
let mut buf = Vec::<u8>::new();
{
let mut writer = ThriftCompactOutputProtocol::new(&mut buf);
val.write_thrift(&mut writer).unwrap();
}
let mut prot = ThriftSliceInputProtocol::new(&buf);
let read_val = T::read_thrift(&mut prot).unwrap();
assert_eq!(val, read_val);
}
#[test]
fn test_enum_roundtrip() {
test_roundtrip(Type::BOOLEAN);
test_roundtrip(Type::INT32);
test_roundtrip(Type::INT64);
test_roundtrip(Type::INT96);
test_roundtrip(Type::FLOAT);
test_roundtrip(Type::DOUBLE);
test_roundtrip(Type::BYTE_ARRAY);
test_roundtrip(Type::FIXED_LEN_BYTE_ARRAY);
}
#[test]
fn test_union_all_empty_roundtrip() {
test_roundtrip(TimeUnit::MILLIS);
test_roundtrip(TimeUnit::MICROS);
test_roundtrip(TimeUnit::NANOS);
}
#[test]
fn test_decode_empty_list() {
let data = vec![0u8; 1];
let mut prot = ThriftSliceInputProtocol::new(&data);
let header = prot.read_list_begin().expect("error reading list header");
assert_eq!(header.size, 0);
assert_eq!(header.element_type, ElementType::Byte);
}
}