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 : }
|