thrift/lib/cpp2/protocol/BinaryProtocol.h (247 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_TBINARYPROTOCOL_H_
#define CPP2_PROTOCOL_TBINARYPROTOCOL_H_ 1
#include <folly/io/Cursor.h>
#include <folly/io/IOBuf.h>
#include <folly/io/IOBufQueue.h>
#include <folly/lang/Bits.h>
#include <folly/portability/GFlags.h>
#include <thrift/lib/cpp/protocol/TProtocol.h>
#include <thrift/lib/cpp2/protocol/Protocol.h>
DECLARE_int32(thrift_cpp2_protocol_reader_string_limit);
DECLARE_int32(thrift_cpp2_protocol_reader_container_limit);
namespace apache {
namespace thrift {
using folly::IOBuf;
using folly::IOBufQueue;
typedef folly::io::RWPrivateCursor RWCursor;
using folly::io::Cursor;
using folly::io::QueueAppender;
class BinaryProtocolReader;
/**
* The default binary protocol for thrift. Writes all data in a very basic
* binary format, essentially just spitting out the raw bytes.
*
*/
class BinaryProtocolWriter {
public:
static const int32_t VERSION_1 = 0x80010000;
using ProtocolReader = BinaryProtocolReader;
explicit BinaryProtocolWriter(
ExternalBufferSharing sharing = COPY_EXTERNAL_BUFFER)
: out_(nullptr, 0), sharing_(sharing) {}
static constexpr ProtocolType protocolType() {
return ProtocolType::T_BINARY_PROTOCOL;
}
static constexpr bool kSortKeys() { return false; }
static constexpr bool kHasIndexSupport() { return true; }
/**
* ...
* The IOBuf itself is managed by the caller.
* It must exist for the life of the BinaryProtocol as well,
* or until the output is reset with setOutput/Input(NULL), or
* set to some other buffer.
*/
inline void setOutput(
IOBufQueue* queue,
size_t maxGrowth = std::numeric_limits<size_t>::max()) {
// Allocate 16KB at a time; leave some room for the IOBuf overhead
constexpr size_t kDesiredGrowth = (1 << 14) - 64;
out_.reset(queue, std::min(maxGrowth, kDesiredGrowth));
}
inline void setOutput(QueueAppender&& output) { out_ = std::move(output); }
inline uint32_t writeMessageBegin(
folly::StringPiece name, MessageType messageType, int32_t seqid);
inline uint32_t writeMessageEnd();
inline uint32_t writeStructBegin(const char* name);
inline uint32_t writeStructEnd();
inline uint32_t writeFieldBegin(
const char* name, TType fieldType, int16_t fieldId);
inline uint32_t writeFieldEnd();
inline uint32_t writeFieldStop();
inline uint32_t writeMapBegin(TType keyType, TType valType, uint32_t size);
inline uint32_t writeMapEnd();
inline uint32_t writeListBegin(TType elemType, uint32_t size);
inline uint32_t writeListEnd();
inline uint32_t writeSetBegin(TType elemType, uint32_t size);
inline uint32_t writeSetEnd();
inline uint32_t writeBool(bool value);
inline uint32_t writeByte(int8_t byte);
inline uint32_t writeI16(int16_t i16);
inline uint32_t writeI32(int32_t i32);
inline uint32_t writeI64(int64_t i64);
inline uint32_t writeDouble(double dub);
inline uint32_t writeFloat(float flt);
inline uint32_t writeString(folly::StringPiece str);
inline uint32_t writeBinary(folly::StringPiece str);
inline uint32_t writeBinary(folly::ByteRange str);
inline uint32_t writeBinary(const std::unique_ptr<folly::IOBuf>& str);
inline uint32_t writeBinary(const folly::IOBuf& str);
inline uint32_t writeRaw(const IOBuf& buf);
/**
* Functions that return the [estimated] serialized size
* Notes:
* * Serialized size estimates for Binary protocol are generally accurate,
* but this is not the case for other protocols, e.g. Compact.
* Don't use these values as more than an estimate.
*
* * ZC versions are the preallocated estimate if any IOBufs are shared (i.e.
* there are IOBuf fields, and their sizes aren't too small to be packed),
* and won't count in the ZC estimate.
*/
inline uint32_t serializedMessageSize(folly::StringPiece name) const;
inline uint32_t serializedFieldSize(
const char* name, TType fieldType, int16_t fieldId) const;
inline uint32_t serializedStructSize(const char* name) const;
inline uint32_t serializedSizeMapBegin(
TType keyType, TType valType, uint32_t size) const;
inline uint32_t serializedSizeMapEnd() const;
inline uint32_t serializedSizeListBegin(TType elemType, uint32_t size) const;
inline uint32_t serializedSizeListEnd() const;
inline uint32_t serializedSizeSetBegin(TType elemType, uint32_t size) const;
inline uint32_t serializedSizeSetEnd() const;
inline uint32_t serializedSizeStop() const;
inline uint32_t serializedSizeBool(bool = false) const;
inline uint32_t serializedSizeByte(int8_t = 0) const;
inline uint32_t serializedSizeI16(int16_t = 0) const;
inline uint32_t serializedSizeI32(int32_t = 0) const;
inline uint32_t serializedSizeI64(int64_t = 0) const;
inline uint32_t serializedSizeDouble(double = 0.0) const;
inline uint32_t serializedSizeFloat(float = 0) const;
inline uint32_t serializedSizeString(folly::StringPiece str) const;
inline uint32_t serializedSizeBinary(folly::StringPiece str) const;
inline uint32_t serializedSizeBinary(folly::ByteRange) const;
inline uint32_t serializedSizeBinary(
std::unique_ptr<folly::IOBuf> const& v) const;
inline uint32_t serializedSizeBinary(folly::IOBuf const& v) const;
inline uint32_t serializedSizeZCBinary(folly::StringPiece str) const;
inline uint32_t serializedSizeZCBinary(folly::ByteRange v) const;
inline uint32_t serializedSizeZCBinary(
std::unique_ptr<folly::IOBuf> const&) const;
inline uint32_t serializedSizeZCBinary(folly::IOBuf const& /*v*/) const;
inline void rewriteDouble(double dub, int64_t offset);
// Get last n bytes we just wrote
inline folly::io::Cursor tail(size_t n);
private:
template <bool kWriteSize>
FOLLY_ERASE uint32_t writeBinaryImpl(const folly::IOBuf& str);
/**
* Cursor to write the data out to.
*/
QueueAppender out_;
ExternalBufferSharing sharing_;
};
class BinaryProtocolReader {
public:
static const int32_t VERSION_MASK = 0xffff0000;
static const int32_t VERSION_1 = 0x80010000;
using ProtocolWriter = BinaryProtocolWriter;
explicit BinaryProtocolReader(
ExternalBufferSharing sharing = COPY_EXTERNAL_BUFFER)
: string_limit_(FLAGS_thrift_cpp2_protocol_reader_string_limit),
container_limit_(FLAGS_thrift_cpp2_protocol_reader_container_limit),
sharing_(sharing),
strict_read_(true),
in_(nullptr) {}
BinaryProtocolReader(
int32_t string_limit,
int32_t container_limit,
ExternalBufferSharing sharing = COPY_EXTERNAL_BUFFER)
: string_limit_(string_limit),
container_limit_(container_limit),
sharing_(sharing),
strict_read_(true),
in_(nullptr) {}
static constexpr ProtocolType protocolType() {
return ProtocolType::T_BINARY_PROTOCOL;
}
static constexpr bool kUsesFieldNames() { return false; }
static constexpr bool kOmitsContainerSizes() { return false; }
static constexpr bool kOmitsStringSizes() { return false; }
static constexpr bool kHasDeferredRead() { return false; }
void setStringSizeLimit(int32_t string_limit) {
string_limit_ = string_limit;
}
void setContainerSizeLimit(int32_t container_limit) {
container_limit_ = container_limit;
}
void setStrict(bool strict_read = true) { strict_read_ = strict_read; }
/**
* The IOBuf itself is managed by the caller.
* It must exist for the life of the BinaryProtocol as well,
* or until the output is reset with setOutput/Input(NULL), or
* set to some other buffer.
*/
void setInput(const Cursor& cursor) { in_ = cursor; }
void setInput(const IOBuf* buf) { in_.reset(buf); }
/**
* Reading functions
*/
inline void readMessageBegin(
std::string& name, MessageType& messageType, int32_t& seqid);
inline void readMessageEnd();
inline void readStructBegin(std::string& name);
inline void readStructEnd();
inline void readFieldBegin(
std::string& name, TType& fieldType, int16_t& fieldId);
inline void readFieldEnd();
inline void readMapBegin(TType& keyType, TType& valType, uint32_t& size);
inline void readMapEnd();
inline void readListBegin(TType& elemType, uint32_t& size);
inline void readListEnd();
inline void readSetBegin(TType& elemType, uint32_t& size);
inline void readSetEnd();
inline void readBool(bool& value);
inline void readBool(std::vector<bool>::reference value);
inline void readByte(int8_t& byte);
inline void readI16(int16_t& i16);
inline void readI32(int32_t& i32);
inline void readI64(int64_t& i64);
inline void readDouble(double& dub);
inline void readFloat(float& flt);
template <typename StrType>
inline void readString(StrType& str);
template <typename StrType>
inline void readBinary(StrType& str);
inline void readBinary(std::unique_ptr<folly::IOBuf>& str);
inline void readBinary(folly::IOBuf& str);
bool peekMap() { return false; }
bool peekSet() { return false; }
bool peekList() { return false; }
static constexpr std::size_t fixedSizeInContainer(TType type);
void skipBytes(size_t bytes) { in_.skip(bytes); }
void skip(TType type) { apache::thrift::skip(*this, type); }
const Cursor& getCursor() const { return in_; }
size_t getCursorPosition() const { return in_.getCurrentPosition(); }
struct StructReadState;
protected:
template <typename StrType>
inline void readStringBody(StrType& str, int32_t sz);
FOLLY_ALWAYS_INLINE bool advanceToNextField(
int16_t nextFieldId, TType nextFieldType, StructReadState& state);
inline void readFieldBeginWithState(StructReadState& state);
inline void checkStringSize(int32_t size);
inline void checkContainerSize(int32_t size);
[[noreturn]] static void throwBadVersionIdentifier(int32_t sz);
[[noreturn]] static void throwMissingVersionIdentifier(int32_t sz);
int32_t string_limit_;
int32_t container_limit_;
ExternalBufferSharing sharing_;
// Enforce presence of version identifier
bool strict_read_;
/**
* Cursor to manipulate the buffer to read from. Throws an exception if
* there is not enough data to read the whole struct.
*/
Cursor in_;
template <typename T>
friend class ProtocolReaderWithRefill;
friend class BinaryProtocolReaderWithRefill;
private:
inline bool readBoolSafe();
};
struct BinaryProtocolReader::StructReadState {
int16_t fieldId;
apache::thrift::protocol::TType fieldType;
constexpr static bool kAcceptsContext = false;
void readStructBegin(BinaryProtocolReader* /*iprot*/) {}
void readStructEnd(BinaryProtocolReader* /*iprot*/) {}
void readFieldBegin(BinaryProtocolReader* iprot) {
iprot->readFieldBeginWithState(*this);
}
FOLLY_NOINLINE void readFieldBeginNoInline(BinaryProtocolReader* iprot) {
iprot->readFieldBeginWithState(*this);
}
void readFieldEnd(BinaryProtocolReader* /*iprot*/) {}
FOLLY_ALWAYS_INLINE bool advanceToNextField(
BinaryProtocolReader* iprot,
int16_t /*currFieldId*/,
int16_t nextFieldId,
TType nextFieldType) {
return iprot->advanceToNextField(nextFieldId, nextFieldType, *this);
}
/*
* This is used in generated deserialization code only. When deserializing
* fields in "non-advanceToNextField" case, we delegate the type check to
* each protocol since some protocol (such as NimbleProtocol) may not encode
* type information.
*/
FOLLY_ALWAYS_INLINE bool isCompatibleWithType(
BinaryProtocolReader* /*iprot*/, TType expectedFieldType) {
return fieldType == expectedFieldType;
}
void skip(BinaryProtocolReader* iprot) { iprot->skip(fieldType); }
std::string& fieldName() {
throw std::logic_error("BinaryProtocol doesn't support field names");
}
void afterAdvanceFailure(BinaryProtocolReader* /*iprot*/) {}
void beforeSubobject(BinaryProtocolReader* /* iprot */) {}
void afterSubobject(BinaryProtocolReader* /* iprot */) {}
bool atStop() { return fieldType == apache::thrift::protocol::T_STOP; }
template <typename StructTraits>
void fillFieldTraitsFromName() {
throw std::logic_error("BinaryProtocol doesn't support field names");
}
};
namespace detail {
template <class Protocol>
struct ProtocolReaderStructReadState;
template <>
struct ProtocolReaderStructReadState<BinaryProtocolReader>
: BinaryProtocolReader::StructReadState {};
} // namespace detail
} // namespace thrift
} // namespace apache
#include <thrift/lib/cpp2/protocol/BinaryProtocol-inl.h>
#endif // #ifndef CPP2_PROTOCOL_TBINARYPROTOCOL_H_