lib/rs/src/protocol/compact.rs (2,067 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use integer_encoding::{VarIntReader, VarIntWriter}; use std::convert::{From, TryFrom}; use std::io; use super::{ TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, }; use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType}; use crate::transport::{TReadTransport, TWriteTransport}; const COMPACT_PROTOCOL_ID: u8 = 0x82; const COMPACT_VERSION: u8 = 0x01; const COMPACT_VERSION_MASK: u8 = 0x1F; /// Read messages encoded in the Thrift compact protocol. /// /// # Examples /// /// Create and use a `TCompactInputProtocol`. /// /// ```no_run /// use thrift::protocol::{TCompactInputProtocol, TInputProtocol}; /// use thrift::transport::TTcpChannel; /// /// let mut channel = TTcpChannel::new(); /// channel.open("localhost:9090").unwrap(); /// /// let mut protocol = TCompactInputProtocol::new(channel); /// /// let recvd_bool = protocol.read_bool().unwrap(); /// let recvd_string = protocol.read_string().unwrap(); /// ``` #[derive(Debug)] pub struct TCompactInputProtocol<T> where T: TReadTransport, { // Identifier of the last field deserialized for a struct. last_read_field_id: i16, // Stack of the last read field ids (a new entry is added each time a nested struct is read). read_field_id_stack: Vec<i16>, // Boolean value for a field. // Saved because boolean fields and their value are encoded in a single byte, // and reading the field only occurs after the field id is read. pending_read_bool_value: Option<bool>, // Underlying transport used for byte-level operations. transport: T, } impl<T> TCompactInputProtocol<T> where T: TReadTransport, { /// Create a `TCompactInputProtocol` that reads bytes from `transport`. pub fn new(transport: T) -> TCompactInputProtocol<T> { TCompactInputProtocol { last_read_field_id: 0, read_field_id_stack: Vec::new(), pending_read_bool_value: None, transport, } } fn read_list_set_begin(&mut self) -> crate::Result<(TType, i32)> { let header = self.read_byte()?; let element_type = collection_u8_to_type(header & 0x0F)?; let possible_element_count = (header & 0xF0) >> 4; let element_count = if possible_element_count != 15 { // high bits set high if count and type encoded separately possible_element_count as i32 } else { self.transport.read_varint::<u32>()? as i32 }; Ok((element_type, element_count)) } } impl<T> TInputProtocol for TCompactInputProtocol<T> where T: TReadTransport, { fn read_message_begin(&mut self) -> crate::Result<TMessageIdentifier> { let compact_id = self.read_byte()?; if compact_id != COMPACT_PROTOCOL_ID { Err(crate::Error::Protocol(crate::ProtocolError { kind: crate::ProtocolErrorKind::BadVersion, message: format!("invalid compact protocol header {:?}", compact_id), })) } else { Ok(()) }?; let type_and_byte = self.read_byte()?; let received_version = type_and_byte & COMPACT_VERSION_MASK; if received_version != COMPACT_VERSION { Err(crate::Error::Protocol(crate::ProtocolError { kind: crate::ProtocolErrorKind::BadVersion, message: format!( "cannot process compact protocol version {:?}", received_version ), })) } else { Ok(()) }?; // NOTE: unsigned right shift will pad with 0s let message_type: TMessageType = TMessageType::try_from(type_and_byte >> 5)?; // writing side wrote signed sequence number as u32 to avoid zigzag encoding let sequence_number = self.transport.read_varint::<u32>()? as i32; let service_call_name = self.read_string()?; self.last_read_field_id = 0; Ok(TMessageIdentifier::new( service_call_name, message_type, sequence_number, )) } fn read_message_end(&mut self) -> crate::Result<()> { Ok(()) } fn read_struct_begin(&mut self) -> crate::Result<Option<TStructIdentifier>> { self.read_field_id_stack.push(self.last_read_field_id); self.last_read_field_id = 0; Ok(None) } fn read_struct_end(&mut self) -> crate::Result<()> { self.last_read_field_id = self .read_field_id_stack .pop() .expect("should have previous field ids"); Ok(()) } fn read_field_begin(&mut self) -> crate::Result<TFieldIdentifier> { // we can read at least one byte, which is: // - the type // - the field delta and the type let field_type = self.read_byte()?; let field_delta = (field_type & 0xF0) >> 4; let field_type = match field_type & 0x0F { 0x01 => { self.pending_read_bool_value = Some(true); Ok(TType::Bool) } 0x02 => { self.pending_read_bool_value = Some(false); Ok(TType::Bool) } ttu8 => u8_to_type(ttu8), }?; match field_type { TType::Stop => Ok( TFieldIdentifier::new::<Option<String>, String, Option<i16>>( None, TType::Stop, None, ), ), _ => { if field_delta != 0 { self.last_read_field_id += field_delta as i16; } else { self.last_read_field_id = self.read_i16()?; }; Ok(TFieldIdentifier { name: None, field_type, id: Some(self.last_read_field_id), }) } } } fn read_field_end(&mut self) -> crate::Result<()> { Ok(()) } fn read_bool(&mut self) -> crate::Result<bool> { match self.pending_read_bool_value.take() { Some(b) => Ok(b), None => { let b = self.read_byte()?; match b { // Previous versions of the thrift compact protocol specification said to use 0 // and 1 inside collections, but that differed from existing implementations. // The specification was updated in https://github.com/apache/thrift/commit/2c29c5665bc442e703480bb0ee60fe925ffe02e8. 0x00 => Ok(false), 0x01 => Ok(true), 0x02 => Ok(false), unkn => Err(crate::Error::Protocol(crate::ProtocolError { kind: crate::ProtocolErrorKind::InvalidData, message: format!("cannot convert {} into bool", unkn), })), } } } } fn read_bytes(&mut self) -> crate::Result<Vec<u8>> { let len = self.transport.read_varint::<u32>()?; let mut buf = vec![0u8; len as usize]; self.transport .read_exact(&mut buf) .map_err(From::from) .map(|_| buf) } fn read_i8(&mut self) -> crate::Result<i8> { self.read_byte().map(|i| i as i8) } fn read_i16(&mut self) -> crate::Result<i16> { self.transport.read_varint::<i16>().map_err(From::from) } fn read_i32(&mut self) -> crate::Result<i32> { self.transport.read_varint::<i32>().map_err(From::from) } fn read_i64(&mut self) -> crate::Result<i64> { self.transport.read_varint::<i64>().map_err(From::from) } fn read_double(&mut self) -> crate::Result<f64> { self.transport .read_f64::<LittleEndian>() .map_err(From::from) } fn read_uuid(&mut self) -> crate::Result<uuid::Uuid> { uuid::Uuid::from_slice(&self.read_bytes()?).map_err(From::from) } fn read_string(&mut self) -> crate::Result<String> { let bytes = self.read_bytes()?; String::from_utf8(bytes).map_err(From::from) } fn read_list_begin(&mut self) -> crate::Result<TListIdentifier> { let (element_type, element_count) = self.read_list_set_begin()?; Ok(TListIdentifier::new(element_type, element_count)) } fn read_list_end(&mut self) -> crate::Result<()> { Ok(()) } fn read_set_begin(&mut self) -> crate::Result<TSetIdentifier> { let (element_type, element_count) = self.read_list_set_begin()?; Ok(TSetIdentifier::new(element_type, element_count)) } fn read_set_end(&mut self) -> crate::Result<()> { Ok(()) } fn read_map_begin(&mut self) -> crate::Result<TMapIdentifier> { let element_count = self.transport.read_varint::<u32>()? as i32; if element_count == 0 { Ok(TMapIdentifier::new(None, None, 0)) } else { let type_header = self.read_byte()?; let key_type = collection_u8_to_type((type_header & 0xF0) >> 4)?; let val_type = collection_u8_to_type(type_header & 0x0F)?; Ok(TMapIdentifier::new(key_type, val_type, element_count)) } } fn read_map_end(&mut self) -> crate::Result<()> { Ok(()) } // utility // fn read_byte(&mut self) -> crate::Result<u8> { let mut buf = [0u8; 1]; self.transport .read_exact(&mut buf) .map_err(From::from) .map(|_| buf[0]) } } impl<T> io::Seek for TCompactInputProtocol<T> where T: io::Seek + TReadTransport, { fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> { self.transport.seek(pos) } } /// Factory for creating instances of `TCompactInputProtocol`. #[derive(Default)] pub struct TCompactInputProtocolFactory; impl TCompactInputProtocolFactory { /// Create a `TCompactInputProtocolFactory`. pub fn new() -> TCompactInputProtocolFactory { TCompactInputProtocolFactory {} } } impl TInputProtocolFactory for TCompactInputProtocolFactory { fn create(&self, transport: Box<dyn TReadTransport + Send>) -> Box<dyn TInputProtocol + Send> { Box::new(TCompactInputProtocol::new(transport)) } } /// Write messages using the Thrift compact protocol. /// /// # Examples /// /// Create and use a `TCompactOutputProtocol`. /// /// ```no_run /// use thrift::protocol::{TCompactOutputProtocol, TOutputProtocol}; /// use thrift::transport::TTcpChannel; /// /// let mut channel = TTcpChannel::new(); /// channel.open("localhost:9090").unwrap(); /// /// let mut protocol = TCompactOutputProtocol::new(channel); /// /// protocol.write_bool(true).unwrap(); /// protocol.write_string("test_string").unwrap(); /// ``` #[derive(Debug)] pub struct TCompactOutputProtocol<T> where T: TWriteTransport, { // Identifier of the last field serialized for a struct. last_write_field_id: i16, // Stack of the last written field ids (new entry added each time a nested struct is written). write_field_id_stack: Vec<i16>, // Field identifier of the boolean field to be written. // Saved because boolean fields and their value are encoded in a single byte pending_write_bool_field_identifier: Option<TFieldIdentifier>, // Underlying transport used for byte-level operations. transport: T, } impl<T> TCompactOutputProtocol<T> where T: TWriteTransport, { /// Create a `TCompactOutputProtocol` that writes bytes to `transport`. pub fn new(transport: T) -> TCompactOutputProtocol<T> { TCompactOutputProtocol { last_write_field_id: 0, write_field_id_stack: Vec::new(), pending_write_bool_field_identifier: None, transport, } } // FIXME: field_type as unconstrained u8 is bad fn write_field_header(&mut self, field_type: u8, field_id: i16) -> crate::Result<()> { let field_delta = field_id - self.last_write_field_id; if field_delta > 0 && field_delta < 15 { self.write_byte(((field_delta as u8) << 4) | field_type)?; } else { self.write_byte(field_type)?; self.write_i16(field_id)?; } self.last_write_field_id = field_id; Ok(()) } fn write_list_set_begin( &mut self, element_type: TType, element_count: i32, ) -> crate::Result<()> { let elem_identifier = collection_type_to_u8(element_type); if element_count <= 14 { let header = (element_count as u8) << 4 | elem_identifier; self.write_byte(header) } else { let header = 0xF0 | elem_identifier; self.write_byte(header)?; // element count is strictly positive as per the spec, so // cast i32 as u32 so that varint writing won't use zigzag encoding self.transport .write_varint(element_count as u32) .map_err(From::from) .map(|_| ()) } } fn assert_no_pending_bool_write(&self) { if let Some(ref f) = self.pending_write_bool_field_identifier { panic!("pending bool field {:?} not written", f) } } } impl<T> TOutputProtocol for TCompactOutputProtocol<T> where T: TWriteTransport, { fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> crate::Result<()> { self.write_byte(COMPACT_PROTOCOL_ID)?; self.write_byte((u8::from(identifier.message_type) << 5) | COMPACT_VERSION)?; // cast i32 as u32 so that varint writing won't use zigzag encoding self.transport .write_varint(identifier.sequence_number as u32)?; self.write_string(&identifier.name)?; Ok(()) } fn write_message_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); Ok(()) } fn write_struct_begin(&mut self, _: &TStructIdentifier) -> crate::Result<()> { self.write_field_id_stack.push(self.last_write_field_id); self.last_write_field_id = 0; Ok(()) } fn write_struct_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); self.last_write_field_id = self .write_field_id_stack .pop() .expect("should have previous field ids"); Ok(()) } fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> crate::Result<()> { match identifier.field_type { TType::Bool => { if self.pending_write_bool_field_identifier.is_some() { panic!( "should not have a pending bool while writing another bool with id: \ {:?}", identifier ) } self.pending_write_bool_field_identifier = Some(identifier.clone()); Ok(()) } _ => { let field_type = type_to_u8(identifier.field_type); let field_id = identifier.id.expect("non-stop field should have field id"); self.write_field_header(field_type, field_id) } } } fn write_field_end(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); Ok(()) } fn write_field_stop(&mut self) -> crate::Result<()> { self.assert_no_pending_bool_write(); self.write_byte(type_to_u8(TType::Stop)) } fn write_bool(&mut self, b: bool) -> crate::Result<()> { match self.pending_write_bool_field_identifier.take() { Some(pending) => { let field_id = pending.id.expect("bool field should have a field id"); let field_type_as_u8 = if b { 0x01 } else { 0x02 }; self.write_field_header(field_type_as_u8, field_id) } None => { if b { self.write_byte(0x01) } else { self.write_byte(0x02) } } } } fn write_bytes(&mut self, b: &[u8]) -> crate::Result<()> { // length is strictly positive as per the spec, so // cast i32 as u32 so that varint writing won't use zigzag encoding self.transport.write_varint(b.len() as u32)?; self.transport.write_all(b).map_err(From::from) } fn write_i8(&mut self, i: i8) -> crate::Result<()> { self.write_byte(i as u8) } fn write_i16(&mut self, i: i16) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } fn write_i32(&mut self, i: i32) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } fn write_i64(&mut self, i: i64) -> crate::Result<()> { self.transport .write_varint(i) .map_err(From::from) .map(|_| ()) } fn write_double(&mut self, d: f64) -> crate::Result<()> { self.transport .write_f64::<LittleEndian>(d) .map_err(From::from) } fn write_uuid(&mut self, uuid: &uuid::Uuid) -> crate::Result<()> { self.write_bytes(uuid.as_bytes()) } fn write_string(&mut self, s: &str) -> crate::Result<()> { self.write_bytes(s.as_bytes()) } fn write_list_begin(&mut self, identifier: &TListIdentifier) -> crate::Result<()> { self.write_list_set_begin(identifier.element_type, identifier.size) } fn write_list_end(&mut self) -> crate::Result<()> { Ok(()) } fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> crate::Result<()> { self.write_list_set_begin(identifier.element_type, identifier.size) } fn write_set_end(&mut self) -> crate::Result<()> { Ok(()) } fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> crate::Result<()> { if identifier.size == 0 { self.write_byte(0) } else { // element count is strictly positive as per the spec, so // cast i32 as u32 so that varint writing won't use zigzag encoding self.transport.write_varint(identifier.size as u32)?; let key_type = identifier .key_type .expect("map identifier to write should contain key type"); let key_type_byte = collection_type_to_u8(key_type) << 4; let val_type = identifier .value_type .expect("map identifier to write should contain value type"); let val_type_byte = collection_type_to_u8(val_type); let map_type_header = key_type_byte | val_type_byte; self.write_byte(map_type_header) } } fn write_map_end(&mut self) -> crate::Result<()> { Ok(()) } fn flush(&mut self) -> crate::Result<()> { self.transport.flush().map_err(From::from) } // utility // fn write_byte(&mut self, b: u8) -> crate::Result<()> { self.transport.write(&[b]).map_err(From::from).map(|_| ()) } } /// Factory for creating instances of `TCompactOutputProtocol`. #[derive(Default)] pub struct TCompactOutputProtocolFactory; impl TCompactOutputProtocolFactory { /// Create a `TCompactOutputProtocolFactory`. pub fn new() -> TCompactOutputProtocolFactory { TCompactOutputProtocolFactory {} } } impl TOutputProtocolFactory for TCompactOutputProtocolFactory { fn create( &self, transport: Box<dyn TWriteTransport + Send>, ) -> Box<dyn TOutputProtocol + Send> { Box::new(TCompactOutputProtocol::new(transport)) } } fn collection_type_to_u8(field_type: TType) -> u8 { match field_type { TType::Bool => 0x01, f => type_to_u8(f), } } fn type_to_u8(field_type: TType) -> u8 { match field_type { TType::Stop => 0x00, TType::I08 => 0x03, // equivalent to TType::Byte TType::I16 => 0x04, TType::I32 => 0x05, TType::I64 => 0x06, TType::Double => 0x07, TType::String => 0x08, TType::List => 0x09, TType::Set => 0x0A, TType::Map => 0x0B, TType::Struct => 0x0C, TType::Uuid => 0x0D, _ => panic!("should not have attempted to convert {} to u8", field_type), } } fn collection_u8_to_type(b: u8) -> crate::Result<TType> { match b { // For historical and compatibility reasons, a reader should be capable to deal with both cases. // The only valid value in the original spec was 2, but due to a widespread implementation bug // the defacto standard across large parts of the library became 1 instead. // As a result, both values are now allowed. 0x01 | 0x02 => Ok(TType::Bool), o => u8_to_type(o), } } fn u8_to_type(b: u8) -> crate::Result<TType> { match b { 0x00 => Ok(TType::Stop), 0x03 => Ok(TType::I08), // equivalent to TType::Byte 0x04 => Ok(TType::I16), 0x05 => Ok(TType::I32), 0x06 => Ok(TType::I64), 0x07 => Ok(TType::Double), 0x08 => Ok(TType::String), 0x09 => Ok(TType::List), 0x0A => Ok(TType::Set), 0x0B => Ok(TType::Map), 0x0C => Ok(TType::Struct), 0x0D => Ok(TType::Uuid), unkn => Err(crate::Error::Protocol(crate::ProtocolError { kind: crate::ProtocolErrorKind::InvalidData, message: format!("cannot convert {} into TType", unkn), })), } } #[cfg(test)] mod tests { use crate::protocol::{ TFieldIdentifier, TInputProtocol, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TOutputProtocol, TSetIdentifier, TStructIdentifier, TType, }; use crate::transport::{ReadHalf, TBufferChannel, TIoChannel, WriteHalf}; use super::*; #[test] fn must_write_message_begin_largest_maximum_positive_sequence_number() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "bar", TMessageType::Reply, i32::MAX ))); #[rustfmt::skip] let expected: [u8; 11] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0xFF, 0xFF, 0xFF, 0xFF, 0x07, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_largest_maximum_positive_sequence_number() { let (mut i_prot, _) = test_objects(); #[rustfmt::skip] let source_bytes: [u8; 11] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0xFF, 0xFF, 0xFF, 0xFF, 0x07, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("bar", TMessageType::Reply, i32::MAX); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_positive_sequence_number_0() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "foo", TMessageType::Call, 431 ))); #[rustfmt::skip] let expected: [u8; 8] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0xAF, 0x03, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_positive_sequence_number_0() { let (mut i_prot, _) = test_objects(); #[rustfmt::skip] let source_bytes: [u8; 8] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0xAF, 0x03, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("foo", TMessageType::Call, 431); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_positive_sequence_number_1() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "bar", TMessageType::Reply, 991_828 ))); #[rustfmt::skip] let expected: [u8; 9] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0xD4, 0xC4, 0x3C, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_positive_sequence_number_1() { let (mut i_prot, _) = test_objects(); #[rustfmt::skip] let source_bytes: [u8; 9] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0xD4, 0xC4, 0x3C, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("bar", TMessageType::Reply, 991_828); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_zero_sequence_number() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "bar", TMessageType::Reply, 0 ))); #[rustfmt::skip] let expected: [u8; 7] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0x00, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_zero_sequence_number() { let (mut i_prot, _) = test_objects(); #[rustfmt::skip] let source_bytes: [u8; 7] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0x00, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("bar", TMessageType::Reply, 0); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_largest_minimum_negative_sequence_number() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "bar", TMessageType::Reply, i32::MIN ))); // two's complement notation of i32::MIN = 1000_0000_0000_0000_0000_0000_0000_0000 #[rustfmt::skip] let expected: [u8; 11] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0x80, 0x80, 0x80, 0x80, 0x08, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_largest_minimum_negative_sequence_number() { let (mut i_prot, _) = test_objects(); // two's complement notation of i32::MIN = 1000_0000_0000_0000_0000_0000_0000_0000 #[rustfmt::skip] let source_bytes: [u8; 11] = [ 0x82, /* protocol ID */ 0x41, /* message type | protocol version */ 0x80, 0x80, 0x80, 0x80, 0x08, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x62, 0x61, 0x72 /* "bar" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("bar", TMessageType::Reply, i32::MIN); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_negative_sequence_number_0() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "foo", TMessageType::Call, -431 ))); // signed two's complement of -431 = 1111_1111_1111_1111_1111_1110_0101_0001 #[rustfmt::skip] let expected: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0xD1, 0xFC, 0xFF, 0xFF, 0x0F, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_negative_sequence_number_0() { let (mut i_prot, _) = test_objects(); // signed two's complement of -431 = 1111_1111_1111_1111_1111_1110_0101_0001 #[rustfmt::skip] let source_bytes: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0xD1, 0xFC, 0xFF, 0xFF, 0x0F, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("foo", TMessageType::Call, -431); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_negative_sequence_number_1() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "foo", TMessageType::Call, -73_184_125 ))); // signed two's complement of -73184125 = 1111_1011_1010_0011_0100_1100_1000_0011 #[rustfmt::skip] let expected: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0x83, 0x99, 0x8D, 0xDD, 0x0F, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_negative_sequence_number_1() { let (mut i_prot, _) = test_objects(); // signed two's complement of -73184125 = 1111_1011_1010_0011_0100_1100_1000_0011 #[rustfmt::skip] let source_bytes: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0x83, 0x99, 0x8D, 0xDD, 0x0F, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("foo", TMessageType::Call, -73_184_125); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_write_message_begin_negative_sequence_number_2() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new( "foo", TMessageType::Call, -1_073_741_823 ))); // signed two's complement of -1073741823 = 1100_0000_0000_0000_0000_0000_0000_0001 #[rustfmt::skip] let expected: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0x81, 0x80, 0x80, 0x80, 0x0C, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F /* "foo" */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_message_begin_negative_sequence_number_2() { let (mut i_prot, _) = test_objects(); // signed two's complement of -1073741823 = 1100_0000_0000_0000_0000_0000_0000_0001 #[rustfmt::skip] let source_bytes: [u8; 11] = [ 0x82, /* protocol ID */ 0x21, /* message type | protocol version */ 0x81, 0x80, 0x80, 0x80, 0x0C, /* non-zig-zag varint sequence number */ 0x03, /* message-name length */ 0x66, 0x6F, 0x6F, /* "foo" */ ]; i_prot.transport.set_readable_bytes(&source_bytes); let expected = TMessageIdentifier::new("foo", TMessageType::Call, -1_073_741_823); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&expected, &res); } #[test] fn must_round_trip_upto_i64_maxvalue() { // See https://issues.apache.org/jira/browse/THRIFT-5131 for i in 0..64 { let (mut i_prot, mut o_prot) = test_objects(); let val: i64 = ((1u64 << i) - 1) as i64; o_prot .write_field_begin(&TFieldIdentifier::new("val", TType::I64, 1)) .unwrap(); o_prot.write_i64(val).unwrap(); o_prot.write_field_end().unwrap(); o_prot.flush().unwrap(); copy_write_buffer_to_read_buffer!(o_prot); i_prot.read_field_begin().unwrap(); assert_eq!(val, i_prot.read_i64().unwrap()); } } #[test] fn must_round_trip_message_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1_283_948); assert_success!(o_prot.write_message_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_message_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_message_end() { assert_no_write(|o| o.write_message_end()); } // NOTE: structs and fields are tested together // #[test] fn must_write_struct_with_delta_fields() { let (_, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with tiny field ids // since they're small the field ids will be encoded as deltas // since this is the first field (and it's zero) it gets the full varint write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 0))); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I16, 4))); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::List, 9))); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 5] = [ 0x03, /* field type */ 0x00, /* first field id */ 0x44, /* field delta (4) | field type */ 0x59, /* field delta (5) | field type */ 0x00 /* field stop */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_struct_with_delta_fields() { let (mut i_prot, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with tiny field ids // since they're small the field ids will be encoded as deltas // since this is the first field (and it's zero) it gets the full varint write let field_ident_1 = TFieldIdentifier::new("foo", TType::I08, 0); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta let field_ident_2 = TFieldIdentifier::new("foo", TType::I16, 4); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta let field_ident_3 = TFieldIdentifier::new("foo", TType::List, 9); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read the struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_struct_with_non_zero_initial_field_and_delta_fields() { let (_, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with tiny field ids // since they're small the field ids will be encoded as deltas // gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 1))); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2))); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 6))); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 4] = [ 0x15, /* field delta (1) | field type */ 0x1A, /* field delta (1) | field type */ 0x48, /* field delta (4) | field type */ 0x00 /* field stop */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_struct_with_non_zero_initial_field_and_delta_fields() { let (mut i_prot, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with tiny field ids // since they're small the field ids will be encoded as deltas // gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta let field_ident_2 = TFieldIdentifier::new("foo", TType::Set, 2); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it can be encoded as a delta let field_ident_3 = TFieldIdentifier::new("foo", TType::String, 6); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read the struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_struct_with_long_fields() { let (_, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with field ids that cannot be encoded as deltas // since this is the first field (and it's zero) it gets the full varint write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 0))); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 16))); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 99))); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 8] = [ 0x05, /* field type */ 0x00, /* first field id */ 0x06, /* field type */ 0x20, /* zig-zag varint field id */ 0x0A, /* field type */ 0xC6, 0x01, /* zig-zag varint field id */ 0x00 /* field stop */, ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_struct_with_long_fields() { let (mut i_prot, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with field ids that cannot be encoded as deltas // since this is the first field (and it's zero) it gets the full varint write let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 0); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint let field_ident_2 = TFieldIdentifier::new("foo", TType::I64, 16); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 99); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read the struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_struct_with_mix_of_long_and_delta_fields() { let (_, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with field ids that cannot be encoded as deltas // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1))); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9))); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 1000))); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2001))); assert_success!(o_prot.write_field_end()); // since this is only 3 up from the previous it is recorded as a delta assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2004))); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 10] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ 0x0A, /* field type */ 0xD0, 0x0F, /* zig-zag varint field id */ 0x0A, /* field type */ 0xA2, 0x1F, /* zig-zag varint field id */ 0x3A, /* field delta (3) | field type */ 0x00 /* field stop */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_struct_with_mix_of_long_and_delta_fields() { let (mut i_prot, mut o_prot) = test_objects(); // no bytes should be written however let struct_ident = TStructIdentifier::new("foo"); assert_success!(o_prot.write_struct_begin(&struct_ident)); // write three fields with field ids that cannot be encoded as deltas // since the delta is > 0 and < 15 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it gets a delta write let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 1000); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // since this delta is > 15 it is encoded as a zig-zag varint let field_ident_4 = TFieldIdentifier::new("foo", TType::Set, 2001); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_field_end()); // since this is only 3 up from the previous it is recorded as a delta let field_ident_5 = TFieldIdentifier::new("foo", TType::Set, 2004); assert_success!(o_prot.write_field_begin(&field_ident_5)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read the struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); assert_success!(i_prot.read_field_end()); let read_ident_5 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_5, TFieldIdentifier { name: None, ..field_ident_5 } ); assert_success!(i_prot.read_field_end()); let read_ident_6 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_6, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_nested_structs_0() { // last field of the containing struct is a delta // first field of the the contained struct is a delta let (_, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1))); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9))); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 7))); assert_success!(o_prot.write_field_end()); // contained struct // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24))); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ 0x73, /* field delta (7) | field type */ 0x07, /* field type */ 0x30, /* zig-zag varint field id */ 0x00, /* field stop - contained */ 0x00 /* field stop - containing */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_0() { // last field of the containing struct is a delta // first field of the the contained struct is a delta let (mut i_prot, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 0 and < 15 it gets a delta write let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_3 = TFieldIdentifier::new("foo", TType::I08, 7); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // contained struct // since this delta > 15 it gets a full write let field_ident_4 = TFieldIdentifier::new("foo", TType::Double, 24); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read containing struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); // read contained struct back assert_success!(i_prot.read_struct_begin()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); assert_success!(i_prot.read_field_end()); // end contained struct let read_ident_6 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_6, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); // end containing struct let read_ident_7 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_7, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_nested_structs_1() { // last field of the containing struct is a delta // first field of the the contained struct is a full write let (_, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1))); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9))); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24))); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27))); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x85, /* field delta (8) | field type */ 0x07, /* field type */ 0x30, /* zig-zag varint field id */ 0x33, /* field delta (3) | field type */ 0x00, /* field stop - contained */ 0x00 /* field stop - containing */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_1() { // last field of the containing struct is a delta // first field of the the contained struct is a full write let (mut i_prot, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 0 and < 15 it gets a delta write let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 15 it gets a full write let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 24); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read containing struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); // read contained struct back assert_success!(i_prot.read_struct_begin()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); assert_success!(i_prot.read_field_end()); // end contained struct let read_ident_6 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_6, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); // end containing struct let read_ident_7 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_7, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_nested_structs_2() { // last field of the containing struct is a full write // first field of the the contained struct is a delta write let (_, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1))); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21))); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 7))); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 10))); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 7] = [ 0x16, /* field delta (1) | field type */ 0x08, /* field type */ 0x2A, /* zig-zag varint field id */ 0x77, /* field delta(7) | field type */ 0x33, /* field delta (3) | field type */ 0x00, /* field stop - contained */ 0x00 /* field stop - containing */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_2() { let (mut i_prot, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 15 it gets a full write let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 0 and < 15 it gets a delta write let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 7); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 10); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read containing struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); // read contained struct back assert_success!(i_prot.read_struct_begin()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); assert_success!(i_prot.read_field_end()); // end contained struct let read_ident_6 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_6, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); // end containing struct let read_ident_7 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_7, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_nested_structs_3() { // last field of the containing struct is a full write // first field of the the contained struct is a full write let (_, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1))); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21))); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 21))); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27))); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 8] = [ 0x16, /* field delta (1) | field type */ 0x08, /* field type */ 0x2A, /* zig-zag varint field id */ 0x07, /* field type */ 0x2A, /* zig-zag varint field id */ 0x63, /* field delta (6) | field type */ 0x00, /* field stop - contained */ 0x00 /* field stop - containing */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_nested_structs_3() { // last field of the containing struct is a full write // first field of the the contained struct is a full write let (mut i_prot, mut o_prot) = test_objects(); // start containing struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // containing struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_field_end()); // containing struct // since this delta > 15 it gets a full write let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_field_end()); // start contained struct assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // contained struct // since this delta > 15 it gets a full write let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 21); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_field_end()); // contained struct // since the delta is > 0 and < 15 it gets a delta write let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_field_end()); // end contained struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); // end containing struct assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read containing struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); assert_success!(i_prot.read_field_end()); // read contained struct back assert_success!(i_prot.read_struct_begin()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); assert_success!(i_prot.read_field_end()); // end contained struct let read_ident_6 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_6, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); // end containing struct let read_ident_7 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_7, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] fn must_write_bool_field() { let (_, mut o_prot) = test_objects(); // no bytes should be written however assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); // write three fields with field ids that cannot be encoded as deltas // since the delta is > 0 and < 16 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1))); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it gets a delta write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 9))); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_field_end()); // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 26))); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_field_end()); // since this delta > 15 it gets a full write assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 45))); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); #[rustfmt::skip] let expected: [u8; 7] = [ 0x11, /* field delta (1) | true */ 0x82, /* field delta (8) | false */ 0x01, /* true */ 0x34, /* field id */ 0x02, /* false */ 0x5A, /* field id */ 0x00 /* stop field */, ]; assert_eq_written_bytes!(o_prot, expected); } #[allow(clippy::cognitive_complexity)] #[test] fn must_round_trip_bool_field() { let (mut i_prot, mut o_prot) = test_objects(); // no bytes should be written however let struct_ident = TStructIdentifier::new("foo"); assert_success!(o_prot.write_struct_begin(&struct_ident)); // write two fields // since the delta is > 0 and < 16 it gets a delta write let field_ident_1 = TFieldIdentifier::new("foo", TType::Bool, 1); assert_success!(o_prot.write_field_begin(&field_ident_1)); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_field_end()); // since this delta > 0 and < 15 it gets a delta write let field_ident_2 = TFieldIdentifier::new("foo", TType::Bool, 9); assert_success!(o_prot.write_field_begin(&field_ident_2)); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_field_end()); // since this delta > 15 it gets a full write let field_ident_3 = TFieldIdentifier::new("foo", TType::Bool, 26); assert_success!(o_prot.write_field_begin(&field_ident_3)); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_field_end()); // since this delta > 15 it gets a full write let field_ident_4 = TFieldIdentifier::new("foo", TType::Bool, 45); assert_success!(o_prot.write_field_begin(&field_ident_4)); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_field_end()); // now, finish the struct off assert_success!(o_prot.write_field_stop()); assert_success!(o_prot.write_struct_end()); copy_write_buffer_to_read_buffer!(o_prot); // read the struct back assert_success!(i_prot.read_struct_begin()); let read_ident_1 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_1, TFieldIdentifier { name: None, ..field_ident_1 } ); let read_value_1 = assert_success!(i_prot.read_bool()); assert!(read_value_1); assert_success!(i_prot.read_field_end()); let read_ident_2 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_2, TFieldIdentifier { name: None, ..field_ident_2 } ); let read_value_2 = assert_success!(i_prot.read_bool()); assert!(!read_value_2); assert_success!(i_prot.read_field_end()); let read_ident_3 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_3, TFieldIdentifier { name: None, ..field_ident_3 } ); let read_value_3 = assert_success!(i_prot.read_bool()); assert!(read_value_3); assert_success!(i_prot.read_field_end()); let read_ident_4 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_4, TFieldIdentifier { name: None, ..field_ident_4 } ); let read_value_4 = assert_success!(i_prot.read_bool()); assert!(!read_value_4); assert_success!(i_prot.read_field_end()); let read_ident_5 = assert_success!(i_prot.read_field_begin()); assert_eq!( read_ident_5, TFieldIdentifier { name: None, field_type: TType::Stop, id: None, } ); assert_success!(i_prot.read_struct_end()); } #[test] #[should_panic] fn must_fail_if_write_field_end_without_writing_bool_value() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1))); o_prot.write_field_end().unwrap(); } #[test] #[should_panic] fn must_fail_if_write_stop_field_without_writing_bool_value() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1))); o_prot.write_field_stop().unwrap(); } #[test] #[should_panic] fn must_fail_if_write_struct_end_without_writing_bool_value() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo"))); assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1))); o_prot.write_struct_end().unwrap(); } #[test] #[should_panic] fn must_fail_if_write_struct_end_without_any_fields() { let (_, mut o_prot) = test_objects(); o_prot.write_struct_end().unwrap(); } #[test] fn must_write_field_end() { assert_no_write(|o| o.write_field_end()); } #[test] fn must_write_small_sized_list_begin() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_list_begin(&TListIdentifier::new(TType::I64, 4))); let expected: [u8; 1] = [0x46 /* size | elem_type */]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_small_sized_list_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TListIdentifier::new(TType::I08, 10); assert_success!(o_prot.write_list_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_list_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_large_sized_list_begin() { let (_, mut o_prot) = test_objects(); let res = o_prot.write_list_begin(&TListIdentifier::new(TType::List, 9999)); assert!(res.is_ok()); let expected: [u8; 3] = [ 0xF9, /* 0xF0 | elem_type */ 0x8F, 0x4E, /* size as varint */ ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_large_sized_list_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TListIdentifier::new(TType::Set, 47381); assert_success!(o_prot.write_list_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_list_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_list_end() { assert_no_write(|o| o.write_list_end()); } #[test] fn must_write_small_sized_set_begin() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Struct, 2))); let expected: [u8; 1] = [0x2C /* size | elem_type */]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_small_sized_set_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TSetIdentifier::new(TType::I16, 7); assert_success!(o_prot.write_set_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_set_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_large_sized_set_begin() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Double, 23891))); let expected: [u8; 4] = [ 0xF7, /* 0xF0 | elem_type */ 0xD3, 0xBA, 0x01, /* size as varint */ ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_large_sized_set_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TSetIdentifier::new(TType::Map, 3_928_429); assert_success!(o_prot.write_set_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_set_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_set_end() { assert_no_write(|o| o.write_set_end()); } #[test] fn must_write_zero_sized_map_begin() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::String, TType::I32, 0))); let expected: [u8; 1] = [0x00]; // since size is zero we don't write anything assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_read_zero_sized_map_begin() { let (mut i_prot, mut o_prot) = test_objects(); assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Double, TType::I32, 0))); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_map_begin()); assert_eq!( &res, &TMapIdentifier { key_type: None, value_type: None, size: 0, } ); } #[test] fn must_write_map_begin() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_map_begin(&TMapIdentifier::new( TType::Double, TType::String, 238 ))); let expected: [u8; 3] = [ 0xEE, 0x01, /* size as varint */ 0x78, /* key type | val type */ ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_map_begin() { let (mut i_prot, mut o_prot) = test_objects(); let ident = TMapIdentifier::new(TType::Map, TType::List, 1_928_349); assert_success!(o_prot.write_map_begin(&ident)); copy_write_buffer_to_read_buffer!(o_prot); let res = assert_success!(i_prot.read_map_begin()); assert_eq!(&res, &ident); } #[test] fn must_write_map_end() { assert_no_write(|o| o.write_map_end()); } #[test] fn must_write_map_with_bool_key_and_value() { let (_, mut o_prot) = test_objects(); assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Bool, TType::Bool, 1))); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_map_end()); let expected: [u8; 4] = [ 0x01, /* size as varint */ 0x11, /* key type | val type */ 0x01, /* key: true */ 0x02, /* val: false */ ]; assert_eq_written_bytes!(o_prot, expected); } #[test] fn must_round_trip_map_with_bool_value() { let (mut i_prot, mut o_prot) = test_objects(); let map_ident = TMapIdentifier::new(TType::Bool, TType::Bool, 2); assert_success!(o_prot.write_map_begin(&map_ident)); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_bool(false)); assert_success!(o_prot.write_bool(true)); assert_success!(o_prot.write_map_end()); copy_write_buffer_to_read_buffer!(o_prot); // map header let rcvd_ident = assert_success!(i_prot.read_map_begin()); assert_eq!(&rcvd_ident, &map_ident); // key 1 let b = assert_success!(i_prot.read_bool()); assert!(b); // val 1 let b = assert_success!(i_prot.read_bool()); assert!(!b); // key 2 let b = assert_success!(i_prot.read_bool()); assert!(!b); // val 2 let b = assert_success!(i_prot.read_bool()); assert!(b); // map end assert_success!(i_prot.read_map_end()); } #[test] fn must_read_map_end() { let (mut i_prot, _) = test_objects(); assert!(i_prot.read_map_end().is_ok()); // will blow up if we try to read from empty buffer } fn test_objects() -> ( TCompactInputProtocol<ReadHalf<TBufferChannel>>, TCompactOutputProtocol<WriteHalf<TBufferChannel>>, ) { let mem = TBufferChannel::with_capacity(80, 80); let (r_mem, w_mem) = mem.split().unwrap(); let i_prot = TCompactInputProtocol::new(r_mem); let o_prot = TCompactOutputProtocol::new(w_mem); (i_prot, o_prot) } #[test] fn must_read_write_double() { let (mut i_prot, mut o_prot) = test_objects(); #[allow(clippy::approx_constant)] let double = 3.141_592_653_589_793; o_prot.write_double(double).unwrap(); copy_write_buffer_to_read_buffer!(o_prot); let read_double = i_prot.read_double().unwrap(); assert!((read_double - double).abs() < f64::EPSILON); } #[test] fn must_encode_double_as_other_langs() { let (_, mut o_prot) = test_objects(); let expected = [24, 45, 68, 84, 251, 33, 9, 64]; #[allow(clippy::approx_constant)] let double = 3.141_592_653_589_793; o_prot.write_double(double).unwrap(); assert_eq_written_bytes!(o_prot, expected); } fn assert_no_write<F>(mut write_fn: F) where F: FnMut(&mut TCompactOutputProtocol<WriteHalf<TBufferChannel>>) -> crate::Result<()>, { let (_, mut o_prot) = test_objects(); assert!(write_fn(&mut o_prot).is_ok()); assert_eq!(o_prot.transport.write_bytes().len(), 0); } #[test] fn must_read_boolean_list() { let (mut i_prot, _) = test_objects(); let source_bytes: [u8; 3] = [0x21, 2, 1]; i_prot.transport.set_readable_bytes(&source_bytes); let (ttype, element_count) = assert_success!(i_prot.read_list_set_begin()); assert_eq!(ttype, TType::Bool); assert_eq!(element_count, 2); assert_eq!(i_prot.read_bool().unwrap(), false); assert_eq!(i_prot.read_bool().unwrap(), true); assert_success!(i_prot.read_list_end()); } #[test] fn must_read_boolean_list_alternative_encoding() { let (mut i_prot, _) = test_objects(); let source_bytes: [u8; 3] = [0x22, 0, 1]; i_prot.transport.set_readable_bytes(&source_bytes); let (ttype, element_count) = assert_success!(i_prot.read_list_set_begin()); assert_eq!(ttype, TType::Bool); assert_eq!(element_count, 2); assert_eq!(i_prot.read_bool().unwrap(), false); assert_eq!(i_prot.read_bool().unwrap(), true); assert_success!(i_prot.read_list_end()); } }