LCOV - code coverage report
Current view: top level - snapdev - brs.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 205 205 100.0 %
Date: 2023-05-29 16:11:08 Functions: 91 91 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2022-2023  Made to Order Software Corp.  All Rights Reserved.
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.com
       5             : // 
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : #pragma once
      19             : 
      20             : /** \file
      21             :  * \brief Classes to serialize and deserialize data.
      22             :  *
      23             :  * This file implements a couple of classes used to serialize binary data to
      24             :  * a stream. In most cases, this is much more efficient than any other method
      25             :  * such as using XML, JSON, or even a simple .ini file.
      26             :  *
      27             :  * It is also very useful if you have data such as floating point numbers
      28             :  * which when saved in a text format can lose precision (unless you save
      29             :  * them as an integer).
      30             :  */
      31             : 
      32             : // libexcept
      33             : //
      34             : #include    <libexcept/exception.h>
      35             : 
      36             : 
      37             : // snapdev
      38             : //
      39             : #include    <snapdev/is_vector.h>
      40             : #include    <snapdev/not_used.h>
      41             : #include    <snapdev/sizeof_bitfield.h>
      42             : 
      43             : 
      44             : // C++
      45             : //
      46             : #include    <cstring>
      47             : #include    <functional>
      48             : #include    <limits>
      49             : #include    <map>
      50             : #include    <memory>
      51             : #include    <vector>
      52             : 
      53             : 
      54             : 
      55             : namespace snapdev
      56             : {
      57             : 
      58             : 
      59             : 
      60          10 : DECLARE_LOGIC_ERROR(brs_logic_error);
      61             : 
      62          35 : DECLARE_OUT_OF_RANGE(brs_out_of_range);
      63             : 
      64          10 : DECLARE_MAIN_EXCEPTION(brs_error);
      65             : 
      66           6 : DECLARE_EXCEPTION(brs_error, brs_cannot_be_empty);
      67           1 : DECLARE_EXCEPTION(brs_error, brs_magic_missing);
      68           1 : DECLARE_EXCEPTION(brs_error, brs_magic_unsupported);
      69           1 : DECLARE_EXCEPTION(brs_error, brs_map_name_cannot_be_empty);
      70           1 : DECLARE_EXCEPTION(brs_error, brs_unknown_type);
      71             : 
      72             : 
      73             : typedef std::uint32_t               magic_t;
      74             : typedef std::uint8_t                version_t;
      75             : typedef std::string                 name_t;
      76             : 
      77             : typedef std::uint32_t               type_t;
      78             : 
      79             : constexpr type_t const              TYPE_FIELD = 0;     // regular name=value
      80             : constexpr type_t const              TYPE_ARRAY = 1;     // item in an array (includes a 16 bit index)
      81             : constexpr type_t const              TYPE_MAP = 2;       // item in a map (includes a second name)
      82             : 
      83             : struct hunk_sizes_t
      84             : {
      85             :     std::uint32_t   f_type : 2;         // type of field (see TYPE_...)
      86             :     std::uint32_t   f_name : 7;         // size of this field name (1 to 127)
      87             :     std::uint32_t   f_hunk : 23;        // size of this field's data (up to 8Mb)
      88             : };
      89             : 
      90             : 
      91             : constexpr version_t const       BRS_ROOT = 0;       // indicate root buffer
      92             : constexpr version_t const       BRS_VERSION = 1;    // version of the format
      93             : 
      94             : 
      95             : constexpr magic_t build_magic(char endian)
      96             : {
      97             : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
      98             :     return ('B' << 24) | ('R' << 16) | (endian <<  8) | (static_cast<unsigned char>(BRS_VERSION) <<  0);
      99             : #else
     100             :     return ('B' <<  0) | ('R' <<  8) | (endian << 16) | (static_cast<unsigned char>(BRS_VERSION) << 24);
     101             : #endif
     102             : }
     103             : 
     104             : constexpr magic_t const         BRS_MAGIC_BIG_ENDIAN    = build_magic('B');
     105             : constexpr magic_t const         BRS_MAGIC_LITTLE_ENDIAN = build_magic('L');
     106             : 
     107             : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
     108             : constexpr magic_t const         BRS_MAGIC = BRS_MAGIC_BIG_ENDIAN;
     109             : #else
     110             : constexpr magic_t const         BRS_MAGIC = BRS_MAGIC_LITTLE_ENDIAN;
     111             : #endif
     112             : 
     113             : 
     114             : 
     115             : /** \brief Class to serialize your data.
     116             :  *
     117             :  * This class is used to serialize your data. You create a serializer and
     118             :  * then call add_value() with each one of your fields.
     119             :  *
     120             :  * The class supports named fields, fields representing arrays, and
     121             :  * fields representing maps.
     122             :  *
     123             :  * \code
     124             :  *     // create a serialize that writes to a stringstream
     125             :  *     //
     126             :  *     std::stringstream buffer;
     127             :  *     snapdev::serializer out(buffer);
     128             :  *
     129             :  *     // add a value as is; all basic types, strings, etc. are supported
     130             :  *     // in this case you pass 2 values
     131             :  *     // note that my_value can be any basic struct type
     132             :  *     //
     133             :  *     // if you use a binary type (i.e. `std::int16_t`) then you must re-read
     134             :  *     // the value with the exact same type; if the type changes later,
     135             :  *     // if the size is different, you can detect which type using the size
     136             :  *     // otherwise make sure to change the field name
     137             :  *     //
     138             :  *     out.add_value("field-name", my_value);
     139             :  *
     140             :  *     // for binary data in a buffer, pass a pointer and a size
     141             :  *     //
     142             :  *     out.add_value("field-name", my_buffer, size_of_my_buffer);
     143             :  *
     144             :  *     // add a value with an index (array)
     145             :  *     // in this case you pass 3 values
     146             :  *     // this also supports the buffer/size as above
     147             :  *     //
     148             :  *     out.add_value("field-name", 123, my_value);
     149             :  *
     150             :  *     // add a value with a sub-field name (map)
     151             :  *     // in this case you pass 3 values, the second being a string as well
     152             :  *     // this also supports the buffer/size as above
     153             :  *     //
     154             :  *     out.add_value("field-name", "sub-field", my_value);
     155             :  *
     156             :  *     // the add to the output stream is immediate
     157             :  *     //
     158             :  *     // so to save it to a file you could do this
     159             :  *     //
     160             :  *     std::ofstream p("my-data.brs");
     161             :  *     p << buffer.str();
     162             :  * \endcode
     163             :  *
     164             :  * If you have a sub-class that you want to serialize within the parent,
     165             :  * you want to use the recursive class like so:
     166             :  *
     167             :  * \code
     168             :  *     s.add_value("type", f_type);     // regular field
     169             :  *
     170             :  *     // start the recursive entry
     171             :  *     {
     172             :  *         brs::recursive r(s, "headers");
     173             :  *         for(auto const & h : f_headers)
     174             :  *         {
     175             :  *             h.serialize(s);  // serialize one header
     176             :  *
     177             :  *             // for example, the header serialization may just be a name/value
     178             :  *             //     s.add_value("header:" + f_name, f_value);
     179             :  *         }
     180             :  *     }
     181             :  *     // end the recursive entry
     182             :  *
     183             :  *     s.add_value("body", f_body);     // another regular field
     184             :  * \endcode
     185             :  *
     186             :  * Place the brs::recursive inside a sub-block, that way when you hit the '}'
     187             :  * it closes the sub-field automatically. This is equivalent to calling the
     188             :  * start_subfield() and end_subfield() in a safe manner.
     189             :  *
     190             :  * \tparam S  The type of output stream to write the data to.
     191             :  */
     192             : template<typename S>
     193             : class serializer
     194             : {
     195             : public:
     196             :     /** \brief Initialize the stream with the magic header.
     197             :      *
     198             :      * This function adds the magic header at the beginning of your file.
     199             :      */
     200          16 :     serializer(S & output)
     201          16 :         : f_output(output)
     202             :     {
     203          16 :         magic_t const magic(BRS_MAGIC);
     204          16 :         f_output.write(
     205             :                   reinterpret_cast<typename S::char_type const *>(&magic)
     206             :                 , sizeof(magic));
     207          16 :     }
     208             : 
     209             :     template<typename T>
     210          41 :     void add_value(name_t name, T const * ptr, std::size_t size)
     211             :     {
     212          41 :         if(name.length() == 0)
     213             :         {
     214           2 :             throw brs_cannot_be_empty("name cannot be an empty string");
     215             :         }
     216             : 
     217             : #pragma GCC diagnostic push
     218             : #pragma GCC diagnostic ignored "-Wpedantic"
     219          39 :         hunk_sizes_t const hunk_sizes = {
     220             :             .f_type = TYPE_FIELD,
     221          39 :             .f_name = static_cast<std::uint8_t>(name.length()),
     222         975 :             .f_hunk = static_cast<std::uint32_t>(size & ((1 << SIZEOF_BITFIELD(hunk_sizes_t, f_hunk)) - 1)),
     223             :         };
     224             : #pragma GCC diagnostic pop
     225             : 
     226          39 :         if(hunk_sizes.f_name != name.length()
     227          39 :         || hunk_sizes.f_hunk != size)
     228             :         {
     229          11 :             throw brs_out_of_range("name or hunk too large");
     230             :         }
     231             : 
     232          28 :         f_output.write(
     233             :                   reinterpret_cast<typename S::char_type const *>(&hunk_sizes)
     234             :                 , sizeof(hunk_sizes));
     235             : 
     236          28 :         f_output.write(
     237             :                   reinterpret_cast<typename S::char_type const *>(name.c_str())
     238          28 :                 , hunk_sizes.f_name);
     239             : 
     240          28 :         f_output.write(
     241             :                   reinterpret_cast<typename S::char_type const *>(ptr)
     242             :                 , size);
     243          28 :     }
     244             : 
     245             : 
     246             :     template<typename T>
     247          72 :     void add_value(name_t name, int index, T const * ptr, std::size_t size)
     248             :     {
     249          72 :         if(name.length() == 0)
     250             :         {
     251           1 :             throw brs_cannot_be_empty("name cannot be an empty string");
     252             :         }
     253             : 
     254             : #pragma GCC diagnostic push
     255             : #pragma GCC diagnostic ignored "-Wpedantic"
     256          71 :         hunk_sizes_t const hunk_sizes = {
     257             :             .f_type = TYPE_ARRAY,
     258          71 :             .f_name = static_cast<std::uint8_t>(name.length()),
     259        1775 :             .f_hunk = static_cast<std::uint32_t>(size & ((1 << SIZEOF_BITFIELD(hunk_sizes_t, f_hunk)) - 1)),
     260             :         };
     261             : #pragma GCC diagnostic pop
     262          71 :         std::uint16_t const idx(static_cast<std::uint16_t>(index));
     263             : 
     264          71 :         if(hunk_sizes.f_name != name.length()
     265          70 :         || hunk_sizes.f_hunk != size
     266         141 :         || index != idx)
     267             :         {
     268          11 :             throw brs_out_of_range("name, index, or hunk too large");
     269             :         }
     270             : 
     271          60 :         f_output.write(
     272             :                   reinterpret_cast<typename S::char_type const *>(&hunk_sizes)
     273             :                 , sizeof(hunk_sizes));
     274             : 
     275          60 :         f_output.write(
     276             :                   reinterpret_cast<typename S::char_type const *>(&idx)
     277             :                 , sizeof(idx));
     278             : 
     279          60 :         f_output.write(
     280             :                   reinterpret_cast<typename S::char_type const *>(name.c_str())
     281          60 :                 , hunk_sizes.f_name);
     282             : 
     283          60 :         f_output.write(
     284             :                   reinterpret_cast<typename S::char_type const *>(ptr)
     285             :                 , size);
     286          60 :     }
     287             : 
     288             : 
     289             :     template<typename T>
     290          94 :     void add_value(name_t name, name_t sub_name, T const * ptr, std::size_t size)
     291             :     {
     292          94 :         if(name.empty())
     293             :         {
     294           1 :             throw brs_cannot_be_empty("name cannot be an empty string");
     295             :         }
     296             : 
     297          93 :         if(sub_name.empty())
     298             :         {
     299           1 :             throw brs_cannot_be_empty("sub-name cannot be an empty string");
     300             :         }
     301             : 
     302             : #pragma GCC diagnostic push
     303             : #pragma GCC diagnostic ignored "-Wpedantic"
     304          92 :         hunk_sizes_t const hunk_sizes = {
     305             :             .f_type = TYPE_MAP,
     306          92 :             .f_name = static_cast<std::uint8_t>(name.length()),
     307        2300 :             .f_hunk = static_cast<std::uint32_t>(size & ((1 << SIZEOF_BITFIELD(hunk_sizes_t, f_hunk)) - 1)),
     308             :         };
     309             : #pragma GCC diagnostic pop
     310          92 :         std::uint8_t const len(static_cast<std::uint8_t>(sub_name.length()));
     311             : 
     312          92 :         if(hunk_sizes.f_name != name.length()
     313          91 :         || hunk_sizes.f_hunk != size
     314         183 :         || sub_name.length() > std::numeric_limits<decltype(len)>::max())
     315             :         {
     316          12 :             throw brs_out_of_range("name, sub-name, or hunk too large");
     317             :         }
     318             : 
     319          80 :         f_output.write(
     320             :                   reinterpret_cast<typename S::char_type const *>(&hunk_sizes)
     321             :                 , sizeof(hunk_sizes));
     322             : 
     323          80 :         f_output.write(
     324             :                   reinterpret_cast<typename S::char_type const *>(&len)
     325             :                 , sizeof(len));
     326             : 
     327          80 :         f_output.write(
     328             :                   reinterpret_cast<typename S::char_type const *>(sub_name.c_str())
     329             :                 , len);
     330             : 
     331          80 :         f_output.write(
     332             :                   reinterpret_cast<typename S::char_type const *>(name.c_str())
     333          80 :                 , hunk_sizes.f_name);
     334             : 
     335          80 :         f_output.write(
     336             :                   reinterpret_cast<typename S::char_type const *>(ptr)
     337             :                 , size);
     338          80 :     }
     339             : 
     340             : 
     341             : // *** FIELDS ***
     342             :     /** \brief Save a basic type or struct of basic types.
     343             :      *
     344             :      * This one function saves the specified value as is. It is expected to be
     345             :      * a basic type such as an int or a double. It also supports structures
     346             :      * that are only composed of basic types. Structures and classes with
     347             :      * complex types such as a string need to be handled manually.
     348             :      *
     349             :      * \tparam T  The type of value,
     350             :      * \param[in] name  The name of the field to be saved.
     351             :      * \param[in] value  The value to be saved with that name.
     352             :      */
     353             :     template<typename T>
     354             :     typename std::enable_if_t<!snapdev::is_vector_v<T>
     355             :                             && !std::is_same_v<char const *, T>
     356             :                             && !std::is_same_v<std::string const &, T>, void>
     357          14 :     add_value(name_t name, T const & value)
     358             :     {
     359          14 :         add_value(name, &value, sizeof(value));
     360          14 :     }
     361             : 
     362             : 
     363           1 :     void add_value(name_t name, std::nullptr_t)
     364             :     {
     365           1 :         NOT_USED(name);
     366           1 :     }
     367             : 
     368             : 
     369           2 :     void add_value(name_t name, char const * value)
     370             :     {
     371           2 :         add_value(name, value, strlen(value));
     372           2 :     }
     373             : 
     374             : 
     375          10 :     void add_value(name_t name, std::string const & value)
     376             :     {
     377          10 :         add_value(name, value.c_str(), value.length());
     378          10 :     }
     379             : 
     380             : 
     381           3 :     void add_value_if_not_empty(name_t name, std::string const & value)
     382             :     {
     383           3 :         if(!value.empty())
     384             :         {
     385           1 :             add_value(name, value.c_str(), value.length());
     386             :         }
     387           3 :     }
     388             : 
     389             : 
     390             :     template<typename T>
     391             :     typename std::enable_if_t<snapdev::is_vector_v<T>>
     392           1 :     add_value(name_t name, T const & value)
     393             :     {
     394           1 :         add_value(name, value.data(), value.size() * sizeof(typename T::value_type));
     395           1 :     }
     396             : 
     397             : 
     398             : // *** INDEXES ***
     399             :     template<typename T>
     400             :     typename std::enable_if_t<!snapdev::is_vector_v<T>
     401             :                             && !std::is_same_v<char const *, T>, void>
     402          10 :     add_value(name_t name, int index, T const & value)
     403             :     {
     404          10 :         add_value(name, index, &value, sizeof(value));
     405          10 :     }
     406             : 
     407             : 
     408          25 :     void add_value(name_t name, int index, std::nullptr_t)
     409             :     {
     410          25 :         NOT_USED(name, index);
     411          25 :     }
     412             : 
     413             : 
     414          15 :     void add_value(name_t name, int index, char const * value)
     415             :     {
     416          15 :         add_value(name, index, value, strlen(value));
     417          15 :     }
     418             : 
     419             : 
     420          35 :     void add_value(name_t name, int index, std::string const & value)
     421             :     {
     422          35 :         add_value(name, index, value.c_str(), value.length());
     423          35 :     }
     424             : 
     425             : 
     426             : // *** MAPS ***
     427             :     template<typename T>
     428             :     typename std::enable_if_t<!std::is_same_v<T, typename std::string>, void>
     429          55 :     add_value(name_t name, name_t sub_name, T const & value)
     430             :     {
     431          55 :         add_value(name, sub_name, &value, sizeof(value));
     432          55 :     }
     433             : 
     434             : 
     435          25 :     void add_value(name_t name, name_t sub_name, std::nullptr_t)
     436             :     {
     437          25 :         NOT_USED(name, sub_name);
     438          25 :     }
     439             : 
     440             : 
     441          12 :     void add_value(name_t name, name_t sub_name, char const * value)
     442             :     {
     443          12 :         add_value(name, sub_name, value, strlen(value));
     444          12 :     }
     445             : 
     446             : 
     447           7 :     void add_value(name_t name, name_t sub_name, std::string const & value)
     448             :     {
     449           7 :         add_value(name, sub_name, value.c_str(), value.length());
     450           7 :     }
     451             : 
     452             : 
     453           7 :     void add_value_if_not_empty(name_t name, name_t sub_name, std::string const & value)
     454             :     {
     455           7 :         if(!value.empty())
     456             :         {
     457           6 :             add_value(name, sub_name, value.c_str(), value.length());
     458             :         }
     459           7 :     }
     460             : 
     461             : 
     462             : // *** SUB-FIELDS ***
     463          10 :     void start_subfield(name_t name)
     464             :     {
     465          10 :         if(name.empty())
     466             :         {
     467           1 :             throw brs_cannot_be_empty("name cannot be an empty string");
     468             :         }
     469             : 
     470             : #pragma GCC diagnostic push
     471             : #pragma GCC diagnostic ignored "-Wpedantic"
     472           9 :         hunk_sizes_t const hunk_sizes = {
     473             :             .f_type = TYPE_FIELD,
     474           9 :             .f_name = static_cast<std::uint8_t>(name.length()),
     475             :             .f_hunk = 0,
     476             :         };
     477             : #pragma GCC diagnostic pop
     478             : 
     479           9 :         if(hunk_sizes.f_name != name.length())
     480             :         {
     481           1 :             throw brs_out_of_range("name too large");
     482             :         }
     483             : 
     484           8 :         f_output.write(
     485             :                   reinterpret_cast<typename S::char_type const *>(&hunk_sizes)
     486             :                 , sizeof(hunk_sizes));
     487             : 
     488           8 :         f_output.write(
     489             :                   reinterpret_cast<typename S::char_type const *>(name.c_str())
     490           8 :                 , hunk_sizes.f_name);
     491           8 :     }
     492             : 
     493             : 
     494           8 :     void end_subfield()
     495             :     {
     496             : #pragma GCC diagnostic push
     497             : #pragma GCC diagnostic ignored "-Wpedantic"
     498           8 :         hunk_sizes_t const hunk_sizes = {
     499             :             .f_type = TYPE_FIELD,
     500             :             .f_name = 0,
     501             :             .f_hunk = 0,
     502             :         };
     503             : #pragma GCC diagnostic pop
     504             : 
     505           8 :         f_output.write(
     506             :                   reinterpret_cast<typename S::char_type const *>(&hunk_sizes)
     507             :                 , sizeof(hunk_sizes));
     508           8 :     }
     509             : 
     510             : 
     511             : private:
     512             :     S &         f_output = S();
     513             : };
     514             : 
     515             : 
     516             : template<typename S>
     517             : class recursive
     518             : {
     519             : public:
     520           8 :     recursive(serializer<S> & s, name_t name)
     521           8 :         : f_serializer(s)
     522             :     {
     523           8 :         f_serializer.start_subfield(name);
     524           8 :     }
     525             : 
     526           8 :     ~recursive()
     527             :     {
     528           8 :         f_serializer.end_subfield();
     529           8 :     }
     530             : 
     531             : private:
     532             :     serializer<S> &     f_serializer;
     533             : };
     534             : 
     535             : 
     536             : 
     537             : 
     538             : 
     539             : 
     540             : 
     541             : 
     542             : /** \brief When deserializing, the data is saved in a field.
     543             :  *
     544             :  * This field holds the data of one field.
     545             :  */
     546             : struct field_t
     547             : {
     548         193 :     void reset()
     549             :     {
     550         193 :         f_name.clear();
     551         193 :         f_sub_name.clear();
     552         193 :         f_index = -1;
     553         193 :         f_size = 0;
     554         193 :     }
     555             : 
     556             :     std::string     f_name = std::string();
     557             :     std::string     f_sub_name = std::string();
     558             :     int             f_index = -1;
     559             :     std::size_t     f_size = 0;         // size of the data (still in stream)
     560             : };
     561             : 
     562             : 
     563             : /** \brief Unserialize the specified buffer.
     564             :  *
     565             :  * This function reads each hunk and calls the specified \p callback
     566             :  * with them. You can then save the data in your object fields.
     567             :  *
     568             :  * The root buffer is expected to include the magic code at the start.
     569             :  * Set the \p include_magic to true in that case to verify that it
     570             :  * is indeed set and valid. If you do not do that, the unserialization
     571             :  * will fail since everything will be off by sizeof(magic_t).
     572             :  *
     573             :  * \param[in] buffer  The buffer to unserialize.
     574             :  * \param[in] callback  The callback manager used to store the callbacks to
     575             :  * call on each hunk.
     576             :  * \param[in] includes_magic  Whether \p buffer includes the a magic code
     577             :  * at the start or not. The top buffer is expected to include a magic
     578             :  * code. Sub-buffers should not include the magic code.
     579             :  *
     580             :  * \return true if the unserialization succeeded, false otherwise.
     581             :  */
     582             : template<typename S>
     583             : class deserializer
     584             : {
     585             : public:
     586             :     typedef std::function<bool(deserializer<S> &, field_t const &)>    process_hunk_t;
     587             : 
     588          24 :     deserializer(S & input)
     589          24 :         : f_input(input)
     590             :     {
     591          24 :         magic_t magic = {};
     592          24 :         f_input.read(reinterpret_cast<typename S::char_type *>(&magic), sizeof(magic));
     593          24 :         if(!f_input || f_input.gcount() != sizeof(magic))
     594             :         {
     595           1 :             throw brs_magic_missing("magic missing from the start of the buffer.");
     596             :         }
     597             : 
     598             :         // once we have multiple versions, this is where we'll start splitting
     599             :         // hairs to make it all work; for now, we have one so it's easy
     600             :         //
     601          23 :         if(magic != BRS_MAGIC)
     602             :         {
     603           1 :             throw brs_magic_unsupported("magic unsupported.");
     604             :         }
     605          24 :     }
     606             : 
     607             : 
     608         207 :     bool deserialize(process_hunk_t & callback)
     609             :     {
     610         177 :         for(;;)
     611             :         {
     612         207 :             hunk_sizes_t hunk_sizes = {};
     613         207 :             f_input.read(reinterpret_cast<typename S::char_type *>(&hunk_sizes), sizeof(hunk_sizes));
     614         207 :             if(!f_input || f_input.gcount() != sizeof(hunk_sizes))
     615             :             {
     616          14 :                 return f_input.eof() && f_input.gcount() == 0;
     617             :             }
     618             : 
     619         193 :             f_field.reset();
     620         193 :             f_field.f_size = hunk_sizes.f_hunk;
     621             : 
     622         193 :             switch(hunk_sizes.f_type)
     623             :             {
     624          46 :             case TYPE_FIELD:
     625          46 :                 if(hunk_sizes.f_name == 0
     626           8 :                 && hunk_sizes.f_hunk == 0)
     627             :                 {
     628             :                     // we found an "end sub-field" entry
     629             :                     //
     630           8 :                     return true;
     631             :                 }
     632          38 :                 break;
     633             : 
     634          62 :             case TYPE_ARRAY:
     635             :                 {
     636          62 :                     std::uint16_t idx(0);
     637          62 :                     f_input.read(reinterpret_cast<typename S::char_type *>(&idx), sizeof(idx));
     638          62 :                     if(!f_input || f_input.gcount() != sizeof(idx))
     639             :                     {
     640           1 :                         return false;
     641             :                     }
     642          61 :                     f_field.f_index = idx;
     643             :                 }
     644          61 :                 break;
     645             : 
     646          84 :             case TYPE_MAP:
     647             :                 {
     648          84 :                     std::uint8_t len(0);
     649          84 :                     f_input.read(reinterpret_cast<typename S::char_type *>(&len), sizeof(len));
     650          84 :                     if(!f_input || f_input.gcount() != sizeof(len))
     651             :                     {
     652           1 :                         return false;
     653             :                     }
     654          83 :                     if(len == 0)
     655             :                     {
     656           1 :                         throw brs_map_name_cannot_be_empty("the length of a map's field name cannot be zero.");
     657             :                     }
     658          82 :                     f_field.f_sub_name.resize(len);
     659          82 :                     f_input.read(reinterpret_cast<typename S::char_type *>(f_field.f_sub_name.data()), len);
     660          82 :                     if(!f_input || f_input.gcount() != len)
     661             :                     {
     662           1 :                         return false;
     663             :                     }
     664             :                 }
     665          81 :                 break;
     666             : 
     667           1 :             default:
     668           1 :                 throw brs_unknown_type("read a field with an unknown type.");
     669             : 
     670             :             }
     671             : 
     672         180 :             f_field.f_name.resize(hunk_sizes.f_name);
     673         180 :             f_input.read(reinterpret_cast<typename S::char_type *>(f_field.f_name.data()), hunk_sizes.f_name);
     674         180 :             if(!f_input || f_input.gcount() != hunk_sizes.f_name)
     675             :             {
     676           3 :                 return false;
     677             :             }
     678             : 
     679         177 :             callback(*this, f_field);
     680             :         }
     681             :     }
     682             : 
     683             :     template<typename T>
     684          89 :     bool read_data(T & data)
     685             :     {
     686          89 :         if(f_field.f_size != sizeof(data))
     687             :         {
     688           9 :             throw brs_logic_error(
     689             :                       "hunk size is "
     690             :                     + std::to_string(f_field.f_size)
     691             :                     + ", but you are trying to read "
     692             :                     + std::to_string(sizeof(data))
     693             :                     + '.');
     694             :         }
     695             : 
     696          80 :         f_input.read(reinterpret_cast<typename S::char_type *>(&data), sizeof(data));
     697          80 :         return verify_size(sizeof(data));
     698             :     }
     699             : 
     700          88 :     bool read_data(std::string & data)
     701             :     {
     702          88 :         data.resize(f_field.f_size);
     703          88 :         f_input.read(reinterpret_cast<typename S::char_type *>(data.data()), f_field.f_size);
     704          88 :         return verify_size(f_field.f_size);
     705             :     }
     706             : 
     707             :     template<typename T>
     708           2 :     bool read_data(std::vector<T> & data)
     709             :     {
     710           2 :         if(f_field.f_size % sizeof(T) != 0)
     711             :         {
     712           1 :             throw brs_logic_error(
     713             :                       "hunk size ("
     714             :                     + std::to_string(f_field.f_size)
     715             :                     + ") is not a multiple of the vector item size: "
     716             :                     + std::to_string(sizeof(T))
     717             :                     + '.');
     718             :         }
     719             : 
     720           1 :         data.resize(f_field.f_size / sizeof(T));
     721           1 :         f_input.read(reinterpret_cast<typename S::char_type *>(data.data()), f_field.f_size);
     722           1 :         return verify_size(f_field.f_size);
     723             :     }
     724             : 
     725             : private:
     726         169 :     bool verify_size(std::size_t expected_size)
     727             :     {
     728         169 :         return f_input && static_cast<ssize_t>(expected_size) == f_input.gcount();
     729             :     }
     730             : 
     731             :     S &         f_input;
     732             :     field_t     f_field = field_t();
     733             : };
     734             : 
     735             : 
     736             : 
     737             : } // namespace snapdev
     738             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14