• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.34
/third_party/libertiff/libertiff.hpp
1
// SPDX-License-Identifier: MIT
2
// Copyright 2024, Even Rouault <even.rouault at spatialys.com>
3

4
// Canonical URL: https://github.com/libertiff/libertiff/blob/master/libertiff.hpp
5

6
#ifndef LIBERTIFF_HPP_INCLUDED
7
#define LIBERTIFF_HPP_INCLUDED
8

9
//////////////////////////////////////////////////////////////
10
// libertiff = libre TIFF or LIB E(ven) R(ouault) TIFF... ? //
11
//////////////////////////////////////////////////////////////
12

13
#if __cplusplus >= 202002L
14
#include <bit>  // std::endian
15
#endif
16

17
#include <algorithm>
18
#include <array>
19
#include <cassert>
20
#include <cstring>
21
#include <limits>
22
#include <memory>
23
#include <set>
24
#include <string>
25
#include <type_traits>
26
#include <vector>
27

28
#ifndef LIBERTIFF_NS
29
#define LIBERTIFF_NS libertiff
30
#endif
31

32
/** Libertiff is a C++11 simple, header-only, TIFF reader. It is MIT licensed.
33
 *
34
 * Handles both ClassicTIFF and BigTIFF, little-endian or big-endian ordered.
35
 *
36
 * The library does not (yet?) offer codec facilities. It is mostly aimed at
37
 * browsing through the linked chain of Image File Directory (IFD) and their tags.
38
 *
39
 * "Offline" tag values are not loaded at IFD opening time, but only upon
40
 * request, which helps handling files with tags with an arbitrarily large
41
 * number of values.
42
 *
43
 * The library is thread-safe (that is the instances that it returns can
44
 * be used from multiple threads), if passed FileReader instances are themselves
45
 * thread-safe.
46
 *
47
 * The library does not throw exceptions (but underlying std library might
48
 * throw exceptions in case of out-of-memory situations)
49
 *
50
 * Optional features:
51
 * - define LIBERTIFF_C_FILE_READER before including libertiff.hpp, so that
52
 *   the libertiff::CFileReader class is available
53
 */
54
namespace LIBERTIFF_NS
55
{
56

57
#if __cplusplus >= 201703L
58
#define LIBERTIFF_STATIC_ASSERT(x) static_assert(x)
59
#define LIBERTIFF_CONSTEXPR constexpr
60
#else
61
#define LIBERTIFF_STATIC_ASSERT(x) static_assert((x), #x)
62
#define LIBERTIFF_CONSTEXPR
63
#endif
64

65
template <typename T, typename... Args>
66
std::unique_ptr<T> make_unique(Args &&...args)
1,442✔
67
{
68
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
1,442✔
69
}
70

71
/** Returns whether the host is little-endian ordered */
72
inline bool isHostLittleEndian()
1,338✔
73
{
74
#if __cplusplus >= 202002L
75
    return std::endian::native == std::endian::little;
76
#elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) &&          \
77
       (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) ||                         \
78
    defined(_MSC_VER)
79
    return true;
1,338✔
80
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) &&              \
81
    (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
82
    return false;
83
#else
84
    uint32_t one = 1;
85
    char one_as_char_array[sizeof(uint32_t)];
86
    std::memcpy(one_as_char_array, &one, sizeof(uint32_t));
87
    return one_as_char_array[0] == 1;
88
#endif
89
}
90

91
/** Byte-swap */
92
template <class T> inline T byteSwap(T v);
93

94
/** Byte-swap a uint8_t */
95
template <> inline uint8_t byteSwap(uint8_t v)
96
{
97
    return v;
98
}
99

100
/** Byte-swap a int8_t */
101
template <> inline int8_t byteSwap(int8_t v)
102
{
103
    return v;
104
}
105

106
/** Byte-swap a uint16_t */
107
template <> inline uint16_t byteSwap(uint16_t v)
5,931✔
108
{
109
    return uint16_t((v >> 8) | ((v & 0xff) << 8));
5,931✔
110
}
111

112
/** Byte-swap a int16_t */
113
template <> inline int16_t byteSwap(int16_t v)
114
{
115
    uint16_t u;
116
    LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
117
    std::memcpy(&u, &v, sizeof(u));
118
    u = byteSwap(u);
119
    std::memcpy(&v, &u, sizeof(u));
120
    return v;
121
}
122

123
/** Byte-swap a uint32_t */
124
template <> inline uint32_t byteSwap(uint32_t v)
309,705✔
125
{
126
    return (v >> 24) | (((v >> 16) & 0xff) << 8) | (((v >> 8) & 0xff) << 16) |
309,705✔
127
           ((v & 0xff) << 24);
309,705✔
128
}
129

130
/** Byte-swap a int32_t */
131
template <> inline int32_t byteSwap(int32_t v)
×
132
{
133
    uint32_t u;
134
    LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
135
    std::memcpy(&u, &v, sizeof(u));
×
136
    u = byteSwap(u);
×
137
    std::memcpy(&v, &u, sizeof(u));
×
138
    return v;
×
139
}
140

141
/** Byte-swap a uint64_t */
142
template <> inline uint64_t byteSwap(uint64_t v)
153,395✔
143
{
144
    return (uint64_t(byteSwap(uint32_t(v & ~(0U)))) << 32) |
153,395✔
145
           byteSwap(uint32_t(v >> 32));
153,395✔
146
}
147

148
/** Byte-swap a int64_t */
149
template <> inline int64_t byteSwap(int64_t v)
150
{
151
    uint64_t u;
152
    std::memcpy(&u, &v, sizeof(u));
153
    u = byteSwap(u);
154
    std::memcpy(&v, &u, sizeof(u));
155
    return v;
156
}
157

158
/** Byte-swap a float */
159
template <> inline float byteSwap(float v)
160
{
161
    uint32_t u;
162
    LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
163
    std::memcpy(&u, &v, sizeof(u));
164
    u = byteSwap(u);
165
    std::memcpy(&v, &u, sizeof(u));
166
    return v;
167
}
168

169
/** Byte-swap a double */
170
template <> inline double byteSwap(double v)
171
{
172
    uint64_t u;
173
    LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
174
    std::memcpy(&u, &v, sizeof(u));
175
    u = byteSwap(u);
176
    std::memcpy(&v, &u, sizeof(u));
177
    return v;
178
}
179
}  // namespace LIBERTIFF_NS
180

181
namespace LIBERTIFF_NS
182
{
183
#if defined(__clang__)
184
#pragma clang diagnostic push
185
#pragma clang diagnostic ignored "-Wweak-vtables"
186
#endif
187

188
/** Interface to read from a file. */
189
class FileReader
190
{
191
  public:
192
    virtual ~FileReader() = default;
1,338✔
193

194
    /** Return file size in bytes */
195
    virtual uint64_t size() const = 0;
196

197
    /** Read 'count' bytes from offset 'offset' into 'buffer' and
198
     * return the number of bytes actually read.
199
     */
200
    virtual size_t read(uint64_t offset, size_t count, void *buffer) const = 0;
201
};
202

203
#if defined(__clang__)
204
#pragma clang diagnostic pop
205
#endif
206
}  // namespace LIBERTIFF_NS
207

208
namespace LIBERTIFF_NS
209
{
210
/** Read context: associates a file, and the byte ordering of the TIFF file */
211
class ReadContext
212
{
213
  public:
214
    /** Constructor */
215
    ReadContext(const std::shared_ptr<const FileReader> &file,
1,338✔
216
                bool mustByteSwap)
217
        : m_file(file), m_mustByteSwap(mustByteSwap)
1,338✔
218
    {
219
    }
1,338✔
220

221
    /** Return if values of more than 1-byte must be byte swapped.
222
     * To be only taken into account when reading pixels. Tag values are
223
     * automatically byte-swapped */
224
    inline bool mustByteSwap() const
6,414✔
225
    {
226
        return m_mustByteSwap;
6,414✔
227
    }
228

229
    /** Return file size */
230
    inline uint64_t size() const
108✔
231
    {
232
        return m_file->size();
108✔
233
    }
234

235
    /** Read count raw bytes at offset into buffer */
236
    void read(uint64_t offset, size_t count, void *buffer, bool &ok) const
7,807✔
237
    {
238
        if (m_file->read(offset, count, buffer) != count)
7,807✔
239
            ok = false;
17✔
240
    }
7,802✔
241

242
    /** Read single value at offset */
243
    template <class T> T read(uint64_t offset, bool &ok) const
89,694✔
244
    {
245
#if __cplusplus >= 201703L
246
        static_assert(
247
            std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
248
            std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
249
            std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
250
            std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t> ||
251
            std::is_same_v<T, float> || std::is_same_v<T, double>);
252
#endif
253

254
        T res = 0;
89,694✔
255
        if (m_file->read(offset, sizeof(res), &res) != sizeof(res))
89,694✔
256
        {
257
            ok = false;
186✔
258
            return 0;
186✔
259
        }
260
        if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
261
        {
262
            if (m_mustByteSwap)
89,546✔
263
                res = byteSwap(res);
8,837✔
264
        }
265
        return res;
89,546✔
266
    }
267

268
    /** Read a unsigned rational (type == Type::Rational) */
269
    template <class T = uint32_t>
270
    double readRational(uint64_t offset, bool &ok) const
×
271
    {
272
        const auto numerator = read<T>(offset, ok);
×
273
        const auto denominator = read<T>(offset + sizeof(T), ok);
×
274
        if (denominator == 0)
×
275
        {
276
            ok = false;
×
277
            return std::numeric_limits<double>::quiet_NaN();
×
278
        }
279
        return double(numerator) / denominator;
×
280
    }
281

282
    /** Read a signed rational (type == Type::SRational) */
283
    double readSignedRational(uint64_t offset, bool &ok) const
×
284
    {
285
        return readRational<int32_t>(offset, ok);
×
286
    }
287

288
    /** Read length bytes at offset (typically for ASCII tag) as a string */
289
    std::string readString(std::string &res, uint64_t offset, size_t length,
96✔
290
                           bool &ok) const
291
    {
292
        res.resize(length);
96✔
293
        if (length > 0 && m_file->read(offset, length, &res[0]) != length)
96✔
294
        {
295
            ok = false;
×
296
            res.clear();
×
297
            return res;
×
298
        }
299
        // Strip trailing nul byte if found
300
        if (length > 0 && res.back() == 0)
96✔
301
            res.pop_back();
95✔
302
        return res;
96✔
303
    }
304

305
    /** Read length bytes at offset (typically for ASCII tag) as a string */
306
    std::string readString(uint64_t offset, size_t length, bool &ok) const
96✔
307
    {
308
        std::string res;
96✔
309
        readString(res, offset, length, ok);
96✔
310
        return res;
96✔
311
    }
312

313
    /** Read an array of count values starting at offset */
314
    template <class T>
315
    void readArray(std::vector<T> &array, uint64_t offset, size_t count,
1,071✔
316
                   bool &ok) const
317
    {
318
#if __cplusplus >= 201703L
319
        static_assert(
320
            std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
321
            std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
322
            std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
323
            std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t> ||
324
            std::is_same_v<T, float> || std::is_same_v<T, double>);
325
#endif
326

327
        array.resize(count);
1,071✔
328
        const size_t countBytes = count * sizeof(T);
1,071✔
329
        if (count > 0 &&
2,142✔
330
            m_file->read(offset, countBytes, &array[0]) != countBytes)
1,071✔
331
        {
332
            ok = false;
11✔
333
            array.clear();
11✔
334
        }
335
        else if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
336
        {
337
            if (m_mustByteSwap)
1,023✔
338
            {
339
                if LIBERTIFF_CONSTEXPR (std::is_same<T, float>::value)
340
                {
341
                    uint32_t *uint32Array =
342
                        reinterpret_cast<uint32_t *>(array.data());
343
                    for (size_t i = 0; i < count; ++i)
344
                    {
345
                        uint32Array[i] = byteSwap(uint32Array[i]);
346
                    }
347
                }
348
                else if LIBERTIFF_CONSTEXPR (std::is_same<T, double>::value)
349
                {
350
                    uint64_t *uint64Array =
351
                        reinterpret_cast<uint64_t *>(array.data());
20✔
352
                    for (size_t i = 0; i < count; ++i)
153,194✔
353
                    {
354
                        uint64Array[i] = byteSwap(uint64Array[i]);
153,174✔
355
                    }
356
                }
357
                else
358
                {
359
                    for (size_t i = 0; i < count; ++i)
240✔
360
                    {
361
                        array[i] = byteSwap(array[i]);
230✔
362
                    }
363
                }
364
            }
365
        }
366
    }
1,071✔
367

368
    /** Read an array of count values starting at offset */
369
    template <class T>
370
    std::vector<T> readArray(uint64_t offset, size_t count, bool &ok) const
1,071✔
371
    {
372
        std::vector<T> array;
1,071✔
373
        readArray(array, offset, count, ok);
1,071✔
374
        return array;
1,071✔
375
    }
376

377
  private:
378
    const std::shared_ptr<const FileReader> m_file;
379
    const bool m_mustByteSwap;
380
};
381
}  // namespace LIBERTIFF_NS
382

383
namespace LIBERTIFF_NS
384
{
385
/** Type of a TIFF tag code */
386
typedef uint16_t TagCodeType;
387

388
/** TIFF tag codes */
389
namespace TagCode
390
{
391
constexpr TagCodeType SubFileType = 254;
392
constexpr TagCodeType OldSubFileType = 255;
393

394
// Base line and extended TIFF tags
395
constexpr TagCodeType ImageWidth = 256;
396
constexpr TagCodeType ImageLength = 257;
397
constexpr TagCodeType BitsPerSample = 258;
398
constexpr TagCodeType Compression = 259;
399
constexpr TagCodeType PhotometricInterpretation = 262;
400
constexpr TagCodeType DocumentName = 269;
401
constexpr TagCodeType ImageDescription = 270;
402
constexpr TagCodeType StripOffsets = 273;
403
constexpr TagCodeType SamplesPerPixel = 277;
404
constexpr TagCodeType RowsPerStrip = 278;
405
constexpr TagCodeType StripByteCounts = 279;
406
constexpr TagCodeType PlanarConfiguration = 284;
407
constexpr TagCodeType Software = 305;
408
constexpr TagCodeType DateTime = 306;
409
constexpr TagCodeType Predictor = 317;
410
constexpr TagCodeType ColorMap = 320;
411
constexpr TagCodeType TileWidth = 322;
412
constexpr TagCodeType TileLength = 323;
413
constexpr TagCodeType TileOffsets = 324;
414
constexpr TagCodeType TileByteCounts = 325;
415
constexpr TagCodeType ExtraSamples = 338;
416
constexpr TagCodeType SampleFormat = 339;
417
constexpr TagCodeType JPEGTables = 347;
418

419
constexpr TagCodeType Copyright = 33432;
420

421
// GeoTIFF tags
422
constexpr TagCodeType GeoTIFFPixelScale = 33550;
423
constexpr TagCodeType GeoTIFFTiePoints = 33922;
424
constexpr TagCodeType GeoTIFFGeoTransMatrix = 34264;
425
constexpr TagCodeType GeoTIFFGeoKeyDirectory = 34735;
426
constexpr TagCodeType GeoTIFFDoubleParams = 34736;
427
constexpr TagCodeType GeoTIFFAsciiParams = 34737;
428

429
// GDAL tags
430
constexpr TagCodeType GDAL_METADATA = 42112;
431
constexpr TagCodeType GDAL_NODATA = 42113;
432

433
// GeoTIFF related
434
constexpr TagCodeType RPCCoefficients = 50844;
435

436
// LERC compression related
437
constexpr TagCodeType LERCParameters =
438
    50674; /* Stores LERC version and additional compression method */
439

440
}  // namespace TagCode
441

442
/** Binary or'ed value of SubFileType flags */
443
namespace SubFileTypeFlags
444
{
445
constexpr uint32_t ReducedImage = 0x1; /* reduced resolution version */
446
constexpr uint32_t Page = 0x2;         /* one page of many */
447
constexpr uint32_t Mask = 0x4;         /* transparency mask */
448
}  // namespace SubFileTypeFlags
449

450
#define LIBERTIFF_CASE_TAGCODE_STR(x)                                          \
451
    case TagCode::x:                                                           \
452
        return #x
453

454
inline const char *tagCodeName(TagCodeType tagCode)
455
{
456
    switch (tagCode)
457
    {
458
        LIBERTIFF_CASE_TAGCODE_STR(SubFileType);
459
        LIBERTIFF_CASE_TAGCODE_STR(OldSubFileType);
460
        LIBERTIFF_CASE_TAGCODE_STR(ImageWidth);
461
        LIBERTIFF_CASE_TAGCODE_STR(ImageLength);
462
        LIBERTIFF_CASE_TAGCODE_STR(BitsPerSample);
463
        LIBERTIFF_CASE_TAGCODE_STR(Compression);
464
        LIBERTIFF_CASE_TAGCODE_STR(PhotometricInterpretation);
465
        LIBERTIFF_CASE_TAGCODE_STR(DocumentName);
466
        LIBERTIFF_CASE_TAGCODE_STR(ImageDescription);
467
        LIBERTIFF_CASE_TAGCODE_STR(StripOffsets);
468
        LIBERTIFF_CASE_TAGCODE_STR(SamplesPerPixel);
469
        LIBERTIFF_CASE_TAGCODE_STR(RowsPerStrip);
470
        LIBERTIFF_CASE_TAGCODE_STR(StripByteCounts);
471
        LIBERTIFF_CASE_TAGCODE_STR(PlanarConfiguration);
472
        LIBERTIFF_CASE_TAGCODE_STR(Software);
473
        LIBERTIFF_CASE_TAGCODE_STR(DateTime);
474
        LIBERTIFF_CASE_TAGCODE_STR(Predictor);
475
        LIBERTIFF_CASE_TAGCODE_STR(ColorMap);
476
        LIBERTIFF_CASE_TAGCODE_STR(TileWidth);
477
        LIBERTIFF_CASE_TAGCODE_STR(TileLength);
478
        LIBERTIFF_CASE_TAGCODE_STR(TileOffsets);
479
        LIBERTIFF_CASE_TAGCODE_STR(TileByteCounts);
480
        LIBERTIFF_CASE_TAGCODE_STR(ExtraSamples);
481
        LIBERTIFF_CASE_TAGCODE_STR(SampleFormat);
482
        LIBERTIFF_CASE_TAGCODE_STR(Copyright);
483
        LIBERTIFF_CASE_TAGCODE_STR(JPEGTables);
484
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFPixelScale);
485
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFTiePoints);
486
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFGeoTransMatrix);
487
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFGeoKeyDirectory);
488
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFDoubleParams);
489
        LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFAsciiParams);
490
        LIBERTIFF_CASE_TAGCODE_STR(GDAL_METADATA);
491
        LIBERTIFF_CASE_TAGCODE_STR(GDAL_NODATA);
492
        LIBERTIFF_CASE_TAGCODE_STR(RPCCoefficients);
493
        LIBERTIFF_CASE_TAGCODE_STR(LERCParameters);
494
        default:
495
            break;
496
    }
497
    return "(unknown)";
498
}
499

500
#undef LIBERTIFF_CASE_TAGCODE_STR
501

502
/** Type of a TIFF tag type */
503
typedef uint16_t TagTypeType;
504

505
/** TIFF tag data types */
506
namespace TagType
507
{
508
constexpr TagTypeType Byte = 1;  /*! Unsigned 8-bit integer */
509
constexpr TagTypeType ASCII = 2; /*! Character */
510
constexpr TagTypeType Short = 3; /*! Unsigned 16-bit integer */
511
constexpr TagTypeType Long = 4;  /*! Unsigned 32-bit integer */
512
constexpr TagTypeType Rational =
513
    5; /*! Positive number as a ratio of two unsigned 32-bit integers */
514
constexpr TagTypeType SByte = 6;     /*! Signed 8-bit integer */
515
constexpr TagTypeType Undefined = 7; /*! Untyped 8-bit data */
516
constexpr TagTypeType SShort = 8;    /*! Signed 16-bit integer */
517
constexpr TagTypeType SLong = 9;     /*! Signed 32-bit integer */
518
constexpr TagTypeType SRational =
519
    10; /*! Signed number as a ratio of two signed 32-bit integers */
520
constexpr TagTypeType Float = 11;  /*! 32-bit IEEE-754 floating point number */
521
constexpr TagTypeType Double = 12; /*! 64-bit IEEE-754 floating point number */
522

523
// BigTIFF additions
524
constexpr TagTypeType Long8 = 16;  /*! Unsigned 64-bit integer */
525
constexpr TagTypeType SLong8 = 17; /*! Signed 64-bit integer */
526
constexpr TagTypeType IFD8 = 18;   /*! Unsigned 64-bit IFD offset */
527
}  // namespace TagType
528

529
#define LIBERTIFF_CASE_TAGTYPE_STR(x)                                          \
530
    case TagType::x:                                                           \
531
        return #x
532

533
inline const char *tagTypeName(TagTypeType tagType)
534
{
535
    switch (tagType)
536
    {
537
        LIBERTIFF_CASE_TAGTYPE_STR(Byte);
538
        LIBERTIFF_CASE_TAGTYPE_STR(ASCII);
539
        LIBERTIFF_CASE_TAGTYPE_STR(Short);
540
        LIBERTIFF_CASE_TAGTYPE_STR(Long);
541
        LIBERTIFF_CASE_TAGTYPE_STR(Rational);
542
        LIBERTIFF_CASE_TAGTYPE_STR(SByte);
543
        LIBERTIFF_CASE_TAGTYPE_STR(Undefined);
544
        LIBERTIFF_CASE_TAGTYPE_STR(SShort);
545
        LIBERTIFF_CASE_TAGTYPE_STR(SLong);
546
        LIBERTIFF_CASE_TAGTYPE_STR(SRational);
547
        LIBERTIFF_CASE_TAGTYPE_STR(Float);
548
        LIBERTIFF_CASE_TAGTYPE_STR(Double);
549
        LIBERTIFF_CASE_TAGTYPE_STR(Long8);
550
        LIBERTIFF_CASE_TAGTYPE_STR(SLong8);
551
        LIBERTIFF_CASE_TAGTYPE_STR(IFD8);
552
        default:
553
            break;
554
    }
555
    return "(unknown)";
556
}
557

558
#undef LIBERTIFF_CASE_TAGTYPE_STR
559

560
/** Type of a PlanarConfiguration value */
561
typedef uint32_t PlanarConfigurationType;
562

563
/** Values of the PlanarConfiguration tag */
564
namespace PlanarConfiguration
565
{
566
constexpr PlanarConfigurationType Contiguous = 1; /*! Single image plane */
567
constexpr PlanarConfigurationType Separate =
568
    2; /*! Separate planes per sample */
569
}  // namespace PlanarConfiguration
570

571
#define LIBERTIFF_CASE_PLANAR_CONFIG_STR(x)                                    \
572
    case PlanarConfiguration::x:                                               \
573
        return #x
574

575
inline const char *
576
planarConfigurationName(PlanarConfigurationType planarConfiguration)
577
{
578
    switch (planarConfiguration)
579
    {
580
        LIBERTIFF_CASE_PLANAR_CONFIG_STR(Contiguous);
581
        LIBERTIFF_CASE_PLANAR_CONFIG_STR(Separate);
582
        default:
583
            break;
584
    }
585
    return "(unknown)";
586
}
587

588
#undef LIBERTIFF_CASE_PLANAR_CONFIG_STR
589

590
/** Type of a PlanarConfiguration value */
591
typedef uint32_t PhotometricInterpretationType;
592

593
/** Values of the PhotometricInterpretation tag */
594
namespace PhotometricInterpretation
595
{
596
constexpr PhotometricInterpretationType MinIsWhite = 0;
597
constexpr PhotometricInterpretationType MinIsBlack = 1;
598
constexpr PhotometricInterpretationType RGB = 2;
599
constexpr PhotometricInterpretationType Palette = 3;
600
constexpr PhotometricInterpretationType Mask = 4;
601
constexpr PhotometricInterpretationType Separated = 5;
602
constexpr PhotometricInterpretationType YCbCr = 6;
603
constexpr PhotometricInterpretationType CIELab = 8;
604
constexpr PhotometricInterpretationType ICCLab = 9;
605
constexpr PhotometricInterpretationType ITULab = 10;
606
}  // namespace PhotometricInterpretation
607

608
#define LIBERTIFF_CASE_PHOTOMETRIC_STR(x)                                      \
609
    case PhotometricInterpretation::x:                                         \
610
        return #x
611

612
inline const char *photometricInterpretationName(
613
    PhotometricInterpretationType photometricInterpretation)
614
{
615
    switch (photometricInterpretation)
616
    {
617
        LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsWhite);
618
        LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsBlack);
619
        LIBERTIFF_CASE_PHOTOMETRIC_STR(RGB);
620
        LIBERTIFF_CASE_PHOTOMETRIC_STR(Palette);
621
        LIBERTIFF_CASE_PHOTOMETRIC_STR(Mask);
622
        LIBERTIFF_CASE_PHOTOMETRIC_STR(Separated);
623
        LIBERTIFF_CASE_PHOTOMETRIC_STR(YCbCr);
624
        LIBERTIFF_CASE_PHOTOMETRIC_STR(CIELab);
625
        LIBERTIFF_CASE_PHOTOMETRIC_STR(ICCLab);
626
        LIBERTIFF_CASE_PHOTOMETRIC_STR(ITULab);
627
        default:
628
            break;
629
    }
630
    return "(unknown)";
631
}
632

633
#undef LIBERTIFF_CASE_PHOTOMETRIC_STR
634

635
/** Type of a Compression value */
636
typedef uint32_t CompressionType;
637

638
/** Compression methods */
639
namespace Compression
640
{
641
constexpr CompressionType None = 1;
642
constexpr CompressionType CCITT_RLE = 2;
643
constexpr CompressionType CCITT_FAX3 = 3;
644
constexpr CompressionType CCITT_FAX4 = 4;
645
constexpr CompressionType LZW = 5;
646
constexpr CompressionType OldJPEG = 6;
647
constexpr CompressionType JPEG = 7;
648
constexpr CompressionType Deflate =
649
    8; /* Deflate compression, as recognized by Adobe */
650
constexpr CompressionType PackBits = 32773;
651
constexpr CompressionType LegacyDeflate =
652
    32946;                              /* Deflate compression, legacy tag */
653
constexpr CompressionType JBIG = 34661; /* ISO JBIG */
654
constexpr CompressionType LERC =
655
    34887; /* ESRI Lerc codec: https://github.com/Esri/lerc */
656
constexpr CompressionType LZMA = 34925; /* LZMA2 */
657
constexpr CompressionType ZSTD =
658
    50000; /* ZSTD: WARNING not registered in Adobe-maintained registry */
659
constexpr CompressionType WEBP =
660
    50001; /* WEBP: WARNING not registered in Adobe-maintained registry */
661
constexpr CompressionType JXL =
662
    50002; /* JPEGXL: WARNING not registered in Adobe-maintained registry */
663
constexpr CompressionType JXL_DNG_1_7 =
664
    52546; /* JPEGXL from DNG 1.7 specification */
665
}  // namespace Compression
666

667
#define LIBERTIFF_CASE_COMPRESSION_STR(x)                                      \
668
    case Compression::x:                                                       \
669
        return #x
670

671
inline const char *compressionName(CompressionType compression)
18✔
672
{
673
    switch (compression)
18✔
674
    {
675
        LIBERTIFF_CASE_COMPRESSION_STR(None);
×
676
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_RLE);
1✔
677
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX3);
×
678
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX4);
1✔
679
        LIBERTIFF_CASE_COMPRESSION_STR(LZW);
×
680
        LIBERTIFF_CASE_COMPRESSION_STR(OldJPEG);
2✔
681
        LIBERTIFF_CASE_COMPRESSION_STR(JPEG);
×
682
        LIBERTIFF_CASE_COMPRESSION_STR(Deflate);
×
683
        LIBERTIFF_CASE_COMPRESSION_STR(PackBits);
×
684
        LIBERTIFF_CASE_COMPRESSION_STR(LegacyDeflate);
×
685
        LIBERTIFF_CASE_COMPRESSION_STR(JBIG);
×
686
        LIBERTIFF_CASE_COMPRESSION_STR(LERC);
×
687
        LIBERTIFF_CASE_COMPRESSION_STR(LZMA);
×
688
        LIBERTIFF_CASE_COMPRESSION_STR(ZSTD);
×
689
        LIBERTIFF_CASE_COMPRESSION_STR(WEBP);
×
690
        LIBERTIFF_CASE_COMPRESSION_STR(JXL);
×
691
        LIBERTIFF_CASE_COMPRESSION_STR(JXL_DNG_1_7);
×
692
        default:
14✔
693
            break;
14✔
694
    }
695
    return "(unknown)";
14✔
696
}
697

698
#undef LIBERTIFF_CASE_COMPRESSION_STR
699

700
/** Type of a SampleFormat value */
701
typedef uint32_t SampleFormatType;
702

703
/** Sample format */
704
namespace SampleFormat
705
{
706
constexpr SampleFormatType UnsignedInt = 1;
707
constexpr SampleFormatType SignedInt = 2;
708
constexpr SampleFormatType IEEEFP = 3;
709
constexpr SampleFormatType Void = 4;
710
constexpr SampleFormatType ComplexInt = 5;
711
constexpr SampleFormatType ComplexIEEEFP = 6;
712
}  // namespace SampleFormat
713

714
#define LIBERTIFF_CASE_SAMPLE_FORMAT_STR(x)                                    \
715
    case SampleFormat::x:                                                      \
716
        return #x
717

718
inline const char *sampleFormatName(SampleFormatType sampleFormat)
719
{
720
    switch (sampleFormat)
721
    {
722
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(UnsignedInt);
723
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(SignedInt);
724
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(IEEEFP);
725
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(Void);
726
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexInt);
727
        LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexIEEEFP);
728
        default:
729
            break;
730
    }
731
    return "(unknown)";
732
}
733

734
#undef LIBERTIFF_CASE_SAMPLE_FORMAT_STR
735

736
/** Type of a ExtraSamples value */
737
typedef uint32_t ExtraSamplesType;
738

739
/** Values of the ExtraSamples tag */
740
namespace ExtraSamples
741
{
742
constexpr ExtraSamplesType Unspecified = 0;
743
constexpr ExtraSamplesType AssociatedAlpha = 1;   /* premultiplied */
744
constexpr ExtraSamplesType UnAssociatedAlpha = 2; /* unpremultiplied */
745
}  // namespace ExtraSamples
746

747
/** Content of a tag entry in a Image File Directory (IFD) */
748
struct TagEntry
749
{
750
    TagCodeType tag = 0;
751
    TagTypeType type = 0;
752
    uint64_t count = 0;  // number of values in the tag
753

754
    // Inline values. Only valid if value_offset == 0.
755
    // The actual number in the arrays is count
756
    union
757
    {
758
        std::array<char, 8> charValues;
759
        std::array<uint8_t, 8> uint8Values;
760
        std::array<int8_t, 8> int8Values;
761
        std::array<uint16_t, 4> uint16Values;
762
        std::array<int16_t, 4> int16Values;
763
        std::array<uint32_t, 2> uint32Values;
764
        std::array<int32_t, 2> int32Values;
765
        std::array<float, 2> float32Values;
766
        std::array<double, 1>
767
            float64Values;  // Valid for Double, Rational, SRational
768
        std::array<uint64_t, 1> uint64Values = {0};
769
        std::array<int64_t, 1> int64Values;
770
    };
771

772
    uint64_t value_offset = 0;         // 0 for inline values
773
    bool invalid_value_offset = true;  // whether value_offset is invalid
774
};
775

776
// clang-format off
777

778
/** Return the size in bytes of a tag data type, or 0 if unknown */
779
inline uint32_t tagTypeSize(TagTypeType type)
17,328✔
780
{
781
    switch (type)
17,328✔
782
    {
783
        case TagType::Byte:      return 1;
13✔
784
        case TagType::ASCII:     return 1;
496✔
785
        case TagType::Short:     return 2;
12,659✔
786
        case TagType::Long:      return 4;
2,502✔
787
        case TagType::Rational:  return 8;  // 2 Long
84✔
788
        case TagType::SByte:     return 1;
×
789
        case TagType::Undefined: return 1;
38✔
790
        case TagType::SShort:    return 2;
×
791
        case TagType::SLong:     return 4;
×
792
        case TagType::SRational: return 8;  // 2 SLong
×
793
        case TagType::Float:     return 4;
×
794
        case TagType::Double:    return 8;
921✔
795
        case TagType::Long8:     return 8;
41✔
796
        case TagType::SLong8:    return 8;
1✔
797
        case TagType::IFD8:      return 8;
×
798
        default: break;
573✔
799
    }
800
    return 0;
573✔
801
}
802

803
// clang-format on
804

805
namespace detail
806
{
807
template <class T>
808
inline std::vector<T> readTagAsVectorInternal(const ReadContext &rc,
1,083✔
809
                                              const TagEntry &tag,
810
                                              TagTypeType expectedType,
811
                                              const T *inlineValues, bool &ok)
812
{
813
    if (tag.type == expectedType)
1,083✔
814
    {
815
        if (tag.value_offset)
1,083✔
816
        {
817
            if (!tag.invalid_value_offset)
1,063✔
818
            {
819
                if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t))
820
                {
821
                    if (tag.count > std::numeric_limits<size_t>::max())
822
                    {
823
                        ok = false;
824
                        return {};
825
                    }
826
                }
827
                return rc.readArray<T>(tag.value_offset,
1,063✔
828
                                       static_cast<size_t>(tag.count), ok);
1,063✔
829
            }
830
        }
831
        else
832
        {
833
            return std::vector<T>(
834
                inlineValues, inlineValues + static_cast<size_t>(tag.count));
20✔
835
        }
836
    }
837
    ok = false;
×
838
    return {};
×
839
}
840

841
template <class T>
842
inline std::vector<T> readTagAsVector(const ReadContext &rc,
843
                                      const TagEntry &tag, bool &ok);
844

845
template <>
846
inline std::vector<int8_t> readTagAsVector(const ReadContext &rc,
847
                                           const TagEntry &tag, bool &ok)
848
{
849
    return readTagAsVectorInternal(rc, tag, TagType::SByte,
850
                                   tag.int8Values.data(), ok);
851
}
852

853
template <>
854
inline std::vector<uint8_t> readTagAsVector(const ReadContext &rc,
37✔
855
                                            const TagEntry &tag, bool &ok)
856
{
857
    return readTagAsVectorInternal(
858
        rc, tag, tag.type == TagType::Undefined ? tag.type : TagType::Byte,
74✔
859
        tag.uint8Values.data(), ok);
37✔
860
}
861

862
template <>
863
inline std::vector<int16_t> readTagAsVector(const ReadContext &rc,
864
                                            const TagEntry &tag, bool &ok)
865
{
866
    return readTagAsVectorInternal(rc, tag, TagType::SShort,
867
                                   tag.int16Values.data(), ok);
868
}
869

870
template <>
871
inline std::vector<uint16_t> readTagAsVector(const ReadContext &rc,
343✔
872
                                             const TagEntry &tag, bool &ok)
873
{
874
    return readTagAsVectorInternal(rc, tag, TagType::Short,
875
                                   tag.uint16Values.data(), ok);
343✔
876
}
877

878
template <>
879
inline std::vector<int32_t> readTagAsVector(const ReadContext &rc,
880
                                            const TagEntry &tag, bool &ok)
881
{
882
    return readTagAsVectorInternal(rc, tag, TagType::SLong,
883
                                   tag.int32Values.data(), ok);
884
}
885

886
template <>
887
inline std::vector<uint32_t> readTagAsVector(const ReadContext &rc,
73✔
888
                                             const TagEntry &tag, bool &ok)
889
{
890
    return readTagAsVectorInternal(rc, tag, TagType::Long,
891
                                   tag.uint32Values.data(), ok);
73✔
892
}
893

894
template <>
895
inline std::vector<int64_t> readTagAsVector(const ReadContext &rc,
896
                                            const TagEntry &tag, bool &ok)
897
{
898
    return readTagAsVectorInternal(rc, tag, TagType::SLong8,
899
                                   tag.int64Values.data(), ok);
900
}
901

902
template <>
903
inline std::vector<uint64_t> readTagAsVector(const ReadContext &rc,
×
904
                                             const TagEntry &tag, bool &ok)
905
{
906
    return readTagAsVectorInternal(rc, tag, TagType::Long8,
907
                                   tag.uint64Values.data(), ok);
×
908
}
909

910
template <>
911
inline std::vector<float> readTagAsVector(const ReadContext &rc,
912
                                          const TagEntry &tag, bool &ok)
913
{
914
    return readTagAsVectorInternal(rc, tag, TagType::Float,
915
                                   tag.float32Values.data(), ok);
916
}
917

918
template <>
919
inline std::vector<double> readTagAsVector(const ReadContext &rc,
630✔
920
                                           const TagEntry &tag, bool &ok)
921
{
922
    return readTagAsVectorInternal(rc, tag, TagType::Double,
923
                                   tag.float64Values.data(), ok);
630✔
924
}
925

926
}  // namespace detail
927

928
/** Represents a TIFF Image File Directory (IFD). */
929
class Image
930
{
931
  public:
932
    /** Constructor. Should not be called directly. Use the open() method */
933
    Image(const std::shared_ptr<const ReadContext> &rc, bool isBigTIFF)
1,442✔
934
        : m_rc(rc), m_isBigTIFF(isBigTIFF)
1,442✔
935
    {
936
    }
1,442✔
937

938
    /** Return read context */
939
    const std::shared_ptr<const ReadContext> &readContext() const
14,315✔
940
    {
941
        return m_rc;
14,315✔
942
    }
943

944
    /** Return whether the file is BigTIFF (if false, classic TIFF) */
945
    inline bool isBigTIFF() const
946
    {
947
        return m_isBigTIFF;
948
    }
949

950
    /** Return if values of more than 1-byte must be byte swapped.
951
     * To be only taken into account when reading pixels. Tag values are
952
     * automatically byte-swapped */
953
    inline bool mustByteSwap() const
4✔
954
    {
955
        return m_rc->mustByteSwap();
4✔
956
    }
957

958
    /** Return the offset of the this IFD */
959
    inline uint64_t offset() const
3✔
960
    {
961
        return m_offset;
3✔
962
    }
963

964
    /** Return the offset of the next IFD (to pass to Image::open()),
965
         * or 0 if there is no more */
966
    inline uint64_t nextImageOffset() const
111✔
967
    {
968
        return m_nextImageOffset;
111✔
969
    }
970

971
    /** Return value of SubFileType tag */
972
    inline uint32_t subFileType() const
2,439✔
973
    {
974
        return m_subFileType;
2,439✔
975
    }
976

977
    /** Return width of the image in pixels */
978
    inline uint32_t width() const
3,520✔
979
    {
980
        return m_width;
3,520✔
981
    }
982

983
    /** Return height of the image in pixels */
984
    inline uint32_t height() const
3,807✔
985
    {
986
        return m_height;
3,807✔
987
    }
988

989
    /** Return number of bits per sample */
990
    inline uint32_t bitsPerSample() const
155,785✔
991
    {
992
        return m_bitsPerSample;
155,785✔
993
    }
994

995
    /** Return number of samples (a.k.a. channels, bands) per pixel */
996
    inline uint32_t samplesPerPixel() const
5,195✔
997
    {
998
        return m_samplesPerPixel;
5,195✔
999
    }
1000

1001
    /** Return planar configuration */
1002
    inline PlanarConfigurationType planarConfiguration() const
13,507✔
1003
    {
1004
        return m_planarConfiguration;
13,507✔
1005
    }
1006

1007
    /** Return planar configuration */
1008
    inline PhotometricInterpretationType photometricInterpretation() const
137,555✔
1009
    {
1010
        return m_photometricInterpretation;
137,555✔
1011
    }
1012

1013
    /** Return compression method used */
1014
    inline CompressionType compression() const
78,550✔
1015
    {
1016
        return m_compression;
78,550✔
1017
    }
1018

1019
    /** Return predictor value (used for Deflate, LZW, ZStd, etc. compression) */
1020
    inline uint32_t predictor() const
15,128✔
1021
    {
1022
        return m_predictor;
15,128✔
1023
    }
1024

1025
    /** Return sample format */
1026
    inline SampleFormatType sampleFormat() const
1,328✔
1027
    {
1028
        return m_sampleFormat;
1,328✔
1029
    }
1030

1031
    /** Return the number of rows per strip */
1032
    inline uint32_t rowsPerStrip() const
6✔
1033
    {
1034
        return m_rowsPerStrip;
6✔
1035
    }
1036

1037
    /** Return the sanitized number of rows per strip */
1038
    inline uint32_t rowsPerStripSanitized() const
2,260✔
1039
    {
1040
        return std::min(m_rowsPerStrip, m_height);
2,260✔
1041
    }
1042

1043
    /** Return the number of strips/tiles.
1044
     * Return 0 if inconsistent values between ByteCounts and Offsets arrays. */
1045
    inline uint64_t strileCount() const
6✔
1046
    {
1047
        return m_strileCount;
6✔
1048
    }
1049

1050
    /** Return whether image is tiled */
1051
    inline bool isTiled() const
26,194✔
1052
    {
1053
        return m_isTiled;
26,194✔
1054
    }
1055

1056
    /** Return tile width */
1057
    inline uint32_t tileWidth() const
421✔
1058
    {
1059
        return m_tileWidth;
421✔
1060
    }
1061

1062
    /** Return tile width */
1063
    inline uint32_t tileHeight() const
417✔
1064
    {
1065
        return m_tileHeight;
417✔
1066
    }
1067

1068
    /** Return number of tiles per row */
1069
    uint32_t tilesPerRow() const
6,418✔
1070
    {
1071
        if (m_tileWidth > 0)
6,418✔
1072
        {
1073
            return uint32_t((uint64_t(m_width) + m_tileWidth - 1) /
6,419✔
1074
                            m_tileWidth);
6,419✔
1075
        }
UNCOV
1076
        return 0;
×
1077
    }
1078

1079
    /** Return number of tiles per column */
1080
    uint32_t tilesPerCol() const
6,404✔
1081
    {
1082
        if (m_tileHeight > 0)
6,404✔
1083
        {
1084
            return uint32_t((uint64_t(m_height) + m_tileHeight - 1) /
6,418✔
1085
                            m_tileHeight);
6,418✔
1086
        }
UNCOV
1087
        return 0;
×
1088
    }
1089

1090
    /** Convert a tile coordinate (xtile, ytile, bandIdx) to a flat index */
1091
    uint64_t tileCoordinateToIdx(uint32_t xtile, uint32_t ytile,
6,421✔
1092
                                 uint32_t bandIdx, bool &ok) const
1093
    {
1094
        if (m_isTiled && m_tileWidth > 0 && m_tileHeight > 0)
6,421✔
1095
        {
1096
            const uint32_t lTilesPerRow = tilesPerRow();
6,416✔
1097
            const uint32_t lTilesPerCol = tilesPerCol();
6,415✔
1098
            if (xtile >= lTilesPerRow || ytile >= lTilesPerCol)
6,413✔
1099
            {
1100
                ok = false;
×
1101
                return 0;
×
1102
            }
1103
            uint64_t idx = uint64_t(ytile) * lTilesPerRow + xtile;
6,413✔
1104
            if (bandIdx &&
6,413✔
1105
                m_planarConfiguration == PlanarConfiguration::Separate)
3,667✔
1106
            {
1107
                const uint64_t lTotalTiles =
3,667✔
1108
                    uint64_t(lTilesPerCol) * lTilesPerRow;
3,667✔
1109
                if (lTotalTiles >
3,674✔
1110
                    std::numeric_limits<uint64_t>::max() / bandIdx)
3,667✔
1111
                {
1112
                    ok = false;
×
1113
                    return 0;
×
1114
                }
1115
                idx += bandIdx * lTotalTiles;
3,674✔
1116
            }
1117
            return idx;
6,420✔
1118
        }
1119
        ok = false;
5✔
1120
        return 0;
5✔
1121
    }
1122

1123
    /** Return the offset of strip/tile of index idx */
1124
    uint64_t strileOffset(uint64_t idx, bool &ok) const
8,077✔
1125
    {
1126
        return readUIntTag(m_strileOffsetsTag, idx, ok);
8,077✔
1127
    }
1128

1129
    /** Return the offset of a tile from its coordinates */
1130
    uint64_t tileOffset(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
1131
                        bool &ok) const
1132
    {
1133
        const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
1134
        return ok ? strileOffset(idx, ok) : 0;
1135
    }
1136

1137
    /** Return the byte count of strip/tile of index idx */
1138
    uint64_t strileByteCount(uint64_t idx, bool &ok) const
7,960✔
1139
    {
1140
        return readUIntTag(m_strileByteCountsTag, idx, ok);
7,960✔
1141
    }
1142

1143
    /** Return the offset of a tile from its coordinates */
1144
    uint64_t tileByteCount(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
1145
                           bool &ok) const
1146
    {
1147
        const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
1148
        return ok ? strileByteCount(idx, ok) : 0;
1149
    }
1150

1151
    /** Return the list of tags */
1152
    inline const std::vector<TagEntry> &tags() const
1153
    {
1154
        return m_tags;
1155
    }
1156

1157
    /** Return the (first) tag corresponding to a code, or nullptr if not found */
1158
    const TagEntry *tag(TagCodeType tagCode) const
16,533✔
1159
    {
1160
        for (const auto &tag : m_tags)
211,497✔
1161
        {
1162
            if (tag.tag == tagCode)
198,972✔
1163
                return &tag;
4,008✔
1164
        }
1165
        return nullptr;
12,525✔
1166
    }
1167

1168
    /** Read an ASCII tag as a string */
1169
    std::string readTagAsString(const TagEntry &tag, bool &ok) const
116✔
1170
    {
1171
        if (tag.type == TagType::ASCII)
116✔
1172
        {
1173
            if (tag.value_offset)
116✔
1174
            {
1175
                if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t))
1176
                {
1177
                    // "- 1" not strictly necessary, but pleases Coverity Scan
1178
                    if (tag.count > std::numeric_limits<size_t>::max() - 1)
1179
                    {
1180
                        ok = false;
1181
                        return std::string();
1182
                    }
1183
                }
1184
                return readContext()->readString(
94✔
1185
                    tag.value_offset, static_cast<size_t>(tag.count), ok);
94✔
1186
            }
1187
            if (tag.count)
22✔
1188
            {
1189
                std::string res(tag.charValues.data(),
1190
                                static_cast<size_t>(tag.count));
44✔
1191
                if (res.back() == 0)
22✔
1192
                    res.pop_back();
22✔
1193
                return res;
22✔
1194
            }
1195
        }
1196
        ok = false;
×
1197
        return std::string();
×
1198
    }
1199

1200
    /** Read a numeric tag as a vector. You must use a type T which is
1201
     * consistent with the tag.type value. For example, if
1202
     * tag.type == libertiff::TagType::Short, T must be uint16_t.
1203
     * libertiff::TagType::Undefined must be read with T=uint8_t.
1204
     */
1205
    template <class T>
1206
    std::vector<T> readTagAsVector(const TagEntry &tag, bool &ok) const
1,083✔
1207
    {
1208
        return detail::readTagAsVector<T>(*(m_rc.get()), tag, ok);
1,083✔
1209
    }
1210

1211
    /** Returns a new Image instance for the IFD starting at offset imageOffset */
1212
    template <bool isBigTIFF>
1213
    static std::unique_ptr<const Image>
1214
    open(const std::shared_ptr<const ReadContext> &rc,
2,285✔
1215
         const uint64_t imageOffset,
1216
         const std::set<uint64_t> &alreadyVisitedImageOffsets =
1217
             std::set<uint64_t>())
1218
    {
1219
        // To prevent infinite looping on corrupted files
1220
        if (imageOffset == 0 || alreadyVisitedImageOffsets.find(imageOffset) !=
3,727✔
1221
                                    alreadyVisitedImageOffsets.end())
3,727✔
1222
        {
1223
            return nullptr;
843✔
1224
        }
1225

1226
        auto image = LIBERTIFF_NS::make_unique<Image>(rc, isBigTIFF);
2,884✔
1227

1228
        image->m_offset = imageOffset;
1,442✔
1229
        image->m_alreadyVisitedImageOffsets = alreadyVisitedImageOffsets;
1,442✔
1230
        image->m_alreadyVisitedImageOffsets.insert(imageOffset);
1,442✔
1231

1232
        bool ok = true;
1,442✔
1233
        int tagCount = 0;
1,442✔
1234
        uint64_t offset = imageOffset;
1,442✔
1235
        if LIBERTIFF_CONSTEXPR (isBigTIFF)
1236
        {
1237
            // To prevent unsigned integer overflows in later additions. The
1238
            // theoretical max should be much closer to UINT64_MAX, but half of
1239
            // it is already more than needed in practice :-)
1240
            if (offset >= std::numeric_limits<uint64_t>::max() / 2)
29✔
1241
                return nullptr;
×
1242

1243
            const auto tagCount64Bit = rc->read<uint64_t>(offset, ok);
29✔
1244
            // Artificially limit to the same number of entries as ClassicTIFF
1245
            if (tagCount64Bit > std::numeric_limits<uint16_t>::max())
29✔
1246
                return nullptr;
×
1247
            tagCount = static_cast<int>(tagCount64Bit);
29✔
1248
            offset += sizeof(uint64_t);
29✔
1249
        }
1250
        else
1251
        {
1252
            tagCount = rc->read<uint16_t>(offset, ok);
1,413✔
1253
            offset += sizeof(uint16_t);
1,413✔
1254
        }
1255
        if (!ok)
1,442✔
1256
            return nullptr;
78✔
1257
        image->m_tags.reserve(tagCount);
1,364✔
1258
        assert(tagCount <= 65535);
1,364✔
1259
        for (int i = 0; i < tagCount; ++i)
18,736✔
1260
        {
1261
            TagEntry entry;
17,415✔
1262

1263
            // Read tag code
1264
            entry.tag = rc->read<uint16_t>(offset, ok);
17,415✔
1265
            offset += sizeof(uint16_t);
17,415✔
1266

1267
            // Read tag data type
1268
            entry.type = rc->read<uint16_t>(offset, ok);
17,415✔
1269
            offset += sizeof(uint16_t);
17,415✔
1270

1271
            // Read number of values
1272
            if LIBERTIFF_CONSTEXPR (isBigTIFF)
1273
            {
1274
                auto count = rc->read<uint64_t>(offset, ok);
327✔
1275
                entry.count = count;
327✔
1276
                offset += sizeof(count);
327✔
1277
            }
1278
            else
1279
            {
1280
                auto count = rc->read<uint32_t>(offset, ok);
17,088✔
1281
                entry.count = count;
17,088✔
1282
                offset += sizeof(count);
17,088✔
1283
            }
1284

1285
            uint32_t singleValue = 0;
17,415✔
1286
            bool singleValueFitsInUInt32 = false;
17,415✔
1287
            if (entry.count)
17,415✔
1288
            {
1289
                if LIBERTIFF_CONSTEXPR (isBigTIFF)
1290
                {
1291
                    image->ParseTagEntryDataOrOffset<uint64_t>(
327✔
1292
                        entry, offset, singleValueFitsInUInt32, singleValue,
1293
                        ok);
1294
                }
1295
                else
1296
                {
1297
                    image->ParseTagEntryDataOrOffset<uint32_t>(
17,001✔
1298
                        entry, offset, singleValueFitsInUInt32, singleValue,
1299
                        ok);
1300
                }
1301
            }
1302
            if (!ok)
17,415✔
1303
                return nullptr;
43✔
1304

1305
            image->processTag(entry, singleValueFitsInUInt32, singleValue);
17,372✔
1306

1307
            image->m_tags.push_back(entry);
17,372✔
1308
        }
1309

1310
        image->finalTagProcessing();
1,321✔
1311

1312
        if LIBERTIFF_CONSTEXPR (isBigTIFF)
1313
            image->m_nextImageOffset = rc->read<uint64_t>(offset, ok);
29✔
1314
        else
1315
            image->m_nextImageOffset = rc->read<uint32_t>(offset, ok);
1,292✔
1316

1317
        image->m_openFunc = open<isBigTIFF>;
1,321✔
1318

1319
        return std::unique_ptr<const Image>(image.release());
1,321✔
1320
    }
1321

1322
    /** Returns a new Image instance at the next IFD, or nullptr if there is none */
1323
    std::unique_ptr<const Image> next() const
947✔
1324
    {
1325
        return m_openFunc(m_rc, m_nextImageOffset,
947✔
1326
                          m_alreadyVisitedImageOffsets);
947✔
1327
    }
1328

1329
  private:
1330
    const std::shared_ptr<const ReadContext> m_rc;
1331
    std::unique_ptr<const Image> (*m_openFunc)(
1332
        const std::shared_ptr<const ReadContext> &, const uint64_t,
1333
        const std::set<uint64_t> &) = nullptr;
1334

1335
    std::set<uint64_t> m_alreadyVisitedImageOffsets{};
1336
    uint64_t m_offset = 0;
1337
    uint64_t m_nextImageOffset = 0;
1338
    uint32_t m_subFileType = 0;
1339
    uint32_t m_width = 0;
1340
    uint32_t m_height = 0;
1341
    uint32_t m_bitsPerSample = 0;
1342
    uint32_t m_samplesPerPixel = 0;
1343
    uint32_t m_rowsPerStrip = 0;
1344
    CompressionType m_compression = Compression::None;
1345
    SampleFormatType m_sampleFormat = SampleFormat::UnsignedInt;
1346
    PlanarConfigurationType m_planarConfiguration =
1347
        PlanarConfiguration::Contiguous;
1348
    PhotometricInterpretationType m_photometricInterpretation =
1349
        PhotometricInterpretation::MinIsBlack;
1350
    uint32_t m_predictor = 0;
1351

1352
    const bool m_isBigTIFF;
1353
    bool m_isTiled = false;
1354
    uint32_t m_tileWidth = 0;
1355
    uint32_t m_tileHeight = 0;
1356
    uint64_t m_strileCount = 0;
1357

1358
    std::vector<TagEntry> m_tags{};
1359
    const TagEntry *m_strileOffsetsTag = nullptr;
1360
    const TagEntry *m_strileByteCountsTag = nullptr;
1361

1362
    Image(const Image &) = delete;
1363
    Image &operator=(const Image &) = delete;
1364

1365
    /** Process tag */
1366
    void processTag(const TagEntry &entry, bool singleValueFitsInUInt32,
17,372✔
1367
                    uint32_t singleValue)
1368
    {
1369
        if (singleValueFitsInUInt32)
17,372✔
1370
        {
1371
            switch (entry.tag)
13,411✔
1372
            {
1373
                case TagCode::SubFileType:
27✔
1374
                    m_subFileType = singleValue;
27✔
1375
                    break;
27✔
1376

1377
                case TagCode::ImageWidth:
1,319✔
1378
                    m_width = singleValue;
1,319✔
1379
                    break;
1,319✔
1380

1381
                case TagCode::ImageLength:
1,309✔
1382
                    m_height = singleValue;
1,309✔
1383
                    break;
1,309✔
1384

1385
                case TagCode::Compression:
1,296✔
1386
                    m_compression = singleValue;
1,296✔
1387
                    break;
1,296✔
1388

1389
                case TagCode::SamplesPerPixel:
1,275✔
1390
                    m_samplesPerPixel = singleValue;
1,275✔
1391
                    break;
1,275✔
1392

1393
                case TagCode::RowsPerStrip:
1,125✔
1394
                    m_rowsPerStrip = singleValue;
1,125✔
1395
                    break;
1,125✔
1396

1397
                case TagCode::PlanarConfiguration:
1,273✔
1398
                    m_planarConfiguration = singleValue;
1,273✔
1399
                    break;
1,273✔
1400

1401
                case TagCode::PhotometricInterpretation:
1,293✔
1402
                    m_photometricInterpretation = singleValue;
1,293✔
1403
                    break;
1,293✔
1404

1405
                case TagCode::Predictor:
113✔
1406
                    m_predictor = singleValue;
113✔
1407
                    break;
113✔
1408

1409
                case TagCode::TileWidth:
149✔
1410
                    m_tileWidth = singleValue;
149✔
1411
                    break;
149✔
1412

1413
                case TagCode::TileLength:
149✔
1414
                    m_tileHeight = singleValue;
149✔
1415
                    break;
149✔
1416

1417
                default:
4,083✔
1418
                    break;
4,083✔
1419
            }
1420
        }
1421

1422
        if (entry.count &&
17,372✔
1423
            (entry.type == TagType::Byte || entry.type == TagType::Short ||
17,312✔
1424
             entry.type == TagType::Long))
4,655✔
1425
        {
1426
            // Values of those 2 tags are repeated per sample, but should be
1427
            // at the same value.
1428
            if (entry.tag == TagCode::SampleFormat)
15,158✔
1429
            {
1430
                bool localOk = true;
1,187✔
1431
                const auto sampleFormat =
1432
                    static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
1,187✔
1433
                if (localOk)
1,187✔
1434
                {
1435
                    m_sampleFormat = sampleFormat;
1,162✔
1436
                }
1437
            }
1438
            else if (entry.tag == TagCode::BitsPerSample)
13,971✔
1439
            {
1440
                bool localOk = true;
1,307✔
1441
                const auto bitsPerSample =
1442
                    static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
1,307✔
1443
                if (localOk)
1,307✔
1444
                {
1445
                    m_bitsPerSample = bitsPerSample;
1,307✔
1446
                }
1447
            }
1448
        }
1449
    }
17,372✔
1450

1451
    /** Final tag processing */
1452
    void finalTagProcessing()
1,321✔
1453
    {
1454
        m_strileOffsetsTag = tag(TagCode::TileOffsets);
1,321✔
1455
        if (m_strileOffsetsTag)
1,321✔
1456
        {
1457
            m_strileByteCountsTag = tag(TagCode::TileByteCounts);
149✔
1458
            if (m_strileByteCountsTag &&
149✔
1459
                m_strileOffsetsTag->count == m_strileByteCountsTag->count)
149✔
1460
            {
1461
                m_isTiled = true;
149✔
1462
                m_strileCount = m_strileOffsetsTag->count;
149✔
1463
            }
1464
        }
1465
        else
1466
        {
1467
            m_strileOffsetsTag = tag(TagCode::StripOffsets);
1,172✔
1468
            if (m_strileOffsetsTag)
1,172✔
1469
            {
1470
                m_strileByteCountsTag = tag(TagCode::StripByteCounts);
1,149✔
1471
                if (m_strileByteCountsTag &&
1,149✔
1472
                    m_strileOffsetsTag->count == m_strileByteCountsTag->count)
1,126✔
1473
                {
1474
                    m_strileCount = m_strileOffsetsTag->count;
1,091✔
1475
                }
1476
            }
1477
        }
1478
    }
1,321✔
1479

1480
    /** Read a value from a byte/short/long/long8 array tag */
1481
    uint64_t readUIntTag(const TagEntry *tag, uint64_t idx, bool &ok) const
18,528✔
1482
    {
1483
        if (tag && idx < tag->count)
18,528✔
1484
        {
1485
            if (tag->type == TagType::Byte)
18,485✔
1486
            {
1487
                if (tag->count <= (m_isBigTIFF ? 8 : 4))
7✔
1488
                {
1489
                    return tag->uint8Values[size_t(idx)];
7✔
1490
                }
1491
                return m_rc->read<uint8_t>(
×
1492
                    tag->value_offset + sizeof(uint8_t) * idx, ok);
×
1493
            }
1494
            else if (tag->type == TagType::Short)
18,478✔
1495
            {
1496
                if (tag->count <= (m_isBigTIFF ? 4 : 2))
9,068✔
1497
                {
1498
                    return tag->uint16Values[size_t(idx)];
1,947✔
1499
                }
1500
                return m_rc->read<uint16_t>(
7,121✔
1501
                    tag->value_offset + sizeof(uint16_t) * idx, ok);
7,122✔
1502
            }
1503
            else if (tag->type == TagType::Long)
9,410✔
1504
            {
1505
                if (tag->count <= (m_isBigTIFF ? 2 : 1))
9,353✔
1506
                {
1507
                    return tag->uint32Values[size_t(idx)];
1,354✔
1508
                }
1509
                return m_rc->read<uint32_t>(
7,999✔
1510
                    tag->value_offset + sizeof(uint32_t) * idx, ok);
7,990✔
1511
            }
1512
            else if (m_isBigTIFF && tag->type == TagType::Long8)
57✔
1513
            {
1514
                if (tag->count <= 1)
53✔
1515
                {
1516
                    return tag->uint64Values[size_t(idx)];
24✔
1517
                }
1518
                return m_rc->read<uint64_t>(
29✔
1519
                    tag->value_offset + sizeof(uint64_t) * idx, ok);
29✔
1520
            }
1521
        }
1522
        ok = false;
47✔
1523
        return 0;
47✔
1524
    }
1525

1526
    template <class DataOrOffsetType>
1527
    void ParseTagEntryDataOrOffset(TagEntry &entry, uint64_t &offset,
17,328✔
1528
                                   bool &singleValueFitsInUInt32,
1529
                                   uint32_t &singleValue, bool &ok)
1530
    {
1531
        LIBERTIFF_STATIC_ASSERT(
1532
            (std::is_same<DataOrOffsetType, uint32_t>::value ||
1533
             std::is_same<DataOrOffsetType, uint64_t>::value));
1534
        assert(entry.count > 0);
17,328✔
1535

1536
        const uint32_t dataTypeSize = tagTypeSize(entry.type);
17,328✔
1537
        if (dataTypeSize == 0)
17,328✔
1538
        {
1539
            return;
573✔
1540
        }
1541

1542
        // There are 2 cases:
1543
        // - either the number of values for the data type can fit
1544
        //   in the next DataOrOffsetType bytes
1545
        // - or it cannot, and then the next DataOrOffsetType bytes are an offset
1546
        //   to the values
1547
        if (dataTypeSize > sizeof(DataOrOffsetType) / entry.count)
16,755✔
1548
        {
1549
            // Out-of-line values. We read a file offset
1550
            entry.value_offset = m_rc->read<DataOrOffsetType>(offset, ok);
3,219✔
1551
            if (entry.value_offset == 0)
3,219✔
1552
            {
1553
                // value_offset = 0 for a out-of-line tag is obviously
1554
                // wrong and would cause later confusion in readTagAsVector<>,
1555
                // so better reject the file.
1556
                ok = false;
16✔
1557
                return;
16✔
1558
            }
1559
            if (dataTypeSize >
6,406✔
1560
                std::numeric_limits<uint64_t>::max() / entry.count)
3,203✔
1561
            {
1562
                entry.invalid_value_offset = true;
1✔
1563
            }
1564
            else
1565
            {
1566
                const uint64_t byteCount = uint64_t(dataTypeSize) * entry.count;
3,202✔
1567

1568
                // Size of tag data beyond which we check the tag position and size
1569
                // w.r.t the file size.
1570
                constexpr uint32_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1000 * 1000;
3,202✔
1571

1572
                entry.invalid_value_offset =
3,202✔
1573
                    (byteCount > THRESHOLD_CHECK_FILE_SIZE &&
3,279✔
1574
                     (m_rc->size() < byteCount ||
77✔
1575
                      entry.value_offset > m_rc->size() - byteCount));
3✔
1576
            }
1577
        }
1578
        else if (dataTypeSize == sizeof(uint8_t))
13,536✔
1579
        {
1580
            // Read up to 4 (classic) or 8 (BigTIFF) inline bytes
1581
            m_rc->read(offset, size_t(entry.count), &entry.uint8Values[0], ok);
38✔
1582
            if (entry.count == 1 && entry.type == TagType::Byte)
38✔
1583
            {
1584
                singleValueFitsInUInt32 = true;
13✔
1585
                singleValue = entry.uint8Values[0];
13✔
1586
            }
1587
        }
1588
        else if (dataTypeSize == sizeof(uint16_t))
13,498✔
1589
        {
1590
            // Read up to 2 (classic) or 4 (BigTIFF) inline 16-bit values
1591
            assert(entry.count <= 4);
11,391✔
1592
            for (uint32_t idx = 0; idx < entry.count; ++idx)
22,860✔
1593
            {
1594
                entry.uint16Values[idx] =
11,469✔
1595
                    m_rc->read<uint16_t>(offset + idx * sizeof(uint16_t), ok);
11,469✔
1596
            }
1597
            if (entry.count == 1 && entry.type == TagType::Short)
11,391✔
1598
            {
1599
                singleValueFitsInUInt32 = true;
11,321✔
1600
                singleValue = entry.uint16Values[0];
11,321✔
1601
            }
1602
        }
1603
        else if (dataTypeSize == sizeof(uint32_t))
2,107✔
1604
        {
1605
            // Read up to 1 (classic) or 2 (BigTIFF) inline 32-bit values
1606
            entry.uint32Values[0] = m_rc->read<uint32_t>(offset, ok);
2,085✔
1607
            if (entry.count == 1 && entry.type == TagType::Long)
2,085✔
1608
            {
1609
                singleValueFitsInUInt32 = true;
2,077✔
1610
                singleValue = entry.uint32Values[0];
2,077✔
1611
            }
1612
            if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
1613
                                                 uint64_t>::value)
1614
            {
1615
                if (entry.count == 2)
16✔
1616
                {
1617
                    entry.uint32Values[1] =
8✔
1618
                        m_rc->read<uint32_t>(offset + sizeof(uint32_t), ok);
8✔
1619
                }
1620
            }
1621
        }
1622
        else if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
1623
                                                  uint64_t>::value)
1624
        {
1625
            if (dataTypeSize == sizeof(uint64_t))
22✔
1626
            {
1627
                // Read one inline 64-bit value
1628
                if (entry.type == TagType::Rational)
22✔
1629
                    entry.float64Values[0] = m_rc->readRational(offset, ok);
×
1630
                else if (entry.type == TagType::SRational)
22✔
1631
                    entry.float64Values[0] =
×
1632
                        m_rc->readSignedRational(offset, ok);
×
1633
                else
1634
                    entry.uint64Values[0] = m_rc->read<uint64_t>(offset, ok);
22✔
1635
            }
1636
            else
1637
            {
1638
                assert(false);
×
1639
            }
1640
        }
1641
        else
1642
        {
1643
            // fprintf(stderr, "Unexpected case: tag=%u, dataType=%u, count=%u\n", entry.tag, entry.type, entry.count);
1644
            assert(false);
×
1645
        }
1646

1647
        offset += sizeof(DataOrOffsetType);
16,739✔
1648
    }
1649
};
1650

1651
/** Open a TIFF file and return its first Image File Directory
1652
 */
1653
template <bool acceptBigTIFF = true>
1654
std::unique_ptr<const Image> open(const std::shared_ptr<const FileReader> &file)
1,338✔
1655
{
1656
    unsigned char signature[2] = {0, 0};
1,338✔
1657
    (void)file->read(0, 2, signature);
1,338✔
1658
    const bool littleEndian = signature[0] == 'I' && signature[1] == 'I';
1,338✔
1659
    const bool bigEndian = signature[0] == 'M' && signature[1] == 'M';
1,338✔
1660
    if (!littleEndian && !bigEndian)
1,338✔
1661
        return nullptr;
×
1662

1663
    const bool mustByteSwap = littleEndian ^ isHostLittleEndian();
1,338✔
1664

1665
    auto rc = std::make_shared<ReadContext>(file, mustByteSwap);
2,676✔
1666
    bool ok = true;
1,338✔
1667
    const int version = rc->read<uint16_t>(2, ok);
1,338✔
1668
    constexpr int CLASSIC_TIFF_VERSION = 42;
1,338✔
1669
    if (version == CLASSIC_TIFF_VERSION)
1,338✔
1670
    {
1671
        const auto firstImageOffset = rc->read<uint32_t>(4, ok);
1,309✔
1672
        return Image::open<false>(rc, firstImageOffset, {});
1,309✔
1673
    }
1674
    else if LIBERTIFF_CONSTEXPR (acceptBigTIFF)
1675
    {
1676
        constexpr int BIGTIFF_VERSION = 43;
29✔
1677
        if (version == BIGTIFF_VERSION)
29✔
1678
        {
1679
            const auto byteSizeOfOffsets = rc->read<uint16_t>(4, ok);
29✔
1680
            if (byteSizeOfOffsets != 8)
29✔
1681
                return nullptr;
×
1682
            const auto zeroWord = rc->read<uint16_t>(6, ok);
29✔
1683
            if (zeroWord != 0 || !ok)
29✔
1684
                return nullptr;
×
1685
            const auto firstImageOffset = rc->read<uint64_t>(8, ok);
29✔
1686
            return Image::open<true>(rc, firstImageOffset, {});
29✔
1687
        }
1688
    }
1689

1690
    return nullptr;
×
1691
}
1692
}  // namespace LIBERTIFF_NS
1693

1694
#ifdef LIBERTIFF_C_FILE_READER
1695
#include <cstdio>
1696
#include <mutex>
1697

1698
namespace LIBERTIFF_NS
1699
{
1700
/** Interface to read from a FILE* handle */
1701
class CFileReader final : public FileReader
1702
{
1703
  public:
1704
    explicit CFileReader(FILE *f) : m_f(f)
1705
    {
1706
    }
1707

1708
    ~CFileReader() override
1709
    {
1710
        fclose(m_f);
1711
    }
1712

1713
    uint64_t size() const override
1714
    {
1715
        std::lock_guard<std::mutex> oLock(m_oMutex);
1716
        fseek(m_f, 0, SEEK_END);
1717
        return ftell(m_f);
1718
    }
1719

1720
    size_t read(uint64_t offset, size_t count, void *buffer) const override
1721
    {
1722
        std::lock_guard<std::mutex> oLock(m_oMutex);
1723
        if (fseek(m_f, static_cast<long>(offset), SEEK_SET) != 0)
1724
            return 0;
1725
        return fread(buffer, 1, count, m_f);
1726
    }
1727

1728
  private:
1729
    FILE *const m_f;
1730
    mutable std::mutex m_oMutex{};
1731

1732
    CFileReader(const CFileReader &) = delete;
1733
    CFileReader &operator=(const CFileReader &) = delete;
1734
};
1735
}  // namespace LIBERTIFF_NS
1736
#endif
1737

1738
#endif  // LIBERTIFF_HPP_INCLUDED
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc