LCOV - code coverage report
Current view: top level - snapdev - brs.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 208 208 100.0 %
Date: 2022-07-09 19:51:09 Functions: 102 125 81.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13