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