Coverage Report

Created: 2026-10-17 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libultrahdr/third_party/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
23
24
FileLayout::FileLayout()
25
31.6k
{
26
31.6k
  auto ftyp = std::make_shared<Box_ftyp>();
27
31.6k
  ftyp->set_output_position(0);
28
31.6k
  m_boxes.push_back(ftyp);
29
30
  // TODO: these variables are not used yet
31
31.6k
  (void)m_writeMode;
32
31.6k
  (void)m_file_size;
33
31.6k
}
34
35
36
Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits)
37
15.8k
{
38
15.8k
  m_boxes.clear();
39
40
15.8k
  m_stream_reader = stream;
41
42
  // --- read initial range, large enough to cover 'ftyp' box
43
44
15.8k
  m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST);
45
46
15.8k
  if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) {
47
5.79k
    return {heif_error_Invalid_input,
48
5.79k
            heif_suberror_Unspecified,
49
5.79k
            "File size too small."};
50
5.79k
  }
51
52
  // --- read 'ftyp' box header
53
54
10.0k
  BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length);
55
10.0k
  BoxHeader ftyp_header;
56
10.0k
  Error err;
57
10.0k
  err = ftyp_header.parse_header(ftyp_hdr_range);
58
10.0k
  if (err) {
59
43
    return err;
60
43
  }
61
62
  // --- check whether it is a valid 'ftyp' box header
63
64
9.98k
  if (ftyp_header.get_short_type() != fourcc("ftyp")) {
65
0
    return {heif_error_Invalid_input,
66
0
            heif_suberror_No_ftyp_box,
67
0
            "File does not start with 'ftyp' box."};
68
0
  }
69
70
9.98k
  uint64_t ftyp_size = ftyp_header.get_box_size();
71
72
9.98k
  if (ftyp_size == 0) {
73
8
    return {heif_error_Invalid_input,
74
8
            heif_suberror_No_ftyp_box,
75
8
            "ftyp box shall not be the only box in the file"};
76
8
  }
77
78
9.97k
  if (ftyp_size > m_max_length) {
79
119
    return {heif_error_Invalid_input,
80
119
            heif_suberror_No_ftyp_box,
81
119
            "ftyp box larger than initial read range"};
82
119
  }
83
84
  // --- read the 'ftyp' box
85
86
9.85k
  BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size);
87
9.85k
  std::shared_ptr<Box> ftyp_box;
88
9.85k
  err = Box::read(ftyp_range, &ftyp_box, limits);
89
90
9.85k
  m_boxes.push_back(ftyp_box);
91
9.85k
  m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(ftyp_box);
92
93
94
  // --- skip through box headers until we find the 'meta' box
95
96
9.85k
  uint64_t next_box_start = ftyp_size;
97
98
11.6k
  for (;;) {
99
    // TODO: overflow
100
11.6k
    uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE;
101
11.6k
    if (next_box_header_end > m_max_length) {
102
1.74k
      m_max_length = stream->request_range(next_box_start, next_box_header_end);
103
1.74k
    }
104
105
11.6k
    if (next_box_header_end > m_max_length) {
106
1.74k
      return {heif_error_Invalid_input,
107
1.74k
              heif_suberror_Unspecified,
108
1.74k
              "Insufficient input data"};
109
1.74k
    }
110
111
9.86k
    BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length);
112
9.86k
    BoxHeader box_header;
113
9.86k
    err = box_header.parse_header(box_range);
114
9.86k
    if (err) {
115
18
      return err;
116
18
    }
117
118
9.84k
    if (box_header.get_short_type() == fourcc("meta")) {
119
8.06k
      const uint64_t meta_box_start = next_box_start;
120
8.06k
      if (box_header.get_box_size() == 0) {
121
        // TODO: get file-size from stream and compute box size
122
8
        return {heif_error_Invalid_input,
123
8
                heif_suberror_No_meta_box,
124
8
                "Cannot read meta box with unspecified size"};
125
8
      }
126
127
      // TODO: overflow
128
8.06k
      uint64_t end_of_meta_box = meta_box_start + box_header.get_box_size();
129
8.06k
      if (m_max_length < end_of_meta_box) {
130
905
        m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box);
131
905
      }
132
133
8.06k
      if (m_max_length < end_of_meta_box) {
134
905
        return {heif_error_Invalid_input,
135
905
                heif_suberror_No_meta_box,
136
905
                "Cannot read full meta box"};
137
905
      }
138
139
7.15k
      BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box);
140
7.15k
      std::shared_ptr<Box> meta_box;
141
7.15k
      err = Box::read(meta_box_range, &meta_box, limits);
142
7.15k
      if (err) {
143
5.44k
        return err;
144
5.44k
      }
145
146
1.71k
      m_boxes.push_back(meta_box);
147
1.71k
      m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box);
148
1.71k
      break;
149
7.15k
    }
150
151
#if ENABLE_EXPERIMENTAL_MINI_FORMAT
152
    // TODO: this is basically the same as the meta box case above, with different error handling.
153
    if (box_header.get_short_type() == fourcc("mini")) {
154
      const uint64_t mini_box_start = next_box_start;
155
      if (box_header.get_box_size() == 0) {
156
        // TODO: get file-size from stream and compute box size
157
        return {heif_error_Invalid_input,
158
                heif_suberror_Invalid_mini_box,
159
                "Cannot read mini box with unspecified size"};
160
      }
161
      uint64_t end_of_mini_box = mini_box_start + box_header.get_box_size();
162
      if (m_max_length < end_of_mini_box) {
163
        m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box);
164
      }
165
166
      if (m_max_length < end_of_mini_box) {
167
        return {heif_error_Invalid_input,
168
                heif_suberror_Invalid_mini_box,
169
                "Cannot read full mini box"};
170
      }
171
      BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box);
172
      std::shared_ptr<Box> mini_box;
173
      err = Box::read(mini_box_range, &mini_box, heif_get_global_security_limits());
174
      if (err) {
175
        std::cout << "error reading mini box" << std::endl;
176
        return err;
177
      }
178
179
      m_boxes.push_back(mini_box);
180
      m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box);
181
      if (m_mini_box == nullptr) {
182
        std::cout << "error casting mini box" << std::endl;
183
      }
184
      return Error::Ok;
185
    }
186
#endif
187
188
1.78k
    if (box_header.get_box_size() == 0) {
189
25
      return {heif_error_Invalid_input,
190
25
              heif_suberror_No_meta_box,
191
25
              "No meta box found"};
192
25
    }
193
194
    // TODO: overflow
195
1.75k
    next_box_start = next_box_start + box_header.get_box_size();
196
1.75k
  }
197
198
1.71k
  return Error::Ok;
199
9.85k
}
200
201
202
void FileLayout::set_write_mode(WriteMode writeMode, const std::shared_ptr<StreamWriter>& writer)
203
0
{
204
205
0
}
206
207
208
Error FileLayout::write(std::shared_ptr<StreamWriter>& stream)
209
0
{
210
0
  return Error::Ok;
211
0
}