Coverage Report

Created: 2026-07-30 06:20

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
8.16k
  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
37.0k
  {
63
37.0k
    return seek(get_position() + position_offset);
64
37.0k
  }
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
11.1k
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
140
11.1k
    return m_length;
141
11.1k
  }
142
143
private:
144
  const uint8_t* m_data;
145
  uint64_t m_length;
146
  uint64_t m_position;
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
0
  bool read(void* data, size_t size) override { return !m_func_table->read(data, size, m_userdata); }
163
164
0
  bool seek(uint64_t position) override { return !m_func_table->seek(position, m_userdata); }
165
166
0
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
167
0
    if (m_func_table->reader_api_version >= 2) {
168
0
      heif_reader_range_request_result result = m_func_table->request_range(start, end_pos, m_userdata);
169
170
      // convert error message string and release input string memory
171
172
0
      std::string error_msg;
173
0
      if (result.reader_error_msg) {
174
0
        error_msg = std::string{result.reader_error_msg};
175
176
0
        if (m_func_table->release_error_msg) {
177
0
          m_func_table->release_error_msg(result.reader_error_msg);
178
0
        }
179
0
      }
180
181
0
      switch (result.status) {
182
0
        case heif_reader_grow_status_size_reached:
183
0
          return end_pos;
184
0
        case heif_reader_grow_status_timeout:
185
0
          return 0; // invalid return value from callback
186
0
        case heif_reader_grow_status_size_beyond_eof:
187
0
          m_last_error = {heif_error_Invalid_input, heif_suberror_End_of_data, "Read beyond file size"};
188
0
          return result.range_end;
189
0
        case heif_reader_grow_status_error: {
190
0
          if (result.reader_error_msg) {
191
0
            std::stringstream sstr;
192
0
            sstr << "Input error (" << result.reader_error_code << ") : " << error_msg;
193
0
            m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
194
0
          }
195
0
          else {
196
0
            std::stringstream sstr;
197
0
            sstr << "Input error (" << result.reader_error_code << ")";
198
0
            m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, sstr.str()};
199
0
          }
200
201
0
          return 0; // error occurred
202
0
        }
203
0
        default:
204
0
          m_last_error = {heif_error_Invalid_input, heif_suberror_Unspecified, "Invalid input reader return value"};
205
0
          return 0;
206
0
      }
207
0
    }
208
0
    else {
209
      // wait_for_file_size() takes a signed int64_t, so we cannot probe a position
210
      // beyond INT64_MAX: it would wrap to a negative value and the reader would
211
      // misreport the size. No file accessed through this API can be that large,
212
      // so clamp the search range to INT64_MAX. (Callers may pass UINT64_MAX to
213
      // ask for the total file size.)
214
0
      uint64_t hi = std::min<uint64_t>(end_pos, std::numeric_limits<int64_t>::max());
215
216
0
      auto result = m_func_table->wait_for_file_size(static_cast<int64_t>(hi), m_userdata);
217
0
      if (result == heif_reader_grow_status_size_reached) {
218
0
        return hi;
219
0
      }
220
0
      else {
221
0
        uint64_t pos = m_func_table->get_position(m_userdata);
222
0
        return bisect_filesize(pos, hi);
223
0
      }
224
0
    }
225
0
  }
226
227
0
  uint64_t bisect_filesize(uint64_t mini, uint64_t maxi) {
228
    // mini - <= filesize
229
    // maxi - >  filesize
230
231
0
    if (maxi == mini + 1) {
232
0
      return mini;
233
0
    }
234
235
    // Overflow-safe midpoint. 'maxi' is bounded by INT64_MAX (see request_range),
236
    // but computing (mini + maxi) directly could still overflow uint64_t.
237
0
    uint64_t pos = mini + (maxi - mini) / 2;
238
0
    auto result = m_func_table->wait_for_file_size(static_cast<int64_t>(pos), m_userdata);
239
0
    if (result == heif_reader_grow_status_size_reached) {
240
0
      return bisect_filesize(pos, maxi);
241
0
    }
242
0
    else {
243
0
      return bisect_filesize(mini, pos);
244
0
    }
245
0
  }
246
247
0
  void release_range(uint64_t start, uint64_t end_pos) override {
248
0
    if (m_func_table->reader_api_version >= 2) {
249
0
      m_func_table->release_file_range(start, end_pos, m_userdata);
250
0
    }
251
0
  }
252
253
0
  void preload_range_hint(uint64_t start, uint64_t end_pos) override {
254
0
    if (m_func_table->reader_api_version >= 2) {
255
0
      m_func_table->preload_range_hint(start, end_pos, m_userdata);
256
0
    }
257
0
  }
258
259
private:
260
  const heif_reader* m_func_table;
261
  void* m_userdata;
262
};
263
264
265
// This class simplifies safely reading part of a file (e.g. a box).
266
// It makes sure that we do not read past the boundaries of a box.
267
class BitstreamRange
268
{
269
public:
270
  BitstreamRange(std::shared_ptr<StreamReader> istr,
271
                 size_t length,
272
                 BitstreamRange* parent = nullptr);
273
274
  BitstreamRange(std::shared_ptr<StreamReader> istr,
275
                 size_t start,
276
                 size_t end); // one past end
277
278
  // This function tries to make sure that the full data of this range is
279
  // available. You should call this before starting reading the range.
280
  // If you don't, you have to make sure that you do not read past the available data.
281
  StreamReader::grow_status wait_until_range_is_available();
282
283
  uint8_t read8();
284
285
  uint16_t read16();
286
287
  int16_t read16s();
288
289
  /**
290
   * Read 24 bit unsigned integer from the bitstream.
291
   *
292
   * The data is assumed to be in big endian format and is returned as a 32 bit value.
293
   */
294
  uint32_t read24();
295
296
  uint32_t read32();
297
298
  int32_t read32s();
299
300
  uint64_t read64();
301
302
  uint64_t read_uint(int len);
303
304
  /**
305
   * Read 32 bit floating point value from the bitstream.
306
   *
307
   * The file data is assumed to be in big endian format.
308
   */
309
  float read_float32();
310
311
  int64_t read64s();
312
313
  std::string read_string();
314
315
  // A string stored with a fixed number of bytes. The first byte contains the string length and the extra bytes
316
  // are filled with a padding 0.
317
  std::string read_fixed_string(int len);
318
319
  std::string read_string_until_eof();
320
321
  bool read(uint8_t* data, size_t n);
322
323
  bool prepare_read(size_t nBytes);
324
325
  StreamReader::grow_status wait_for_available_bytes(size_t nBytes);
326
327
  void skip_to_end_of_file()
328
25
  {
329
    // we do not actually move the file position here (because the stream may still be incomplete),
330
    // but we set all m_remaining to zero
331
25
    m_remaining = 0;
332
333
25
    if (m_parent_range) {
334
19
      m_parent_range->skip_to_end_of_file();
335
19
    }
336
25
  }
337
338
  void skip(uint64_t n)
339
25.3k
  {
340
25.3k
    size_t actual_skip = std::min(static_cast<size_t>(n), m_remaining);
341
342
25.3k
    if (m_parent_range) {
343
      // also advance position in parent range
344
25.3k
      m_parent_range->skip_without_advancing_file_pos(actual_skip);
345
25.3k
    }
346
347
25.3k
    assert(actual_skip <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
348
349
25.3k
    m_istr->seek_cur(static_cast<int64_t>(actual_skip));
350
25.3k
    m_remaining -= actual_skip;
351
25.3k
  }
352
353
  void skip_to_end_of_box()
354
77.9M
  {
355
77.9M
    if (m_remaining > 0) {
356
10.6k
      if (m_parent_range) {
357
        // also advance position in parent range
358
10.5k
        m_parent_range->skip_without_advancing_file_pos(m_remaining);
359
10.5k
      }
360
361
10.6k
      m_istr->seek_cur(m_remaining);
362
10.6k
      m_remaining = 0;
363
10.6k
    }
364
77.9M
  }
365
366
  void set_eof_while_reading()
367
0
  {
368
0
    m_remaining = 0;
369
370
0
    if (m_parent_range) {
371
0
      m_parent_range->set_eof_while_reading();
372
0
    }
373
374
0
    m_error = true;
375
0
  }
376
377
  bool eof() const
378
90.3k
  {
379
90.3k
    return m_remaining == 0;
380
90.3k
  }
381
382
  bool error() const
383
118k
  {
384
118k
    return m_error;
385
118k
  }
386
387
  Error get_error() const
388
133k
  {
389
133k
    if (m_error) {
390
673
      return Error(heif_error_Invalid_input,
391
673
                   heif_suberror_End_of_data);
392
673
    }
393
132k
    else {
394
132k
      return Error::Ok;
395
132k
    }
396
133k
  }
397
398
474k
  std::shared_ptr<StreamReader> get_istream() { return m_istr; }
399
400
53.3k
  int get_nesting_level() const { return m_nesting_level; }
401
402
59.3k
  size_t get_remaining_bytes() const { return m_remaining; }
403
404
private:
405
  std::shared_ptr<StreamReader> m_istr;
406
  BitstreamRange* m_parent_range = nullptr;
407
  int m_nesting_level = 0;
408
409
  size_t m_remaining;
410
  bool m_error = false;
411
412
  // Note: 'nBytes' may not be larger than the number of remaining bytes
413
  void skip_without_advancing_file_pos(size_t nBytes);
414
};
415
416
417
class BitReader
418
{
419
public:
420
  BitReader(const uint8_t* buffer, size_t len);
421
422
  void reset();
423
424
  uint32_t get_bits(int n);
425
426
  uint8_t get_bits8(int n);
427
428
  uint16_t get_bits16(int n);
429
430
  uint32_t get_bits32(int n);
431
432
  int32_t get_bits32s();
433
434
  /**
435
   * Get a one-bit flag value.
436
   *
437
   * @returns true if the next bit value is 1, otherwise false
438
   */
439
  bool get_flag();
440
441
  std::vector<uint8_t> read_bytes(uint32_t n);
442
443
  int get_bits_fast(int n);
444
445
  int peek_bits(int n);
446
447
  void skip_bytes(uint32_t nBytes);
448
449
  void skip_bits(int n);
450
451
  void skip_bits_fast(int n);
452
453
  void skip_to_byte_boundary();
454
455
  bool get_uvlc(uint32_t* value);
456
457
  bool get_svlc(int32_t* value);
458
459
  size_t get_current_byte_index() const
460
60.7M
  {
461
60.7M
    return data_length - bytes_remaining - nextbits_cnt / 8;
462
60.7M
  }
463
464
  int64_t get_bits_remaining() const
465
2.73k
  {
466
2.73k
    return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
467
2.73k
  }
468
469
private:
470
  const uint8_t* const data_start;
471
  const uint8_t* data;
472
  const size_t data_length;
473
  size_t bytes_remaining;
474
475
  uint64_t nextbits; // left-aligned bits
476
  int nextbits_cnt;
477
478
  void refill(); // refill to at least 56+1 bits
479
};
480
481
482
class BitWriter
483
{
484
public:
485
  // Write n bits from the LSBs of value (n must be in [0,32])
486
  void write_bits(uint32_t value, int n);
487
488
0
  void write_bits8(uint8_t value, int n) { assert(n >= 0 && n <= 8); write_bits(value, n); }
489
490
0
  void write_bits16(uint16_t value, int n) { assert(n >= 0 && n <= 16); write_bits(value, n); }
491
492
0
  void write_bits32(uint32_t value, int n) { assert(n >= 0 && n <= 32); write_bits(value, n); }
493
494
0
  void write_bits32s(int32_t value) { write_bits(static_cast<uint32_t>(value), 32); }
495
496
0
  void write_flag(bool flag) { write_bits(flag ? 1 : 0, 1); }
497
498
  // Write raw bytes. Must be at a byte boundary.
499
  void write_bytes(const std::vector<uint8_t>& data);
500
501
  void write_bytes(const uint8_t* data, size_t len);
502
503
  // Pad with zero bits to the next byte boundary. No-op if already aligned.
504
  void skip_to_byte_boundary();
505
506
  // Return all written data. Flushes any partial byte (zero-padded).
507
  std::vector<uint8_t> get_data() const;
508
509
0
  int get_current_byte_index() const { return static_cast<int>(m_data.size()); }
510
511
0
  int64_t get_bits_written() const { return static_cast<int64_t>(m_data.size()) * 8 + m_bits_in_current_byte; }
512
513
private:
514
  std::vector<uint8_t> m_data;
515
  uint8_t m_current_byte = 0;
516
  int m_bits_in_current_byte = 0; // bits already written into m_current_byte (0-7), packed from MSB
517
};
518
519
520
class StreamWriter
521
{
522
public:
523
  void write8(uint8_t);
524
525
  void write16(uint16_t);
526
527
  void write16s(int16_t);
528
529
  void write24(uint32_t);
530
531
  void write32(uint32_t);
532
533
  void write32s(int32_t);
534
535
  void write64(uint64_t);
536
537
  void write_float32(float);
538
539
  void write64s(int64_t);
540
541
  void write(int size, uint64_t value);
542
543
  void write(const std::string&, bool end_with_null = true);
544
545
  void write_fixed_string(std::string s, size_t len);
546
547
  void write(const std::vector<uint8_t>&);
548
549
  void write(const StreamWriter&);
550
551
  void skip(int n);
552
553
  void insert(int nBytes);
554
555
580
  size_t data_size() const { return m_data.size(); }
556
557
580
  size_t get_position() const { return m_position; }
558
559
580
  void set_position(size_t pos) { m_position = pos; }
560
561
580
  void set_position_to_end() { m_position = m_data.size(); }
562
563
580
  const std::vector<uint8_t> get_data() const { return m_data; }
564
565
private:
566
  std::vector<uint8_t> m_data;
567
  size_t m_position = 0;
568
};
569
570
#endif