Coverage Report

Created: 2026-10-17 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/bitstream.h
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
#ifndef LIBHEIF_BITSTREAM_H
22
#define LIBHEIF_BITSTREAM_H
23
24
#include <cinttypes>
25
#include <cstddef>
26
27
#include <vector>
28
#include <string>
29
#include <memory>
30
#include <limits>
31
#include <istream>
32
#include <string>
33
#include <cassert>
34
35
#include "error.h"
36
#include <algorithm>
37
38
39
class StreamReader
40
{
41
public:
42
194k
  virtual ~StreamReader() = default;
43
44
  virtual uint64_t get_position() const = 0;
45
46
  enum class grow_status : uint8_t
47
  {
48
    size_reached,   // requested size has been reached
49
    timeout,        // size has not been reached yet, but it may still grow further
50
    size_beyond_eof // size has not been reached and never will. The file has grown to its full size
51
  };
52
53
  // a StreamReader can maintain a timeout for waiting for new data
54
  virtual grow_status wait_for_file_size(uint64_t target_size) = 0;
55
56
  // returns 'false' when we read out of the available file size
57
  virtual bool read(void* data, size_t size) = 0;
58
59
  virtual bool seek(uint64_t position) = 0;
60
61
  bool seek_cur(uint64_t position_offset)
62
555k
  {
63
555k
    return seek(get_position() + position_offset);
64
555k
  }
65
66
  // Informs the reader implementation that we will process data in the given range.
67
  // The reader can use this information to retrieve a larger chunk of data instead of individual read() calls.
68
  // Returns the file size that was made available, but you still have to check each read() call.
69
  // Returning a value shorter than the requested range end indicates to libheif that the data is not available.
70
  // Returns 0 on error.
71
0
  virtual uint64_t request_range(uint64_t start, uint64_t end_pos) {
72
0
    return std::numeric_limits<uint64_t>::max();
73
0
  }
74
75
0
  virtual void release_range(uint64_t start, uint64_t end_pos) { }
76
77
0
  virtual void preload_range_hint(uint64_t start, uint64_t end_pos) { }
78
79
0
  Error get_error() const {
80
0
    return m_last_error;
81
0
  }
82
83
0
  void clear_last_error() { m_last_error = {}; }
84
85
protected:
86
  Error m_last_error;
87
};
88
89
#include <iostream>
90
91
class StreamReader_istream : public StreamReader
92
{
93
public:
94
  StreamReader_istream(std::unique_ptr<std::istream>&& istr);
95
96
  uint64_t get_position() const override;
97
98
  grow_status wait_for_file_size(uint64_t target_size) override;
99
100
  bool read(void* data, size_t size) override;
101
102
  bool seek(uint64_t position) override;
103
104
0
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
105
    // std::cout << "[istream] request_range " << start << " - " << end_pos << "\n";
106
0
    return std::min(end_pos, m_length);
107
0
  }
108
109
0
  void release_range(uint64_t start, uint64_t end_pos) override {
110
    // std::cout << "[istream] release_range " << start << " - " << end_pos << "\n";
111
0
  }
112
113
0
  void preload_range_hint(uint64_t start, uint64_t end_pos) override {
114
    // std::cout << "[istream] preload_range_hint " << start << " - " << end_pos << "\n";
115
0
  }
116
117
private:
118
  std::unique_ptr<std::istream> m_istr;
119
  uint64_t m_length;
120
};
121
122
123
class StreamReader_memory : public StreamReader
124
{
125
public:
126
  StreamReader_memory(const uint8_t* data, size_t size, bool copy);
127
128
  ~StreamReader_memory() override;
129
130
  uint64_t get_position() const override;
131
132
  grow_status wait_for_file_size(uint64_t target_size) override;
133
134
  bool read(void* data, size_t size) override;
135
136
  bool seek(uint64_t position) override;
137
138
  // end_pos is last byte to read + 1. I.e. like a file size.
139
500k
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
140
500k
    return m_length;
141
500k
  }
142
143
private:
144
  const uint8_t* m_data;
145
  uint64_t m_length;
146
  uint64_t m_position = 0;
147
148
  // if we made a copy of the data, we store a pointer to the owned memory area here
149
  uint8_t* m_owned_data = nullptr;
150
};
151
152
153
class StreamReader_CApi : public StreamReader
154
{
155
public:
156
  StreamReader_CApi(const heif_reader* func_table, void* userdata);
157
158
0
  uint64_t get_position() const override { return m_func_table->get_position(m_userdata); }
159
160
  StreamReader::grow_status wait_for_file_size(uint64_t target_size) override;
161
162
  bool read(void* data, size_t size) override
163
0
  {
164
0
    if (size == 0) {
165
      // Do not hand a NULL buffer (an empty std::vector) to the user's read callback; it may pass
166
      // it on to memcpy(), which is UB even for size 0 (until C2y/N3322), see StreamReader_memory::read().
167
0
      return true;
168
0
    }
169
170
0
    return !m_func_table->read(data, size, m_userdata);
171
0
  }
172
173
0
  bool seek(uint64_t position) override { return !m_func_table->seek(position, m_userdata); }
174
175
0
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
176
0
    if (m_func_table->reader_api_version >= 2) {
177
0
      heif_reader_range_request_result result = m_func_table->request_range(start, end_pos, m_userdata);
178
179
      // convert error message string and release input string memory
180
181
0
      std::string error_msg;
182
0
      if (result.reader_error_msg) {
183
0
        error_msg = std::string{result.reader_error_msg};
184
185
0
        if (m_func_table->release_error_msg) {
186
0
          m_func_table->release_error_msg(result.reader_error_msg);
187
0
        }
188
0
      }
189
190
0
      switch (result.status) {
191
0
        case heif_reader_grow_status_size_reached:
192
0
          return end_pos;
193
0
        case heif_reader_grow_status_timeout:
194
0
          return 0; // invalid return value from callback
195
0
        case heif_reader_grow_status_size_beyond_eof:
196
0
          m_last_error = {heif_error_Invalid_input, heif_suberror_End_of_data, "Read beyond file size"};
197
0
          return result.range_end;
198
0
        case heif_reader_grow_status_error: {
199
0
          if (result.reader_error_msg) {
200
0
            std::stringstream sstr;
201
0
            sstr << "Input error (" << result.reader_error_code << ") : " << error_msg;
202
0
            m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
203
0
          }
204
0
          else {
205
0
            std::stringstream sstr;
206
0
            sstr << "Input error (" << result.reader_error_code << ")";
207
0
            m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
208
0
          }
209
210
0
          return 0; // error occurred
211
0
        }
212
0
        default:
213
0
          m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid input reader return value"};
214
0
          return 0;
215
0
      }
216
0
    }
217
0
    else {
218
      // wait_for_file_size() takes a signed int64_t, so we cannot probe a position
219
      // beyond INT64_MAX: it would wrap to a negative value and the reader would
220
      // misreport the size. No file accessed through this API can be that large,
221
      // so clamp the search range to INT64_MAX. (Callers may pass UINT64_MAX to
222
      // ask for the total file size.)
223
0
      uint64_t hi = std::min<uint64_t>(end_pos, std::numeric_limits<int64_t>::max());
224
225
0
      auto result = m_func_table->wait_for_file_size(static_cast<int64_t>(hi), m_userdata);
226
0
      if (result == heif_reader_grow_status_size_reached) {
227
0
        return hi;
228
0
      }
229
0
      else {
230
0
        uint64_t pos = m_func_table->get_position(m_userdata);
231
0
        return bisect_filesize(pos, hi);
232
0
      }
233
0
    }
234
0
  }
235
236
0
  uint64_t bisect_filesize(uint64_t mini, uint64_t maxi) {
237
    // mini - <= filesize
238
    // maxi - >  filesize
239
240
0
    if (maxi == mini + 1) {
241
0
      return mini;
242
0
    }
243
244
    // Overflow-safe midpoint. 'maxi' is bounded by INT64_MAX (see request_range),
245
    // but computing (mini + maxi) directly could still overflow uint64_t.
246
0
    uint64_t pos = mini + (maxi - mini) / 2;
247
0
    auto result = m_func_table->wait_for_file_size(static_cast<int64_t>(pos), m_userdata);
248
0
    if (result == heif_reader_grow_status_size_reached) {
249
0
      return bisect_filesize(pos, maxi);
250
0
    }
251
0
    else {
252
0
      return bisect_filesize(mini, pos);
253
0
    }
254
0
  }
255
256
0
  void release_range(uint64_t start, uint64_t end_pos) override {
257
0
    if (m_func_table->reader_api_version >= 2) {
258
0
      m_func_table->release_file_range(start, end_pos, m_userdata);
259
0
    }
260
0
  }
261
262
0
  void preload_range_hint(uint64_t start, uint64_t end_pos) override {
263
0
    if (m_func_table->reader_api_version >= 2) {
264
0
      m_func_table->preload_range_hint(start, end_pos, m_userdata);
265
0
    }
266
0
  }
267
268
private:
269
  const heif_reader* m_func_table;
270
  void* m_userdata;
271
};
272
273
274
// This class simplifies safely reading part of a file (e.g. a box).
275
// It makes sure that we do not read past the boundaries of a box.
276
class BitstreamRange
277
{
278
public:
279
  BitstreamRange(std::shared_ptr<StreamReader> istr,
280
                 size_t length,
281
                 BitstreamRange* parent = nullptr);
282
283
  BitstreamRange(std::shared_ptr<StreamReader> istr,
284
                 size_t start,
285
                 size_t end); // one past end
286
287
  // This function tries to make sure that the full data of this range is
288
  // available. You should call this before starting reading the range.
289
  // If you don't, you have to make sure that you do not read past the available data.
290
  StreamReader::grow_status wait_until_range_is_available();
291
292
  uint8_t read8();
293
294
  uint16_t read16();
295
296
  int16_t read16s();
297
298
  /**
299
   * Read 24 bit unsigned integer from the bitstream.
300
   *
301
   * The data is assumed to be in big endian format and is returned as a 32 bit value.
302
   */
303
  uint32_t read24();
304
305
  uint32_t read32();
306
307
  int32_t read32s();
308
309
  uint64_t read64();
310
311
  uint64_t read_uint(int len);
312
313
  /**
314
   * Read 32 bit floating point value from the bitstream.
315
   *
316
   * The file data is assumed to be in big endian format.
317
   */
318
  float read_float32();
319
320
  int64_t read64s();
321
322
  std::string read_string();
323
324
  // A string stored with a fixed number of bytes. The first byte contains the string length and the extra bytes
325
  // are filled with a padding 0.
326
  std::string read_fixed_string(int len);
327
328
  std::string read_string_until_eof();
329
330
  bool read(uint8_t* data, size_t n);
331
332
  bool prepare_read(size_t nBytes);
333
334
  StreamReader::grow_status wait_for_available_bytes(size_t nBytes);
335
336
  void skip_to_end_of_file()
337
1.29k
  {
338
    // we do not actually move the file position here (because the stream may still be incomplete),
339
    // but we set all m_remaining to zero
340
1.29k
    m_remaining = 0;
341
342
1.29k
    if (m_parent_range) {
343
912
      m_parent_range->skip_to_end_of_file();
344
912
    }
345
1.29k
  }
346
347
  void skip(uint64_t n)
348
256k
  {
349
256k
    size_t actual_skip = std::min(static_cast<size_t>(n), m_remaining);
350
351
256k
    if (m_parent_range) {
352
      // also advance position in parent range
353
256k
      m_parent_range->skip_without_advancing_file_pos(actual_skip);
354
256k
    }
355
356
256k
    assert(actual_skip <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
357
358
256k
    m_istr->seek_cur(static_cast<int64_t>(actual_skip));
359
256k
    m_remaining -= actual_skip;
360
256k
  }
361
362
  void skip_to_end_of_box()
363
1.67G
  {
364
1.67G
    if (m_remaining > 0) {
365
278k
      if (m_parent_range) {
366
        // also advance position in parent range
367
278k
        m_parent_range->skip_without_advancing_file_pos(m_remaining);
368
278k
      }
369
370
278k
      m_istr->seek_cur(m_remaining);
371
278k
      m_remaining = 0;
372
278k
    }
373
1.67G
  }
374
375
  void set_eof_while_reading()
376
0
  {
377
0
    m_remaining = 0;
378
379
0
    if (m_parent_range) {
380
0
      m_parent_range->set_eof_while_reading();
381
0
    }
382
383
0
    m_error = true;
384
0
  }
385
386
  bool eof() const
387
8.57M
  {
388
8.57M
    return m_remaining == 0;
389
8.57M
  }
390
391
  bool error() const
392
7.31M
  {
393
7.31M
    return m_error;
394
7.31M
  }
395
396
  Error get_error() const
397
5.78M
  {
398
5.78M
    if (m_error) {
399
35.5k
      return Error(heif_error_Invalid_input,
400
35.5k
                   heif_suberror_End_of_data);
401
35.5k
    }
402
5.75M
    else {
403
5.75M
      return Error::Ok;
404
5.75M
    }
405
5.78M
  }
406
407
31.3M
  std::shared_ptr<StreamReader> get_istream() { return m_istr; }
408
409
1.91M
  int get_nesting_level() const { return m_nesting_level; }
410
411
2.40M
  size_t get_remaining_bytes() const { return m_remaining; }
412
413
private:
414
  std::shared_ptr<StreamReader> m_istr;
415
  BitstreamRange* m_parent_range = nullptr;
416
  int m_nesting_level = 0;
417
418
  size_t m_remaining;
419
  bool m_error = false;
420
421
  // Note: 'nBytes' may not be larger than the number of remaining bytes
422
  void skip_without_advancing_file_pos(size_t nBytes);
423
};
424
425
426
class BitReader
427
{
428
public:
429
  BitReader(const uint8_t* buffer, size_t len);
430
431
  void reset();
432
433
  uint32_t get_bits(int n);
434
435
  uint8_t get_bits8(int n);
436
437
  uint16_t get_bits16(int n);
438
439
  uint32_t get_bits32(int n);
440
441
  int32_t get_bits32s();
442
443
  /**
444
   * Get a one-bit flag value.
445
   *
446
   * @returns true if the next bit value is 1, otherwise false
447
   */
448
  bool get_flag();
449
450
  std::vector<uint8_t> read_bytes(uint32_t n);
451
452
  int get_bits_fast(int n);
453
454
  int peek_bits(int n);
455
456
  void skip_bytes(uint32_t nBytes);
457
458
  void skip_bits(int n);
459
460
  void skip_bits_fast(int n);
461
462
  void skip_to_byte_boundary();
463
464
  bool get_uvlc(uint32_t* value);
465
466
  bool get_svlc(int32_t* value);
467
468
  size_t get_current_byte_index() const
469
48.7M
  {
470
    // Computed in signed arithmetic: when we skipped past the end of the data,
471
    // nextbits_cnt is negative and the index keeps growing beyond data_length (the
472
    // 'uncC' alignment handling relies on that). Doing the subtraction in size_t
473
    // would convert the negative operand first, which trips the 'integer' sanitizer.
474
    // The result itself is always >= 0.
475
48.7M
    int64_t bytes_read = static_cast<int64_t>(data_length - bytes_remaining);
476
48.7M
    return static_cast<size_t>(bytes_read - nextbits_cnt / 8);
477
48.7M
  }
478
479
  int64_t get_bits_remaining() const
480
1.57M
  {
481
1.57M
    return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
482
1.57M
  }
483
484
private:
485
  const uint8_t* const data_start;
486
  const uint8_t* data;
487
  const size_t data_length;
488
  size_t bytes_remaining;
489
490
  uint64_t nextbits; // left-aligned bits
491
492
  // Number of valid bits in 'nextbits'. Goes negative when we read or skip past the
493
  // end of the data, in which case it holds the (negated) overshoot so that
494
  // get_current_byte_index() keeps advancing. Has to be 64-bit because skip_bytes()
495
  // may be asked to skip up to 2^32 bytes (= 2^35 bits) past the end.
496
  int64_t nextbits_cnt;
497
498
  void refill(); // refill to at least 56+1 bits
499
};
500
501
502
class BitWriter
503
{
504
public:
505
  // Write n bits from the LSBs of value (n must be in [0,32])
506
  void write_bits(uint32_t value, int n);
507
508
0
  void write_bits8(uint8_t value, int n) { assert(n >= 0 && n <= 8); write_bits(value, n); }
509
510
0
  void write_bits16(uint16_t value, int n) { assert(n >= 0 && n <= 16); write_bits(value, n); }
511
512
0
  void write_bits32(uint32_t value, int n) { assert(n >= 0 && n <= 32); write_bits(value, n); }
513
514
0
  void write_bits32s(int32_t value) { write_bits(static_cast<uint32_t>(value), 32); }
515
516
0
  void write_flag(bool flag) { write_bits(flag ? 1 : 0, 1); }
517
518
  // Write raw bytes. Must be at a byte boundary.
519
  void write_bytes(const std::vector<uint8_t>& data);
520
521
  void write_bytes(const uint8_t* data, size_t len);
522
523
  // Pad with zero bits to the next byte boundary. No-op if already aligned.
524
  void skip_to_byte_boundary();
525
526
  // Return all written data. Flushes any partial byte (zero-padded).
527
  std::vector<uint8_t> get_data() const;
528
529
0
  int get_current_byte_index() const { return static_cast<int>(m_data.size()); }
530
531
0
  int64_t get_bits_written() const { return static_cast<int64_t>(m_data.size()) * 8 + m_bits_in_current_byte; }
532
533
private:
534
  std::vector<uint8_t> m_data;
535
  uint8_t m_current_byte = 0;
536
  int m_bits_in_current_byte = 0; // bits already written into m_current_byte (0-7), packed from MSB
537
};
538
539
540
class StreamWriter
541
{
542
public:
543
  void write8(uint8_t);
544
545
  void write16(uint16_t);
546
547
  void write16s(int16_t);
548
549
  void write24(uint32_t);
550
551
  void write32(uint32_t);
552
553
  void write32s(int32_t);
554
555
  void write64(uint64_t);
556
557
  void write_float32(float);
558
559
  void write64s(int64_t);
560
561
  void write(int size, uint64_t value);
562
563
  void write(const std::string&, bool end_with_null = true);
564
565
  void write_fixed_string(std::string s, size_t len);
566
567
  void write(const std::vector<uint8_t>&);
568
569
  void write(const StreamWriter&);
570
571
  void skip(int n);
572
573
  void insert(int nBytes);
574
575
347k
  size_t data_size() const { return m_data.size(); }
576
577
366k
  size_t get_position() const { return m_position; }
578
579
362k
  void set_position(size_t pos) { m_position = pos; }
580
581
347k
  void set_position_to_end() { m_position = m_data.size(); }
582
583
300k
  const std::vector<uint8_t> get_data() const { return m_data; }
584
585
private:
586
  std::vector<uint8_t> m_data;
587
  size_t m_position = 0;
588
};
589
590
#endif