Coverage Report

Created: 2025-11-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/bitstream.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "bitstream.h"
22
23
#include <utility>
24
#include <cstring>
25
#include <cassert>
26
27
#if !defined(HAVE_BIT)
28
#include <type_traits>
29
#else
30
#include <bit>
31
#endif
32
33
230
#define MAX_UVLC_LEADING_ZEROS 20
34
35
36
StreamReader_istream::StreamReader_istream(std::unique_ptr<std::istream>&& istr)
37
0
    : m_istr(std::move(istr))
38
0
{
39
0
  m_istr->seekg(0, std::ios_base::end);
40
0
  m_length = m_istr->tellg();
41
0
  m_istr->seekg(0, std::ios_base::beg);
42
0
}
43
44
uint64_t StreamReader_istream::get_position() const
45
0
{
46
0
  return m_istr->tellg();
47
0
}
48
49
StreamReader::grow_status StreamReader_istream::wait_for_file_size(uint64_t target_size)
50
0
{
51
0
  return (target_size > m_length) ? grow_status::size_beyond_eof : grow_status::size_reached;
52
0
}
53
54
bool StreamReader_istream::read(void* data, size_t size)
55
0
{
56
0
  uint64_t end_pos = get_position() + size;
57
0
  if (end_pos > m_length) {
58
0
    return false;
59
0
  }
60
61
0
  m_istr->read((char*) data, size);
62
0
  return true;
63
0
}
64
65
bool StreamReader_istream::seek(uint64_t position)
66
0
{
67
0
  if (position > m_length)
68
0
    return false;
69
70
0
  m_istr->seekg(position, std::ios_base::beg);
71
0
  return true;
72
0
}
73
74
75
StreamReader_memory::StreamReader_memory(const uint8_t* data, size_t size, bool copy)
76
120k
    : m_length(size),
77
120k
      m_position(0)
78
120k
{
79
120k
  if (copy) {
80
0
    m_owned_data = new uint8_t[m_length];
81
0
    memcpy(m_owned_data, data, size);
82
83
0
    m_data = m_owned_data;
84
0
  }
85
120k
  else {
86
120k
    m_data = data;
87
120k
  }
88
120k
}
89
90
StreamReader_memory::~StreamReader_memory()
91
120k
{
92
120k
  if (m_owned_data) {
93
0
    delete[] m_owned_data;
94
0
  }
95
120k
}
96
97
uint64_t StreamReader_memory::get_position() const
98
1.92M
{
99
1.92M
  return m_position;
100
1.92M
}
101
102
StreamReader::grow_status StreamReader_memory::wait_for_file_size(uint64_t target_size)
103
1.88M
{
104
1.88M
  return (target_size > m_length) ? grow_status::size_beyond_eof : grow_status::size_reached;
105
1.88M
}
106
107
bool StreamReader_memory::read(void* data, size_t size)
108
6.96M
{
109
6.96M
  uint64_t end_pos = m_position + size;
110
6.96M
  if (end_pos > m_length) {
111
0
    return false;
112
0
  }
113
114
6.96M
  memcpy(data, &m_data[m_position], size);
115
6.96M
  m_position += size;
116
117
6.96M
  return true;
118
6.96M
}
119
120
bool StreamReader_memory::seek(uint64_t position)
121
390k
{
122
390k
  if (position > m_length)
123
0
    return false;
124
125
390k
  m_position = position;
126
390k
  return true;
127
390k
}
128
129
130
StreamReader_CApi::StreamReader_CApi(const heif_reader* func_table, void* userdata)
131
0
    : m_func_table(func_table), m_userdata(userdata)
132
0
{
133
0
}
134
135
StreamReader::grow_status StreamReader_CApi::wait_for_file_size(uint64_t target_size)
136
0
{
137
0
  heif_reader_grow_status status = m_func_table->wait_for_file_size(target_size, m_userdata);
138
0
  switch (status) {
139
0
    case heif_reader_grow_status_size_reached:
140
0
      return grow_status::size_reached;
141
0
    case heif_reader_grow_status_timeout:
142
0
      return grow_status::timeout;
143
0
    case heif_reader_grow_status_size_beyond_eof:
144
0
      return grow_status::size_beyond_eof;
145
0
    default:
146
0
      assert(0);
147
0
      return grow_status::size_beyond_eof;
148
0
  }
149
0
}
150
151
152
BitstreamRange::BitstreamRange(std::shared_ptr<StreamReader> istr,
153
                               size_t length,
154
                               BitstreamRange* parent)
155
933k
    : m_istr(std::move(istr)), m_parent_range(parent), m_remaining(length)
156
933k
{
157
933k
  if (parent) {
158
812k
    m_nesting_level = parent->m_nesting_level + 1;
159
812k
  }
160
933k
}
161
162
163
BitstreamRange::BitstreamRange(std::shared_ptr<StreamReader> istr,
164
                               size_t start,
165
                               size_t end) // one past end
166
271k
  : m_istr(std::move(istr)), m_remaining(end)
167
271k
{
168
271k
  bool success = m_istr->seek(start);
169
271k
  assert(success);
170
271k
  (void)success; // TODO
171
271k
}
172
173
174
StreamReader::grow_status BitstreamRange::wait_until_range_is_available()
175
0
{
176
0
  return m_istr->wait_for_file_size(m_istr->get_position() + m_remaining);
177
0
}
178
179
180
uint8_t BitstreamRange::read8()
181
1.28M
{
182
1.28M
  if (!prepare_read(1)) {
183
115k
    return 0;
184
115k
  }
185
186
1.16M
  uint8_t buf;
187
188
1.16M
  auto istr = get_istream();
189
1.16M
  bool success = istr->read((char*) &buf, 1);
190
191
1.16M
  if (!success) {
192
0
    set_eof_while_reading();
193
0
    return 0;
194
0
  }
195
196
1.16M
  return buf;
197
1.16M
}
198
199
200
uint16_t BitstreamRange::read16()
201
780k
{
202
780k
  if (!prepare_read(2)) {
203
22.5k
    return 0;
204
22.5k
  }
205
206
757k
  uint8_t buf[2];
207
208
757k
  auto istr = get_istream();
209
757k
  bool success = istr->read((char*) buf, 2);
210
211
757k
  if (!success) {
212
0
    set_eof_while_reading();
213
0
    return 0;
214
0
  }
215
216
757k
  return static_cast<uint16_t>((buf[0] << 8) | (buf[1]));
217
757k
}
218
219
220
int16_t BitstreamRange::read16s()
221
4.71k
{
222
4.71k
  uint16_t v = read16();
223
224
4.71k
  if (v & 0x8000) {
225
481
    auto val = static_cast<int16_t>((~v) & 0x7fff);
226
481
    return static_cast<int16_t>(-val - 1);
227
481
  }
228
4.23k
  else {
229
4.23k
    return static_cast<int16_t>(v);
230
4.23k
  }
231
4.71k
}
232
233
234
uint32_t BitstreamRange::read24()
235
602
{
236
602
  if (!prepare_read(3)) {
237
0
    return 0;
238
0
  }
239
240
602
  uint8_t buf[3];
241
242
602
  auto istr = get_istream();
243
602
  bool success = istr->read((char*) buf, 3);
244
245
602
  if (!success) {
246
0
    set_eof_while_reading();
247
0
    return 0;
248
0
  }
249
250
602
  return (uint32_t) ((buf[0] << 16) |
251
602
                     (buf[1] << 8) |
252
602
                     (buf[2]));
253
602
}
254
255
uint32_t BitstreamRange::read32()
256
1.49G
{
257
1.49G
  if (!prepare_read(4)) {
258
1.48G
    return 0;
259
1.48G
  }
260
261
4.23M
  uint8_t buf[4];
262
263
4.23M
  auto istr = get_istream();
264
4.23M
  bool success = istr->read((char*) buf, 4);
265
266
4.23M
  if (!success) {
267
0
    set_eof_while_reading();
268
0
    return 0;
269
0
  }
270
271
4.23M
  return (uint32_t) ((buf[0] << 24) |
272
4.23M
                     (buf[1] << 16) |
273
4.23M
                     (buf[2] << 8) |
274
4.23M
                     (buf[3]));
275
4.23M
}
276
277
278
uint64_t BitstreamRange::read_uint(int len)
279
72.8k
{
280
72.8k
  switch (len)
281
72.8k
  {
282
1.63k
    case 8:
283
1.63k
      return read8();
284
59.8k
    case 16:
285
59.8k
      return read16();
286
602
    case 24:
287
602
      return read24();
288
10.4k
    case 32:
289
10.4k
      return read32();
290
339
    case 64:
291
339
      return read64();
292
0
    default:
293
0
      assert(false);
294
0
      return 0;
295
72.8k
  }
296
72.8k
}
297
298
299
int32_t BitstreamRange::read32s()
300
13.2k
{
301
13.2k
  uint32_t v = read32();
302
303
13.2k
  if (v & 0x80000000) {
304
1.21k
    return -static_cast<int32_t>((~v) & 0x7fffffff) -1;
305
1.21k
  }
306
12.0k
  else {
307
12.0k
    return static_cast<int32_t>(v);
308
12.0k
  }
309
13.2k
}
310
311
312
uint64_t BitstreamRange::read64()
313
1.42k
{
314
1.42k
  if (!prepare_read(8)) {
315
82
    return 0;
316
82
  }
317
318
1.34k
  uint8_t buf[8];
319
320
1.34k
  auto istr = get_istream();
321
1.34k
  bool success = istr->read((char*) buf, 8);
322
323
1.34k
  if (!success) {
324
0
    set_eof_while_reading();
325
0
    return 0;
326
0
  }
327
328
1.34k
  return ((static_cast<uint64_t>(buf[0]) << 56) |
329
1.34k
          (static_cast<uint64_t>(buf[1]) << 48) |
330
1.34k
          (static_cast<uint64_t>(buf[2]) << 40) |
331
1.34k
          (static_cast<uint64_t>(buf[3]) << 32) |
332
1.34k
          (static_cast<uint64_t>(buf[4]) << 24) |
333
1.34k
          (static_cast<uint64_t>(buf[5]) << 16) |
334
1.34k
          (static_cast<uint64_t>(buf[6]) << 8) |
335
1.34k
          (static_cast<uint64_t>(buf[7])));
336
1.34k
}
337
338
339
int64_t BitstreamRange::read64s()
340
134
{
341
134
  uint64_t v = read64();
342
343
134
  if (v & 0x8000000000000000) {
344
23
    return -static_cast<int64_t >((~v) & 0x7fffffffffffffff) -1;
345
23
  }
346
111
  else {
347
111
    return static_cast<int64_t >(v);
348
111
  }
349
134
}
350
351
352
float BitstreamRange::read_float32()
353
3.64k
{
354
3.64k
#if __cpp_lib_bit_cast >= 201806L
355
3.64k
  uint32_t i = read32();
356
3.64k
  return std::bit_cast<float>(i); // this works directly on the value layout, thus we do not have to worry about memory layout
357
#else
358
  // compiler too old to support bit_cast
359
360
  // TODO: I am not sure this works everywhere as there seem to be systems where
361
  //       the float byte order is different from the integer endianness
362
  //       https://en.wikipedia.org/wiki/Endianness#Floating_point
363
  uint32_t i = read32();
364
  float f;
365
  memcpy(&f, &i, sizeof(float));
366
  return f;
367
#endif
368
3.64k
}
369
370
371
void StreamWriter::write_float32(float v)
372
0
{
373
0
#if __cpp_lib_bit_cast >= 201806L
374
0
  write32(std::bit_cast<uint32_t>(v)); // this works directly on the value layout, thus we do not have to worry about memory layout
375
#else
376
  // compiler too old to support bit_cast
377
378
  // TODO: I am not sure this works everywhere as there seem to be systems where
379
  //       the float byte order is different from the integer endianness
380
  //       https://en.wikipedia.org/wiki/Endianness#Floating_point
381
  uint32_t i;
382
  memcpy(&i, &v, sizeof(float));
383
  write32(i);
384
#endif
385
0
}
386
387
388
std::string BitstreamRange::read_string()
389
91.5k
{
390
91.5k
  std::string str;
391
392
  // Reading a string when no more data is available, returns an empty string.
393
  // Such a case happens, for example, when reading a 'url' box without content.
394
91.5k
  if (eof()) {
395
4.47k
    return std::string();
396
4.47k
  }
397
398
87.0k
  auto istr = get_istream();
399
400
564k
  for (;;) {
401
564k
    if (!prepare_read(1)) {
402
1.74k
      return std::string();
403
1.74k
    }
404
405
563k
    char c;
406
563k
    bool success = istr->read(&c, 1);
407
408
563k
    if (!success) {
409
0
      set_eof_while_reading();
410
0
      return std::string();
411
0
    }
412
413
563k
    if (c == 0) {
414
85.3k
      break;
415
85.3k
    }
416
477k
    else {
417
477k
      str += (char) c;
418
477k
    }
419
563k
  }
420
421
85.3k
  return str;
422
87.0k
}
423
424
425
std::string BitstreamRange::read_fixed_string(int len)
426
416
{
427
416
  std::string str;
428
429
416
  if (!prepare_read(len)) {
430
39
    return std::string();
431
39
  }
432
433
377
  auto istr = get_istream();
434
435
377
  uint8_t n;
436
377
  bool success = istr->read(&n, 1);
437
377
  if (!success || n > len - 1) {
438
160
    return {};
439
160
  }
440
441
2.59k
  for (int i = 0; i < n; i++) {
442
2.37k
    char c;
443
2.37k
    success = istr->read(&c, 1);
444
445
2.37k
    if (!success) {
446
0
      set_eof_while_reading();
447
0
      return std::string();
448
0
    }
449
450
2.37k
    str += (char) c;
451
2.37k
  }
452
453
217
  istr->seek_cur(len-n-1);
454
455
217
  return str;
456
217
}
457
458
459
bool BitstreamRange::read(uint8_t* data, size_t n)
460
186k
{
461
186k
  if (!prepare_read(n)) {
462
213
    return false;
463
213
  }
464
465
186k
  auto istr = get_istream();
466
186k
  bool success = istr->read(data, n);
467
468
186k
  if (!success) {
469
0
    set_eof_while_reading();
470
0
  }
471
472
186k
  return success;
473
186k
}
474
475
476
bool BitstreamRange::prepare_read(size_t nBytes)
477
1.50G
{
478
  // Note: we do not test for negative nBytes anymore because we now use the unsigned size_t
479
480
1.50G
  if (m_remaining < nBytes) {
481
    // --- not enough data left in box -> move to end of box and set error flag
482
483
1.48G
    skip_to_end_of_box();
484
485
1.48G
    m_error = true;
486
1.48G
    return false;
487
1.48G
  }
488
21.2M
  else {
489
    // --- this is the normal case (m_remaining >= nBytes)
490
491
21.2M
    if (m_parent_range) {
492
14.3M
      if (!m_parent_range->prepare_read(nBytes)) {
493
0
        return false;
494
0
      }
495
14.3M
    }
496
497
21.2M
    m_remaining -= nBytes;
498
499
21.2M
    return true;
500
21.2M
  }
501
1.50G
}
502
503
504
StreamReader::grow_status BitstreamRange::wait_for_available_bytes(size_t nBytes)
505
1.84M
{
506
1.84M
  int64_t target_size = m_istr->get_position() + nBytes;
507
508
1.84M
  return m_istr->wait_for_file_size(target_size);
509
1.84M
}
510
511
512
void BitstreamRange::skip_without_advancing_file_pos(size_t n)
513
176k
{
514
176k
  assert(n <= m_remaining);
515
516
176k
  m_remaining -= n;
517
518
176k
  if (m_parent_range) {
519
99.7k
    m_parent_range->skip_without_advancing_file_pos(n);
520
99.7k
  }
521
176k
}
522
523
524
BitReader::BitReader(const uint8_t* buffer, int len)
525
124
{
526
124
  data = buffer;
527
124
  data_length = len;
528
124
  bytes_remaining = len;
529
530
124
  nextbits = 0;
531
124
  nextbits_cnt = 0;
532
533
124
  refill();
534
124
}
535
536
537
uint32_t BitReader::get_bits(int n)
538
1.19k
{
539
1.19k
  assert(n <= 32);
540
541
1.19k
  if (nextbits_cnt < n) {
542
191
    refill();
543
191
  }
544
545
1.19k
  uint64_t val = nextbits;
546
1.19k
  val >>= 64 - n;
547
548
#if AVOID_FUZZER_FALSE_POSITIVE
549
  // Shifting an unsigned integer left such that some MSBs fall out is well defined in C++ despite the fuzzer claiming otherwise.
550
  nextbits &= (0xffffffffffffffffULL >> n);
551
#endif
552
553
1.19k
  nextbits <<= n;
554
1.19k
  nextbits_cnt -= n;
555
556
1.19k
  return static_cast<uint32_t>(val);
557
1.19k
}
558
559
560
uint8_t BitReader::get_bits8(int n)
561
364
{
562
364
  assert(n>0 && n <= 8);
563
364
  return static_cast<uint8_t>(get_bits(n));
564
364
}
565
566
uint16_t BitReader::get_bits16(int n)
567
0
{
568
0
  assert(n>0 && n <= 16);
569
0
  return static_cast<uint16_t>(get_bits(n));
570
0
}
571
572
uint32_t BitReader::get_bits32(int n)
573
227
{
574
227
  assert(n>0 && n <= 32);
575
227
  return static_cast<uint32_t>(get_bits(n));
576
227
}
577
578
int32_t BitReader::get_bits32s()
579
0
{
580
0
  uint32_t bits = get_bits(32);
581
0
  return static_cast<int32_t>(bits);
582
0
}
583
584
585
bool BitReader::get_flag()
586
0
{
587
0
  return (get_bits(1) == 0x01);
588
0
}
589
590
std::vector<uint8_t> BitReader::read_bytes(uint32_t n)
591
0
{
592
  // TODO: this implementation isn't very efficient
593
0
  std::vector<uint8_t> bytes;
594
0
  for (uint32_t i = 0; i < n; i++) {
595
0
    bytes.push_back(get_bits8(8));
596
0
  }
597
0
  return bytes;
598
0
}
599
600
int BitReader::get_bits_fast(int n)
601
0
{
602
0
  assert(nextbits_cnt >= n);
603
604
0
  uint64_t val = nextbits;
605
0
  val >>= 64 - n;
606
607
0
  nextbits <<= n;
608
0
  nextbits_cnt -= n;
609
610
0
  return (int) val;
611
0
}
612
613
int BitReader::peek_bits(int n)
614
0
{
615
0
  if (nextbits_cnt < n) {
616
0
    refill();
617
0
  }
618
619
0
  uint64_t val = nextbits;
620
0
  val >>= 64 - n;
621
622
0
  return (int) val;
623
0
}
624
625
void BitReader::skip_bytes(int nBytes)
626
0
{
627
  // TODO: this is slow
628
0
  while (nBytes--) {
629
0
    skip_bits(8);
630
0
  }
631
0
}
632
633
void BitReader::skip_bits(int n)
634
56
{
635
56
  if (nextbits_cnt < n) {
636
3
    refill();
637
3
  }
638
639
#if AVOID_FUZZER_FALSE_POSITIVE
640
  nextbits &= (0xffffffffffffffffULL >> n);
641
#endif
642
643
56
  nextbits <<= n;
644
56
  nextbits_cnt -= n;
645
56
}
646
647
void BitReader::skip_bits_fast(int n)
648
0
{
649
#if AVOID_FUZZER_FALSE_POSITIVE
650
  nextbits &= (0xffffffffffffffffULL >> n);
651
#endif
652
653
0
  nextbits <<= n;
654
0
  nextbits_cnt -= n;
655
0
}
656
657
void BitReader::skip_to_byte_boundary()
658
0
{
659
0
  int nskip = (nextbits_cnt & 7);
660
661
#if AVOID_FUZZER_FALSE_POSITIVE
662
  nextbits &= (0xffffffffffffffffULL >> nskip);
663
#endif
664
665
0
  nextbits <<= nskip;
666
0
  nextbits_cnt -= nskip;
667
0
}
668
669
bool BitReader::get_uvlc(int* value)
670
74
{
671
74
  int num_zeros = 0;
672
673
302
  while (get_bits(1) == 0) {
674
230
    num_zeros++;
675
676
230
    if (num_zeros > MAX_UVLC_LEADING_ZEROS) { return false; }
677
230
  }
678
679
72
  int offset = 0;
680
72
  if (num_zeros != 0) {
681
33
    offset = (int) get_bits(num_zeros);
682
33
    *value = offset + (1 << num_zeros) - 1;
683
33
    assert(*value > 0);
684
33
    return true;
685
33
  }
686
39
  else {
687
39
    *value = 0;
688
39
    return true;
689
39
  }
690
72
}
691
692
bool BitReader::get_svlc(int* value)
693
0
{
694
0
  int v;
695
0
  if (!get_uvlc(&v)) {
696
0
    return false;
697
0
  }
698
0
  else if (v == 0) {
699
0
    *value = v;
700
0
    return true;
701
0
  }
702
703
0
  bool negative = ((v & 1) == 0);
704
0
  *value = negative ? -v / 2 : (v + 1) / 2;
705
0
  return true;
706
0
}
707
708
void BitReader::refill()
709
318
{
710
#if 0
711
  // TODO: activate me once I'm sure this works
712
  while (nextbits_cnt <= 64-8 && bytes_remaining) {
713
    uint64_t newval = *data++;
714
    bytes_remaining--;
715
716
    nextbits_cnt += 8;
717
    newval <<= 64-nextbits_cnt;
718
    nextbits |= newval;
719
  }
720
#else
721
318
  int shift = 64 - nextbits_cnt;
722
723
1.74k
  while (shift >= 8 && bytes_remaining) {
724
1.43k
    uint64_t newval = *data++;
725
1.43k
    bytes_remaining--;
726
727
1.43k
    shift -= 8;
728
1.43k
    newval <<= shift;
729
1.43k
    nextbits |= newval;
730
1.43k
  }
731
732
318
  nextbits_cnt = 64 - shift;
733
318
#endif
734
318
}
735
736
737
void StreamWriter::write8(uint8_t v)
738
9.25k
{
739
9.25k
  if (m_position == m_data.size()) {
740
9.25k
    m_data.push_back(v);
741
9.25k
    m_position++;
742
9.25k
  }
743
0
  else {
744
0
    m_data[m_position++] = v;
745
0
  }
746
9.25k
}
747
748
749
void StreamWriter::write16(uint16_t v)
750
27.7k
{
751
27.7k
  size_t required_size = m_position + 2;
752
753
27.7k
  if (required_size > m_data.size()) {
754
27.7k
    m_data.resize(required_size);
755
27.7k
  }
756
757
27.7k
  m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
758
27.7k
  m_data[m_position++] = uint8_t(v & 0xFF);
759
27.7k
}
760
761
762
void StreamWriter::write16s(int16_t v16s)
763
0
{
764
0
  uint16_t v;
765
0
  if (v16s >= 0) {
766
0
    v = static_cast<uint16_t>(v16s);
767
0
  }
768
0
  else {
769
0
    auto val = static_cast<uint16_t>((-v16s-1));
770
0
    v = static_cast<uint16_t>(~val);
771
0
  }
772
773
0
  write16(v);
774
0
}
775
776
777
void StreamWriter::write24(uint32_t v)
778
0
{
779
0
  size_t required_size = m_position + 3;
780
781
0
  if (required_size > m_data.size()) {
782
0
    m_data.resize(required_size);
783
0
  }
784
785
0
  m_data[m_position++] = uint8_t((v >> 16) & 0xFF);
786
0
  m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
787
0
  m_data[m_position++] = uint8_t(v & 0xFF);
788
0
}
789
790
791
void StreamWriter::write32(uint32_t v)
792
48.6k
{
793
48.6k
  size_t required_size = m_position + 4;
794
795
48.6k
  if (required_size > m_data.size()) {
796
16.2k
    m_data.resize(required_size);
797
16.2k
  }
798
799
48.6k
  m_data[m_position++] = uint8_t((v >> 24) & 0xFF);
800
48.6k
  m_data[m_position++] = uint8_t((v >> 16) & 0xFF);
801
48.6k
  m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
802
48.6k
  m_data[m_position++] = uint8_t(v & 0xFF);
803
48.6k
}
804
805
806
void StreamWriter::write32s(int32_t v32s)
807
0
{
808
0
  uint32_t v;
809
0
  if (v32s >= 0) {
810
0
    v = static_cast<uint32_t>(v32s);
811
0
  }
812
0
  else {
813
0
    v = ~static_cast<uint32_t>((-v32s-1));
814
0
  }
815
816
0
  write32(v);
817
0
}
818
819
820
void StreamWriter::write64(uint64_t v)
821
0
{
822
0
  size_t required_size = m_position + 8;
823
824
0
  if (required_size > m_data.size()) {
825
0
    m_data.resize(required_size);
826
0
  }
827
828
0
  m_data[m_position++] = uint8_t((v >> 56) & 0xFF);
829
0
  m_data[m_position++] = uint8_t((v >> 48) & 0xFF);
830
0
  m_data[m_position++] = uint8_t((v >> 40) & 0xFF);
831
0
  m_data[m_position++] = uint8_t((v >> 32) & 0xFF);
832
0
  m_data[m_position++] = uint8_t((v >> 24) & 0xFF);
833
0
  m_data[m_position++] = uint8_t((v >> 16) & 0xFF);
834
0
  m_data[m_position++] = uint8_t((v >> 8) & 0xFF);
835
0
  m_data[m_position++] = uint8_t(v & 0xFF);
836
0
}
837
838
839
void StreamWriter::write64s(int64_t v)
840
0
{
841
0
  write64(reinterpret_cast<uint64_t&>(v));
842
0
}
843
844
845
void StreamWriter::write(int size, uint64_t value)
846
0
{
847
0
  if (size == 1) {
848
0
    assert(value <= 0xFF);
849
0
    write8((uint8_t) value);
850
0
  }
851
0
  else if (size == 2) {
852
0
    assert(value <= 0xFFFF);
853
0
    write16((uint16_t) value);
854
0
  }
855
0
  else if (size == 4) {
856
0
    assert(value <= 0xFFFFFFFF);
857
0
    write32((uint32_t) value);
858
0
  }
859
0
  else if (size == 8) {
860
0
    write64((uint64_t) value);
861
0
  }
862
0
  else {
863
0
    assert(false); // unimplemented size
864
0
  }
865
0
}
866
867
868
void StreamWriter::write(const std::string& str)
869
0
{
870
0
  size_t required_size = m_position + str.size() + 1;
871
872
0
  if (required_size > m_data.size()) {
873
0
    m_data.resize(required_size);
874
0
  }
875
876
0
  for (size_t i = 0; i < str.size(); i++) {
877
0
    m_data[m_position++] = str[i];
878
0
  }
879
880
0
  m_data[m_position++] = 0;
881
0
}
882
883
884
void StreamWriter::write_fixed_string(std::string s, size_t len)
885
0
{
886
0
  size_t required_size = m_position + len;
887
888
0
  if (required_size > m_data.size()) {
889
0
    m_data.resize(required_size);
890
0
  }
891
892
0
  size_t n_chars = std::min(s.length(), len - 1);
893
0
  assert(n_chars <= 255);
894
0
  m_data[m_position++] = static_cast<uint8_t>(n_chars);
895
896
0
  for (size_t i = 0; i < s.size() && i < len - 1; i++) {
897
0
    m_data[m_position++] = s[i];
898
0
  }
899
900
0
  for (size_t i = s.size(); i < len - 1; i++) {
901
0
    m_data[m_position++] = 0;
902
0
  }
903
0
}
904
905
906
void StreamWriter::write(const std::vector<uint8_t>& vec)
907
6.95k
{
908
6.95k
  size_t required_size = m_position + vec.size();
909
910
6.95k
  if (required_size > m_data.size()) {
911
6.94k
    m_data.resize(required_size);
912
6.94k
  }
913
914
6.95k
  memcpy(m_data.data() + m_position, vec.data(), vec.size());
915
6.95k
  m_position += vec.size();
916
6.95k
}
917
918
919
void StreamWriter::write(const StreamWriter& writer)
920
0
{
921
0
  size_t required_size = m_position + writer.get_data().size();
922
923
0
  if (required_size > m_data.size()) {
924
0
    m_data.resize(required_size);
925
0
  }
926
927
0
  const auto& data = writer.get_data();
928
929
0
  memcpy(m_data.data() + m_position, data.data(), data.size());
930
931
0
  m_position += data.size();
932
0
}
933
934
935
void StreamWriter::skip(int n)
936
16.2k
{
937
16.2k
  assert(m_position == m_data.size());
938
16.2k
  m_data.resize(m_data.size() + n);
939
16.2k
  m_position += n;
940
16.2k
}
941
942
943
void StreamWriter::insert(int nBytes)
944
0
{
945
0
  assert(nBytes >= 0);
946
947
0
  if (nBytes == 0) {
948
0
    return;
949
0
  }
950
951
0
  m_data.resize(m_data.size() + nBytes);
952
953
0
  if (m_position < m_data.size() - nBytes) {
954
0
    memmove(m_data.data() + m_position + nBytes,
955
0
            m_data.data() + m_position,
956
0
            m_data.size() - nBytes - m_position);
957
0
  }
958
0
}