Coverage Report

Created: 2025-06-09 08:44

/src/gdal/alg/gdalchecksum.cpp
Line
Count
Source (jump to first uncovered line)
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
    const static int anPrimes[11] = {7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43};
55
56
0
    int nChecksum = 0;
57
0
    int iPrime = 0;
58
0
    const GDALDataType eDataType = GDALGetRasterDataType(hBand);
59
0
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eDataType));
60
0
    const bool bIsFloatingPoint =
61
0
        (eDataType == GDT_Float16 || eDataType == GDT_Float32 ||
62
0
         eDataType == GDT_Float64 || eDataType == GDT_CFloat16 ||
63
0
         eDataType == GDT_CFloat32 || eDataType == GDT_CFloat64);
64
65
0
    const auto IntFromDouble = [](double dfVal)
66
0
    {
67
0
        int nVal;
68
0
        if (!std::isfinite(dfVal))
69
0
        {
70
0
            nVal = INT_MIN;
71
0
        }
72
0
        else
73
0
        {
74
            // Standard behavior of GDALCopyWords when converting
75
            // from floating point to Int32.
76
0
            dfVal += 0.5;
77
78
0
            if (dfVal < -2147483647.0)
79
0
                nVal = -2147483647;
80
0
            else if (dfVal > 2147483647)
81
0
                nVal = 2147483647;
82
0
            else
83
0
                nVal = static_cast<GInt32>(floor(dfVal));
84
0
        }
85
0
        return nVal;
86
0
    };
87
88
0
    if (bIsFloatingPoint && nXOff == 0 && nYOff == 0)
89
0
    {
90
0
        const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
91
0
        int nBlockXSize = 0;
92
0
        int nBlockYSize = 0;
93
0
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
94
0
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
95
0
        int nChunkXSize = nBlockXSize;
96
0
        const int nChunkYSize = nBlockYSize;
97
0
        if (nBlockXSize < nXSize)
98
0
        {
99
0
            const GIntBig nMaxChunkSize =
100
0
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
101
0
                         GDALGetCacheMax64() / 10);
102
0
            if (nDstDataTypeSize > 0 &&
103
0
                static_cast<GIntBig>(nXSize) * nChunkYSize <
104
0
                    nMaxChunkSize / nDstDataTypeSize)
105
0
            {
106
                // A full line of height nChunkYSize can fit in the maximum
107
                // allowed memory
108
0
                nChunkXSize = nXSize;
109
0
            }
110
0
            else if (nDstDataTypeSize > 0)
111
0
            {
112
                // Otherwise compute a size that is a multiple of nBlockXSize
113
0
                nChunkXSize = static_cast<int>(std::min(
114
0
                    static_cast<GIntBig>(nXSize),
115
0
                    nBlockXSize *
116
0
                        std::max(static_cast<GIntBig>(1),
117
0
                                 nMaxChunkSize /
118
0
                                     (static_cast<GIntBig>(nBlockXSize) *
119
0
                                      nChunkYSize * nDstDataTypeSize))));
120
0
            }
121
0
        }
122
123
0
        double *padfLineData = static_cast<double *>(
124
0
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
125
0
        if (padfLineData == nullptr)
126
0
        {
127
0
            return -1;
128
0
        }
129
0
        const int nValsPerIter = bComplex ? 2 : 1;
130
131
0
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
132
0
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
133
0
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
134
0
        {
135
0
            const int iYStart = iYBlock * nChunkYSize;
136
0
            const int iYEnd =
137
0
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
138
0
            const int nChunkActualHeight = iYEnd - iYStart;
139
0
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
140
0
            {
141
0
                const int iXStart = iXBlock * nChunkXSize;
142
0
                const int iXEnd =
143
0
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
144
0
                const int nChunkActualXSize = iXEnd - iXStart;
145
0
                if (GDALRasterIO(
146
0
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
147
0
                        nChunkActualHeight, padfLineData, nChunkActualXSize,
148
0
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
149
0
                {
150
0
                    CPLError(CE_Failure, CPLE_FileIO,
151
0
                             "Checksum value could not be computed due to I/O "
152
0
                             "read error.");
153
0
                    nChecksum = -1;
154
0
                    break;
155
0
                }
156
0
                const size_t xIters =
157
0
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
158
0
                for (int iY = iYStart; iY < iYEnd; ++iY)
159
0
                {
160
                    // Initialize iPrime so that it is consistent with a
161
                    // per full line iteration strategy
162
0
                    iPrime = (nValsPerIter *
163
0
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
164
0
                             11;
165
0
                    const size_t nOffset = nValsPerIter *
166
0
                                           static_cast<size_t>(iY - iYStart) *
167
0
                                           nChunkActualXSize;
168
0
                    for (size_t i = 0; i < xIters; ++i)
169
0
                    {
170
0
                        const double dfVal = padfLineData[nOffset + i];
171
0
                        nChecksum += IntFromDouble(dfVal) % anPrimes[iPrime++];
172
0
                        if (iPrime > 10)
173
0
                            iPrime = 0;
174
0
                    }
175
0
                    nChecksum &= 0xffff;
176
0
                }
177
0
            }
178
179
0
            if (nChecksum < 0)
180
0
                break;
181
0
        }
182
183
0
        CPLFree(padfLineData);
184
0
    }
185
0
    else if (bIsFloatingPoint)
186
0
    {
187
0
        const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
188
189
0
        double *padfLineData = static_cast<double *>(VSI_MALLOC2_VERBOSE(
190
0
            nXSize, GDALGetDataTypeSizeBytes(eDstDataType)));
191
0
        if (padfLineData == nullptr)
192
0
        {
193
0
            return -1;
194
0
        }
195
196
0
        for (int iLine = nYOff; iLine < nYOff + nYSize; iLine++)
197
0
        {
198
0
            if (GDALRasterIO(hBand, GF_Read, nXOff, iLine, nXSize, 1,
199
0
                             padfLineData, nXSize, 1, eDstDataType, 0,
200
0
                             0) != CE_None)
201
0
            {
202
0
                CPLError(CE_Failure, CPLE_FileIO,
203
0
                         "Checksum value couldn't be computed due to "
204
0
                         "I/O read error.");
205
0
                nChecksum = -1;
206
0
                break;
207
0
            }
208
0
            const size_t nCount = bComplex ? static_cast<size_t>(nXSize) * 2
209
0
                                           : static_cast<size_t>(nXSize);
210
211
0
            for (size_t i = 0; i < nCount; i++)
212
0
            {
213
0
                const double dfVal = padfLineData[i];
214
0
                nChecksum += IntFromDouble(dfVal) % anPrimes[iPrime++];
215
0
                if (iPrime > 10)
216
0
                    iPrime = 0;
217
218
0
                nChecksum &= 0xffff;
219
0
            }
220
0
        }
221
222
0
        CPLFree(padfLineData);
223
0
    }
224
0
    else if (nXOff == 0 && nYOff == 0)
225
0
    {
226
0
        const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
227
0
        int nBlockXSize = 0;
228
0
        int nBlockYSize = 0;
229
0
        GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
230
0
        const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
231
0
        int nChunkXSize = nBlockXSize;
232
0
        const int nChunkYSize = nBlockYSize;
233
0
        if (nBlockXSize < nXSize)
234
0
        {
235
0
            const GIntBig nMaxChunkSize =
236
0
                std::max(static_cast<GIntBig>(10 * 1000 * 1000),
237
0
                         GDALGetCacheMax64() / 10);
238
0
            if (nDstDataTypeSize > 0 &&
239
0
                static_cast<GIntBig>(nXSize) * nChunkYSize <
240
0
                    nMaxChunkSize / nDstDataTypeSize)
241
0
            {
242
                // A full line of height nChunkYSize can fit in the maximum
243
                // allowed memory
244
0
                nChunkXSize = nXSize;
245
0
            }
246
0
            else if (nDstDataTypeSize > 0)
247
0
            {
248
                // Otherwise compute a size that is a multiple of nBlockXSize
249
0
                nChunkXSize = static_cast<int>(std::min(
250
0
                    static_cast<GIntBig>(nXSize),
251
0
                    nBlockXSize *
252
0
                        std::max(static_cast<GIntBig>(1),
253
0
                                 nMaxChunkSize /
254
0
                                     (static_cast<GIntBig>(nBlockXSize) *
255
0
                                      nChunkYSize * nDstDataTypeSize))));
256
0
            }
257
0
        }
258
259
0
        int *panChunkData = static_cast<GInt32 *>(
260
0
            VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
261
0
        if (panChunkData == nullptr)
262
0
        {
263
0
            return -1;
264
0
        }
265
0
        const int nValsPerIter = bComplex ? 2 : 1;
266
267
0
        const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
268
0
        const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
269
0
        for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
270
0
        {
271
0
            const int iYStart = iYBlock * nChunkYSize;
272
0
            const int iYEnd =
273
0
                iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
274
0
            const int nChunkActualHeight = iYEnd - iYStart;
275
0
            for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
276
0
            {
277
0
                const int iXStart = iXBlock * nChunkXSize;
278
0
                const int iXEnd =
279
0
                    iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
280
0
                const int nChunkActualXSize = iXEnd - iXStart;
281
0
                if (GDALRasterIO(
282
0
                        hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
283
0
                        nChunkActualHeight, panChunkData, nChunkActualXSize,
284
0
                        nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
285
0
                {
286
0
                    CPLError(CE_Failure, CPLE_FileIO,
287
0
                             "Checksum value could not be computed due to I/O "
288
0
                             "read error.");
289
0
                    nChecksum = -1;
290
0
                    break;
291
0
                }
292
0
                const size_t xIters =
293
0
                    static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
294
0
                for (int iY = iYStart; iY < iYEnd; ++iY)
295
0
                {
296
                    // Initialize iPrime so that it is consistent with a
297
                    // per full line iteration strategy
298
0
                    iPrime = (nValsPerIter *
299
0
                              (static_cast<int64_t>(iY) * nXSize + iXStart)) %
300
0
                             11;
301
0
                    const size_t nOffset = nValsPerIter *
302
0
                                           static_cast<size_t>(iY - iYStart) *
303
0
                                           nChunkActualXSize;
304
0
                    for (size_t i = 0; i < xIters; ++i)
305
0
                    {
306
0
                        nChecksum +=
307
0
                            panChunkData[nOffset + i] % anPrimes[iPrime++];
308
0
                        if (iPrime > 10)
309
0
                            iPrime = 0;
310
0
                    }
311
0
                    nChecksum &= 0xffff;
312
0
                }
313
0
            }
314
315
0
            if (nChecksum < 0)
316
0
                break;
317
0
        }
318
319
0
        CPLFree(panChunkData);
320
0
    }
321
0
    else
322
0
    {
323
0
        const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
324
325
0
        int *panLineData = static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(
326
0
            nXSize, GDALGetDataTypeSizeBytes(eDstDataType)));
327
0
        if (panLineData == nullptr)
328
0
        {
329
0
            return -1;
330
0
        }
331
332
0
        for (int iLine = nYOff; iLine < nYOff + nYSize; iLine++)
333
0
        {
334
0
            if (GDALRasterIO(hBand, GF_Read, nXOff, iLine, nXSize, 1,
335
0
                             panLineData, nXSize, 1, eDstDataType, 0,
336
0
                             0) != CE_None)
337
0
            {
338
0
                CPLError(CE_Failure, CPLE_FileIO,
339
0
                         "Checksum value could not be computed due to I/O "
340
0
                         "read error.");
341
0
                nChecksum = -1;
342
0
                break;
343
0
            }
344
0
            const size_t nCount = bComplex ? static_cast<size_t>(nXSize) * 2
345
0
                                           : static_cast<size_t>(nXSize);
346
347
0
            for (size_t i = 0; i < nCount; i++)
348
0
            {
349
0
                nChecksum += panLineData[i] % anPrimes[iPrime++];
350
0
                if (iPrime > 10)
351
0
                    iPrime = 0;
352
353
0
                nChecksum &= 0xffff;
354
0
            }
355
0
        }
356
357
0
        CPLFree(panLineData);
358
0
    }
359
360
    // coverity[return_overflow]
361
0
    return nChecksum;
362
0
}