1 #ifndef PROTOZERO_PBF_WRITER_HPP
2 #define PROTOZERO_PBF_WRITER_HPP
24 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
31 #include <initializer_list>
41 template <
typename T>
class packed_field_varint;
42 template <
typename T>
class packed_field_svarint;
43 template <
typename T>
class packed_field_fixed;
58 std::string* m_data =
nullptr;
68 std::size_t m_rollback_pos = 0;
72 std::size_t m_pos = 0;
74 void add_varint(uint64_t value) {
75 protozero_assert(m_pos == 0 &&
"you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
76 protozero_assert(m_data);
81 protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) &&
"tag out of range");
82 const uint32_t b = (tag << 3U) | uint32_t(type);
86 void add_tagged_varint(
pbf_tag_type tag, uint64_t value) {
87 add_field(tag, pbf_wire_type::varint);
92 void add_fixed(T value) {
93 protozero_assert(m_pos == 0 &&
"you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
94 protozero_assert(m_data);
95 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
98 m_data->append(reinterpret_cast<const char*>(&value),
sizeof(T));
101 template <
typename T,
typename It>
102 void add_packed_fixed(
pbf_tag_type tag, It first, It last, std::input_iterator_tag ) {
109 while (first != last) {
110 sw.add_fixed<T>(*first++);
114 template <
typename T,
typename It>
115 void add_packed_fixed(
pbf_tag_type tag, It first, It last, std::forward_iterator_tag ) {
120 const auto length = std::distance(first, last);
122 reserve(
sizeof(T) * std::size_t(length));
124 while (first != last) {
125 add_fixed<T>(*first++);
129 template <
typename It>
130 void add_packed_varint(
pbf_tag_type tag, It first, It last) {
137 while (first != last) {
138 sw.add_varint(uint64_t(*first++));
142 template <
typename It>
143 void add_packed_svarint(
pbf_tag_type tag, It first, It last) {
150 while (first != last) {
158 enum constant_reserve_bytes :
int {
165 enum constant_size_is_known : std::size_t {
166 size_is_known = std::numeric_limits<std::size_t>::max()
169 void open_submessage(
pbf_tag_type tag, std::size_t size) {
170 protozero_assert(m_pos == 0);
171 protozero_assert(m_data);
173 m_rollback_pos = m_data->size();
174 add_field(tag, pbf_wire_type::length_delimited);
175 m_data->append(std::size_t(reserve_bytes),
'\0');
177 m_rollback_pos = size_is_known;
181 m_pos = m_data->size();
184 void rollback_submessage() {
185 protozero_assert(m_pos != 0);
186 protozero_assert(m_rollback_pos != size_is_known);
187 protozero_assert(m_data);
188 m_data->resize(m_rollback_pos);
192 void commit_submessage() {
193 protozero_assert(m_pos != 0);
194 protozero_assert(m_rollback_pos != size_is_known);
195 protozero_assert(m_data);
198 protozero_assert(m_data->size() >= m_pos - reserve_bytes);
199 const auto n =
write_varint(m_data->begin() + int64_t(m_pos) - reserve_bytes, length);
201 m_data->erase(m_data->begin() + int64_t(m_pos) - reserve_bytes + n, m_data->begin() + int64_t(m_pos));
205 void close_submessage() {
206 protozero_assert(m_data);
207 if (m_pos == 0 || m_rollback_pos == size_is_known) {
210 if (m_data->size() - m_pos == 0) {
211 rollback_submessage();
218 add_field(tag, pbf_wire_type::length_delimited);
229 explicit pbf_writer(std::string& data) noexcept :
250 m_data{parent_writer.m_data},
251 m_parent_writer{&parent_writer} {
252 m_parent_writer->open_submessage(tag, size);
266 m_data{other.m_data},
267 m_parent_writer{other.m_parent_writer},
268 m_rollback_pos{other.m_rollback_pos},
270 other.m_data =
nullptr;
271 other.m_parent_writer =
nullptr;
272 other.m_rollback_pos = 0;
281 m_data = other.m_data;
282 m_parent_writer = other.m_parent_writer;
283 m_rollback_pos = other.m_rollback_pos;
285 other.m_data =
nullptr;
286 other.m_parent_writer =
nullptr;
287 other.m_rollback_pos = 0;
294 if (m_parent_writer !=
nullptr) {
295 m_parent_writer->close_submessage();
310 bool valid() const noexcept {
311 return m_data !=
nullptr;
321 swap(m_data, other.m_data);
322 swap(m_parent_writer, other.m_parent_writer);
323 swap(m_rollback_pos, other.m_rollback_pos);
324 swap(m_pos, other.m_pos);
335 void reserve(std::size_t size) {
336 protozero_assert(m_data);
337 m_data->reserve(m_data->size() + size);
349 protozero_assert(m_parent_writer &&
"you can't call commit() on a pbf_writer without a parent");
350 protozero_assert(m_pos == 0 &&
"you can't call commit() on a pbf_writer that has an open nested submessage");
351 m_parent_writer->close_submessage();
352 m_parent_writer =
nullptr;
365 protozero_assert(m_parent_writer &&
"you can't call rollback() on a pbf_writer without a parent");
366 protozero_assert(m_pos == 0 &&
"you can't call rollback() on a pbf_writer that has an open nested submessage");
367 m_parent_writer->rollback_submessage();
368 m_parent_writer =
nullptr;
384 add_field(tag, pbf_wire_type::varint);
385 protozero_assert(m_pos == 0 &&
"you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
386 protozero_assert(m_data);
387 m_data->append(1,
char(value));
397 add_tagged_varint(tag, uint64_t(value));
407 add_tagged_varint(tag, uint64_t(value));
427 add_tagged_varint(tag, value);
437 add_tagged_varint(tag, uint64_t(value));
457 add_tagged_varint(tag, value);
467 add_field(tag, pbf_wire_type::fixed32);
468 add_fixed<uint32_t>(value);
478 add_field(tag, pbf_wire_type::fixed32);
479 add_fixed<int32_t>(value);
489 add_field(tag, pbf_wire_type::fixed64);
490 add_fixed<uint64_t>(value);
500 add_field(tag, pbf_wire_type::fixed64);
501 add_fixed<int64_t>(value);
511 add_field(tag, pbf_wire_type::fixed32);
512 add_fixed<float>(value);
522 add_field(tag, pbf_wire_type::fixed64);
523 add_fixed<double>(value);
534 protozero_assert(m_pos == 0 &&
"you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
535 protozero_assert(m_data);
536 protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
538 m_data->append(value, size);
548 add_bytes(tag, value.data(), value.size());
558 add_bytes(tag, value.data(), value.size());
569 add_bytes(tag, value, std::strlen(value));
591 template <
typename... Ts>
593 protozero_assert(m_pos == 0 &&
"you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
594 protozero_assert(m_data);
596 (void)std::initializer_list<size_t>{sum_size += values.size()...};
597 protozero_assert(sum_size <= std::numeric_limits<pbf_length_type>::max());
599 m_data->reserve(m_data->size() + sum_size);
600 (void)std::initializer_list<int>{(m_data->append(values.data(), values.size()), 0)...};
621 add_bytes(tag, value.data(), value.size());
631 add_bytes(tag, value.data(), value.size());
642 add_bytes(tag, value, std::strlen(value));
663 add_bytes(tag, value.data(), value.size());
673 add_bytes(tag, value.data(), value.size());
692 template <
typename InputIterator>
694 add_packed_varint(tag, first, last);
706 template <
typename InputIterator>
708 add_packed_varint(tag, first, last);
720 template <
typename InputIterator>
722 add_packed_varint(tag, first, last);
734 template <
typename InputIterator>
736 add_packed_svarint(tag, first, last);
748 template <
typename InputIterator>
750 add_packed_varint(tag, first, last);
762 template <
typename InputIterator>
764 add_packed_varint(tag, first, last);
776 template <
typename InputIterator>
778 add_packed_svarint(tag, first, last);
790 template <
typename InputIterator>
792 add_packed_varint(tag, first, last);
812 template <
typename ValueType,
typename InputIterator>
813 void add_packed_fixed(
pbf_tag_type tag, InputIterator first, InputIterator last) {
814 static_assert(std::is_same<ValueType, uint32_t>::value ||
815 std::is_same<ValueType, int32_t>::value ||
816 std::is_same<ValueType, int64_t>::value ||
817 std::is_same<ValueType, uint64_t>::value ||
818 std::is_same<ValueType, double>::value ||
819 std::is_same<ValueType, float>::value,
"Only some types are allowed");
820 add_packed_fixed<ValueType, InputIterator>(tag, first, last,
821 typename std::iterator_traits<InputIterator>::iterator_category{});
833 template <
typename InputIterator>
835 add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
836 typename std::iterator_traits<InputIterator>::iterator_category{});
848 template <
typename InputIterator>
850 add_packed_fixed<int32_t, InputIterator>(tag, first, last,
851 typename std::iterator_traits<InputIterator>::iterator_category{});
863 template <
typename InputIterator>
865 add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
866 typename std::iterator_traits<InputIterator>::iterator_category{});
878 template <
typename InputIterator>
880 add_packed_fixed<int64_t, InputIterator>(tag, first, last,
881 typename std::iterator_traits<InputIterator>::iterator_category{});
893 template <
typename InputIterator>
895 add_packed_fixed<float, InputIterator>(tag, first, last,
896 typename std::iterator_traits<InputIterator>::iterator_category{});
908 template <
typename InputIterator>
910 add_packed_fixed<double, InputIterator>(tag, first, last,
911 typename std::iterator_traits<InputIterator>::iterator_category{});
916 template <
typename T>
friend class detail::packed_field_varint;
917 template <
typename T>
friend class detail::packed_field_svarint;
918 template <
typename T>
friend class detail::packed_field_fixed;
928 inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept {
938 pbf_writer m_writer{};
942 packed_field(
const packed_field&) =
delete;
943 packed_field& operator=(
const packed_field&) =
delete;
945 packed_field(packed_field&&) noexcept = default;
946 packed_field& operator=(packed_field&&) noexcept = default;
948 packed_field() = default;
950 packed_field(pbf_writer& parent_writer,
pbf_tag_type tag) :
951 m_writer{parent_writer, tag} {
954 packed_field(pbf_writer& parent_writer,
pbf_tag_type tag, std::size_t size) :
955 m_writer{parent_writer, tag, size} {
958 ~packed_field() noexcept = default;
960 bool valid() const noexcept {
961 return m_writer.valid();
974 template <
typename T>
975 class packed_field_fixed :
public packed_field {
979 packed_field_fixed() :
983 template <
typename P>
984 packed_field_fixed(pbf_writer& parent_writer, P tag) :
985 packed_field{parent_writer, static_cast<pbf_tag_type>(tag)} {
988 template <
typename P>
989 packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) :
990 packed_field{parent_writer, static_cast<pbf_tag_type>(tag), size *
sizeof(T)} {
993 void add_element(T value) {
994 m_writer.add_fixed<T>(value);
999 template <
typename T>
1000 class packed_field_varint :
public packed_field {
1004 packed_field_varint() :
1008 template <
typename P>
1009 packed_field_varint(pbf_writer& parent_writer, P tag) :
1010 packed_field{parent_writer, static_cast<pbf_tag_type>(tag)} {
1013 void add_element(T value) {
1014 m_writer.add_varint(uint64_t(value));
1019 template <
typename T>
1020 class packed_field_svarint :
public packed_field {
1024 packed_field_svarint() :
1028 template <
typename P>
1029 packed_field_svarint(pbf_writer& parent_writer, P tag) :
1030 packed_field{parent_writer, static_cast<pbf_tag_type>(tag)} {
1033 void add_element(T value) {
1085 #endif // PROTOZERO_PBF_WRITER_HPP