Coverage Report

Created: 2026-10-17 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/include/exiv2/slice.hpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
#ifndef EXIV2_SLICE_HPP
4
#define EXIV2_SLICE_HPP
5
6
#include <cassert>
7
#include <cstddef>
8
#include <iterator>
9
#include <stdexcept>
10
11
namespace Exiv2 {
12
namespace Internal {
13
/*!
14
 * Common base class of all slice implementations.
15
 *
16
 * Implements only the most basic functions, which do not require any
17
 * knowledge about the stored data.
18
 */
19
struct SliceBase {
20
76.6M
  SliceBase(size_t begin, size_t end) : begin_(begin), end_(end) {
21
76.6M
    if (begin >= end) {
22
57
      throw std::out_of_range("Begin must be smaller than end");
23
57
    }
24
76.6M
  }
25
26
  /*!
27
   * Return the number of elements in the slice.
28
   */
29
158M
  [[nodiscard]] size_t size() const noexcept {
30
    // cannot underflow, as we know that begin < end
31
158M
    return end_ - begin_;
32
158M
  }
33
34
 protected:
35
  /*!
36
   * Throw an exception when index is too large.
37
   *
38
   * @throw std::out_of_range when `index` will access an element
39
   * outside of the slice
40
   */
41
155M
  void rangeCheck(size_t index) const {
42
155M
    if (index >= size()) {
43
0
      throw std::out_of_range("Index outside of the slice");
44
0
    }
45
155M
  }
46
47
  /*!
48
   * lower and upper bounds of the slice with respect to the
49
   * container/array stored in storage_
50
   */
51
  size_t begin_;
52
  size_t end_;
53
};
54
55
/*!
56
 * @brief This class provides the public-facing const-qualified methods
57
 * of a slice.
58
 *
59
 * The public methods are implemented in a generic fashion using a
60
 * storage_type. This type contains the actual reference to the data to
61
 * which the slice points and provides the following methods:
62
 *
63
 * - (const) value_type& unsafeAt(size_t index) (const)
64
 *   Return the value at the given index of the underlying container,
65
 *   without promising to perform a range check and without any
66
 *   knowledge of the slices' size
67
 *
68
 * - const_iterator/iterator unsafeGetIteratorAt(size_t index) (const)
69
 *   Return a (constant) iterator at the given index of the underlying
70
 *   container. Again, no range checks are promised.
71
 *
72
 * - Constructor(data_type& data, size_t begin, size_t end)
73
 *   Can use `begin` & `end` to perform range checks on `data`, but
74
 *   should not store both values. Must not take ownership of `data`!
75
 *
76
 * - Must save data as a public member named `data_`.
77
 *
78
 * - Must provide appropriate typedefs for iterator, const_iterator and
79
 *   value_type
80
 */
81
template <template <typename data_type> class storage_type, typename data_type>
82
struct ConstSliceBase : SliceBase {
83
  using value_type = typename storage_type<data_type>::value_type;
84
85
  /*!
86
   * Default constructor, requires begin to be smaller than end,
87
   * otherwise an exception is thrown. Also forwards all parameters to
88
   * the constructor of storage_
89
   */
90
76.6M
  ConstSliceBase(data_type& data, size_t begin, size_t end) : SliceBase(begin, end), storage_(data, begin, end) {
91
76.6M
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::PtrSliceStorage, unsigned char*>::ConstSliceBase(unsigned char*&, unsigned long, unsigned long)
Line
Count
Source
90
120k
  ConstSliceBase(data_type& data, size_t begin, size_t end) : SliceBase(begin, end), storage_(data, begin, end) {
91
120k
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::PtrSliceStorage, unsigned char* const>::ConstSliceBase(unsigned char* const&, unsigned long, unsigned long)
Line
Count
Source
90
1.24k
  ConstSliceBase(data_type& data, size_t begin, size_t end) : SliceBase(begin, end), storage_(data, begin, end) {
91
1.24k
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::PtrSliceStorage, unsigned char const*>::ConstSliceBase(unsigned char const*&, unsigned long, unsigned long)
Line
Count
Source
90
76.5M
  ConstSliceBase(data_type& data, size_t begin, size_t end) : SliceBase(begin, end), storage_(data, begin, end) {
91
76.5M
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::ContainerStorage, Exiv2::Slice<unsigned char*> const>::ConstSliceBase(Exiv2::Slice<unsigned char*> const&, unsigned long, unsigned long)
Line
Count
Source
90
1.22k
  ConstSliceBase(data_type& data, size_t begin, size_t end) : SliceBase(begin, end), storage_(data, begin, end) {
91
1.22k
  }
92
93
  /*!
94
   * Obtain a constant reference to the element with the specified
95
   * index in the slice.
96
   *
97
   * @throw std::out_of_range when index is out of bounds of the slice
98
   */
99
155M
  [[nodiscard]] const auto& at(size_t index) const {
100
155M
    rangeCheck(index);
101
    // we know: begin_ < end <= size() <= SIZE_T_MAX
102
    // and: index < end - begin
103
    // thus: index + begin < end <= SIZE_T_MAX
104
    // => no overflow is possible
105
155M
    return storage_.unsafeAt(begin_ + index);
106
155M
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::PtrSliceStorage, unsigned char*>::at(unsigned long) const
Line
Count
Source
99
2.43M
  [[nodiscard]] const auto& at(size_t index) const {
100
2.43M
    rangeCheck(index);
101
    // we know: begin_ < end <= size() <= SIZE_T_MAX
102
    // and: index < end - begin
103
    // thus: index + begin < end <= SIZE_T_MAX
104
    // => no overflow is possible
105
2.43M
    return storage_.unsafeAt(begin_ + index);
106
2.43M
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::PtrSliceStorage, unsigned char const*>::at(unsigned long) const
Line
Count
Source
99
153M
  [[nodiscard]] const auto& at(size_t index) const {
100
153M
    rangeCheck(index);
101
    // we know: begin_ < end <= size() <= SIZE_T_MAX
102
    // and: index < end - begin
103
    // thus: index + begin < end <= SIZE_T_MAX
104
    // => no overflow is possible
105
153M
    return storage_.unsafeAt(begin_ + index);
106
153M
  }
Exiv2::Internal::ConstSliceBase<Exiv2::Internal::ContainerStorage, Exiv2::Slice<unsigned char*> const>::at(unsigned long) const
Line
Count
Source
99
7.81k
  [[nodiscard]] const auto& at(size_t index) const {
100
7.81k
    rangeCheck(index);
101
    // we know: begin_ < end <= size() <= SIZE_T_MAX
102
    // and: index < end - begin
103
    // thus: index + begin < end <= SIZE_T_MAX
104
    // => no overflow is possible
105
7.81k
    return storage_.unsafeAt(begin_ + index);
106
7.81k
  }
107
108
  /*!
109
   * Obtain a constant iterator to the first element in the slice.
110
   */
111
  [[nodiscard]] auto cbegin() const noexcept {
112
    return storage_.unsafeGetIteratorAt(begin_);
113
  }
114
115
  /*!
116
   * Obtain a constant iterator to the first beyond the slice.
117
   */
118
  [[nodiscard]] auto cend() const noexcept {
119
    return storage_.unsafeGetIteratorAt(end_);
120
  }
121
122
  /*!
123
   * Create a constant sub-slice with the given bounds (with respect
124
   * to the current slice).
125
   *
126
   * @tparam slice_type  Type of the slice that this function shall
127
   * return. Provide it with the type of the class that derives from
128
   * mutable_slice_base.
129
   */
130
  template <typename slice_type>
131
1.24k
  [[nodiscard]] slice_type subSlice(size_t begin, size_t end) const {
132
1.24k
    this->rangeCheck(begin);
133
    // end == size() is a legal value, since end is the first
134
    // element beyond the slice
135
    // end == 0 is not a legal value (subtraction will underflow and
136
    // throw an exception)
137
1.24k
    this->rangeCheck(end - 1);
138
    // additions are safe, begin and end are smaller than size()
139
1.24k
    const size_t new_begin = begin + this->begin_;
140
1.24k
    const size_t new_end = this->begin_ + end;
141
1.24k
    if (new_end > this->end_) {
142
0
      throw std::out_of_range("Invalid input parameters to slice");
143
0
    }
144
1.24k
    return {storage_.data_, new_begin, new_end};
145
1.24k
  }
146
147
 protected:
148
  /*!
149
   * Stores a reference to the actual data.
150
   */
151
  storage_type<data_type> storage_;
152
};
153
154
/*!
155
 * This class provides all public-facing non-const-qualified methods of
156
 * slices. It only re-implements the const-qualified versions as
157
 * non-const.
158
 */
159
template <template <typename> class storage_type, typename data_type>
160
struct MutableSliceBase : public ConstSliceBase<storage_type, data_type> {
161
  using ConstSliceBase<storage_type, data_type>::ConstSliceBase;
162
163
  /*!
164
   * Obtain a reference to the element with the specified index in the
165
   * slice.
166
   *
167
   * @throw std::out_of_range when index is out of bounds of the slice
168
   */
169
  auto& at(size_t index) {
170
    this->rangeCheck(index);
171
    return this->storage_.unsafeAt(this->begin_ + index);
172
  }
173
174
2.43M
  [[nodiscard]] const auto& at(size_t index) const {
175
2.43M
    return base_type::at(index);
176
2.43M
  }
177
178
  /*!
179
   * Obtain an iterator to the first element in the slice.
180
   */
181
  [[nodiscard]] auto begin() noexcept {
182
    return this->storage_.unsafeGetIteratorAt(this->begin_);
183
  }
184
185
  /*!
186
   * Obtain an iterator to the first element beyond the slice.
187
   */
188
  [[nodiscard]] auto end() noexcept {
189
    return this->storage_.unsafeGetIteratorAt(this->end_);
190
  }
191
192
 protected:
193
  /*!
194
   * Explicitly convert this instance into a base-class of the
195
   * appropriate constant version of this slice.
196
   *
197
   * This function is required to properly implement the `subSlice()
198
   * const` function for mutable slices. The problem here is, that a
199
   * slice<T> and a slice<const T> actually don't share the same base
200
   * class `ConstSliceBase<storage_type, T>`. Instead `slice<T>`
201
   * inherits from `ConstSliceBase<storage_type, T>` and `slice<const
202
   * T>` inherits from `ConstSliceBase<storage_type, const T>`.
203
   *
204
   * Now, `slice<T>` can call the `subSlice() const` method from its
205
   * base class, but that will return a mutable `slice<T>`! Instead we
206
   * use this function to convert the `slice<T>` into the parent of
207
   * the appropriate `slice<const T>` and call its `subSlice() const`,
208
   * which returns the correct type.
209
   */
210
1.24k
  [[nodiscard]] ConstSliceBase<storage_type, const data_type> to_const_base() const noexcept {
211
1.24k
    return {this->storage_.data_, this->begin_, this->end_};
212
1.24k
  }
213
214
  using base_type = ConstSliceBase<storage_type, data_type>;
215
216
  /*!
217
   * Create a mutable sub-slice with the given bounds (with respect to
218
   * the current slice).
219
   *
220
   * @tparam slice_type  Type of the slice that this function shall
221
   * return. Provide it with the type of the class that derives from
222
   * mutable_slice_base.
223
   */
224
  template <typename slice_type>
225
  [[nodiscard]] slice_type subSlice(size_t begin, size_t end) {
226
    this->rangeCheck(begin);
227
    // end == size() is a legal value, since end is the first
228
    // element beyond the slice
229
    // end == 0 is not a legal value (subtraction will underflow and
230
    // throw an exception)
231
    this->rangeCheck(end - 1);
232
233
    // additions are safe, begin & end are smaller than size()
234
    const size_t new_begin = begin + this->begin_;
235
    const size_t new_end = this->begin_ + end;
236
    if (new_end > this->end_) {
237
      throw std::out_of_range("Invalid input parameters to slice");
238
    }
239
    return {this->storage_.data_, new_begin, new_end};
240
  }
241
};
242
243
/*!
244
 * Implementation of the storage concept for STL-containers.
245
 *
246
 * @tparam container  Type of the STL-container.
247
 */
248
template <typename container>
249
struct ContainerStorage {
250
  using value_type = std::remove_cv_t<typename container::value_type>;
251
252
  /*!
253
   * @throw std::out_of_range when end is larger than the container's
254
   * size.
255
   */
256
1.20k
  ContainerStorage(container& data, size_t /* begin*/, size_t end) : data_(data) {
257
1.20k
    if (end > data.size()) {
258
0
      throw std::out_of_range("Invalid input parameters to slice");
259
0
    }
260
1.20k
  }
261
262
  /*!
263
   * Obtain a constant reference to the element with the given `index`
264
   * in the container.
265
   *
266
   * @throw whatever container::at() throws
267
   */
268
7.81k
  [[nodiscard]] const auto& unsafeAt(size_t index) const {
269
7.81k
    return data_.at(index);
270
7.81k
  }
271
272
  [[nodiscard]] auto& unsafeAt(size_t index) {
273
    return data_.at(index);
274
  }
275
276
  /*!
277
   * Obtain an iterator at the position of the element with the given
278
   * index in the container.
279
   *
280
   * @throw whatever container::begin() and std::advance() throw
281
   */
282
  [[nodiscard]] auto unsafeGetIteratorAt(size_t index) {
283
    // we are screwed if the container got changed => try to catch it
284
    assert(index <= data_.size());
285
286
    auto it = data_.begin();
287
    std::advance(it, index);
288
    return it;
289
  }
290
291
  [[nodiscard]] auto unsafeGetIteratorAt(size_t index) const {
292
    assert(index <= data_.size());
293
294
    auto it = data_.begin();
295
    std::advance(it, index);
296
    return it;
297
  }
298
299
  container& data_;
300
};
301
302
/*!
303
 * @brief Implementation of the storage concept for slices of C arrays.
304
 *
305
 * @tparam storage_type  Type as which the C-array should be stored. Use
306
 * this parameter to save constant arrays as `const` and mutable ones as
307
 * non-`const`.
308
 */
309
template <typename storage_type>
310
struct PtrSliceStorage {
311
  using value_type = std::remove_cv_t<std::remove_pointer_t<storage_type>>;
312
313
  /*!
314
   * Stores ptr and checks that it is not `NULL`. The slice's bounds
315
   * are ignored, as we do not know the array's length.
316
   *
317
   * @throw std::invalid_argument when ptr is `NULL`
318
   */
319
76.6M
  PtrSliceStorage(storage_type ptr, size_t /*begin*/, size_t /*end*/) : data_(ptr) {
320
76.6M
    if (!ptr) {
321
0
      throw std::invalid_argument("Null pointer passed to slice constructor");
322
0
    }
323
76.6M
  }
Exiv2::Internal::PtrSliceStorage<unsigned char*>::PtrSliceStorage(unsigned char*, unsigned long, unsigned long)
Line
Count
Source
319
120k
  PtrSliceStorage(storage_type ptr, size_t /*begin*/, size_t /*end*/) : data_(ptr) {
320
120k
    if (!ptr) {
321
0
      throw std::invalid_argument("Null pointer passed to slice constructor");
322
0
    }
323
120k
  }
Exiv2::Internal::PtrSliceStorage<unsigned char* const>::PtrSliceStorage(unsigned char*, unsigned long, unsigned long)
Line
Count
Source
319
1.24k
  PtrSliceStorage(storage_type ptr, size_t /*begin*/, size_t /*end*/) : data_(ptr) {
320
1.24k
    if (!ptr) {
321
0
      throw std::invalid_argument("Null pointer passed to slice constructor");
322
0
    }
323
1.24k
  }
Exiv2::Internal::PtrSliceStorage<unsigned char const*>::PtrSliceStorage(unsigned char const*, unsigned long, unsigned long)
Line
Count
Source
319
76.5M
  PtrSliceStorage(storage_type ptr, size_t /*begin*/, size_t /*end*/) : data_(ptr) {
320
76.5M
    if (!ptr) {
321
0
      throw std::invalid_argument("Null pointer passed to slice constructor");
322
0
    }
323
76.5M
  }
324
325
  /*!
326
   * Obtain a reference to the element with the given `index` in the
327
   * array.
328
   *
329
   * @note Does not throw
330
   */
331
  [[nodiscard]] auto& unsafeAt(size_t index) noexcept {
332
    return data_[index];
333
  }
334
335
155M
  [[nodiscard]] const auto& unsafeAt(size_t index) const noexcept {
336
155M
    return data_[index];
337
155M
  }
Exiv2::Internal::PtrSliceStorage<unsigned char*>::unsafeAt(unsigned long) const
Line
Count
Source
335
2.43M
  [[nodiscard]] const auto& unsafeAt(size_t index) const noexcept {
336
2.43M
    return data_[index];
337
2.43M
  }
Exiv2::Internal::PtrSliceStorage<unsigned char const*>::unsafeAt(unsigned long) const
Line
Count
Source
335
153M
  [[nodiscard]] const auto& unsafeAt(size_t index) const noexcept {
336
153M
    return data_[index];
337
153M
  }
338
339
  /*!
340
   * Obtain an iterator (=pointer) at the position of the element with
341
   * the given index in the container.
342
   *
343
   * @note Does not throw
344
   */
345
  [[nodiscard]] auto unsafeGetIteratorAt(size_t index) noexcept {
346
    return data_ + index;
347
  }
348
349
  [[nodiscard]] auto unsafeGetIteratorAt(size_t index) const noexcept {
350
    return data_ + index;
351
  }
352
353
  storage_type data_;
354
};
355
356
}  // namespace Internal
357
358
/*!
359
 * @brief Slice (= view) for STL containers.
360
 *
361
 * This is a very simple implementation of slices (i.e. views of sub-arrays)
362
 * for STL containers that support O(1) element access and random access
363
 * iterators (like std::vector, std::array and std::string).
364
 *
365
 * A slice represents the semi-open interval [begin, end) and provides a
366
 * (mutable) view, it does however not own the data! It can be used to
367
 * conveniently pass parts of containers into functions without having to use
368
 * iterators or offsets.
369
 *
370
 * In contrast to C++20's std::span<T> it is impossible to read beyond the
371
 * container's bounds and unchecked access is not-possible (by design).
372
 *
373
 * Example usage:
374
 * ~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
375
 * std::vector<int> vec = {0, 1, 2, 3, 4};
376
 * slice<std::vector<int> > one_two(vec, 1, 3);
377
 * assert(one_two.size() == 2);
378
 * assert(one_two.at(0) == 1 && one_two.at(1) == 2);
379
 * // mutate the contents:
380
 * one_two.at(0) *= 2;
381
 * one_two.at(1) *= 3;
382
 * assert(one_two.at(0) == 2 && one_two.at(1) == 6);
383
 * ~~~~~~~~~~~~~~~~~~~~~~~~~
384
 *
385
 * Slices also offer access via iterators of the same type as the underlying
386
 * container, so that they can be used in a comparable fashion:
387
 * ~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
388
 * std::vector<int> vec = {0, 1, 2, 3, 4};
389
 * slice<std::vector<int>> three_four(vec, 3, 5);
390
 * assert(*three_four.begin() == 3 && *three_four.end() == 4);
391
 * // this prints:
392
 * // 3
393
 * // 4
394
 * for (const auto & elem : three_four) {
395
 *     std::cout << elem << '\n';
396
 * }
397
 * ~~~~~~~~~~~~~~~~~~~~~~~~~
398
 *
399
 * @tparam container A STL container type, like vector or array. Must support
400
 * array-like access via the `at()` method.
401
 */
402
template <typename container>
403
struct Slice : public Internal::MutableSliceBase<Internal::ContainerStorage, container> {
404
  using Internal::MutableSliceBase<Internal::ContainerStorage, container>::MutableSliceBase;
405
406
  /*!
407
   * Constructs a new constant subSlice. Behaves otherwise exactly like
408
   * the non-const version.
409
   */
410
  [[nodiscard]] Slice<const container> subSlice(size_t begin, size_t end) const {
411
    return this->to_const_base().template subSlice<Slice<const container>>(begin, end);
412
  }
413
};
414
415
/*!
416
 * @brief Specialization of slices for constant containers.
417
 */
418
template <typename container>
419
struct Slice<const container> : public Internal::ConstSliceBase<Internal::ContainerStorage, const container> {
420
  using Internal::ConstSliceBase<Internal::ContainerStorage, const container>::ConstSliceBase;
421
422
  [[nodiscard]] Slice subSlice(size_t begin, size_t end) const {
423
    return Internal::ConstSliceBase<Internal::ContainerStorage,
424
                                    const container>::template subSlice<Slice<const container>>(begin, end);
425
  }
426
};
427
428
/*!
429
 * Specialization of slices for constant C-arrays.
430
 *
431
 * These have exactly the same interface as the slices for STL-containers,
432
 * with the *crucial* exception, that the slice's constructor *cannot* make
433
 * a proper bounds check! It can only verify that you didn't accidentally
434
 * swap begin and end!
435
 */
436
template <typename T>
437
struct Slice<const T*> : public Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*> {
438
  /*!
439
   * Constructor.
440
   *
441
   * @param[in] ptr  C-array of which a slice should be constructed. Must
442
   *     not be a null pointer.
443
   * @param[in] begin  Index of the first element in the slice.
444
   * @param[in] end  Index of the first element that is no longer in the
445
   *     slice.
446
   *
447
   * Please note that the constructor has no way how to verify that
448
   * `begin` and `end` are not out of bounds of the provided array!
449
   */
450
  Slice(const T* ptr, size_t begin, size_t end) :
451
76.5M
      Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*>(ptr, begin, end) {
452
    // TODO: use using in C++11
453
76.5M
  }
454
455
  [[nodiscard]] Slice<const T*> subSlice(size_t begin, size_t end) const {
456
    return Internal::ConstSliceBase<Internal::PtrSliceStorage, const T*>::template subSlice<Slice<const T*>>(begin,
457
                                                                                                             end);
458
  }
459
};
460
461
/*!
462
 * Specialization of slices for (mutable) C-arrays.
463
 */
464
template <typename T>
465
struct Slice<T*> : public Internal::MutableSliceBase<Internal::PtrSliceStorage, T*> {
466
120k
  Slice(T* ptr, size_t begin, size_t end) : Internal::MutableSliceBase<Internal::PtrSliceStorage, T*>(ptr, begin, end) {
467
    // TODO: use using in C++11
468
120k
  }
469
470
  [[nodiscard]] Slice<T*> subSlice(size_t begin, size_t end) {
471
    return Internal::MutableSliceBase<Internal::PtrSliceStorage, T*>::template subSlice<Slice<T*>>(begin, end);
472
  }
473
474
1.24k
  [[nodiscard]] Slice<const T*> subSlice(size_t begin, size_t end) const {
475
1.24k
    return this->to_const_base().template subSlice<Slice<const T*>>(begin, end);
476
1.24k
  }
477
};
478
479
/*!
480
 * @brief Return a new slice with the given bounds.
481
 *
482
 * Convenience wrapper around the slice's constructor for automatic template
483
 * parameter deduction.
484
 */
485
template <typename T>
486
1.22k
[[nodiscard]] Slice<T> makeSlice(T& cont, size_t begin, size_t end) {
487
1.22k
  return {cont, begin, end};
488
1.22k
}
489
490
/*!
491
 * Overload of makeSlice for slices of C-arrays.
492
 */
493
template <typename T>
494
[[nodiscard]] Slice<T*> makeSlice(T* ptr, size_t begin, size_t end) {
495
  return {ptr, begin, end};
496
}
497
498
/*!
499
 * @brief Return a new slice spanning the whole container.
500
 */
501
template <typename container>
502
[[nodiscard]] Slice<container> makeSlice(container& cont) {
503
  return {cont, 0, cont.size()};
504
}
505
506
/*!
507
 * @brief Return a new slice spanning from begin until the end of the
508
 * container.
509
 */
510
template <typename container>
511
[[nodiscard]] Slice<container> makeSliceFrom(container& cont, size_t begin) {
512
  return {cont, begin, cont.size()};
513
}
514
515
/*!
516
 * @brief Return a new slice spanning until `end`.
517
 */
518
template <typename container>
519
[[nodiscard]] Slice<container> makeSliceUntil(container& cont, size_t end) {
520
  return {cont, 0, end};
521
}
522
523
/*!
524
 * Overload of makeSliceUntil for pointer based slices.
525
 */
526
template <typename T>
527
76.5M
[[nodiscard]] Slice<T*> makeSliceUntil(T* ptr, size_t end) {
528
76.5M
  return {ptr, 0, end};
529
76.5M
}
Exiv2::Slice<unsigned char*> Exiv2::makeSliceUntil<unsigned char>(unsigned char*, unsigned long)
Line
Count
Source
527
2.49k
[[nodiscard]] Slice<T*> makeSliceUntil(T* ptr, size_t end) {
528
2.49k
  return {ptr, 0, end};
529
2.49k
}
Exiv2::Slice<unsigned char const*> Exiv2::makeSliceUntil<unsigned char const>(unsigned char const*, unsigned long)
Line
Count
Source
527
76.5M
[[nodiscard]] Slice<T*> makeSliceUntil(T* ptr, size_t end) {
528
76.5M
  return {ptr, 0, end};
529
76.5M
}
530
531
}  // namespace Exiv2
532
533
#endif  // EXIV2_SLICE_HPP