LCOV - code coverage report
Current view: top level - spb/pb - serialize.hpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.3 % 140 139
Test Date: 2026-05-30 19:41:37 Functions: 45.6 % 627 286

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : serialize library for protobuf                              *
       3              : * Description : all protobuf serialization functions                        *
       4              : * Author      : antonin.kriz@gmail.com                                      *
       5              : * ------------------------------------------------------------------------- *
       6              : * This is free software; you can redistribute it and/or modify it under the *
       7              : * terms of the MIT license. A copy of the license can be found in the file  *
       8              : * "LICENSE" at the root of this distribution.                               *
       9              : \***************************************************************************/
      10              : 
      11              : #pragma once
      12              : 
      13              : #include "../concepts.h"
      14              : #include "../utf8.h"
      15              : #include "wire-types.h"
      16              : #include <cctype>
      17              : #include <cstddef>
      18              : #include <cstdint>
      19              : #include <cstring>
      20              : #include <map>
      21              : #include <memory>
      22              : #include <spb/io/io.hpp>
      23              : #include <sys/types.h>
      24              : #include <type_traits>
      25              : 
      26              : namespace spb::pb::detail
      27              : {
      28              : struct ostream
      29              : {
      30              :   private:
      31              :     size_t bytes_written = 0;
      32              :     spb::io::writer on_write;
      33              : 
      34              :   public:
      35              :     /**
      36              :      * @brief Construct a new ostream object
      37              :      *
      38              :      * @param writer if null, stream will skip all writes but will still count number of written
      39              :      * bytes
      40              :      */
      41         2289 :     explicit ostream(spb::io::writer writer = nullptr) noexcept : on_write(writer)
      42              :     {
      43         2289 :     }
      44              : 
      45         6085 :     void write(const void *p_data, size_t size) noexcept
      46              :     {
      47         6085 :         if (on_write)
      48         2140 :             on_write(p_data, size);
      49              : 
      50         6085 :         bytes_written += size;
      51         6085 :     }
      52              : 
      53         2217 :     [[nodiscard]] auto size() const noexcept -> size_t
      54              :     {
      55         2217 :         return bytes_written;
      56              :     }
      57              : 
      58              :     void serialize(const field_attributes &field, const auto &value);
      59              : 
      60              :     template <scalar_encoder encoder> void serialize_as(const field_attributes &field, const auto &value);
      61              : };
      62              : 
      63              : static inline auto serialize_size(const auto &value) -> size_t;
      64              : 
      65              : using namespace std::literals;
      66              : 
      67         4291 : static inline void serialize_varint(ostream &stream, uint64_t value)
      68              : {
      69         4291 :     size_t i = 0;
      70              :     uint8_t buffer[10];
      71              : 
      72              :     do
      73              :     {
      74         4810 :         uint8_t byte = value & 0x7F;
      75         4810 :         value >>= 7;
      76         4810 :         byte |= value > 0 ? 0x80U : 0;
      77         4810 :         buffer[i++] = byte;
      78         4810 :     } while (value > 0);
      79              : 
      80         4291 :     return stream.write(buffer, i);
      81              : }
      82          190 : static inline void serialize_svarint(ostream &stream, int64_t value)
      83              : {
      84          190 :     const auto tmp = uint64_t((value << 1) ^ (value >> 63));
      85          190 :     return serialize_varint(stream, tmp);
      86              : }
      87              : 
      88         2291 : static inline void serialize_tag(ostream &stream, uint32_t field_number, wire_type type)
      89              : {
      90         2291 :     const auto tag = (field_number << 3) | uint32_t(type);
      91         2291 :     serialize_varint(stream, tag);
      92         2291 : }
      93              : 
      94              : static inline void serialize(ostream &stream, const field_attributes &field,
      95              :                              const spb::detail::proto_message auto &value);
      96              : static inline void serialize(ostream &stream, const field_attributes &field,
      97              :                              const spb::detail::proto_field_string auto &value);
      98              : static inline void serialize(ostream &stream, const field_attributes &field,
      99              :                              const spb::detail::proto_field_bytes auto &value);
     100              : static inline void serialize(ostream &stream, const field_attributes &field,
     101              :                              const spb::detail::proto_label_repeated auto &value);
     102              : 
     103              : template <scalar_encoder encoder, spb::detail::proto_label_repeated C>
     104              : static inline void serialize_as(ostream &stream, const field_attributes &field, const C &value);
     105              : 
     106              : template <scalar_encoder encoder, spb::detail::proto_label_repeated_fixed_size C>
     107              : static inline void serialize_as(ostream &stream, const field_attributes &field, const C &value);
     108              : 
     109              : template <scalar_encoder encoder, typename keyT, typename valueT>
     110              : static inline void serialize_as(ostream &stream, const field_attributes &field,
     111              :                                 const std::map<keyT, valueT> &value);
     112              : 
     113              : template <scalar_encoder encoder>
     114         1237 : static inline void serialize_as(ostream &stream, const field_attributes &field,
     115              :                                 spb::detail::proto_field_number auto value)
     116              : {
     117         1237 :     serialize_tag(stream, field.number, wire_type_from_scalar_encoder(encoder));
     118         1237 :     serialize_as<encoder>(stream, value);
     119         1237 : }
     120              : 
     121              : template <scalar_encoder encoder>
     122          145 : static inline void serialize_as(ostream &stream, const spb::detail::proto_enum auto &value)
     123              : {
     124          145 :     serialize_varint(stream, int32_t(value));
     125          145 : }
     126              : 
     127              : template <scalar_encoder encoder>
     128         1862 : static inline void serialize_as(ostream &stream, spb::detail::proto_field_int_or_float auto value)
     129              : {
     130              :     using T = std::remove_cvref_t<decltype(value)>;
     131              : 
     132         1862 :     const auto type = scalar_encoder_type1(encoder);
     133              : 
     134              :     if constexpr (type == scalar_encoder::varint)
     135              :     {
     136              :         static_assert(std::is_integral_v<T>);
     137              : 
     138              :         if constexpr (std::is_same_v<bool, T>)
     139              :         {
     140           80 :             const uint8_t tmp = value ? 1 : 0;
     141           80 :             return stream.write(&tmp, 1);
     142              :         }
     143              :         else if constexpr (std::is_signed_v<T>)
     144              :         {
     145              :             //- GPB is serializing all negative ints always as int64_t
     146          497 :             const auto u_value = uint64_t(int64_t(value));
     147          497 :             return serialize_varint(stream, u_value);
     148              :         }
     149              :         else
     150              :         {
     151          110 :             return serialize_varint(stream, value);
     152              :         }
     153              :     }
     154              :     else if constexpr (type == scalar_encoder::svarint)
     155              :     {
     156              :         static_assert(std::is_signed_v<T> && std::is_integral_v<T>);
     157              : 
     158          190 :         return serialize_svarint(stream, value);
     159              :     }
     160              :     else if constexpr (type == scalar_encoder::i32)
     161              :     {
     162              :         if constexpr (sizeof(value) == sizeof(uint32_t))
     163              :         {
     164          275 :             return stream.write(&value, sizeof(value));
     165              :         }
     166              :         else
     167              :         {
     168          285 :             const auto tmp = uint32_t(value);
     169          285 :             return stream.write(&tmp, sizeof(tmp));
     170              :         }
     171              :     }
     172              :     else if constexpr (type == scalar_encoder::i64)
     173              :     {
     174              :         if constexpr (sizeof(value) == sizeof(uint64_t))
     175              :         {
     176          235 :             return stream.write(&value, sizeof(value));
     177              :         }
     178              :         else
     179              :         {
     180          190 :             const auto tmp = uint64_t(value);
     181          190 :             return stream.write(&tmp, sizeof(tmp));
     182              :         }
     183              :     }
     184              : }
     185              : 
     186          521 : static inline void serialize(ostream &stream, const field_attributes &field,
     187              :                              const spb::detail::proto_field_string auto &value)
     188              : {
     189          521 :     if (value.empty())
     190            5 :         return;
     191              : 
     192          516 :     if (field.max_size && value.size() > field.max_size) [[unlikely]]
     193           16 :         throw std::length_error("string is too large");
     194              : 
     195          500 :     spb::detail::utf8::validate(std::string_view(value.data(), value.size()));
     196          500 :     serialize_tag(stream, field.number, wire_type::length_delimited);
     197          500 :     serialize_varint(stream, value.size());
     198          500 :     stream.write(value.data(), value.size());
     199              : }
     200              : 
     201          250 : static inline void serialize(ostream &stream, const field_attributes &field,
     202              :                              const spb::detail::proto_field_bytes auto &value)
     203              : {
     204          250 :     if (value.empty())
     205            5 :         return;
     206              : 
     207          245 :     if (field.max_size && value.size() > field.max_size) [[unlikely]]
     208           16 :         throw std::length_error("bytes is too large");
     209              : 
     210          229 :     serialize_tag(stream, field.number, wire_type::length_delimited);
     211          229 :     serialize_varint(stream, value.size());
     212          229 :     stream.write(value.data(), value.size());
     213              : }
     214              : 
     215              : template <scalar_encoder encoder, typename keyT, typename valueT>
     216           24 : static inline void serialize_as(ostream &stream, const std::map<keyT, valueT> &value)
     217              : {
     218           24 :     const auto key_encoder = scalar_encoder_type1(encoder);
     219           24 :     const auto value_encoder = scalar_encoder_type2(encoder);
     220              : 
     221           52 :     for (const auto &[k, v] : value)
     222              :     {
     223              :         if constexpr (std::is_integral_v<keyT>)
     224              :         {
     225           16 :             serialize_as<key_encoder>(stream, field_attributes{.number = 1}, k);
     226              :         }
     227              :         else
     228              :         {
     229           12 :             serialize(stream, field_attributes{.number = 1}, k);
     230              :         }
     231              :         if constexpr (spb::detail::proto_field_number<valueT>)
     232              :         {
     233           16 :             serialize_as<value_encoder>(stream, field_attributes{.number = 2}, v);
     234              :         }
     235              :         else
     236              :         {
     237           12 :             serialize(stream, field_attributes{.number = 2}, v);
     238              :         }
     239              :     }
     240           24 : }
     241              : 
     242              : template <scalar_encoder encoder, typename keyT, typename valueT>
     243           12 : static inline void serialize_as(ostream &stream, const field_attributes &field,
     244              :                                 const std::map<keyT, valueT> &value)
     245              : {
     246           12 :     auto size_stream = ostream();
     247           12 :     serialize_as<encoder>(size_stream, value);
     248           12 :     const auto size = size_stream.size();
     249              : 
     250           12 :     serialize_tag(stream, field.number, wire_type::length_delimited);
     251           12 :     serialize_varint(stream, size);
     252           12 :     serialize_as<encoder>(stream, value);
     253           12 : }
     254              : 
     255              : template <scalar_encoder encoder, spb::detail::proto_label_repeated_fixed_size C>
     256           20 : static inline void serialize_packed_as(ostream &stream, const C &container)
     257              : {
     258          200 :     for (size_t i = 0; i < container.size(); i++)
     259              :     {
     260              :         if constexpr (std::is_same_v<typename C::value_type, bool>)
     261              :         {
     262              :             serialize_as<encoder>(stream, bool(container[i]));
     263              :         }
     264              :         else
     265              :         {
     266           80 :             serialize_as<encoder>(stream, container[i]);
     267              :         }
     268              :     }
     269           20 : }
     270              : 
     271              : template <scalar_encoder encoder, spb::detail::proto_label_repeated C>
     272          368 : static inline void serialize_packed_as(ostream &stream, const C &container)
     273              : {
     274         1058 :     for (const auto &v : container)
     275              :     {
     276              :         if constexpr (std::is_same_v<typename C::value_type, bool>)
     277              :         {
     278           30 :             serialize_as<encoder>(stream, bool(v));
     279              :         }
     280              :         else
     281              :         {
     282          660 :             serialize_as<encoder>(stream, v);
     283              :         }
     284              :     }
     285          368 : }
     286              : 
     287              : template <scalar_encoder encoder, spb::detail::proto_label_repeated C>
     288          643 : static inline void serialize_as(ostream &stream, const field_attributes &field, const C &value)
     289              : {
     290          643 :     if (field.max_count && value.size() > field.max_count) [[unlikely]]
     291           10 :         throw std::length_error("repeated is too large");
     292              : 
     293              :     if constexpr (scalar_encoder_is_packed(encoder))
     294              :     {
     295          289 :         if (value.empty())
     296          105 :             return;
     297              : 
     298          184 :         auto size_stream = ostream();
     299          184 :         serialize_packed_as<encoder>(size_stream, value);
     300          184 :         const auto size = size_stream.size();
     301              : 
     302          184 :         serialize_tag(stream, field.number, wire_type::length_delimited);
     303          184 :         serialize_varint(stream, size);
     304          184 :         serialize_packed_as<encoder>(stream, value);
     305              :     }
     306              :     else
     307              :     {
     308          694 :         for (const auto &v : value)
     309              :         {
     310              :             if constexpr (std::is_same_v<typename C::value_type, bool>)
     311              :             {
     312           15 :                 serialize_as<encoder>(stream, field, bool(v));
     313              :             }
     314              :             else
     315              :             {
     316          335 :                 serialize_as<encoder>(stream, field, v);
     317              :             }
     318              :         }
     319              :     }
     320          344 : }
     321              : 
     322              : template <scalar_encoder encoder, spb::detail::proto_label_repeated_fixed_size C>
     323           10 : static inline void serialize_as(ostream &stream, const field_attributes &field, const C &value)
     324              : {
     325              :     static_assert(scalar_encoder_is_packed(encoder),
     326              :                   "repeated field with fixed size has to have attribute 'packed'");
     327              : 
     328           10 :     auto size_stream = ostream();
     329           10 :     serialize_packed_as<encoder>(size_stream, value);
     330           10 :     const auto size = size_stream.size();
     331              : 
     332           10 :     serialize_tag(stream, field.number, wire_type::length_delimited);
     333           10 :     serialize_varint(stream, size);
     334           10 :     serialize_packed_as<encoder>(stream, value);
     335           10 : }
     336              : 
     337          166 : static inline void serialize(ostream &stream, const field_attributes &field,
     338              :                              const spb::detail::proto_label_repeated auto &value)
     339              : {
     340          166 :     if (field.max_count && value.size() > field.max_count) [[unlikely]]
     341           24 :         throw std::length_error("repeated is too large");
     342              : 
     343          512 :     for (const auto &v : value)
     344              :     {
     345              :         if constexpr (std::is_same_v<typename std::decay_t<decltype(value)>::value_type, bool>)
     346              :         {
     347              :             serialize_as<scalar_encoder::varint>(stream, field, bool(v));
     348              :         }
     349              :         else
     350              :         {
     351          370 :             serialize(stream, field, v);
     352              :         }
     353              :     }
     354          142 : }
     355              : 
     356          389 : static inline void serialize(ostream &stream, const field_attributes &field,
     357              :                              const spb::detail::proto_label_optional auto &p_value)
     358              : {
     359          389 :     if (p_value.has_value())
     360          341 :         return serialize(stream, field, *p_value);
     361              : }
     362              : 
     363              : template <scalar_encoder encoder>
     364          280 : static inline void serialize_as(ostream &stream, const field_attributes &field,
     365              :                                 const spb::detail::proto_label_optional auto &p_value)
     366              : {
     367          280 :     if (p_value.has_value())
     368          235 :         return serialize_as<encoder>(stream, field, *p_value);
     369              : }
     370              : 
     371              : template <typename T>
     372              : static inline void serialize(ostream &stream, const field_attributes &field,
     373              :                              const std::unique_ptr<T> &p_value)
     374              : {
     375              :     if (p_value)
     376              :         return serialize(stream, field, *p_value);
     377              : }
     378              : 
     379          119 : static inline void serialize(ostream &stream, const field_attributes &field,
     380              :                              const spb::detail::proto_message auto &value)
     381              : {
     382          119 :     const auto size = serialize_size(value);
     383          119 :     if (!size)
     384            0 :         return;
     385              : 
     386          119 :     serialize_tag(stream, field.number, wire_type::length_delimited);
     387          119 :     serialize_varint(stream, size);
     388              :     //
     389              :     //- serialize is generated by the spb-protoc
     390              :     //
     391          119 :     serialize(stream, value);
     392              : }
     393              : 
     394              : static inline auto serialize(const auto &value, spb::io::writer on_write) -> size_t
     395              : {
     396              :     auto stream = ostream(on_write);
     397              :     serialize(stream, value);
     398              :     return stream.size();
     399              : }
     400              : 
     401          123 : static inline auto serialize_size(const auto &value) -> size_t
     402              : {
     403          123 :     auto stream = ostream(nullptr);
     404              : 
     405          123 :     serialize(stream, value);
     406          246 :     return stream.size();
     407              : }
     408              : 
     409          710 : void ostream::serialize(const field_attributes &field, const auto &value)
     410              : {
     411          710 :     detail::serialize(*this, field, value);
     412          654 : }
     413              : 
     414         1553 : template <scalar_encoder encoder> void ostream::serialize_as(const field_attributes &field, const auto &value)
     415              : {
     416         1553 :     detail::serialize_as<encoder>(*this, field, value);
     417         1543 : }
     418              : 
     419              : } // namespace spb::pb::detail
        

Generated by: LCOV version 2.0-1