|
| 1 | +#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP |
| 2 | +#define PROTOZERO_BASIC_PBF_BUILDER_HPP |
| 3 | + |
| 4 | +/***************************************************************************** |
| 5 | +
|
| 6 | +protozero - Minimalistic protocol buffer decoder and encoder in C++. |
| 7 | +
|
| 8 | +This file is from https://github.com/mapbox/protozero where you can find more |
| 9 | +documentation. |
| 10 | +
|
| 11 | +*****************************************************************************/ |
| 12 | + |
| 13 | +/** |
| 14 | + * @file basic_pbf_builder.hpp |
| 15 | + * |
| 16 | + * @brief Contains the basic_pbf_builder template class. |
| 17 | + */ |
| 18 | + |
| 19 | +#include "basic_pbf_writer.hpp" |
| 20 | +#include "types.hpp" |
| 21 | + |
| 22 | +#include <type_traits> |
| 23 | + |
| 24 | +namespace protozero { |
| 25 | + |
| 26 | +/** |
| 27 | + * The basic_pbf_builder is used to write PBF formatted messages into a buffer. |
| 28 | + * It is based on the basic_pbf_writer class and has all the same methods. The |
| 29 | + * difference is that while the pbf_writer class takes an integer tag, |
| 30 | + * this template class takes a tag of the template type T. The idea is that |
| 31 | + * T will be an enumeration value and this helps reduce the possibility of |
| 32 | + * programming errors. |
| 33 | + * |
| 34 | + * Almost all methods in this class can throw an std::bad_alloc exception if |
| 35 | + * the underlying buffer class wants to resize. |
| 36 | + * |
| 37 | + * Read the tutorial to understand how this class is used. In most cases you |
| 38 | + * want to use the pbf_builder class which uses a std::string as buffer type. |
| 39 | + */ |
| 40 | +template <typename TBuffer, typename T> |
| 41 | +class basic_pbf_builder : public basic_pbf_writer<TBuffer> { |
| 42 | + |
| 43 | + static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::type>::value, |
| 44 | + "T must be enum with underlying type protozero::pbf_tag_type"); |
| 45 | + |
| 46 | +public: |
| 47 | + |
| 48 | + /// The type of messages this class will build. |
| 49 | + using enum_type = T; |
| 50 | + |
| 51 | + basic_pbf_builder() = default; |
| 52 | + |
| 53 | + /** |
| 54 | + * Create a builder using the given string as a data store. The object |
| 55 | + * stores a reference to that string and adds all data to it. The string |
| 56 | + * doesn't have to be empty. The pbf_message object will just append data. |
| 57 | + */ |
| 58 | + explicit basic_pbf_builder(TBuffer& data) noexcept : |
| 59 | + basic_pbf_writer<TBuffer>{data} { |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Construct a pbf_builder for a submessage from the pbf_message or |
| 64 | + * pbf_writer of the parent message. |
| 65 | + * |
| 66 | + * @param parent_writer The parent pbf_message or pbf_writer |
| 67 | + * @param tag Tag of the field that will be written |
| 68 | + */ |
| 69 | + template <typename P> |
| 70 | + basic_pbf_builder(basic_pbf_writer<TBuffer>& parent_writer, P tag) noexcept : |
| 71 | + basic_pbf_writer<TBuffer>{parent_writer, pbf_tag_type(tag)} { |
| 72 | + } |
| 73 | + |
| 74 | +/// @cond INTERNAL |
| 75 | +#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ |
| 76 | + void add_##name(T tag, type value) { \ |
| 77 | + basic_pbf_writer<TBuffer>::add_##name(pbf_tag_type(tag), value); \ |
| 78 | + } |
| 79 | + |
| 80 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool) |
| 81 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t) |
| 82 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t) |
| 83 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t) |
| 84 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t) |
| 85 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t) |
| 86 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t) |
| 87 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t) |
| 88 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t) |
| 89 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t) |
| 90 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t) |
| 91 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t) |
| 92 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) |
| 93 | + PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) |
| 94 | + |
| 95 | +#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR |
| 96 | +/// @endcond |
| 97 | + |
| 98 | + /** |
| 99 | + * Add "bytes" field to data. |
| 100 | + * |
| 101 | + * @param tag Tag of the field |
| 102 | + * @param value Pointer to value to be written |
| 103 | + * @param size Number of bytes to be written |
| 104 | + */ |
| 105 | + void add_bytes(T tag, const char* value, std::size_t size) { |
| 106 | + basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value, size); |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * Add "bytes" field to data. |
| 111 | + * |
| 112 | + * @param tag Tag of the field |
| 113 | + * @param value Value to be written |
| 114 | + */ |
| 115 | + void add_bytes(T tag, const data_view& value) { |
| 116 | + basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value); |
| 117 | + } |
| 118 | + |
| 119 | + /** |
| 120 | + * Add "bytes" field to data. |
| 121 | + * |
| 122 | + * @param tag Tag of the field |
| 123 | + * @param value Value to be written |
| 124 | + */ |
| 125 | + void add_bytes(T tag, const std::string& value) { |
| 126 | + basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value); |
| 127 | + } |
| 128 | + |
| 129 | + /** |
| 130 | + * Add "bytes" field to data. Bytes from the value are written until |
| 131 | + * a null byte is encountered. The null byte is not added. |
| 132 | + * |
| 133 | + * @param tag Tag of the field |
| 134 | + * @param value Pointer to zero-delimited value to be written |
| 135 | + */ |
| 136 | + void add_bytes(T tag, const char* value) { |
| 137 | + basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value); |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * Add "bytes" field to data using vectored input. All the data in the |
| 142 | + * 2nd and further arguments is "concatenated" with only a single copy |
| 143 | + * into the final buffer. |
| 144 | + * |
| 145 | + * This will work with objects of any type supporting the data() and |
| 146 | + * size() methods like std::string or protozero::data_view. |
| 147 | + * |
| 148 | + * Example: |
| 149 | + * @code |
| 150 | + * std::string data1 = "abc"; |
| 151 | + * std::string data2 = "xyz"; |
| 152 | + * builder.add_bytes_vectored(1, data1, data2); |
| 153 | + * @endcode |
| 154 | + * |
| 155 | + * @tparam Ts List of types supporting data() and size() methods. |
| 156 | + * @param tag Tag of the field |
| 157 | + * @param values List of objects of types Ts with data to be appended. |
| 158 | + */ |
| 159 | + template <typename... Ts> |
| 160 | + void add_bytes_vectored(T tag, Ts&&... values) { |
| 161 | + basic_pbf_writer<TBuffer>::add_bytes_vectored(pbf_tag_type(tag), std::forward<Ts>(values)...); |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * Add "string" field to data. |
| 166 | + * |
| 167 | + * @param tag Tag of the field |
| 168 | + * @param value Pointer to value to be written |
| 169 | + * @param size Number of bytes to be written |
| 170 | + */ |
| 171 | + void add_string(T tag, const char* value, std::size_t size) { |
| 172 | + basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value, size); |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * Add "string" field to data. |
| 177 | + * |
| 178 | + * @param tag Tag of the field |
| 179 | + * @param value Value to be written |
| 180 | + */ |
| 181 | + void add_string(T tag, const data_view& value) { |
| 182 | + basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value); |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * Add "string" field to data. |
| 187 | + * |
| 188 | + * @param tag Tag of the field |
| 189 | + * @param value Value to be written |
| 190 | + */ |
| 191 | + void add_string(T tag, const std::string& value) { |
| 192 | + basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value); |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * Add "string" field to data. Bytes from the value are written until |
| 197 | + * a null byte is encountered. The null byte is not added. |
| 198 | + * |
| 199 | + * @param tag Tag of the field |
| 200 | + * @param value Pointer to value to be written |
| 201 | + */ |
| 202 | + void add_string(T tag, const char* value) { |
| 203 | + basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value); |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Add "message" field to data. |
| 208 | + * |
| 209 | + * @param tag Tag of the field |
| 210 | + * @param value Pointer to message to be written |
| 211 | + * @param size Length of the message |
| 212 | + */ |
| 213 | + void add_message(T tag, const char* value, std::size_t size) { |
| 214 | + basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value, size); |
| 215 | + } |
| 216 | + |
| 217 | + /** |
| 218 | + * Add "message" field to data. |
| 219 | + * |
| 220 | + * @param tag Tag of the field |
| 221 | + * @param value Value to be written. The value must be a complete message. |
| 222 | + */ |
| 223 | + void add_message(T tag, const data_view& value) { |
| 224 | + basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value); |
| 225 | + } |
| 226 | + |
| 227 | + /** |
| 228 | + * Add "message" field to data. |
| 229 | + * |
| 230 | + * @param tag Tag of the field |
| 231 | + * @param value Value to be written. The value must be a complete message. |
| 232 | + */ |
| 233 | + void add_message(T tag, const std::string& value) { |
| 234 | + basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value); |
| 235 | + } |
| 236 | + |
| 237 | +/// @cond INTERNAL |
| 238 | +#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ |
| 239 | + template <typename InputIterator> \ |
| 240 | + void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ |
| 241 | + basic_pbf_writer<TBuffer>::add_packed_##name(pbf_tag_type(tag), first, last); \ |
| 242 | + } |
| 243 | + |
| 244 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(bool) |
| 245 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(enum) |
| 246 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(int32) |
| 247 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32) |
| 248 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32) |
| 249 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(int64) |
| 250 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64) |
| 251 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64) |
| 252 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32) |
| 253 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32) |
| 254 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64) |
| 255 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64) |
| 256 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(float) |
| 257 | + PROTOZERO_WRITER_WRAP_ADD_PACKED(double) |
| 258 | + |
| 259 | +#undef PROTOZERO_WRITER_WRAP_ADD_PACKED |
| 260 | +/// @endcond |
| 261 | + |
| 262 | +}; // class basic_pbf_builder |
| 263 | + |
| 264 | +} // end namespace protozero |
| 265 | + |
| 266 | +#endif // PROTOZERO_BASIC_PBF_BUILDER_HPP |
0 commit comments