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