thrift/lib/cpp2/protocol/Protocol.h (224 lines of code) (raw):
/*
* 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.
*/
#ifndef CPP2_PROTOCOL_PROTOCOL_H_
#define CPP2_PROTOCOL_PROTOCOL_H_ 1
#include <sys/types.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <folly/io/IOBuf.h>
#include <folly/io/IOBufQueue.h>
#include <thrift/lib/cpp/Thrift.h>
#include <thrift/lib/cpp/protocol/TProtocol.h>
#include <thrift/lib/cpp/protocol/TProtocolException.h>
#include <thrift/lib/cpp/protocol/TProtocolTypes.h>
#include <thrift/lib/cpp2/CloneableIOBuf.h>
#include <thrift/lib/cpp2/protocol/ProtocolReaderWireTypeInfo.h>
/**
* Protocol Readers and Writers are ducktyped in cpp2.
* This means they have no base class or virtual methods,
* and you must explicitly choose the correct class to use.
*
* This results in a 2x performance increase over a traditional
* object oriented hierarchy. If you need a base class, you can
* use the VirtualReader / VitualWriter in VirtualProtocol.h
*/
namespace apache {
namespace thrift {
/**
* Certain serialization / deserialization operations allow sharing
* external (user-owned) buffers. This means that the external buffers must
* remain allocated (and unchanged) until the serialization / deserialization
* is complete.
*
* This is often counter-intuitive; for example, deserializing from a string
* wouldn't work with a temporary string if sharing is allowed. So sharing
* external buffers must be requested explicitly.
*
* Note that we always share memory that is under IOBuf's control (that is,
* IOBuf chains for which isManaged() is true). To prevent that, call unshare()
* on the IOBuf chain as appropriate.
*/
enum ExternalBufferSharing {
COPY_EXTERNAL_BUFFER,
SHARE_EXTERNAL_BUFFER,
};
using apache::thrift::protocol::TProtocolException;
using apache::thrift::protocol::TType;
typedef apache::thrift::protocol::PROTOCOL_TYPES ProtocolType;
/*
* Enumerated definition of the message types that the Thrift protocol
* supports.
*/
enum class MessageType {
T_CALL = 1,
T_REPLY = 2,
T_EXCEPTION = 3,
T_ONEWAY = 4,
};
namespace detail {
struct SkipNoopString {
void append(char const*, size_t) {}
void clear() {}
void reserve(size_t) {}
};
// Checks if bool hold a valid value (true or false) and throws exception
// otherwise. Without the check we may produce undeserializable data.
inline bool validate_bool(uint8_t value) {
#if defined(__x86_64__) && defined(__GNUC__) && \
(!defined(__clang_major__) || __clang_major__ >= 9)
// An optimized version that avoid extra load/store.
asm volatile goto(
"cmpb $1, %0\n"
"ja %l1"
: // no outputs.
: "r"(value)
: "cc"
: invalid);
return value;
invalid:
CHECK(false) << "invalid bool value";
#else
// Store in a volatile variable to prevent the compiler from optimizing the
// check away.
volatile uint8_t volatileByte = value;
uint8_t byte = volatileByte;
CHECK(byte == 0 || byte == 1) << "invalid bool value";
return byte != 0;
#endif
}
} // namespace detail
/* forward declaration */
template <class Protocol_, class WireType>
void skip_n(Protocol_& prot, uint32_t n, std::initializer_list<WireType> types);
/**
* Helper template for implementing Protocol::skip().
*
* Templatized to avoid having to make virtual function calls. Protocols with
* their own opinions about skipping implementation can specialize, although
* currently only Nimble does.
*/
template <class Protocol_, class WireType>
void skip(Protocol_& prot, WireType arg_type) {
switch (arg_type) {
case TType::T_BOOL: {
bool boolv;
prot.readBool(boolv);
return;
}
case TType::T_BYTE: {
int8_t bytev;
prot.readByte(bytev);
return;
}
case TType::T_I16: {
int16_t i16;
prot.readI16(i16);
return;
}
case TType::T_I32: {
int32_t i32;
prot.readI32(i32);
return;
}
case TType::T_I64: {
int64_t i64;
prot.readI64(i64);
return;
}
case TType::T_DOUBLE: {
double dub;
prot.readDouble(dub);
return;
}
case TType::T_FLOAT: {
float flt;
prot.readFloat(flt);
return;
}
case TType::T_STRING: {
apache::thrift::detail::SkipNoopString str;
prot.readBinary(str);
return;
}
case TType::T_STRUCT: {
std::string name;
int16_t fid;
TType ftype;
prot.readStructBegin(name);
while (true) {
prot.readFieldBegin(name, ftype, fid);
if (ftype == TType::T_STOP) {
break;
}
apache::thrift::skip(prot, ftype);
prot.readFieldEnd();
}
prot.readStructEnd();
return;
}
case TType::T_MAP: {
TType keyType;
TType valType;
uint32_t size;
prot.readMapBegin(keyType, valType, size);
skip_n(prot, size, {keyType, valType});
prot.readMapEnd();
return;
}
case TType::T_SET: {
TType elemType;
uint32_t size;
prot.readSetBegin(elemType, size);
skip_n(prot, size, {elemType});
prot.readSetEnd();
return;
}
case TType::T_LIST: {
TType elemType;
uint32_t size;
prot.readListBegin(elemType, size);
skip_n(prot, size, {elemType});
prot.readListEnd();
return;
}
default: {
TProtocolException::throwInvalidSkipType(arg_type);
}
}
}
/**
* Check if the remaining part of buffers contain least necessary amount of
* bytes to encode N elements of given type.
*
* Note: this is a lightweight lower bound check, it doesn't necessary mean
* that we would actually succeed at reading N items.
*/
template <class Protocol_>
inline bool canReadNElements(
Protocol_& prot,
uint32_t n,
std::initializer_list<
typename apache::thrift::detail::ProtocolReaderWireTypeInfo<
Protocol_>::WireType> types) {
return prot.getCursor().canAdvance(n * types.size());
}
/*
* Skip n tuples - used for skpping lists, sets, maps.
*
* As with skip(), protocols can specialize.
*/
template <class Protocol_, class WireType>
void skip_n(
Protocol_& prot, uint32_t n, std::initializer_list<WireType> types) {
size_t sum = 0;
bool allFixedSizes = true;
for (auto type : types) {
auto size = prot.fixedSizeInContainer(type);
sum += size;
allFixedSizes = allFixedSizes && size;
}
if (allFixedSizes) {
prot.skipBytes(sum * n);
return;
}
for (uint32_t i = 0; i < n; i++) {
for (auto type : types) {
apache::thrift::skip(prot, type);
}
}
}
template <class StrType>
struct StringTraits {
static StrType fromStringLiteral(const char* str) { return StrType(str); }
static bool isEmpty(const StrType& str) { return str.empty(); }
static bool isEqual(const StrType& lhs, const StrType& rhs) {
return lhs == rhs;
}
static bool isLess(const StrType& lhs, const StrType& rhs) {
return lhs < rhs;
}
};
template <>
struct StringTraits<folly::IOBuf> {
// Use with string literals only!
static folly::IOBuf fromStringLiteral(const char* str) {
return folly::IOBuf::wrapBufferAsValue(str, strlen(str));
}
static bool isEmpty(const folly::IOBuf& str) { return str.empty(); }
static bool isEqual(const folly::IOBuf& lhs, const folly::IOBuf& rhs) {
return folly::IOBufEqualTo{}(lhs, rhs);
}
static bool isLess(const folly::IOBuf& lhs, const folly::IOBuf& rhs) {
return folly::IOBufLess{}(lhs, rhs);
}
};
template <>
struct StringTraits<std::unique_ptr<folly::IOBuf>> {
// Use with string literals only!
static std::unique_ptr<folly::IOBuf> fromStringLiteral(const char* str) {
return (
str[0] != '\0' ? folly::IOBuf::wrapBuffer(str, strlen(str)) : nullptr);
}
static bool isEmpty(const std::unique_ptr<folly::IOBuf>& str) {
return !str || str->empty();
}
static bool isEqual(
const std::unique_ptr<folly::IOBuf>& lhs,
const std::unique_ptr<folly::IOBuf>& rhs) {
return folly::IOBufEqualTo{}(lhs, rhs);
}
static bool isLess(
const std::unique_ptr<folly::IOBuf>& lhs,
const std::unique_ptr<folly::IOBuf>& rhs) {
return folly::IOBufLess{}(lhs, rhs);
}
};
} // namespace thrift
} // namespace apache
#endif