Coverage Report

Created: 2025-12-03 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/file_layout.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2024 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 "file_layout.h"
22
#include "sequences/seq_boxes.h"
23
#include <limits>
24
25
26
FileLayout::FileLayout()
27
122k
{
28
122k
  auto ftyp = std::make_shared<Box_ftyp>();
29
122k
  ftyp->set_output_position(0);
30
122k
  m_boxes.push_back(ftyp);
31
32
  // TODO: these variables are not used yet
33
122k
  (void)m_writeMode;
34
122k
  (void)m_file_size;
35
122k
}
36
37
38
Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits)
39
61.3k
{
40
61.3k
  m_boxes.clear();
41
42
61.3k
  m_stream_reader = stream;
43
44
  // --- read initial range, large enough to cover 'ftyp' box
45
46
61.3k
  m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST);
47
48
61.3k
  if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) {
49
116
    return {heif_error_Invalid_input,
50
116
            heif_suberror_Unspecified,
51
116
            "File size too small."};
52
116
  }
53
54
  // --- read 'ftyp' box header
55
56
61.1k
  BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length);
57
61.1k
  BoxHeader ftyp_header;
58
61.1k
  Error err;
59
61.1k
  err = ftyp_header.parse_header(ftyp_hdr_range);
60
61.1k
  if (err) {
61
13
    return err;
62
13
  }
63
64
  // --- check whether it is a valid 'ftyp' box header
65
66
61.1k
  if (ftyp_header.get_short_type() != fourcc("ftyp")) {
67
255
    return {heif_error_Invalid_input,
68
255
            heif_suberror_No_ftyp_box,
69
255
            "File does not start with 'ftyp' box."};
70
255
  }
71
72
60.9k
  uint64_t ftyp_size = ftyp_header.get_box_size();
73
74
60.9k
  if (ftyp_size == 0) {
75
84
    return {heif_error_Invalid_input,
76
84
            heif_suberror_No_ftyp_box,
77
84
            "ftyp box shall not be the only box in the file"};
78
84
  }
79
80
60.8k
  if (ftyp_size > m_max_length) {
81
4.75k
    return {heif_error_Invalid_input,
82
4.75k
            heif_suberror_No_ftyp_box,
83
4.75k
            "ftyp box larger than initial read range"};
84
4.75k
  }
85
86
  // --- read the 'ftyp' box
87
88
56.0k
  BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size);
89
56.0k
  std::shared_ptr<Box> ftyp_box;
90
56.0k
  err = Box::read(ftyp_range, &ftyp_box, limits);
91
92
56.0k
  m_boxes.push_back(ftyp_box);
93
56.0k
  m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(ftyp_box);
94
95
96
  // --- skip through box headers until we find the 'meta' box
97
98
56.0k
  uint64_t next_box_start = ftyp_size;
99
100
56.0k
  bool meta_found = false;
101
56.0k
  bool mini_found = false;
102
56.0k
  bool moov_found = false;
103
104
188k
  for (;;) {
105
    // TODO: overflow
106
188k
    uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE;
107
188k
    if (next_box_header_end > m_max_length) {
108
45.6k
      m_max_length = stream->request_range(next_box_start, next_box_header_end);
109
45.6k
    }
110
111
188k
    if (next_box_header_end > m_max_length) {
112
45.6k
      if (meta_found || mini_found || moov_found) {
113
42.7k
        return Error::Ok;
114
42.7k
      }
115
2.92k
      else {
116
2.92k
        return {heif_error_Invalid_input,
117
2.92k
                heif_suberror_Unspecified,
118
2.92k
                "Insufficient input data"};
119
2.92k
      }
120
45.6k
    }
121
122
143k
    BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length);
123
143k
    BoxHeader box_header;
124
143k
    err = box_header.parse_header(box_range);
125
143k
    if (err) {
126
7
      return err;
127
7
    }
128
129
143k
    if (box_header.get_short_type() == fourcc("meta")) {
130
71.7k
      const uint64_t meta_box_start = next_box_start;
131
71.7k
      if (box_header.get_box_size() == 0) {
132
        // TODO: get file-size from stream and compute box size
133
6
        return {heif_error_Invalid_input,
134
6
                heif_suberror_No_meta_box,
135
6
                "Cannot read meta box with unspecified size"};
136
6
      }
137
138
      // TODO: overflow
139
71.7k
      uint64_t end_of_meta_box = meta_box_start + box_header.get_box_size();
140
71.7k
      if (m_max_length < end_of_meta_box) {
141
270
        m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box);
142
270
      }
143
144
71.7k
      if (m_max_length < end_of_meta_box) {
145
270
        return {heif_error_Invalid_input,
146
270
                heif_suberror_No_meta_box,
147
270
                "Cannot read full meta box"};
148
270
      }
149
150
71.5k
      BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box);
151
71.5k
      std::shared_ptr<Box> meta_box;
152
71.5k
      err = Box::read(meta_box_range, &meta_box, limits);
153
71.5k
      if (err) {
154
7.93k
        return err;
155
7.93k
      }
156
157
63.5k
      m_boxes.push_back(meta_box);
158
63.5k
      m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box);
159
63.5k
      meta_found = true;
160
63.5k
    }
161
162
#if ENABLE_EXPERIMENTAL_MINI_FORMAT
163
    // TODO: this is basically the same as the meta box case above, with different error handling.
164
    if (box_header.get_short_type() == fourcc("mini")) {
165
      const uint64_t mini_box_start = next_box_start;
166
      if (box_header.get_box_size() == 0) {
167
        // TODO: get file-size from stream and compute box size
168
        return {heif_error_Invalid_input,
169
                heif_suberror_Invalid_mini_box,
170
                "Cannot read mini box with unspecified size"};
171
      }
172
      uint64_t end_of_mini_box = mini_box_start + box_header.get_box_size();
173
      if (m_max_length < end_of_mini_box) {
174
        m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box);
175
      }
176
177
      if (m_max_length < end_of_mini_box) {
178
        return {heif_error_Invalid_input,
179
                heif_suberror_Invalid_mini_box,
180
                "Cannot read full mini box"};
181
      }
182
      BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box);
183
      std::shared_ptr<Box> mini_box;
184
      err = Box::read(mini_box_range, &mini_box, heif_get_global_security_limits());
185
      if (err) {
186
        std::cout << "error reading mini box" << std::endl;
187
        return err;
188
      }
189
190
      m_boxes.push_back(mini_box);
191
      m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box);
192
      if (m_mini_box == nullptr) {
193
        std::cout << "error casting mini box" << std::endl;
194
      }
195
196
      mini_found = true;
197
    }
198
#endif
199
200
134k
    if (box_header.get_short_type() == fourcc("moov")) {
201
9.26k
      const uint64_t moov_box_start = next_box_start;
202
9.26k
      if (box_header.get_box_size() == 0) {
203
        // TODO: get file-size from stream and compute box size
204
5
        return {heif_error_Invalid_input,
205
5
                heif_suberror_No_moov_box,
206
5
                "Cannot read moov box with unspecified size"};
207
5
      }
208
209
      // TODO: overflow
210
9.25k
      uint64_t end_of_moov_box = moov_box_start + box_header.get_box_size();
211
9.25k
      if (m_max_length < end_of_moov_box) {
212
154
        m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box);
213
154
      }
214
215
9.25k
      if (m_max_length < end_of_moov_box) {
216
154
        return {heif_error_Invalid_input,
217
154
                heif_suberror_No_moov_box,
218
154
                "Cannot read full moov box"};
219
154
      }
220
221
9.10k
      BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box);
222
9.10k
      std::shared_ptr<Box> moov_box;
223
9.10k
      err = Box::read(moov_box_range, &moov_box, limits);
224
9.10k
      if (err) {
225
1.24k
        return err;
226
1.24k
      }
227
228
7.85k
      m_boxes.push_back(moov_box);
229
7.85k
      m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box);
230
231
7.85k
      moov_found = true;
232
7.85k
    }
233
234
133k
    uint64_t boxSize = box_header.get_box_size();
235
133k
    if (boxSize == Box::size_until_end_of_file) {
236
771
      if (meta_found || mini_found || moov_found) {
237
747
        return Error::Ok;
238
747
      }
239
24
      else {
240
24
        return {heif_error_Invalid_input,
241
24
                heif_suberror_Unspecified,
242
24
                "No meta box found"};
243
24
      }
244
771
    }
245
246
132k
    if (std::numeric_limits<uint64_t>::max() - boxSize < next_box_start) {
247
0
      return {heif_error_Invalid_input,
248
0
              heif_suberror_Unspecified,
249
0
              "Box size too large, integer overflow"};
250
0
    }
251
252
132k
    next_box_start = next_box_start + boxSize;
253
132k
  }
254
255
0
  return Error::Ok;
256
56.0k
}
257
258
259
void FileLayout::set_write_mode(WriteMode writeMode, const std::shared_ptr<StreamWriter>& writer)
260
0
{
261
262
0
}
263
264
265
Error FileLayout::write(std::shared_ptr<StreamWriter>& stream)
266
0
{
267
0
  return Error::Ok;
268
0
}