LCOV - code coverage report
Current view: top level - io - BinaryReader.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 229 232 98.7 %
Date: 2025-06-15 16:18:27 Functions: 29 29 100.0 %

          Line data    Source code
       1             : /* Copyright (C) 2025 Robert Schiwon
       2             :  *
       3             :  * This file is part of libjdx.
       4             :  *
       5             :  * libjdx is free software: you can redistribute it and/or modify it under the
       6             :  * terms of the GNU Lesser General Public License as published by the Free
       7             :  * Software Foundation, either version 3 of the License, or (at your option) any
       8             :  * later version.
       9             :  *
      10             :  * libjdx is distributed in the hope that it will be useful, but WITHOUT ANY
      11             :  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12             :  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with libjdx. If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : #include "io/BinaryReader.hpp"
      19             : #include "io/Endianness.hpp"
      20             : 
      21             : #include <unicode/ucnv.h>
      22             : #include <unicode/unistr.h>
      23             : 
      24             : #include <array>
      25             : #include <climits>
      26             : #include <cstring>
      27             : #include <limits>
      28             : 
      29             : // see https://stackoverflow.com/a/43205345,
      30             : // https://stackoverflow.com/a/42074134
      31             : #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
      32             : #define GNU_COMPILER
      33             : #endif
      34             : 
      35             : // switch off disabled-macro-expansion warning
      36             : // see:
      37             : // https://stackoverflow.com/questions/52485278/pragma-diagnostic-when-mixing-clang-analyzers-with-a-gcc-compiler
      38             : #if defined(__has_warning)
      39             : #if __has_warning("-Wdisabled-macro-expansion")
      40             : #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
      41             : #endif
      42             : #endif
      43             : 
      44           2 : libjdx::io::BinaryReader::BinaryReader(
      45           2 :     const std::string& filePath, libjdx::io::Endianness endian)
      46           2 :     : m_ifstream{std::ifstream{}}
      47             :     , m_stringstream{std::nullopt}
      48           2 :     , m_istream{m_ifstream.value()}
      49           4 :     , m_endianness{endian}
      50             : {
      51           2 :     m_ifstream.value().exceptions(
      52             :         std::ios::eofbit | std::ios::failbit | std::ios::badbit);
      53           2 :     m_ifstream.value().open(filePath, std::ios::in | std::ios::binary);
      54           2 : }
      55             : 
      56           4 : libjdx::io::BinaryReader::BinaryReader(std::istream& inputStream,
      57           4 :     libjdx::io::Endianness endian, bool activateExceptions)
      58             :     : m_ifstream{std::nullopt}
      59             :     , m_stringstream{std::nullopt}
      60             :     , m_istream{inputStream}
      61           4 :     , m_endianness{endian}
      62             : {
      63           4 :     if (activateExceptions)
      64             :     {
      65             :         // this also activates exceptions on input_stream, as as m_istream is
      66             :         // a reference to input_stream
      67           2 :         m_istream.exceptions(
      68             :             std::ios::eofbit | std::ios::failbit | std::ios::badbit);
      69             :     }
      70           4 : }
      71             : 
      72           3 : libjdx::io::BinaryReader::BinaryReader(
      73           3 :     std::vector<char>& vec, libjdx::io::Endianness endian)
      74             :     : m_ifstream{std::nullopt}
      75           3 :     , m_stringstream{std::stringstream{}}
      76           3 :     , m_istream{m_stringstream.value()}
      77           3 :     , m_endianness{endian}
      78             : {
      79             :     // see:
      80             :     // https://stackoverflow.com/questions/8815164/c-wrapping-vectorchar-with-istream/8815308
      81             :     // watch out:
      82             :     // https://stackoverflow.com/questions/53199966/tellg-and-seekg-not-working-when-wrap-vectorchar-in-istream
      83             :     // =>
      84             :     // https://stackoverflow.com/questions/23630386/read-vectorchar-as-stream?rq=1
      85             :     // https://stackoverflow.com/questions/45722747/how-can-i-create-a-istream-from-a-uint8-t-vector
      86           3 :     m_stringstream.value().exceptions(
      87             :         std::ios::eofbit | std::ios::failbit | std::ios::badbit);
      88             : #ifdef GNU_COMPILER
      89           6 :     m_stringstream.value().rdbuf()->pubsetbuf(
      90           3 :         vec.data(), static_cast<std::streamsize>(vec.size()));
      91             : #else
      92             :     // pubsetbuf() does not work for Emscripten or Clang
      93             :     // probable reason, it seems to be implementation specific:
      94             :     // https://stackoverflow.com/questions/12481463/stringstream-rdbuf-pubsetbuf-is-not-setting-the-buffer
      95             :     m_stringstream.value().write(
      96             :         vec.data(), static_cast<std::streamsize>(vec.size()));
      97             :     // TODO: better options might be:
      98             :     // https://stackoverflow.com/a/13059195
      99             :     // https://stackoverflow.com/a/8815308
     100             : #endif
     101           3 : }
     102             : 
     103          52 : libjdx::io::BinaryReader::BinaryReader(
     104          52 :     std::vector<uint8_t>& vec, libjdx::io::Endianness endian)
     105             :     : m_ifstream{std::nullopt}
     106          52 :     , m_stringstream{std::stringstream{}}
     107          52 :     , m_istream{m_stringstream.value()}
     108          52 :     , m_endianness{endian}
     109             : {
     110          52 :     m_stringstream.value().exceptions(
     111             :         std::ios::eofbit | std::ios::failbit | std::ios::badbit);
     112             :     // make sure the reinterpret_cast is legal
     113             :     // https://stackoverflow.com/questions/16260033/reinterpret-cast-between-char-and-stduint8-t-safe
     114             :     static_assert(std::is_same_v<std::uint8_t,
     115             :                       char> || std::is_same_v<std::uint8_t, unsigned char>,
     116             :         "uint8_t is not a typedef of char or unsigned char.");
     117             : #ifdef GNU_COMPILER
     118         104 :     m_stringstream.value().rdbuf()->pubsetbuf(
     119             :         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     120          52 :         reinterpret_cast<char*>(vec.data()),
     121          52 :         static_cast<std::streamsize>(vec.size()));
     122             : #else
     123             :     // pubsetbuf() does not work for Emscripten
     124             :     // probable reason:
     125             :     // https://stackoverflow.com/questions/12481463/stringstream-rdbuf-pubsetbuf-is-not-setting-the-buffer
     126             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     127             :     m_stringstream.value().write(reinterpret_cast<char*>(vec.data()),
     128             :         static_cast<std::streamsize>(vec.size()));
     129             :     // TODO: better options might be:
     130             :     // https://stackoverflow.com/a/13059195
     131             :     // https://stackoverflow.com/a/8815308
     132             : #endif
     133          52 : }
     134             : 
     135          13 : std::ios::pos_type libjdx::io::BinaryReader::tellg() const
     136             : {
     137          13 :     return m_istream.tellg();
     138             : }
     139             : 
     140          27 : void libjdx::io::BinaryReader::seekg(
     141             :     std::ios::pos_type position, std::ios_base::seekdir seekdir)
     142             : {
     143          27 :     m_istream.seekg(position, seekdir);
     144          27 : }
     145             : 
     146           5 : std::ios::pos_type libjdx::io::BinaryReader::getLength()
     147             : {
     148           5 :     std::ios::pos_type current = m_istream.tellg();
     149           5 :     m_istream.seekg(0, std::ios::end);
     150           5 :     std::ios::pos_type length = m_istream.tellg();
     151           5 :     m_istream.seekg(current, std::ios::beg);
     152           5 :     return length;
     153             : }
     154             : 
     155           3 : int8_t libjdx::io::BinaryReader::readInt8()
     156             : {
     157             :     static_assert(sizeof(char) == sizeof(int8_t),
     158             :         "Char size does not match int8_t size.");
     159           3 :     return static_cast<int8_t>(m_istream.get());
     160             : }
     161             : 
     162          31 : uint8_t libjdx::io::BinaryReader::readUInt8()
     163             : {
     164             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     165          31 :     return static_cast<uint8_t>(m_istream.get());
     166             : }
     167             : 
     168           2 : uint16_t libjdx::io::BinaryReader::readUInt16()
     169             : {
     170           2 :     return readUInt16(m_endianness);
     171             : }
     172             : 
     173          18 : uint16_t libjdx::io::BinaryReader::readUInt16(libjdx::io::Endianness endian)
     174             : {
     175             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     176             :     // don't initialize array for potentially better performance
     177             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
     178             :     std::array<char, 2> bytes;
     179          18 :     m_istream.read(bytes.data(), bytes.size());
     180             :     return endian == Endianness::LittleEndian
     181          18 :                ? static_cast<uint16_t>(
     182          13 :                    ((static_cast<uint8_t>(bytes[0]) & 0xFFU) << 0U)
     183          13 :                    | ((static_cast<uint8_t>(bytes[1]) & 0xFFU) << 8U))
     184             :                : static_cast<uint16_t>(
     185           5 :                    ((static_cast<uint8_t>(bytes[1]) & 0xFFU) << 0U)
     186          18 :                    | ((static_cast<uint8_t>(bytes[0]) & 0xFFU) << 8U));
     187             : }
     188             : 
     189           2 : int16_t libjdx::io::BinaryReader::readInt16()
     190             : {
     191           2 :     return readInt16(m_endianness);
     192             : }
     193             : 
     194          10 : int16_t libjdx::io::BinaryReader::readInt16(libjdx::io::Endianness endian)
     195             : {
     196             :     static_assert(sizeof(uint16_t) == sizeof(int16_t),
     197             :         "Size of uinte16_t does not match size of int16_t.");
     198          10 :     return static_cast<int16_t>(readUInt16(endian));
     199             : }
     200             : 
     201           2 : uint32_t libjdx::io::BinaryReader::readUInt32()
     202             : {
     203           2 :     return readUInt32(m_endianness);
     204             : }
     205             : 
     206          12 : uint32_t libjdx::io::BinaryReader::readUInt32(libjdx::io::Endianness endian)
     207             : {
     208             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     209             :     // don't initialize array for potentially better performance
     210             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
     211             :     std::array<char, sizeof(uint32_t)> bytes;
     212          12 :     m_istream.read(bytes.data(), bytes.size());
     213             :     return endian == libjdx::io::Endianness::LittleEndian
     214          12 :                ? ((static_cast<uint8_t>(bytes[0]) & 0xFFU) << 0U)
     215           6 :                      | ((static_cast<uint8_t>(bytes[1]) & 0xFFU) << 8U)
     216           6 :                      | ((static_cast<uint8_t>(bytes[2]) & 0xFFU) << 16U)
     217           6 :                      | ((static_cast<uint8_t>(bytes[3]) & 0xFFU) << 24U)
     218           6 :                : ((static_cast<uint8_t>(bytes[3]) & 0xFFU) << 0U)
     219           6 :                      | ((static_cast<uint8_t>(bytes[2]) & 0xFFU) << 8U)
     220           6 :                      | ((static_cast<uint8_t>(bytes[1]) & 0xFFU) << 16U)
     221          12 :                      | ((static_cast<uint8_t>(bytes[0]) & 0xFFU) << 24U);
     222             : }
     223             : 
     224           2 : int32_t libjdx::io::BinaryReader::readInt32()
     225             : {
     226           2 :     return readInt32(m_endianness);
     227             : }
     228             : 
     229           8 : int32_t libjdx::io::BinaryReader::readInt32(libjdx::io::Endianness endian)
     230             : {
     231             :     static_assert(sizeof(uint32_t) == sizeof(int32_t),
     232             :         "Size of uinte32_t does not match size of int32_t.");
     233           8 :     return static_cast<int32_t>(readUInt32(endian));
     234             : }
     235             : 
     236           2 : uint64_t libjdx::io::BinaryReader::readUInt64()
     237             : {
     238           2 :     return readUInt64(m_endianness);
     239             : }
     240             : 
     241          12 : uint64_t libjdx::io::BinaryReader::readUInt64(libjdx::io::Endianness endian)
     242             : {
     243             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     244             :     static_assert(sizeof(uint64_t) == 8, "uint8_t size is not 8 bytes.");
     245             :     // don't initialize array for potentially better performance
     246             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
     247             :     std::array<char, sizeof(uint64_t)> bytes;
     248          12 :     m_istream.read(bytes.data(), bytes.size());
     249             :     return endian == libjdx::io::Endianness::LittleEndian
     250          12 :                ? (static_cast<uint64_t>(static_cast<uint8_t>(bytes[0]) & 0xFFU)
     251             :                      << 0U)
     252           6 :                      | (static_cast<uint64_t>(
     253           6 :                             static_cast<uint8_t>(bytes[1]) & 0xFFU)
     254           6 :                          << 8U)
     255           6 :                      | (static_cast<uint64_t>(
     256           6 :                             static_cast<uint8_t>(bytes[2]) & 0xFFU)
     257           6 :                          << 16U)
     258           6 :                      | (static_cast<uint64_t>(
     259           6 :                             static_cast<uint8_t>(bytes[3]) & 0xFFU)
     260           6 :                          << 24U)
     261           6 :                      | (static_cast<uint64_t>(
     262           6 :                             static_cast<uint8_t>(bytes[4]) & 0xFFU)
     263           6 :                          << 32U)
     264           6 :                      | (static_cast<uint64_t>(
     265           6 :                             static_cast<uint8_t>(bytes[5]) & 0xFFU)
     266           6 :                          << 40U)
     267           6 :                      | (static_cast<uint64_t>(
     268           6 :                             static_cast<uint8_t>(bytes[6]) & 0xFFU)
     269           6 :                          << 48U)
     270           6 :                      | (static_cast<uint64_t>(
     271           6 :                             static_cast<uint8_t>(bytes[7]) & 0xFFU)
     272           6 :                          << 56U)
     273           6 :                : (static_cast<uint64_t>(static_cast<uint8_t>(bytes[7]) & 0xFFU)
     274             :                      << 0U)
     275           6 :                      | (static_cast<uint64_t>(
     276           6 :                             static_cast<uint8_t>(bytes[6]) & 0xFFU)
     277           6 :                          << 8U)
     278           6 :                      | (static_cast<uint64_t>(
     279           6 :                             static_cast<uint8_t>(bytes[5]) & 0xFFU)
     280           6 :                          << 16U)
     281           6 :                      | (static_cast<uint64_t>(
     282           6 :                             static_cast<uint8_t>(bytes[4]) & 0xFFU)
     283           6 :                          << 24U)
     284           6 :                      | (static_cast<uint64_t>(
     285           6 :                             static_cast<uint8_t>(bytes[3]) & 0xFFU)
     286           6 :                          << 32U)
     287           6 :                      | (static_cast<uint64_t>(
     288           6 :                             static_cast<uint8_t>(bytes[2]) & 0xFFU)
     289           6 :                          << 40U)
     290           6 :                      | (static_cast<uint64_t>(
     291           6 :                             static_cast<uint8_t>(bytes[1]) & 0xFFU)
     292           6 :                          << 48U)
     293           6 :                      | (static_cast<uint64_t>(
     294           6 :                             static_cast<uint8_t>(bytes[0]) & 0xFFU)
     295          12 :                          << 56U);
     296             : }
     297             : 
     298           2 : int64_t libjdx::io::BinaryReader::readInt64()
     299             : {
     300           2 :     return readInt64(m_endianness);
     301             : }
     302             : 
     303           8 : int64_t libjdx::io::BinaryReader::readInt64(libjdx::io::Endianness endian)
     304             : {
     305             :     static_assert(sizeof(uint64_t) == sizeof(int64_t),
     306             :         "Size of uinte64_t does not match size of int64_t.");
     307           8 :     return static_cast<int64_t>(readUInt64(endian));
     308             : }
     309             : 
     310           2 : float libjdx::io::BinaryReader::readFloat()
     311             : {
     312           2 :     return readFloat(m_endianness);
     313             : }
     314             : 
     315           4 : float libjdx::io::BinaryReader::readFloat(libjdx::io::Endianness endian)
     316             : {
     317             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     318             :     static_assert(sizeof(float) == sizeof(int32_t),
     319             :         "Size of float does not match size of int32_t.");
     320             :     static_assert(std::numeric_limits<float>::is_iec559,
     321             :         "Float does not conform to IEC 559");
     322             : 
     323             :     // reinterpret_cast<float&>(value) is not guaranteed to work, see:
     324             :     // https://stackoverflow.com/questions/13982340/is-it-safe-to-reinterpret-cast-an-integer-to-float
     325             :     // https://stackoverflow.com/questions/20762952/most-efficient-standard-compliant-way-of-reinterpreting-int-as-float
     326             :     // https://stackoverflow.com/questions/15531232/reinterpreting-an-unsigned-int-to-a-float-in-c
     327           4 :     const int32_t value = readInt32(endian);
     328             :     // don't initialize variable for potentially better performance
     329             :     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
     330             :     float output;
     331           4 :     memcpy(&output, &value, sizeof(int32_t));
     332           4 :     return output;
     333             : }
     334             : 
     335           2 : double libjdx::io::BinaryReader::readDouble()
     336             : {
     337           2 :     return readDouble(m_endianness);
     338             : }
     339             : 
     340           4 : double libjdx::io::BinaryReader::readDouble(libjdx::io::Endianness endian)
     341             : {
     342             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     343             :     static_assert(sizeof(double) == sizeof(int64_t),
     344             :         "Size of double does not match size of int64_t.");
     345             :     static_assert(std::numeric_limits<double>::is_iec559,
     346             :         "Double does not conform to IEC 559");
     347             : 
     348             :     // reinterpret_cast<double&>(value) is not guaranteed to work, see:
     349             :     // https://stackoverflow.com/questions/13982340/is-it-safe-to-reinterpret-cast-an-integer-to-float
     350             :     // https://stackoverflow.com/questions/20762952/most-efficient-standard-compliant-way-of-reinterpreting-int-as-float
     351             :     // https://stackoverflow.com/questions/15531232/reinterpreting-an-unsigned-int-to-a-float-in-c
     352           4 :     const int64_t value = readInt64(endian);
     353             :     // don't initialize variable for potentially better performance
     354             :     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
     355             :     double output;
     356           4 :     memcpy(&output, &value, sizeof(int64_t));
     357           4 :     return output;
     358             : }
     359             : 
     360          27 : std::vector<char> libjdx::io::BinaryReader::readChars(size_t size)
     361             : {
     362          27 :     std::vector<char> dest;
     363          27 :     dest.resize(size);
     364          27 :     m_istream.read(dest.data(), static_cast<std::streamsize>(size));
     365          27 :     return dest;
     366             : }
     367             : 
     368           1 : std::vector<uint8_t> libjdx::io::BinaryReader::readBytes(size_t size)
     369             : {
     370             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     371           1 :     std::vector<uint8_t> dest;
     372             :     // reinterpret cast is safe as signed and unsigned char have same
     373             :     // representation and alignment
     374             :     // see:
     375             :     // https://en.cppreference.com/w/cpp/language/types
     376             :     // also:
     377             :     // https://stackoverflow.com/questions/15078638/can-i-turn-unsigned-char-into-char-and-vice-versa/15172304
     378           1 :     dest.resize(size);
     379             :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     380           1 :     m_istream.read(reinterpret_cast<char*>(dest.data()),
     381           1 :         static_cast<std::streamsize>(size));
     382             :     // alternative implementation:
     383             :     // dest.reserve(size);
     384             :     // for (auto i = 0; i<size; i++)
     385             :     // {
     386             :     //     dest.push_back(m_istream.get());
     387             :     // }
     388           1 :     return dest;
     389             : }
     390             : 
     391          29 : std::string libjdx::io::BinaryReader::readString(
     392             :     const std::string& encoding, int32_t size)
     393             : {
     394          29 :     if (size < 0)
     395             :     {
     396           2 :         return std::string{};
     397             :     }
     398          27 :     if (size > std::numeric_limits<int32_t>::max() / 2)
     399             :     {
     400             :         std::string message
     401           2 :             = std::to_string(size)
     402           4 :               + std::string{" exceeds maximum permitted string char size: "}
     403           3 :               + std::to_string(std::numeric_limits<int32_t>::max() / 2);
     404           1 :         throw std::runtime_error(message.c_str());
     405             :     }
     406             :     // see: https://unicode-org.github.io/icu/userguide/conversion/
     407             :     // see:
     408             :     // https://github.com/unicode-org/icu/blob/master/icu4c/source/samples/ucnv/convsamp.cpp
     409             :     // see:
     410             :     // https://stackoverflow.com/questions/6010793/looking-for-simple-practical-c-examples-of-how-to-use-icu
     411          52 :     std::vector<char> input = readChars(static_cast<size_t>(size));
     412          26 :     UErrorCode status = U_ZERO_ERROR;
     413          26 :     UConverter* converter = ucnv_open(encoding.c_str(), &status);
     414             :     try
     415             :     {
     416             :         // ideomatic ICU
     417             :         // NOLINTNEXTLINE(readability-implicit-bool-conversion)
     418          26 :         if (U_FAILURE(status))
     419             :         {
     420           2 :             std::string message = std::string{std::to_string(status)} + ": "
     421           3 :                                   + u_errorName(status);
     422           1 :             throw std::runtime_error(message.c_str());
     423             :         }
     424             : 
     425             :         // U_SUCCESS(status) must be truthy
     426             :         // reserve buffer
     427             :         // 2 * size UChars is the upper required limit
     428             :         // see:
     429             :         // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/ucnv_8h.html#aa3d7e4ae84f8a95b9735ed3491cdb77e
     430          25 :         int32_t maxUCharBufferSize = 2 * size;
     431          50 :         icu::UnicodeString target(maxUCharBufferSize, UChar32{0}, size);
     432          25 :         UChar* buffer = target.getBuffer(maxUCharBufferSize);
     433             :         // convert input and store in buffer
     434             :         // ignore returned length as it will always give the number of UChars
     435             :         // for the whole input sequence, possibly including NULL UChars
     436          25 :         ucnv_toUChars(converter, buffer, maxUCharBufferSize, input.data(),
     437          25 :             static_cast<int32_t>(input.size()), &status);
     438             :         // do not use length returned from ucnv_toUChars as the string then may
     439             :         // include intermediate nulls
     440          25 :         target.releaseBuffer();
     441             :         // ideomatic ICU
     442             :         // NOLINTNEXTLINE(readability-implicit-bool-conversion)
     443          25 :         if (U_FAILURE(status))
     444             :         {
     445           0 :             std::string message = std::string{std::to_string(status)} + ": "
     446           0 :                                   + u_errorName(status);
     447           0 :             throw std::runtime_error(message.c_str());
     448             :         }
     449             :         // convert to UTF-8 string
     450          50 :         std::string outputString{};
     451          25 :         target.toUTF8String(outputString);
     452             :         // clean up and return
     453          25 :         ucnv_close(converter);
     454          25 :         return outputString;
     455             :     }
     456           2 :     catch (...)
     457             :     {
     458           1 :         ucnv_close(converter);
     459           1 :         throw;
     460             :     }
     461             : }
     462             : 
     463          15 : std::string libjdx::io::BinaryReader::readPrefixedString(
     464             :     StringPrefixType prefixType, const std::string& encoding, int32_t maxSize)
     465             : {
     466             :     static_assert(CHAR_BIT == 8, "Char size is not 8.");
     467             : 
     468          15 :     if (maxSize > std::numeric_limits<uint16_t>::max())
     469             :     {
     470           2 :         std::string message = std::string{"maxSize exceeds permitted maximum "
     471             :                                           "size of 32767: "}
     472           3 :                               + std::to_string(maxSize);
     473           1 :         throw std::runtime_error(message.c_str());
     474             :     }
     475             : 
     476          14 :     int32_t numChars = -1;
     477          14 :     int32_t multiplicationFactor = 1;
     478             : 
     479          14 :     switch (prefixType.numericType)
     480             :     {
     481           1 :     case StringPrefixNumericType::Int8Chars8:
     482             :         // signed char should be interpreted as a signed number here
     483             :         // NOLINTNEXTLINE(bugprone-signed-char-misuse,cert-str34-c)
     484           1 :         numChars = readInt8();
     485           1 :         multiplicationFactor = 1;
     486           1 :         break;
     487           1 :     case StringPrefixNumericType::UInt8Chars8:
     488           1 :         numChars = readUInt8();
     489           1 :         multiplicationFactor = 1;
     490           1 :         break;
     491           1 :     case StringPrefixNumericType::Int8Chars16:
     492             :         // signed char should be interpreted as a signed number here
     493             :         // NOLINTNEXTLINE(bugprone-signed-char-misuse,cert-str34-c)
     494           1 :         numChars = readInt8();
     495           1 :         multiplicationFactor = 2;
     496           1 :         break;
     497           1 :     case StringPrefixNumericType::UInt8Chars16:
     498           1 :         numChars = readUInt8();
     499           1 :         multiplicationFactor = 2;
     500           1 :         break;
     501           2 :     case StringPrefixNumericType::Int16Chars8:
     502           2 :         numChars = readInt16(prefixType.endianness);
     503           2 :         multiplicationFactor = 1;
     504           2 :         break;
     505           1 :     case StringPrefixNumericType::UInt16Chars8:
     506           1 :         numChars = readUInt16(prefixType.endianness);
     507           1 :         multiplicationFactor = 1;
     508           1 :         break;
     509           4 :     case StringPrefixNumericType::Int16Chars16:
     510           4 :         numChars = readInt16(prefixType.endianness);
     511           4 :         multiplicationFactor = 2;
     512           4 :         break;
     513           3 :     case StringPrefixNumericType::UInt16Chars16:
     514           3 :         numChars = readUInt16(prefixType.endianness);
     515           3 :         multiplicationFactor = 2;
     516           3 :         break;
     517             :     }
     518             : 
     519          14 :     int32_t numBytes = numChars * multiplicationFactor;
     520             : 
     521          14 :     if (numBytes > maxSize)
     522             :     {
     523           2 :         std::string message = std::string{"Number of bytes \""}
     524           4 :                               + std::to_string(numBytes)
     525           4 :                               + std::string{"\" from string prefix "
     526             :                                             "exceeds specified maximum "
     527             :                                             "size of: "}
     528           3 :                               + std::to_string(maxSize);
     529           1 :         throw std::runtime_error(message.c_str());
     530             :     }
     531             : 
     532          13 :     return readString(encoding, numBytes);
     533             : }

Generated by: LCOV version 1.14