Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdalchecksum.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Compute simple checksum for a region of image data.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2003, Frank Warmerdam
9
 * Copyright (c) 2007-2008, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gdal_alg.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <algorithm>
20
21
#include "cpl_conv.h"
22
#include "cpl_error.h"
23
#include "cpl_vsi.h"
24
#include "gdal.h"
25
#include "gdal_priv.h"
26
27
/************************************************************************/
28
/*                         GDALChecksumImage()                          */
29
/************************************************************************/
30
31
/**
32
 * Compute checksum for image region.
33
 *
34
 * Computes a 16bit (0-65535) checksum from a region of raster data on a GDAL
35
 * supported band.   Floating point data is converted to 32bit integer
36
 * so decimal portions of such raster data will not affect the checksum.
37
 * Real and Imaginary components of complex bands influence the result.
38
 *
39
 * @param hBand the raster band to read from.
40
 * @param nXOff pixel offset of window to read.
41
 * @param nYOff line offset of window to read.
42
 * @param nXSize pixel size of window to read.
43
 * @param nYSize line size of window to read.
44
 *
45
 * @return Checksum value, or -1 in case of error (starting with GDAL 3.6)
46
 */
47
48
int CPL_STDCALL GDALChecksumImage(GDALRasterBandH hBand, int nXOff, int nYOff,
49
                                  int nXSize, int nYSize)
50
51
356k
{
52
356k
    VALIDATE_POINTER1(hBand, "GDALChecksumImage", 0);
53
54
356k
    constexpr int LARGEST_MODULO = 43;
55
356k
    const static int anPrimes[11] = {
56
356k
        7, 11, 13, 17, 19, 23, 29, 31, 37, 41, LARGEST_MODULO};
57
58
356k
    int nChecksum = 0;
59
356k
    int iPrime = 0;
60
356k
    const GDALDataType eDataType = GDALGetRasterDataType(hBand);
61
356k
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eDataType));
62
356k
    const bool bIsFloatingPoint =
63
356k
        (eDataType == GDT_Float16 || eDataType == GDT_Float32 ||
64
346k
         eDataType == GDT_Float64 || eDataType == GDT_CFloat16 ||
65
327k
         eDataType == GDT_CFloat32 || eDataType == GDT_CFloat64);
66
67
356k
    const auto IntFromDouble = [](double dfVal)
68
569M
    {
69
569M
        int nVal;
70
569M
        if (!std::isfinite(dfVal))
71
16.7M
        {
72
16.7M
            nVal = INT_MIN;
73
16.7M
        }
74
552M
        else
75
552M
        {
76
            // Standard behavior of GDALCopyWords when converting
77
            // from floating point to Int32.
78
552M
            dfVal += 0.5;
79
80
552M
            if (dfVal < -2147483647.0)
81
187M
                nVal = -2147483647;
82
364M
            else if (dfVal > 2147483647)
83
6.31M
                nVal = 2147483647;
84
358M
            else
85
358M
                nVal = static_cast<GInt32>(floor(dfVal));
86
552M
        }
87
569M
        return nVal;
88
569M
    };
89
90
356k
    const auto ClampForCoverity = [](int x)
91
569M
    {
92
#ifdef __COVERITY__
93
        return std::max(0, std::min(x, LARGEST_MODULO - 1));
94
#else
95
569M
        return x;
96
569M
#endif
97
569M
    };
98
99
356k
    if (bIsFloatingPoint && nXOff == 0 && nYOff == 0)
100
30.8k
    {
101
30.8k
        const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
102
30.8k
        int nBlockXSize = 0;
103
30.8k
        int nBlockYSize = 0;
104
30.8k
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
105
30.8k
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
106
30.8k
        int nChunkXSize = nBlockXSize;
107
30.8k
        const int nChunkYSize = nBlockYSize;
108
30.8k
        if (nBlockXSize < nXSize)
109
2.66k
        {
110
2.66k
            const GIntBig nMaxChunkSize =
111
2.66k
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
112
2.66k
                         GDALGetCacheMax64() / 10);
113
2.66k
            if (nDstDataTypeSize > 0 &&
114
2.66k
                static_cast<GIntBig>(nXSize) * nChunkYSize <
115
2.66k
                    nMaxChunkSize / nDstDataTypeSize)
116
2.66k
            {
117
                // A full line of height nChunkYSize can fit in the maximum
118
                // allowed memory
119
2.66k
                nChunkXSize = nXSize;
120
2.66k
            }
121
0
            else if (nDstDataTypeSize > 0)
122
0
            {
123
                // Otherwise compute a size that is a multiple of nBlockXSize
124
0
                nChunkXSize = static_cast<int>(std::min(
125
0
                    static_cast<GIntBig>(nXSize),
126
0
                    nBlockXSize *
127
0
                        std::max(static_cast<GIntBig>(1),
128
0
                                 nMaxChunkSize /
129
0
                                     (static_cast<GIntBig>(nBlockXSize) *
130
0
                                      nChunkYSize * nDstDataTypeSize))));
131
0
            }
132
2.66k
        }
133
134
30.8k
        double *padfLineData = static_cast<double *>(
135
30.8k
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
136
30.8k
        if (padfLineData == nullptr)
137
0
        {
138
0
            return -1;
139
0
        }
140
30.8k
        const int nValsPerIter = bComplex ? 2 : 1;
141
142
30.8k
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
143
30.8k
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
144
3.67M
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
145
3.65M
        {
146
3.65M
            const int iYStart = iYBlock * nChunkYSize;
147
3.65M
            const int iYEnd =
148
3.65M
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
149
3.65M
            const int nChunkActualHeight = iYEnd - iYStart;
150
7.29M
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
151
3.65M
            {
152
3.65M
                const int iXStart = iXBlock * nChunkXSize;
153
3.65M
                const int iXEnd =
154
3.65M
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
155
3.65M
                const int nChunkActualXSize = iXEnd - iXStart;
156
3.65M
                if (GDALRasterIO(
157
3.65M
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
158
3.65M
                        nChunkActualHeight, padfLineData, nChunkActualXSize,
159
3.65M
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
160
9.14k
                {
161
9.14k
                    CPLError(CE_Failure, CPLE_FileIO,
162
9.14k
                             "Checksum value could not be computed due to I/O "
163
9.14k
                             "read error.");
164
9.14k
                    nChecksum = -1;
165
9.14k
                    break;
166
9.14k
                }
167
3.64M
                const size_t xIters =
168
3.64M
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
169
8.56M
                for (int iY = iYStart; iY < iYEnd; ++iY)
170
4.92M
                {
171
                    // Initialize iPrime so that it is consistent with a
172
                    // per full line iteration strategy
173
4.92M
                    iPrime = (nValsPerIter *
174
4.92M
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
175
4.92M
                             11;
176
4.92M
                    const size_t nOffset = nValsPerIter *
177
4.92M
                                           static_cast<size_t>(iY - iYStart) *
178
4.92M
                                           nChunkActualXSize;
179
574M
                    for (size_t i = 0; i < xIters; ++i)
180
569M
                    {
181
569M
                        const double dfVal = padfLineData[nOffset + i];
182
569M
                        nChecksum += ClampForCoverity(IntFromDouble(dfVal) %
183
569M
                                                      anPrimes[iPrime++]);
184
569M
                        if (iPrime > 10)
185
51.7M
                            iPrime = 0;
186
569M
                    }
187
4.92M
                    nChecksum &= 0xffff;
188
4.92M
                }
189
3.64M
            }
190
191
3.65M
            if (nChecksum < 0)
192
9.14k
                break;
193
3.65M
        }
194
195
30.8k
        CPLFree(padfLineData);
196
30.8k
    }
197
325k
    else if (bIsFloatingPoint)
198
0
    {
199
0
        const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
200
201
0
        double *padfLineData = static_cast<double *>(VSI_MALLOC2_VERBOSE(
202
0
            nXSize, GDALGetDataTypeSizeBytes(eDstDataType)));
203
0
        if (padfLineData == nullptr)
204
0
        {
205
0
            return -1;
206
0
        }
207
208
0
        for (int iLine = nYOff; iLine < nYOff + nYSize; iLine++)
209
0
        {
210
0
            if (GDALRasterIO(hBand, GF_Read, nXOff, iLine, nXSize, 1,
211
0
                             padfLineData, nXSize, 1, eDstDataType, 0,
212
0
                             0) != CE_None)
213
0
            {
214
0
                CPLError(CE_Failure, CPLE_FileIO,
215
0
                         "Checksum value couldn't be computed due to "
216
0
                         "I/O read error.");
217
0
                nChecksum = -1;
218
0
                break;
219
0
            }
220
0
            const size_t nCount = bComplex ? static_cast<size_t>(nXSize) * 2
221
0
                                           : static_cast<size_t>(nXSize);
222
223
0
            for (size_t i = 0; i < nCount; i++)
224
0
            {
225
0
                const double dfVal = padfLineData[i];
226
0
                nChecksum +=
227
0
                    ClampForCoverity(IntFromDouble(dfVal) % anPrimes[iPrime++]);
228
0
                if (iPrime > 10)
229
0
                    iPrime = 0;
230
231
0
                nChecksum &= 0xffff;
232
0
            }
233
0
        }
234
235
0
        CPLFree(padfLineData);
236
0
    }
237
325k
    else if (nXOff == 0 && nYOff == 0)
238
325k
    {
239
325k
        const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
240
325k
        int nBlockXSize = 0;
241
325k
        int nBlockYSize = 0;
242
325k
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
243
325k
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
244
325k
        int nChunkXSize = nBlockXSize;
245
325k
        const int nChunkYSize = nBlockYSize;
246
325k
        if (nBlockXSize < nXSize)
247
19.5k
        {
248
19.5k
            const GIntBig nMaxChunkSize =
249
19.5k
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
250
19.5k
                         GDALGetCacheMax64() / 10);
251
19.5k
            if (nDstDataTypeSize > 0 &&
252
19.5k
                static_cast<GIntBig>(nXSize) * nChunkYSize <
253
19.5k
                    nMaxChunkSize / nDstDataTypeSize)
254
19.5k
            {
255
                // A full line of height nChunkYSize can fit in the maximum
256
                // allowed memory
257
19.5k
                nChunkXSize = nXSize;
258
19.5k
            }
259
1
            else if (nDstDataTypeSize > 0)
260
1
            {
261
                // Otherwise compute a size that is a multiple of nBlockXSize
262
1
                nChunkXSize = static_cast<int>(std::min(
263
1
                    static_cast<GIntBig>(nXSize),
264
1
                    nBlockXSize *
265
1
                        std::max(static_cast<GIntBig>(1),
266
1
                                 nMaxChunkSize /
267
1
                                     (static_cast<GIntBig>(nBlockXSize) *
268
1
                                      nChunkYSize * nDstDataTypeSize))));
269
1
            }
270
19.5k
        }
271
272
325k
        int *panChunkData = static_cast<GInt32 *>(
273
325k
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
274
325k
        if (panChunkData == nullptr)
275
0
        {
276
0
            return -1;
277
0
        }
278
325k
        const int nValsPerIter = bComplex ? 2 : 1;
279
280
325k
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
281
325k
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
282
26.8M
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
283
26.6M
        {
284
26.6M
            const int iYStart = iYBlock * nChunkYSize;
285
26.6M
            const int iYEnd =
286
26.6M
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
287
26.6M
            const int nChunkActualHeight = iYEnd - iYStart;
288
53.2M
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
289
26.6M
            {
290
26.6M
                const int iXStart = iXBlock * nChunkXSize;
291
26.6M
                const int iXEnd =
292
26.6M
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
293
26.6M
                const int nChunkActualXSize = iXEnd - iXStart;
294
26.6M
                if (GDALRasterIO(
295
26.6M
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
296
26.6M
                        nChunkActualHeight, panChunkData, nChunkActualXSize,
297
26.6M
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
298
143k
                {
299
143k
                    CPLError(CE_Failure, CPLE_FileIO,
300
143k
                             "Checksum value could not be computed due to I/O "
301
143k
                             "read error.");
302
143k
                    nChecksum = -1;
303
143k
                    break;
304
143k
                }
305
26.5M
                const size_t xIters =
306
26.5M
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
307
58.2M
                for (int iY = iYStart; iY < iYEnd; ++iY)
308
31.6M
                {
309
                    // Initialize iPrime so that it is consistent with a
310
                    // per full line iteration strategy
311
31.6M
                    iPrime = (nValsPerIter *
312
31.6M
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
313
31.6M
                             11;
314
31.6M
                    const size_t nOffset = nValsPerIter *
315
31.6M
                                           static_cast<size_t>(iY - iYStart) *
316
31.6M
                                           nChunkActualXSize;
317
12.1G
                    for (size_t i = 0; i < xIters; ++i)
318
12.0G
                    {
319
12.0G
                        nChecksum +=
320
12.0G
                            panChunkData[nOffset + i] % anPrimes[iPrime++];
321
12.0G
                        if (iPrime > 10)
322
1.09G
                            iPrime = 0;
323
12.0G
                    }
324
31.6M
                    nChecksum &= 0xffff;
325
31.6M
                }
326
26.5M
            }
327
328
26.6M
            if (nChecksum < 0)
329
143k
                break;
330
26.6M
        }
331
332
325k
        CPLFree(panChunkData);
333
325k
    }
334
0
    else
335
0
    {
336
0
        const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
337
338
0
        int *panLineData = static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(
339
0
            nXSize, GDALGetDataTypeSizeBytes(eDstDataType)));
340
0
        if (panLineData == nullptr)
341
0
        {
342
0
            return -1;
343
0
        }
344
345
0
        for (int iLine = nYOff; iLine < nYOff + nYSize; iLine++)
346
0
        {
347
0
            if (GDALRasterIO(hBand, GF_Read, nXOff, iLine, nXSize, 1,
348
0
                             panLineData, nXSize, 1, eDstDataType, 0,
349
0
                             0) != CE_None)
350
0
            {
351
0
                CPLError(CE_Failure, CPLE_FileIO,
352
0
                         "Checksum value could not be computed due to I/O "
353
0
                         "read error.");
354
0
                nChecksum = -1;
355
0
                break;
356
0
            }
357
0
            const size_t nCount = bComplex ? static_cast<size_t>(nXSize) * 2
358
0
                                           : static_cast<size_t>(nXSize);
359
360
0
            for (size_t i = 0; i < nCount; i++)
361
0
            {
362
0
                nChecksum += panLineData[i] % anPrimes[iPrime++];
363
0
                if (iPrime > 10)
364
0
                    iPrime = 0;
365
366
0
                nChecksum &= 0xffff;
367
0
            }
368
0
        }
369
370
0
        CPLFree(panLineData);
371
0
    }
372
373
    // coverity[return_overflow]
374
356k
    return nChecksum;
375
356k
}