Coverage Report

Created: 2025-09-08 07:52

/src/libheif/libheif/bitstream.h
Line
Count
Source (jump to first uncovered line)
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
19.0k
  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
13.5k
  {
63
13.5k
    return seek(get_position() + position_offset);
64
13.5k
  }
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
52.0k
  uint64_t request_range(uint64_t start, uint64_t end_pos) override {
140
52.0k
    return m_length;
141
52.0k
  }
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
0
      auto result = m_func_table->wait_for_file_size(end_pos, m_userdata);
210
0
      if (result == heif_reader_grow_status_size_reached) {
211
0
        return end_pos;
212
0
      }
213
0
      else {
214
0
        uint64_t pos = m_func_table->get_position(m_userdata);
215
0
        return bisect_filesize(pos,end_pos);
216
0
      }
217
0
    }
218
0
  }
219
220
0
  uint64_t bisect_filesize(uint64_t mini, uint64_t maxi) {
221
    // mini - <= filesize
222
    // maxi - >  filesize
223
224
0
    if (maxi == mini + 1) {
225
0
      return mini;
226
0
    }
227
228
0
    uint64_t pos = (mini + maxi) / 2;
229
0
    auto result = m_func_table->wait_for_file_size(pos, m_userdata);
230
0
    if (result == heif_reader_grow_status_size_reached) {
231
0
      return bisect_filesize(pos, maxi);
232
0
    }
233
0
    else {
234
0
      return bisect_filesize(mini, pos);
235
0
    }
236
0
  }
237
238
0
  void release_range(uint64_t start, uint64_t end_pos) override {
239
0
    if (m_func_table->reader_api_version >= 2) {
240
0
      m_func_table->release_file_range(start, end_pos, m_userdata);
241
0
    }
242
0
  }
243
244
0
  void preload_range_hint(uint64_t start, uint64_t end_pos) override {
245
0
    if (m_func_table->reader_api_version >= 2) {
246
0
      m_func_table->preload_range_hint(start, end_pos, m_userdata);
247
0
    }
248
0
  }
249
250
private:
251
  const heif_reader* m_func_table;
252
  void* m_userdata;
253
};
254
255
256
// This class simplifies safely reading part of a file (e.g. a box).
257
// It makes sure that we do not read past the boundaries of a box.
258
class BitstreamRange
259
{
260
public:
261
  BitstreamRange(std::shared_ptr<StreamReader> istr,
262
                 size_t length,
263
                 BitstreamRange* parent = nullptr);
264
265
  BitstreamRange(std::shared_ptr<StreamReader> istr,
266
                 size_t start,
267
                 size_t end); // one past end
268
269
  // This function tries to make sure that the full data of this range is
270
  // available. You should call this before starting reading the range.
271
  // If you don't, you have to make sure that you do not read past the available data.
272
  StreamReader::grow_status wait_until_range_is_available();
273
274
  uint8_t read8();
275
276
  uint16_t read16();
277
278
  int16_t read16s();
279
280
  /**
281
   * Read 24 bit unsigned integer from the bitstream.
282
   *
283
   * The data is assumed to be in big endian format and is returned as a 32 bit value.
284
   */
285
  uint32_t read24();
286
287
  uint32_t read32();
288
289
  int32_t read32s();
290
291
  uint64_t read64();
292
293
  uint64_t read_uint(int len);
294
295
  /**
296
   * Read 32 bit floating point value from the bitstream.
297
   *
298
   * The file data is assumed to be in big endian format.
299
   */
300
  float read_float32();
301
302
  int64_t read64s();
303
304
  std::string read_string();
305
306
  // A string stored with a fixed number of bytes. The first byte contains the string length and the extra bytes
307
  // are filled with a padding 0.
308
  std::string read_fixed_string(int len);
309
310
  bool read(uint8_t* data, size_t n);
311
312
  bool prepare_read(size_t nBytes);
313
314
  StreamReader::grow_status wait_for_available_bytes(size_t nBytes);
315
316
  void skip_to_end_of_file()
317
15
  {
318
    // we do not actually move the file position here (because the stream may still be incomplete),
319
    // but we set all m_remaining to zero
320
15
    m_remaining = 0;
321
322
15
    if (m_parent_range) {
323
11
      m_parent_range->skip_to_end_of_file();
324
11
    }
325
15
  }
326
327
  void skip(uint64_t n)
328
112
  {
329
112
    size_t actual_skip = std::min(static_cast<size_t>(n), m_remaining);
330
331
112
    if (m_parent_range) {
332
      // also advance position in parent range
333
112
      m_parent_range->skip_without_advancing_file_pos(actual_skip);
334
112
    }
335
336
112
    assert(actual_skip <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
337
338
112
    m_istr->seek_cur(static_cast<int64_t>(actual_skip));
339
112
    m_remaining -= actual_skip;
340
112
  }
341
342
  void skip_to_end_of_box()
343
18.8M
  {
344
18.8M
    if (m_remaining > 0) {
345
13.4k
      if (m_parent_range) {
346
        // also advance position in parent range
347
13.4k
        m_parent_range->skip_without_advancing_file_pos(m_remaining);
348
13.4k
      }
349
350
13.4k
      m_istr->seek_cur(m_remaining);
351
13.4k
      m_remaining = 0;
352
13.4k
    }
353
18.8M
  }
354
355
  void set_eof_while_reading()
356
0
  {
357
0
    m_remaining = 0;
358
359
0
    if (m_parent_range) {
360
0
      m_parent_range->set_eof_while_reading();
361
0
    }
362
363
0
    m_error = true;
364
0
  }
365
366
  bool eof() const
367
399k
  {
368
399k
    return m_remaining == 0;
369
399k
  }
370
371
  bool error() const
372
598k
  {
373
598k
    return m_error;
374
598k
  }
375
376
  Error get_error() const
377
677k
  {
378
677k
    if (m_error) {
379
663
      return Error(heif_error_Invalid_input,
380
663
                   heif_suberror_End_of_data);
381
663
    }
382
676k
    else {
383
676k
      return Error::Ok;
384
676k
    }
385
677k
  }
386
387
3.08M
  std::shared_ptr<StreamReader> get_istream() { return m_istr; }
388
389
244k
  int get_nesting_level() const { return m_nesting_level; }
390
391
255k
  size_t get_remaining_bytes() const { return m_remaining; }
392
393
private:
394
  std::shared_ptr<StreamReader> m_istr;
395
  BitstreamRange* m_parent_range = nullptr;
396
  int m_nesting_level = 0;
397
398
  size_t m_remaining;
399
  bool m_error = false;
400
401
  // Note: 'nBytes' may not be larger than the number of remaining bytes
402
  void skip_without_advancing_file_pos(size_t nBytes);
403
};
404
405
406
class BitReader
407
{
408
public:
409
  BitReader(const uint8_t* buffer, int len);
410
411
  uint32_t get_bits(int n);
412
413
  uint8_t get_bits8(int n);
414
415
  uint16_t get_bits16(int n);
416
417
  uint32_t get_bits32(int n);
418
419
  int32_t get_bits32s();
420
421
  /**
422
   * Get a one-bit flag value.
423
   *
424
   * @returns true if the next bit value is 1, otherwise false
425
   */
426
  bool get_flag();
427
428
  std::vector<uint8_t> read_bytes(uint32_t n);
429
430
  int get_bits_fast(int n);
431
432
  int peek_bits(int n);
433
434
  void skip_bytes(int nBytes);
435
436
  void skip_bits(int n);
437
438
  void skip_bits_fast(int n);
439
440
  void skip_to_byte_boundary();
441
442
  bool get_uvlc(int* value);
443
444
  bool get_svlc(int* value);
445
446
  int get_current_byte_index() const
447
0
  {
448
0
    return data_length - bytes_remaining - nextbits_cnt / 8;
449
0
  }
450
451
  int64_t get_bits_remaining() const
452
0
  {
453
0
    return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
454
0
  }
455
456
private:
457
  const uint8_t* data;
458
  int data_length;
459
  int bytes_remaining;
460
461
  uint64_t nextbits; // left-aligned bits
462
  int nextbits_cnt;
463
464
  void refill(); // refill to at least 56+1 bits
465
};
466
467
468
class StreamWriter
469
{
470
public:
471
  void write8(uint8_t);
472
473
  void write16(uint16_t);
474
475
  void write16s(int16_t);
476
477
  void write24(uint32_t);
478
479
  void write32(uint32_t);
480
481
  void write32s(int32_t);
482
483
  void write64(uint64_t);
484
485
  void write_float32(float);
486
487
  void write64s(int64_t);
488
489
  void write(int size, uint64_t value);
490
491
  void write(const std::string&);
492
493
  void write_fixed_string(std::string s, size_t len);
494
495
  void write(const std::vector<uint8_t>&);
496
497
  void write(const StreamWriter&);
498
499
  void skip(int n);
500
501
  void insert(int nBytes);
502
503
0
  size_t data_size() const { return m_data.size(); }
504
505
0
  size_t get_position() const { return m_position; }
506
507
0
  void set_position(size_t pos) { m_position = pos; }
508
509
0
  void set_position_to_end() { m_position = m_data.size(); }
510
511
0
  const std::vector<uint8_t> get_data() const { return m_data; }
512
513
private:
514
  std::vector<uint8_t> m_data;
515
  size_t m_position = 0;
516
};
517
518
#endif