Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: gdalmultidim_array_regularly_spaced.cpp
4 : * Project: GDAL Core
5 : * Purpose: GDALMDArray::IsRegularlySpaced() and GDALMDArrayRegularlySpaced implementation
6 : * Author: Even Rouault <even.rouault at spatialys.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gdal_multidim.h"
15 :
16 : #include <algorithm>
17 :
18 : /************************************************************************/
19 : /* IsRegularlySpaced() */
20 : /************************************************************************/
21 :
22 : /** Returns whether an array is a 1D regularly spaced array.
23 : *
24 : * @param[out] dfStart First value in the array
25 : * @param[out] dfIncrement Increment/spacing between consecutive values.
26 : * @return true if the array is regularly spaced.
27 : */
28 359 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
29 : {
30 359 : dfStart = 0;
31 359 : dfIncrement = 0;
32 359 : if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
33 0 : return false;
34 359 : const auto nSize = GetDimensions()[0]->GetSize();
35 359 : if (nSize <= 1 || nSize > 10 * 1000 * 1000)
36 2 : return false;
37 :
38 357 : size_t nCount = static_cast<size_t>(nSize);
39 714 : std::vector<double> adfTmp;
40 : try
41 : {
42 357 : adfTmp.resize(nCount);
43 : }
44 0 : catch (const std::exception &)
45 : {
46 0 : return false;
47 : }
48 :
49 357 : GUInt64 anStart[1] = {0};
50 357 : size_t anCount[1] = {nCount};
51 :
52 : const auto IsRegularlySpacedInternal =
53 45773 : [&dfStart, &dfIncrement, &anCount, &adfTmp]()
54 : {
55 414 : dfStart = adfTmp[0];
56 414 : dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
57 414 : if (dfIncrement == 0)
58 : {
59 3 : return false;
60 : }
61 11327 : for (size_t i = 1; i < anCount[0]; i++)
62 : {
63 10930 : if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
64 10930 : 1e-3 * fabs(dfIncrement))
65 : {
66 14 : return false;
67 : }
68 : }
69 397 : return true;
70 357 : };
71 :
72 : // First try with the first block(s). This can avoid excessive processing
73 : // time, for example with Zarr datasets.
74 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
75 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
76 357 : const auto nBlockSize = GetBlockSize()[0];
77 357 : if (nCount >= 5 && nBlockSize <= nCount / 2)
78 : {
79 : size_t nReducedCount =
80 60 : std::max<size_t>(3, static_cast<size_t>(nBlockSize));
81 188 : while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
82 128 : nReducedCount *= 2;
83 60 : anCount[0] = nReducedCount;
84 60 : if (!Read(anStart, anCount, nullptr, nullptr,
85 120 : GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
86 : {
87 0 : return false;
88 : }
89 60 : if (!IsRegularlySpacedInternal())
90 : {
91 3 : return false;
92 : }
93 :
94 : // Get next values
95 57 : anStart[0] = nReducedCount;
96 57 : anCount[0] = nCount - nReducedCount;
97 : }
98 :
99 354 : if (!Read(anStart, anCount, nullptr, nullptr,
100 708 : GDALExtendedDataType::Create(GDT_Float64),
101 354 : &adfTmp[static_cast<size_t>(anStart[0])]))
102 : {
103 0 : return false;
104 : }
105 :
106 354 : return IsRegularlySpacedInternal();
107 : }
108 :
109 : //! @cond Doxygen_Suppress
110 :
111 388 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
112 : const std::string &osParentName, const std::string &osName,
113 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
114 388 : double dfIncrement, double dfOffsetInIncrement)
115 : : GDALAbstractMDArray(osParentName, osName),
116 : GDALMDArray(osParentName, osName), m_dfStart(dfStart),
117 : m_dfIncrement(dfIncrement), m_dfOffsetInIncrement(dfOffsetInIncrement),
118 776 : m_dims{poDim}
119 : {
120 388 : }
121 :
122 388 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
123 : const std::string &osParentName, const std::string &osName,
124 : const std::shared_ptr<GDALDimension> &poDim, double dfStart,
125 : double dfIncrement, double dfOffsetInIncrement)
126 : {
127 : auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
128 388 : osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
129 388 : poArray->SetSelf(poArray);
130 388 : return poArray;
131 : }
132 :
133 : const std::vector<std::shared_ptr<GDALDimension>> &
134 1892 : GDALMDArrayRegularlySpaced::GetDimensions() const
135 : {
136 1892 : return m_dims;
137 : }
138 :
139 683 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
140 : {
141 683 : return m_dt;
142 : }
143 :
144 : std::vector<std::shared_ptr<GDALAttribute>>
145 8 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
146 : {
147 8 : return m_attributes;
148 : }
149 :
150 0 : void GDALMDArrayRegularlySpaced::AddAttribute(
151 : const std::shared_ptr<GDALAttribute> &poAttr)
152 : {
153 0 : m_attributes.emplace_back(poAttr);
154 0 : }
155 :
156 248 : bool GDALMDArrayRegularlySpaced::IRead(
157 : const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
158 : const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
159 : void *pDstBuffer) const
160 : {
161 248 : GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
162 4690 : for (size_t i = 0; i < count[0]; i++)
163 : {
164 4442 : const double dfVal =
165 4442 : m_dfStart +
166 4442 : (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
167 4442 : m_dfOffsetInIncrement) *
168 4442 : m_dfIncrement;
169 4442 : GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
170 : bufferDataType);
171 4442 : pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
172 : }
173 248 : return true;
174 : }
175 :
176 268 : bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
177 : double &dfIncrement) const
178 : {
179 268 : dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
180 268 : dfIncrement = m_dfIncrement;
181 268 : return true;
182 : }
183 :
184 : //! @endcond
|