| #ifndef GOOGLE_PROTOBUF_CONFORMANCE_BINARY_WIREFORMAT_H__ | |
| #define GOOGLE_PROTOBUF_CONFORMANCE_BINARY_WIREFORMAT_H__ | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <initializer_list> | |
| #include <ostream> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #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<!std::is_same_v<void(std::decay_t<Args>...), void(Wire)>, | |
| int> = 0> | |
| explicit Wire(Args&&... args) | |
| : buf_(TypeCheckedCat(std::forward<Args>(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 <typename Sink> | |
| 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 <typename... Args> | |
| struct TypeCheckImpl : std::true_type {}; | |
| template <typename T, typename... Args> | |
| struct TypeCheckImpl<T, Args...> : std::false_type {}; | |
| template <typename... Args> | |
| struct TypeCheckImpl<Wire, Args...> : TypeCheckImpl<Args...> {}; | |
| template <typename... Args> | |
| struct TypeCheckImpl<absl::string_view, Args...> : TypeCheckImpl<Args...> {}; | |
| template <typename... Args> | |
| struct TypeCheckImpl<std::string, Args...> : TypeCheckImpl<Args...> {}; | |
| template <typename... Args> | |
| struct TypeCheckImpl<const char*, Args...> : TypeCheckImpl<Args...> {}; | |
| template <typename... Args> | |
| static std::string TypeCheckedCat(Args&&... args) { | |
| static_assert( | |
| TypeCheckImpl<std::decay_t<Args>...>::value, | |
| "Wire format can only be constructed from Wire elements and strings."); | |
| return absl::StrCat(std::forward<Args>(args)...); | |
| } | |
| std::string buf_; | |
| }; | |
| // Expose our internal wire types for conformance tests. | |
| enum class WireType : uint8_t { | |
| kVarint = ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT, | |
| kFixed32 = ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED32, | |
| kFixed64 = ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64, | |
| kLengthPrefixed = | |
| ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, | |
| kStartGroup = ::google::protobuf::internal::WireFormatLite::WIRETYPE_START_GROUP, | |
| kEndGroup = ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP, | |
| kInvalid = 6 | |
| }; | |
| template <typename Sink> | |
| 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<int>, {1, 2, 3}) | |
| namespace internal { | |
| template <typename Func, typename Container> | |
| 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 <typename Func, typename T> | |
| Wire Packed(Func func, std::initializer_list<T> container) { | |
| return internal::PackedImpl(func, container); | |
| } | |
| template <typename Func, typename Container> | |
| Wire Packed(Func func, const Container& container) { | |
| return internal::PackedImpl(func, container); | |
| } | |
| // Overloads to avoid forcing sign/size conversion warnings on call-sites. | |
| template <typename T> | |
| Wire Varint(T x) { | |
| return Varint(static_cast<uint64_t>(x)); | |
| } | |
| template <typename T> | |
| Wire LongVarint(T x, int extra) { | |
| return LongVarint(static_cast<uint64_t>(x), extra); | |
| } | |
| template <typename T> | |
| Wire Fixed32(T x) { | |
| return Fixed32(static_cast<uint32_t>(x)); | |
| } | |
| template <typename T> | |
| Wire Fixed64(T x) { | |
| return Fixed64(static_cast<uint64_t>(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 <typename T> | |
| Wire VarintField(uint32_t fieldnum, T value) { | |
| return Wire(Tag(fieldnum, WireType::kVarint), Varint(value)); | |
| } | |
| template <typename T> | |
| 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 <typename T> | |
| Wire Fixed32Field(uint32_t fieldnum, T value) { | |
| return Wire(Tag(fieldnum, WireType::kFixed32), Fixed32(value)); | |
| } | |
| template <typename T> | |
| 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 <typename Func, typename T> | |
| Wire PackedField(uint32_t fieldnum, Func func, | |
| std::initializer_list<T> container) { | |
| return Wire(Tag(fieldnum, WireType::kLengthPrefixed), | |
| Packed(func, std::move(container))); | |
| } | |
| template <typename Func, typename Container> | |
| Wire PackedField(uint32_t fieldnum, Func func, const Container& container) { | |
| return Wire(Tag(fieldnum, WireType::kLengthPrefixed), | |
| Packed(func, container)); | |
| } | |
| template <typename T> | |
| 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__ |