LCOV - code coverage report
Current view: top level - alg - gdalchecksum.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 158 184 85.9 %
Date: 2025-06-19 12:30:01 Functions: 3 3 100.0 %

          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             : }

Generated by: LCOV version 1.14