#ifndef GOOGLE_PROTOBUF_CONFORMANCE_BINARY_WIREFORMAT_H__ #define GOOGLE_PROTOBUF_CONFORMANCE_BINARY_WIREFORMAT_H__ #include #include #include #include #include #include #include #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "google/protobuf/wire_format_lite.h" // This file contains helpers for building arbitrary proto payloads in binary // wire format. These are used by the conformance tests to construct test // inputs (both valid and invalid) and expected outputs. namespace google { namespace protobuf { namespace conformance { // Create a relatively opaque wrapper around a string that represents a binary // wire format. This allows us to provide custom printing for better // debuggability, and also to provide better type safety than just using // std::string. // Example: // Wire wire(Tag(1, WireType::kVarint), Varint(123)) class Wire { public: template < typename... Args, std::enable_if_t...), void(Wire)>, int> = 0> explicit Wire(Args&&... args) : buf_(TypeCheckedCat(std::forward(args)...)) {} Wire() = default; Wire(const Wire& other) = default; Wire(Wire&& other) = default; Wire& operator=(const Wire& other) = default; Wire& operator=(Wire&& other) = default; bool operator==(const Wire& other) const { return buf_ == other.buf_; } bool operator!=(const Wire& other) const { return !(*this == other); } absl::string_view data() const { return buf_; } std::string str() && { return std::move(buf_); } size_t size() const { return buf_.size(); } template friend void AbslStringify(Sink& sink, const Wire& wire) { sink.Append(wire.data()); } friend void PrintTo(const Wire& wire, std::ostream* os) { *os << absl::CEscape(wire.data()); } private: template struct TypeCheckImpl : std::true_type {}; template struct TypeCheckImpl : std::false_type {}; template struct TypeCheckImpl : TypeCheckImpl {}; template struct TypeCheckImpl : TypeCheckImpl {}; template struct TypeCheckImpl : TypeCheckImpl {}; template struct TypeCheckImpl : TypeCheckImpl {}; template static std::string TypeCheckedCat(Args&&... args) { static_assert( TypeCheckImpl...>::value, "Wire format can only be constructed from Wire elements and strings."); return absl::StrCat(std::forward(args)...); } std::string buf_; }; // Expose our internal wire types for conformance tests. enum class WireType : uint8_t { kVarint = internal::WireFormatLite::WIRETYPE_VARINT, kFixed32 = internal::WireFormatLite::WIRETYPE_FIXED32, kFixed64 = internal::WireFormatLite::WIRETYPE_FIXED64, kLengthPrefixed = internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, kStartGroup = internal::WireFormatLite::WIRETYPE_START_GROUP, kEndGroup = internal::WireFormatLite::WIRETYPE_END_GROUP, kInvalid = 6 }; template void AbslStringify(Sink& sink, WireType wire_type) { switch (wire_type) { case WireType::kVarint: sink.Append("Varint"); break; case WireType::kFixed32: sink.Append("Fixed32"); break; case WireType::kFixed64: sink.Append("Fixed64"); break; case WireType::kLengthPrefixed: sink.Append("LengthPrefixed"); break; case WireType::kStartGroup: sink.Append("StartGroup"); break; case WireType::kEndGroup: sink.Append("EndGroup"); break; case WireType::kInvalid: sink.Append("Invalid"); break; } } /******** Partial data helpers ********/ // The following functions are helpers for building wire format elements, but // don't individually produce a valid wire format. They must be combined into // tag/value pairs using Wire() or one of the *Field() functions below. // Encodes a field tag for a given field number and wire type. Wire Tag(uint32_t fieldnum, WireType wire_type); // Encodes a base-128 variable-width integer. Wire Varint(uint64_t x); // Encodes a varint that is |extra| bytes longer than it needs to be, but // still valid. Varints longer than 10 bytes are not valid, and will assert. Wire LongVarint(uint64_t x, int extra); // Encodes a zig-zag encoded 32-bit signed varint. Wire SInt32(int32_t x); // Encodes a zig-zag encoded 64-bit signed varint. Wire SInt64(int64_t x); // Encodes a fixed-width 32-bit integer. Wire Fixed32(uint32_t x); // Encodes a fixed-width 64-bit integer. Wire Fixed64(uint64_t x); // Encodes a float. Wire Float(float f); // Encodes a double. Wire Double(double d); // Encodes a string with a length prefix. // e.g. LengthPrefixed("foo") for string data // e.g. LengthPrefixed(Wire(...)) for sub-message data Wire LengthPrefixed(absl::string_view data); inline Wire LengthPrefixed(const Wire& data) { return LengthPrefixed(data.data()); } // Encodes packed repeated data. // e.g. Packed(Varint, {1, 2, 3}) namespace internal { template Wire PackedImpl(Func func, const Container& container) { std::string buf = Varint(container.size()).str(); for (auto x : container) { absl::StrAppend(&buf, func(x).str()); } return Wire(buf); } } // namespace internal template Wire Packed(Func func, std::initializer_list container) { return internal::PackedImpl(func, container); } template Wire Packed(Func func, const Container& container) { return internal::PackedImpl(func, container); } // Overloads to avoid forcing sign/size conversion warnings on call-sites. template Wire Varint(T x) { return Varint(static_cast(x)); } template Wire LongVarint(T x, int extra) { return LongVarint(static_cast(x), extra); } template Wire Fixed32(T x) { return Fixed32(static_cast(x)); } template Wire Fixed64(T x) { return Fixed64(static_cast(x)); } /******** Field helpers ********/ // The following functions encode an entire field, including both a tag and // value. That means that the output of all of these functions *is* valid wire // format, although it will typically be composed with additional elements // using Wire(...). These are really just meant as lightweight helpers to make // call-sites a little less verbose. template Wire VarintField(uint32_t fieldnum, T value) { return Wire(Tag(fieldnum, WireType::kVarint), Varint(value)); } template Wire LongVarintField(uint32_t fieldnum, T value, int extra) { return Wire(Tag(fieldnum, WireType::kVarint), LongVarint(value, extra)); } inline Wire SInt32Field(uint32_t fieldnum, int32_t value) { return Wire(Tag(fieldnum, WireType::kVarint), SInt32(value)); } inline Wire SInt64Field(uint32_t fieldnum, int64_t value) { return Wire(Tag(fieldnum, WireType::kVarint), SInt64(value)); } template Wire Fixed32Field(uint32_t fieldnum, T value) { return Wire(Tag(fieldnum, WireType::kFixed32), Fixed32(value)); } template Wire Fixed64Field(uint32_t fieldnum, T value) { return Wire(Tag(fieldnum, WireType::kFixed64), Fixed64(value)); } inline Wire FloatField(uint32_t fieldnum, float value) { return Wire(Tag(fieldnum, WireType::kFixed32), Float(value)); } inline Wire DoubleField(uint32_t fieldnum, double value) { return Wire(Tag(fieldnum, WireType::kFixed64), Double(value)); } template Wire PackedField(uint32_t fieldnum, Func func, std::initializer_list container) { return Wire(Tag(fieldnum, WireType::kLengthPrefixed), Packed(func, std::move(container))); } template Wire PackedField(uint32_t fieldnum, Func func, const Container& container) { return Wire(Tag(fieldnum, WireType::kLengthPrefixed), Packed(func, container)); } template inline Wire LengthPrefixedField(uint32_t fieldnum, T content) { return Wire(Tag(fieldnum, WireType::kLengthPrefixed), LengthPrefixed(content)); } inline Wire DelimitedField(uint32_t fieldnum, const Wire& content) { return Wire(Tag(fieldnum, WireType::kStartGroup), content.data(), Tag(fieldnum, WireType::kEndGroup)); } } // namespace conformance } // namespace protobuf } // namespace google #endif // GOOGLE_PROTOBUF_CONFORMANCE_BINARY_WIREFORMAT_H__