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 38737 : int CPL_STDCALL GDALChecksumImage(GDALRasterBandH hBand, int nXOff, int nYOff,
49 : int nXSize, int nYSize)
50 :
51 : {
52 38737 : VALIDATE_POINTER1(hBand, "GDALChecksumImage", 0);
53 :
54 38737 : 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 38737 : int nChecksum = 0;
59 38737 : int iPrime = 0;
60 38737 : const GDALDataType eDataType = GDALGetRasterDataType(hBand);
61 38737 : const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eDataType));
62 38737 : const bool bIsFloatingPoint =
63 38732 : (eDataType == GDT_Float16 || eDataType == GDT_Float32 ||
64 37698 : eDataType == GDT_Float64 || eDataType == GDT_CFloat16 ||
65 77469 : eDataType == GDT_CFloat32 || eDataType == GDT_CFloat64);
66 :
67 80279500 : const auto IntFromDouble = [](double dfVal)
68 : {
69 : int nVal;
70 80279500 : 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 80261400 : dfVal += 0.5;
79 :
80 80261400 : if (dfVal < -2147483647.0)
81 1215 : nVal = -2147483647;
82 80260200 : else if (dfVal > 2147483647)
83 2690 : nVal = 2147483647;
84 : else
85 80257500 : nVal = static_cast<GInt32>(floor(dfVal));
86 : }
87 80279500 : return nVal;
88 : };
89 :
90 80279500 : const auto ClampForCoverity = [](int x)
91 : {
92 : #ifdef __COVERITY__
93 : return std::max(0, std::min(x, LARGEST_MODULO - 1));
94 : #else
95 80279500 : return x;
96 : #endif
97 : };
98 :
99 38737 : if (bIsFloatingPoint && nXOff == 0 && nYOff == 0)
100 : {
101 1191 : const GDALDataType eDstDataType = bComplex ? GDT_CFloat64 : GDT_Float64;
102 1191 : int nBlockXSize = 0;
103 1191 : int nBlockYSize = 0;
104 1191 : GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
105 1191 : const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
106 1191 : int nChunkXSize = nBlockXSize;
107 1191 : const int nChunkYSize = nBlockYSize;
108 1191 : 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 1191 : VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
136 1191 : if (padfLineData == nullptr)
137 : {
138 0 : return -1;
139 : }
140 1191 : const int nValsPerIter = bComplex ? 2 : 1;
141 :
142 1191 : const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
143 1191 : const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
144 48795 : for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
145 : {
146 47607 : const int iYStart = iYBlock * nChunkYSize;
147 47607 : const int iYEnd =
148 47607 : iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
149 47607 : const int nChunkActualHeight = iYEnd - iYStart;
150 95211 : for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
151 : {
152 47607 : const int iXStart = iXBlock * nChunkXSize;
153 47607 : const int iXEnd =
154 47607 : iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
155 47607 : const int nChunkActualXSize = iXEnd - iXStart;
156 47607 : if (GDALRasterIO(
157 : hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
158 : nChunkActualHeight, padfLineData, nChunkActualXSize,
159 47607 : nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
160 : {
161 3 : CPLError(CE_Failure, CPLE_FileIO,
162 : "Checksum value could not be computed due to I/O "
163 : "read error.");
164 3 : nChecksum = -1;
165 3 : break;
166 : }
167 47604 : const size_t xIters =
168 47604 : static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
169 122001 : 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 74397 : iPrime = (nValsPerIter *
174 74397 : (static_cast<int64_t>(iY) * nXSize + iXStart)) %
175 : 11;
176 74397 : const size_t nOffset = nValsPerIter *
177 74397 : static_cast<size_t>(iY - iYStart) *
178 74397 : nChunkActualXSize;
179 80353400 : for (size_t i = 0; i < xIters; ++i)
180 : {
181 80279000 : const double dfVal = padfLineData[nOffset + i];
182 80279000 : nChecksum += ClampForCoverity(IntFromDouble(dfVal) %
183 80279000 : anPrimes[iPrime++]);
184 80279000 : if (iPrime > 10)
185 7297580 : iPrime = 0;
186 : }
187 74397 : nChecksum &= 0xffff;
188 : }
189 : }
190 :
191 47607 : if (nChecksum < 0)
192 3 : break;
193 : }
194 :
195 1191 : CPLFree(padfLineData);
196 : }
197 37546 : 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 37543 : else if (nXOff == 0 && nYOff == 0)
238 : {
239 37480 : const GDALDataType eDstDataType = bComplex ? GDT_CInt32 : GDT_Int32;
240 37480 : int nBlockXSize = 0;
241 37480 : int nBlockYSize = 0;
242 37480 : GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
243 37480 : const int nDstDataTypeSize = GDALGetDataTypeSizeBytes(eDstDataType);
244 37480 : int nChunkXSize = nBlockXSize;
245 37480 : const int nChunkYSize = nBlockYSize;
246 37480 : 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 37480 : VSI_MALLOC3_VERBOSE(nChunkXSize, nChunkYSize, nDstDataTypeSize));
274 37480 : if (panChunkData == nullptr)
275 : {
276 0 : return -1;
277 : }
278 37480 : const int nValsPerIter = bComplex ? 2 : 1;
279 :
280 37480 : const int nYBlocks = DIV_ROUND_UP(nYSize, nChunkYSize);
281 37480 : const int nXBlocks = DIV_ROUND_UP(nXSize, nChunkXSize);
282 3133880 : for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
283 : {
284 3106020 : const int iYStart = iYBlock * nChunkYSize;
285 3106020 : const int iYEnd =
286 3106020 : iYBlock == nYBlocks - 1 ? nYSize : iYStart + nChunkYSize;
287 3106020 : const int nChunkActualHeight = iYEnd - iYStart;
288 6183940 : for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
289 : {
290 3087540 : const int iXStart = iXBlock * nChunkXSize;
291 3087540 : const int iXEnd =
292 3087540 : iXBlock == nXBlocks - 1 ? nXSize : iXStart + nChunkXSize;
293 3087540 : const int nChunkActualXSize = iXEnd - iXStart;
294 3087540 : if (GDALRasterIO(
295 : hBand, GF_Read, iXStart, iYStart, nChunkActualXSize,
296 : nChunkActualHeight, panChunkData, nChunkActualXSize,
297 3078010 : nChunkActualHeight, eDstDataType, 0, 0) != CE_None)
298 : {
299 91 : CPLError(CE_Failure, CPLE_FileIO,
300 : "Checksum value could not be computed due to I/O "
301 : "read error.");
302 91 : nChecksum = -1;
303 91 : break;
304 : }
305 3077920 : const size_t xIters =
306 3077920 : static_cast<size_t>(nValsPerIter) * nChunkActualXSize;
307 7973950 : 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 4896040 : iPrime = (nValsPerIter *
312 4896040 : (static_cast<int64_t>(iY) * nXSize + iXStart)) %
313 : 11;
314 4896040 : const size_t nOffset = nValsPerIter *
315 4896040 : static_cast<size_t>(iY - iYStart) *
316 4896040 : nChunkActualXSize;
317 1192360000 : for (size_t i = 0; i < xIters; ++i)
318 : {
319 1187470000 : nChecksum +=
320 1187470000 : panChunkData[nOffset + i] % anPrimes[iPrime++];
321 1187470000 : if (iPrime > 10)
322 115449000 : iPrime = 0;
323 : }
324 4896040 : nChecksum &= 0xffff;
325 : }
326 : }
327 :
328 3096490 : if (nChecksum < 0)
329 91 : break;
330 : }
331 :
332 27947 : 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 38719 : return nChecksum;
375 : }
|