/src/gdal/apps/gdalalg_compare_common.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: Common code between raster compare and mdim compare |
5 | | * Author: Even Rouault <even dot rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "gdalalg_compare_common.h" |
14 | | #include "gdalalgorithm.h" |
15 | | #include "gdal_dataset.h" |
16 | | #include "gdal_driver.h" |
17 | | |
18 | | #include "cpl_vsi_virtual.h" |
19 | | |
20 | | //! @cond Doxygen_Suppress |
21 | | |
22 | 0 | GDALCompareCommon::GDALCompareCommon() = default; |
23 | | |
24 | 0 | GDALCompareCommon::~GDALCompareCommon() = default; |
25 | | |
26 | | /************************************************************************/ |
27 | | /* GDALRasterCompareAlgorithm::BinaryComparison() */ |
28 | | /************************************************************************/ |
29 | | |
30 | | /* static */ |
31 | | bool GDALCompareCommon::BinaryComparison(GDALAlgorithm *alg, |
32 | | std::vector<std::string> &aosReport, |
33 | | GDALDataset *poRefDS, |
34 | | GDALDataset *poInputDS) |
35 | 0 | { |
36 | 0 | if (poRefDS->GetDescription()[0] == 0) |
37 | 0 | { |
38 | 0 | alg->ReportError( |
39 | 0 | CE_Warning, CPLE_AppDefined, |
40 | 0 | "Reference dataset has no name. Skipping binary file comparison"); |
41 | 0 | return false; |
42 | 0 | } |
43 | | |
44 | 0 | auto poRefDrv = poRefDS->GetDriver(); |
45 | 0 | if (poRefDrv && EQUAL(poRefDrv->GetDescription(), "MEM")) |
46 | 0 | { |
47 | 0 | alg->ReportError( |
48 | 0 | CE_Warning, CPLE_AppDefined, |
49 | 0 | "Reference dataset is a in-memory dataset. Skipping binary " |
50 | 0 | "file comparison"); |
51 | 0 | return false; |
52 | 0 | } |
53 | | |
54 | 0 | if (poInputDS->GetDescription()[0] == 0) |
55 | 0 | { |
56 | 0 | alg->ReportError( |
57 | 0 | CE_Warning, CPLE_AppDefined, |
58 | 0 | "Input dataset has no name. Skipping binary file comparison"); |
59 | 0 | return false; |
60 | 0 | } |
61 | | |
62 | 0 | auto poInputDrv = poInputDS->GetDriver(); |
63 | 0 | if (poInputDrv && EQUAL(poInputDrv->GetDescription(), "MEM")) |
64 | 0 | { |
65 | 0 | alg->ReportError( |
66 | 0 | CE_Warning, CPLE_AppDefined, |
67 | 0 | "Input dataset is a in-memory dataset. Skipping binary " |
68 | 0 | "file comparison"); |
69 | 0 | return false; |
70 | 0 | } |
71 | | |
72 | 0 | VSIVirtualHandleUniquePtr fpRef(VSIFOpenL(poRefDS->GetDescription(), "rb")); |
73 | 0 | VSIVirtualHandleUniquePtr fpInput( |
74 | 0 | VSIFOpenL(poInputDS->GetDescription(), "rb")); |
75 | 0 | if (!fpRef) |
76 | 0 | { |
77 | 0 | alg->ReportError( |
78 | 0 | CE_Warning, CPLE_AppDefined, |
79 | 0 | "Reference dataset '%s' is not a file. Skipping binary " |
80 | 0 | "file comparison", |
81 | 0 | poRefDS->GetDescription()); |
82 | 0 | return false; |
83 | 0 | } |
84 | | |
85 | 0 | if (!fpInput) |
86 | 0 | { |
87 | 0 | alg->ReportError( |
88 | 0 | CE_Warning, CPLE_AppDefined, |
89 | 0 | "Input dataset '%s' is not a file. Skipping binary file comparison", |
90 | 0 | poInputDS->GetDescription()); |
91 | 0 | return false; |
92 | 0 | } |
93 | | |
94 | 0 | fpRef->Seek(0, SEEK_END); |
95 | 0 | fpInput->Seek(0, SEEK_END); |
96 | 0 | const auto nRefSize = fpRef->Tell(); |
97 | 0 | const auto nInputSize = fpInput->Tell(); |
98 | 0 | if (nRefSize != nInputSize) |
99 | 0 | { |
100 | 0 | aosReport.push_back("Reference file has size " + |
101 | 0 | std::to_string(nRefSize) + |
102 | 0 | " bytes, whereas input file has size " + |
103 | 0 | std::to_string(nInputSize) + " bytes."); |
104 | |
|
105 | 0 | return false; |
106 | 0 | } |
107 | | |
108 | 0 | constexpr size_t BUF_SIZE = 1024 * 1024; |
109 | 0 | std::vector<GByte> abyRef(BUF_SIZE); |
110 | 0 | std::vector<GByte> abyInput(BUF_SIZE); |
111 | |
|
112 | 0 | fpRef->Seek(0, SEEK_SET); |
113 | 0 | fpInput->Seek(0, SEEK_SET); |
114 | |
|
115 | 0 | do |
116 | 0 | { |
117 | 0 | const size_t nRefRead = fpRef->Read(abyRef.data(), 1, BUF_SIZE); |
118 | 0 | const size_t nInputRead = fpInput->Read(abyInput.data(), 1, BUF_SIZE); |
119 | |
|
120 | 0 | if (nRefRead != BUF_SIZE && fpRef->Tell() != nRefSize) |
121 | 0 | { |
122 | 0 | aosReport.push_back("Failed to fully read reference file"); |
123 | 0 | return false; |
124 | 0 | } |
125 | | |
126 | 0 | if (nInputRead != BUF_SIZE && fpInput->Tell() != nRefSize) |
127 | 0 | { |
128 | 0 | aosReport.push_back("Failed to fully read input file"); |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | 0 | if (abyRef != abyInput) |
133 | 0 | { |
134 | 0 | aosReport.push_back( |
135 | 0 | "Reference file and input file differ at the binary level."); |
136 | 0 | return false; |
137 | 0 | } |
138 | 0 | } while (fpRef->Tell() < nRefSize); |
139 | | |
140 | 0 | return true; |
141 | 0 | } |
142 | | |
143 | | //! @endcond |