/* * 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 #include #include #include #include #include #include FOLLY_GFLAGS_DECLARE_int32(thrift_cpp2_protocol_reader_string_limit); FOLLY_GFLAGS_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 detail::ProtocolBase { 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. */ void setOutput( IOBufQueue* queue, size_t maxGrowth = std::numeric_limits::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)); } void setOutput(QueueAppender&& output) { out_ = std::move(output); } uint32_t writeMessageBegin( folly::StringPiece name, MessageType messageType, int32_t seqid); uint32_t writeMessageEnd(); uint32_t writeStructBegin(const char* name); uint32_t writeStructEnd(); uint32_t writeFieldBegin(const char* name, TType fieldType, int16_t fieldId); uint32_t writeFieldEnd(); uint32_t writeFieldStop(); uint32_t writeMapBegin(TType keyType, TType valType, uint32_t size); uint32_t writeMapEnd(); uint32_t writeListBegin(TType elemType, uint32_t size); uint32_t writeListEnd(); uint32_t writeSetBegin(TType elemType, uint32_t size); uint32_t writeSetEnd(); uint32_t writeBool(bool value); uint32_t writeByte(int8_t byte); uint32_t writeI16(int16_t i16); uint32_t writeI32(int32_t i32); uint32_t writeI64(int64_t i64); uint32_t writeDouble(double dub); uint32_t writeFloat(float flt); uint32_t writeString(folly::StringPiece str); uint32_t writeBinary(folly::StringPiece str); uint32_t writeBinary(folly::ByteRange str); uint32_t writeBinary(const std::unique_ptr& str); uint32_t writeBinary(const folly::IOBuf& str); 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. */ uint32_t serializedMessageSize(folly::StringPiece name) const; uint32_t serializedFieldSize( const char* name, TType fieldType, int16_t fieldId) const; uint32_t serializedStructSize(const char* name) const; uint32_t serializedSizeMapBegin( TType keyType, TType valType, uint32_t size) const; uint32_t serializedSizeMapEnd() const; uint32_t serializedSizeListBegin(TType elemType, uint32_t size) const; uint32_t serializedSizeListEnd() const; uint32_t serializedSizeSetBegin(TType elemType, uint32_t size) const; uint32_t serializedSizeSetEnd() const; uint32_t serializedSizeStop() const; uint32_t serializedSizeBool(bool = false) const; uint32_t serializedSizeByte(int8_t = 0) const; uint32_t serializedSizeI16(int16_t = 0) const; uint32_t serializedSizeI32(int32_t = 0) const; uint32_t serializedSizeI64(int64_t = 0) const; uint32_t serializedSizeDouble(double = 0.0) const; uint32_t serializedSizeFloat(float = 0) const; uint32_t serializedSizeString(folly::StringPiece str) const; uint32_t serializedSizeBinary(folly::StringPiece str) const; uint32_t serializedSizeBinary(folly::ByteRange) const; uint32_t serializedSizeBinary(const std::unique_ptr& v) const; uint32_t serializedSizeBinary(const folly::IOBuf& v) const; uint32_t serializedSizeZCBinary(folly::StringPiece str) const; uint32_t serializedSizeZCBinary(folly::ByteRange v) const; uint32_t serializedSizeZCBinary(const std::unique_ptr&) const; uint32_t serializedSizeZCBinary(const folly::IOBuf& /*v*/) const; void rewriteDouble(double dub, int64_t offset); // Get last n bytes we just wrote folly::io::Cursor tail(size_t n); private: static void checkBinarySize(uint64_t size); template FOLLY_ERASE uint32_t writeBinaryImpl(const folly::IOBuf& str); /** * Cursor to write the data out to. */ QueueAppender out_; ExternalBufferSharing sharing_; }; class BinaryProtocolReader : public detail::ProtocolBase { 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 */ void readMessageBegin( std::string& name, MessageType& messageType, int32_t& seqid); void readMessageEnd(); void readStructBegin(std::string& name); void readStructEnd(); void readFieldBegin(std::string& name, TType& fieldType, int16_t& fieldId); void readFieldEnd(); void readMapBegin(TType& keyType, TType& valType, uint32_t& size); void readMapEnd(); void readListBegin(TType& elemType, uint32_t& size); void readListEnd(); void readSetBegin(TType& elemType, uint32_t& size); void readSetEnd(); void readBool(bool& value); void readBool(std::vector::reference value); void readByte(int8_t& byte); void readI16(int16_t& i16); void readI32(int32_t& i32); void readI64(int64_t& i64); void readDouble(double& dub); void readFloat(float& flt); template void readString(StrType& str); template void readBinary(StrType& str); void readBinary(std::unique_ptr& str); 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 void readStringBody(StrType& str, int32_t sz); FOLLY_ALWAYS_INLINE bool advanceToNextField( int16_t nextFieldId, TType nextFieldType, StructReadState& state); void readFieldBeginWithState(StructReadState& state); void checkStringSize(int32_t size); 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 friend class ProtocolReaderWithRefill; friend class BinaryProtocolReaderWithRefill; private: 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 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 void fillFieldTraitsFromName() { throw std::logic_error("BinaryProtocol doesn't support field names"); } }; namespace detail { template struct ProtocolReaderStructReadState; template <> struct ProtocolReaderStructReadState : BinaryProtocolReader::StructReadState {}; } // namespace detail } // namespace thrift } // namespace apache #include #endif // #ifndef CPP2_PROTOCOL_TBINARYPROTOCOL_H_