/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed 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 crate::binary_type::CopyFromBuf;
use crate::binary_type::Discard;
use crate::bufext::BufMutExt;
use crate::errors::ProtocolError;
use crate::framing::Framing;
use crate::framing::FramingDecoded;
use crate::framing::FramingEncoded;
use crate::framing::FramingEncodedFinal;
use crate::thrift_protocol::MessageType;
use crate::thrift_protocol::ProtocolID;
use crate::ttype::TType;
use crate::Result;
/// The maximum recursive depth the skip() function will traverse
pub const DEFAULT_RECURSION_DEPTH: i32 = 64;
/// Helper type alias to get the pre-finalization encoded type of a protocol frame.
pub type ProtocolEncoded
= FramingEncoded<
::Frame>;
/// Helper type alias to get the final encoded type of a protocol frame
pub type ProtocolEncodedFinal
= FramingEncodedFinal<
::Frame>;
/// Helper type alias to get the buffer type for a frame to be decoded.
pub type ProtocolDecoded
= FramingDecoded<
::Frame>;
/// An instance of Protocol glues a Framing implementation to a serializer
/// (ProtocolWriter) and deserializer (ProtocolReader). It constructs, as
/// needed, a serializer to construct a frame with a given protocol, or a
/// deserializer from a frame into a stream of deserialized objects.
pub trait Protocol: 'static {
/// Type of the framing implementation
type Frame: Framing;
/// Compute the size of a frame for a given protocol. This can be exact or too large, but
/// must not be too small.
type Sizer: ProtocolWriter;
/// Serialize into a buffer. The buffer is allocated with the size computed by Sizer, so
/// it must be large enough.
type Serializer: ProtocolWriter::EncBuf as BufMutExt>::Final>;
/// Set up a deserializer from a frame's buffer.
type Deserializer: ProtocolReader;
const PROTOCOL_ID: ProtocolID;
fn serializer(sz: SZ, ser: SER) -> ::Final
where
SZ: FnOnce(&mut Self::Sizer),
SER: FnOnce(&mut Self::Serializer);
fn deserializer(buf: ::DecBuf) -> Self::Deserializer;
fn into_buffer(_: Self::Deserializer) -> ::DecBuf;
}
fn skip_inner(
p: &mut P,
field_type: TType,
max_depth: i32,
) -> Result<()> {
if max_depth <= 0 {
bail_err!(ProtocolError::SkipDepthExceeded)
}
match field_type {
TType::Void => {}
TType::Bool => {
p.read_bool()?;
}
TType::Byte => {
p.read_byte()?;
}
TType::I16 => {
p.read_i16()?;
}
TType::I32 => {
p.read_i32()?;
}
TType::I64 => {
p.read_i64()?;
}
TType::Double => {
p.read_double()?;
}
TType::Float => {
p.read_float()?;
}
TType::Struct => {
p.read_struct_begin(|_| ())?;
loop {
let fields = &[];
let (_, type_id, _) = p.read_field_begin(|_| (), fields)?;
if type_id == TType::Stop {
break;
}
skip_inner(p, type_id, max_depth - 1)?;
p.read_field_end()?;
}
p.read_struct_end()?;
}
TType::Map => {
let (key_type, value_type, len) = p.read_map_begin()?;
if len != Some(0) {
let mut idx = 0;
loop {
let more = p.read_map_key_begin()?;
if !more {
break;
}
skip_inner(p, key_type, max_depth - 1)?;
p.read_map_value_begin()?;
skip_inner(p, value_type, max_depth)?;
p.read_map_value_end()?;
idx += 1;
if should_break(len, more, idx) {
break;
}
}
}
p.read_map_end()?;
}
TType::Set => {
let (elem_type, len) = p.read_set_begin()?;
if len != Some(0) {
let mut idx = 0;
loop {
let more = p.read_set_value_begin()?;
if !more {
break;
}
skip_inner(p, elem_type, max_depth - 1)?;
p.read_set_value_end()?;
idx += 1;
if should_break(len, more, idx) {
break;
}
}
}
p.read_set_end()?;
}
TType::List => {
let (elem_type, len) = p.read_list_begin()?;
if len != Some(0) {
let mut idx = 0;
loop {
let more = p.read_list_value_begin()?;
if !more {
break;
}
skip_inner(p, elem_type, max_depth - 1)?;
p.read_list_value_end()?;
idx += 1;
if should_break(len, more, idx) {
break;
}
}
}
p.read_list_end()?;
}
TType::UTF8 => {
p.read_string()?;
}
TType::UTF16 => {
p.read_string()?;
}
TType::String => {
p.read_binary::()?;
}
TType::Stream => bail_err!(ProtocolError::StreamUnsupported),
TType::Stop => bail_err!(ProtocolError::UnexpectedStopInSkip),
};
Ok(())
}
/// Trait for emitting a frame formatted in a given protocol
pub trait ProtocolWriter {
type Final;
fn write_message_begin(&mut self, name: &str, type_id: MessageType, seqid: u32);
fn write_message_end(&mut self);
fn write_struct_begin(&mut self, name: &str);
fn write_struct_end(&mut self);
fn write_field_begin(&mut self, name: &str, type_id: TType, id: i16);
fn write_field_end(&mut self);
fn write_field_stop(&mut self);
fn write_map_begin(&mut self, key_type: TType, value_type: TType, size: usize);
fn write_map_key_begin(&mut self);
fn write_map_value_begin(&mut self);
fn write_map_end(&mut self);
fn write_list_begin(&mut self, elem_type: TType, size: usize);
fn write_list_value_begin(&mut self);
fn write_list_end(&mut self);
fn write_set_begin(&mut self, elem_type: TType, size: usize);
fn write_set_value_begin(&mut self);
fn write_set_end(&mut self);
fn write_bool(&mut self, value: bool);
fn write_byte(&mut self, value: i8);
fn write_i16(&mut self, value: i16);
fn write_i32(&mut self, value: i32);
fn write_i64(&mut self, value: i64);
fn write_double(&mut self, value: f64);
fn write_float(&mut self, value: f32);
fn write_string(&mut self, value: &str);
fn write_binary(&mut self, value: &[u8]);
fn finish(self) -> Self::Final;
}
/// Trait for decoding a frame in a given protocol
pub trait ProtocolReader {
fn read_message_begin(&mut self, method: F) -> Result<(T, MessageType, u32)>
where
F: FnOnce(&[u8]) -> T;
fn read_message_end(&mut self) -> Result<()>;
fn read_struct_begin(&mut self, strukt: F) -> Result
where
F: FnOnce(&[u8]) -> T;
fn read_struct_end(&mut self) -> Result<()>;
fn read_field_begin(&mut self, field: F, fields: &[Field]) -> Result<(T, TType, i16)>
where
F: FnOnce(&[u8]) -> T;
fn read_field_end(&mut self) -> Result<()>;
fn read_map_begin(&mut self) -> Result<(TType, TType, Option)>;
fn read_map_key_begin(&mut self) -> Result;
fn read_map_value_begin(&mut self) -> Result<()>;
fn read_map_value_end(&mut self) -> Result<()>;
fn read_map_end(&mut self) -> Result<()>;
fn read_list_begin(&mut self) -> Result<(TType, Option)>;
fn read_list_value_begin(&mut self) -> Result;
fn read_list_value_end(&mut self) -> Result<()>;
fn read_list_end(&mut self) -> Result<()>;
fn read_set_begin(&mut self) -> Result<(TType, Option)>;
fn read_set_value_begin(&mut self) -> Result;
fn read_set_value_end(&mut self) -> Result<()>;
fn read_set_end(&mut self) -> Result<()>;
fn read_bool(&mut self) -> Result;
fn read_byte(&mut self) -> Result;
fn read_i16(&mut self) -> Result;
fn read_i32(&mut self) -> Result;
fn read_i64(&mut self) -> Result;
fn read_double(&mut self) -> Result;
fn read_float(&mut self) -> Result;
fn read_string(&mut self) -> Result;
fn read_binary(&mut self) -> Result;
/// Skip over the next data element from the provided input Protocol object
fn skip(&mut self, field_type: TType) -> Result<()> {
skip_inner(self, field_type, DEFAULT_RECURSION_DEPTH)
}
}
pub fn should_break(len: Option, more: bool, idx: usize) -> bool {
match (len, more) {
(Some(real_length), _) => idx >= real_length,
(None, true) => false,
(None, false) => true,
}
}
/// Protocol::serializer wants the same serializer closure passed twice so
/// that it can specialize it for two types: once to compute the size, and a second time to
/// actually serialize the content. This macro helps by taking the factory type and
/// applying the serializer expression twice.
#[macro_export]
macro_rules! serialize {
($protocol:ty, $serializer:expr) => {
<$protocol as $crate::Protocol>::serializer($serializer, $serializer)
};
}
pub struct Field {
pub name: &'static str,
pub ttype: TType,
pub id: i16,
}
impl Field {
pub const fn new(name: &'static str, ttype: TType, id: i16) -> Self {
Field { name, ttype, id }
}
}