Coverage Report

Created: 2026-02-14 06:52

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
0
{
52
0
    VALIDATE_POINTER1(hBand, "GDALChecksumImage", 0);
53
54
0
    constexpr int LARGEST_MODULO = 43;
55
0
    const static int anPrimes[11] = {
56
0
        7, 11, 13, 17, 19, 23, 29, 31, 37, 41, LARGEST_MODULO};
57
58
0
    int nChecksum = 0;
59
0
    int iPrime = 0;
60
0
    const GDALDataType eDataType = GDALGetRasterDataType(hBand);
61
0
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eDataType));
62
0
    const bool bIsFloatingPoint =
63
0
        (eDataType == GDT_Float16 || eDataType == GDT_Float32 ||
64
0
         eDataType == GDT_Float64 || eDataType == GDT_CFloat16 ||
65
0
         eDataType == GDT_CFloat32 || eDataType == GDT_CFloat64);
66
67
0
    const auto IntFromDouble = [](double dfVal)
68
0
    {
69
0
        int nVal;
70
0
        if (!std::isfinite(dfVal))
71
0
        {
72
0
            nVal = INT_MIN;
73
0
        }
74
0
        else
75
0
        {
76
            // Standard behavior of GDALCopyWords when converting
77
            // from floating point to Int32.
78
0
            dfVal += 0.5;
79
80
0
            if (dfVal < -2147483647.0)
81
0
                nVal = -2147483647;
82
0
            else if (dfVal > 2147483647)
83
0
                nVal = 2147483647;
84
0
            else
85
0
                nVal = static_cast<GInt32>(floor(dfVal));
86
0
        }
87
0
        return nVal;
88
0
    };
89
90
0
    const auto ClampForCoverity = [](int x)
91
0
    {
92
#ifdef __COVERITY__
93
        return std::max(0, std::min(x, LARGEST_MODULO - 1));
94
#else
95
0
        return x;
96
0
#endif
97
0
    };
98
99
0
    if (bIsFloatingPoint && nXOff == 0 && nYOff == 0)
100
0
    {
101
0
        const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
102
0
        int nBlockXSize = 0;
103
0
        int nBlockYSize = 0;
104
0
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
105
0
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
106
0
        int nChunkXSize = nBlockXSize;
107
0
        const int nChunkYSize = nBlockYSize;
108
0
        if (nBlockXSize < nXSize)
109
0
        {
110
0
            const GIntBig nMaxChunkSize =
111
0
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
112
0
                         GDALGetCacheMax64() / 10);
113
0
            if (nDstDataTypeSize > 0 &&
114
0
                static_cast<GIntBig>(nXSize) * nChunkYSize <
115
0
                    nMaxChunkSize / nDstDataTypeSize)
116
0
            {
117
                // A full line of height nChunkYSize can fit in the maximum
118
                // allowed memory
119
0
                nChunkXSize = nXSize;
120
0
            }
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
0
        }
133
134
0
        double *padfLineData = static_cast<double *>(
135
0
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
136
0
        if (padfLineData == nullptr)
137
0
        {
138
0
            return -1;
139
0
        }
140
0
        const int nValsPerIter = bComplex ? 2 : 1;
141
142
0
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
143
0
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
144
0
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
145
0
        {
146
0
            const int iYStart = iYBlock * nChunkYSize;
147
0
            const int iYEnd =
148
0
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
149
0
            const int nChunkActualHeight = iYEnd - iYStart;
150
0
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
151
0
            {
152
0
                const int iXStart = iXBlock * nChunkXSize;
153
0
                const int iXEnd =
154
0
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
155
0
                const int nChunkActualXSize = iXEnd - iXStart;
156
0
                if (GDALRasterIO(
157
0
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
158
0
                        nChunkActualHeight, padfLineData, nChunkActualXSize,
159
0
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
160
0
                {
161
0
                    CPLError(CE_Failure, CPLE_FileIO,
162
0
                             "Checksum value could not be computed due to I/O "
163
0
                             "read error.");
164
0
                    nChecksum = -1;
165
0
                    break;
166
0
                }
167
0
                const size_t xIters =
168
0
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
169
0
                for (int iY = iYStart; iY < iYEnd; ++iY)
170
0
                {
171
                    // Initialize iPrime so that it is consistent with a
172
                    // per full line iteration strategy
173
0
                    iPrime = (nValsPerIter *
174
0
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
175
0
                             11;
176
0
                    const size_t nOffset = nValsPerIter *
177
0
                                           static_cast<size_t>(iY - iYStart) *
178
0
                                           nChunkActualXSize;
179
0
                    for (size_t i = 0; i < xIters; ++i)
180
0
                    {
181
0
                        const double dfVal = padfLineData[nOffset + i];
182
0
                        nChecksum += ClampForCoverity(IntFromDouble(dfVal) %
183
0
                                                      anPrimes[iPrime++]);
184
0
                        if (iPrime > 10)
185
0
                            iPrime = 0;
186
0
                    }
187
0
                    nChecksum &= 0xffff;
188
0
                }
189
0
            }
190
191
0
            if (nChecksum < 0)
192
0
                break;
193
0
        }
194
195
0
        CPLFree(padfLineData);
196
0
    }
197
0
    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
0
    else if (nXOff == 0 && nYOff == 0)
238
0
    {
239
0
        const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
240
0
        int nBlockXSize = 0;
241
0
        int nBlockYSize = 0;
242
0
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
243
0
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
244
0
        int nChunkXSize = nBlockXSize;
245
0
        const int nChunkYSize = nBlockYSize;
246
0
        if (nBlockXSize < nXSize)
247
0
        {
248
0
            const GIntBig nMaxChunkSize =
249
0
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
250
0
                         GDALGetCacheMax64() / 10);
251
0
            if (nDstDataTypeSize > 0 &&
252
0
                static_cast<GIntBig>(nXSize) * nChunkYSize <
253
0
                    nMaxChunkSize / nDstDataTypeSize)
254
0
            {
255
                // A full line of height nChunkYSize can fit in the maximum
256
                // allowed memory
257
0
                nChunkXSize = nXSize;
258
0
            }
259
0
            else if (nDstDataTypeSize > 0)
260
0
            {
261
                // Otherwise compute a size that is a multiple of nBlockXSize
262
0
                nChunkXSize = static_cast<int>(std::min(
263
0
                    static_cast<GIntBig>(nXSize),
264
0
                    nBlockXSize *
265
0
                        std::max(static_cast<GIntBig>(1),
266
0
                                 nMaxChunkSize /
267
0
                                     (static_cast<GIntBig>(nBlockXSize) *
268
0
                                      nChunkYSize * nDstDataTypeSize))));
269
0
            }
270
0
        }
271
272
0
        int *panChunkData = static_cast<GInt32 *>(
273
0
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
274
0
        if (panChunkData == nullptr)
275
0
        {
276
0
            return -1;
277
0
        }
278
0
        const int nValsPerIter = bComplex ? 2 : 1;
279
280
0
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
281
0
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
282
0
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
283
0
        {
284
0
            const int iYStart = iYBlock * nChunkYSize;
285
0
            const int iYEnd =
286
0
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
287
0
            const int nChunkActualHeight = iYEnd - iYStart;
288
0
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
289
0
            {
290
0
                const int iXStart = iXBlock * nChunkXSize;
291
0
                const int iXEnd =
292
0
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
293
0
                const int nChunkActualXSize = iXEnd - iXStart;
294
0
                if (GDALRasterIO(
295
0
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
296
0
                        nChunkActualHeight, panChunkData, nChunkActualXSize,
297
0
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
298
0
                {
299
0
                    CPLError(CE_Failure, CPLE_FileIO,
300
0
                             "Checksum value could not be computed due to I/O "
301
0
                             "read error.");
302
0
                    nChecksum = -1;
303
0
                    break;
304
0
                }
305
0
                const size_t xIters =
306
0
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
307
0
                for (int iY = iYStart; iY < iYEnd; ++iY)
308
0
                {
309
                    // Initialize iPrime so that it is consistent with a
310
                    // per full line iteration strategy
311
0
                    iPrime = (nValsPerIter *
312
0
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
313
0
                             11;
314
0
                    const size_t nOffset = nValsPerIter *
315
0
                                           static_cast<size_t>(iY - iYStart) *
316
0
                                           nChunkActualXSize;
317
0
                    for (size_t i = 0; i < xIters; ++i)
318
0
                    {
319
0
                        nChecksum +=
320
0
                            panChunkData[nOffset + i] % anPrimes[iPrime++];
321
0
                        if (iPrime > 10)
322
0
                            iPrime = 0;
323
0
                    }
324
0
                    nChecksum &= 0xffff;
325
0
                }
326
0
            }
327
328
0
            if (nChecksum < 0)
329
0
                break;
330
0
        }
331
332
0
        CPLFree(panChunkData);
333
0
    }
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
0
    return nChecksum;
375
0
}