Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OGR
4 : * Purpose: WKB geometry related methods
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_error.h"
30 : #include "ogr_wkb.h"
31 : #include "ogr_core.h"
32 : #include "ogr_geometry.h"
33 : #include "ogr_p.h"
34 :
35 : #include <algorithm>
36 : #include <cmath>
37 : #include <climits>
38 : #include <limits>
39 :
40 : #include <algorithm>
41 : #include <limits>
42 :
43 : #define USE_FAST_FLOAT
44 : #ifdef USE_FAST_FLOAT
45 : #include "include_fast_float.h"
46 : #endif
47 :
48 : /************************************************************************/
49 : /* OGRWKBNeedSwap() */
50 : /************************************************************************/
51 :
52 192 : static inline bool OGRWKBNeedSwap(GByte b)
53 : {
54 : #if CPL_IS_LSB
55 192 : const bool bNeedSwap = b == 0;
56 : #else
57 : const bool bNeedSwap = b == 1;
58 : #endif
59 192 : return bNeedSwap;
60 : }
61 :
62 : /************************************************************************/
63 : /* OGRWKBReadUInt32() */
64 : /************************************************************************/
65 :
66 215 : static inline uint32_t OGRWKBReadUInt32(const GByte *pabyWkb, bool bNeedSwap)
67 : {
68 : uint32_t nVal;
69 215 : memcpy(&nVal, pabyWkb, sizeof(nVal));
70 215 : if (bNeedSwap)
71 0 : CPL_SWAP32PTR(&nVal);
72 215 : return nVal;
73 : }
74 :
75 : /************************************************************************/
76 : /* OGRWKBReadFloat64() */
77 : /************************************************************************/
78 :
79 100 : static inline double OGRWKBReadFloat64(const GByte *pabyWkb, bool bNeedSwap)
80 : {
81 : double dfVal;
82 100 : memcpy(&dfVal, pabyWkb, sizeof(dfVal));
83 100 : if (bNeedSwap)
84 0 : CPL_SWAP64PTR(&dfVal);
85 100 : return dfVal;
86 : }
87 :
88 : /************************************************************************/
89 : /* OGRWKBRingGetArea() */
90 : /************************************************************************/
91 :
92 12 : static bool OGRWKBRingGetArea(const GByte *&pabyWkb, size_t &nWKBSize, int nDim,
93 : bool bNeedSwap, double &dfArea)
94 : {
95 12 : const uint32_t nPoints = OGRWKBReadUInt32(pabyWkb, bNeedSwap);
96 12 : if (nPoints >= 4 &&
97 12 : (nWKBSize - sizeof(uint32_t)) / (nDim * sizeof(double)) >= nPoints)
98 : {
99 12 : nWKBSize -= sizeof(uint32_t) + nDim * sizeof(double);
100 12 : pabyWkb += sizeof(uint32_t);
101 : // Computation according to Green's Theorem
102 : // Cf OGRSimpleCurve::get_LinearArea()
103 12 : double x_m1 = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
104 12 : double y_m1 = OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
105 12 : double y_m2 = y_m1;
106 12 : dfArea = 0;
107 12 : pabyWkb += nDim * sizeof(double);
108 50 : for (uint32_t i = 1; i < nPoints; ++i)
109 : {
110 38 : const double x = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
111 : const double y =
112 38 : OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
113 38 : pabyWkb += nDim * sizeof(double);
114 38 : dfArea += x_m1 * (y - y_m2);
115 38 : y_m2 = y_m1;
116 38 : x_m1 = x;
117 38 : y_m1 = y;
118 : }
119 12 : dfArea += x_m1 * (y_m1 - y_m2);
120 12 : dfArea = 0.5 * std::fabs(dfArea);
121 12 : return true;
122 : }
123 0 : return false;
124 : }
125 :
126 : /************************************************************************/
127 : /* OGRWKBGetGeomType() */
128 : /************************************************************************/
129 :
130 187 : bool OGRWKBGetGeomType(const GByte *pabyWkb, size_t nWKBSize, bool &bNeedSwap,
131 : uint32_t &nType)
132 : {
133 187 : if (nWKBSize >= 5)
134 : {
135 187 : bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
136 187 : nType = OGRWKBReadUInt32(pabyWkb + 1, bNeedSwap);
137 187 : return true;
138 : }
139 0 : return false;
140 : }
141 :
142 : /************************************************************************/
143 : /* OGRWKBPolygonGetArea() */
144 : /************************************************************************/
145 :
146 11 : bool OGRWKBPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
147 : double &dfArea)
148 : {
149 : bool bNeedSwap;
150 : uint32_t nType;
151 11 : if (nWKBSize < 9 || !OGRWKBGetGeomType(pabyWkb, nWKBSize, bNeedSwap, nType))
152 0 : return false;
153 :
154 11 : int nDims = 2;
155 11 : if (nType == wkbPolygon)
156 : {
157 : // do nothing
158 : }
159 6 : else if (nType == wkbPolygon + 1000 || // wkbPolygonZ
160 4 : nType == wkbPolygon25D || nType == wkbPolygonM)
161 : {
162 4 : nDims = 3;
163 : }
164 2 : else if (nType == wkbPolygonZM)
165 : {
166 2 : nDims = 4;
167 : }
168 : else
169 : {
170 0 : return false;
171 : }
172 :
173 11 : const uint32_t nRings = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
174 11 : if ((nWKBSize - 9) / sizeof(uint32_t) >= nRings)
175 : {
176 11 : pabyWkb += 9;
177 11 : nWKBSize -= 9;
178 11 : dfArea = 0;
179 11 : if (nRings > 0)
180 : {
181 11 : if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap, dfArea))
182 0 : return false;
183 12 : for (uint32_t i = 1; i < nRings; ++i)
184 : {
185 : double dfRingArea;
186 1 : if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap,
187 : dfRingArea))
188 0 : return false;
189 1 : dfArea -= dfRingArea;
190 : }
191 : }
192 11 : return true;
193 : }
194 0 : return false;
195 : }
196 :
197 : /************************************************************************/
198 : /* OGRWKBMultiPolygonGetArea() */
199 : /************************************************************************/
200 :
201 5 : bool OGRWKBMultiPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
202 : double &dfArea)
203 : {
204 5 : if (nWKBSize < 9)
205 0 : return false;
206 :
207 5 : const bool bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
208 5 : const uint32_t nPolys = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
209 5 : if ((nWKBSize - 9) / 9 >= nPolys)
210 : {
211 5 : pabyWkb += 9;
212 5 : nWKBSize -= 9;
213 5 : dfArea = 0;
214 11 : for (uint32_t i = 0; i < nPolys; ++i)
215 : {
216 : double dfPolyArea;
217 6 : if (!OGRWKBPolygonGetArea(pabyWkb, nWKBSize, dfPolyArea))
218 0 : return false;
219 6 : dfArea += dfPolyArea;
220 : }
221 5 : return true;
222 : }
223 0 : return false;
224 : }
225 :
226 : /************************************************************************/
227 : /* WKBFromEWKB() */
228 : /************************************************************************/
229 :
230 1412 : const GByte *WKBFromEWKB(GByte *pabyEWKB, size_t nEWKBSize, size_t &nWKBSizeOut,
231 : int *pnSRIDOut)
232 : {
233 1412 : if (nEWKBSize < 5U)
234 : {
235 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid EWKB content : %u bytes",
236 : static_cast<unsigned>(nEWKBSize));
237 0 : return nullptr;
238 : }
239 :
240 1412 : const GByte *pabyWKB = pabyEWKB;
241 :
242 : /* -------------------------------------------------------------------- */
243 : /* PostGIS EWKB format includes an SRID, but this won't be */
244 : /* understood by OGR, so if the SRID flag is set, we remove the */
245 : /* SRID (bytes at offset 5 to 8). */
246 : /* -------------------------------------------------------------------- */
247 1412 : if (nEWKBSize > 9 &&
248 1405 : ((pabyEWKB[0] == 0 /* big endian */ && (pabyEWKB[1] & 0x20)) ||
249 1405 : (pabyEWKB[0] != 0 /* little endian */ && (pabyEWKB[4] & 0x20))))
250 : {
251 514 : if (pnSRIDOut)
252 : {
253 0 : memcpy(pnSRIDOut, pabyEWKB + 5, 4);
254 0 : const OGRwkbByteOrder eByteOrder =
255 0 : (pabyEWKB[0] == 0 ? wkbXDR : wkbNDR);
256 0 : if (OGR_SWAP(eByteOrder))
257 0 : *pnSRIDOut = CPL_SWAP32(*pnSRIDOut);
258 : }
259 :
260 : // Drop the SRID flag
261 514 : if (pabyEWKB[0] == 0)
262 0 : pabyEWKB[1] &= (~0x20);
263 : else
264 514 : pabyEWKB[4] &= (~0x20);
265 :
266 : // Move 5 first bytes of EWKB 4 bytes later to create regular WKB
267 514 : memmove(pabyEWKB + 4, pabyEWKB, 5);
268 514 : memset(pabyEWKB, 0, 4);
269 : // and make pabyWKB point to that
270 514 : pabyWKB += 4;
271 514 : nWKBSizeOut = nEWKBSize - 4;
272 : }
273 : else
274 : {
275 898 : if (pnSRIDOut)
276 : {
277 0 : *pnSRIDOut = INT_MIN;
278 : }
279 898 : nWKBSizeOut = nEWKBSize;
280 : }
281 :
282 1412 : return pabyWKB;
283 : }
284 :
285 : /************************************************************************/
286 : /* OGRWKBReadUInt32AtOffset() */
287 : /************************************************************************/
288 :
289 2001 : static uint32_t OGRWKBReadUInt32AtOffset(const uint8_t *data,
290 : OGRwkbByteOrder eByteOrder,
291 : size_t &iOffset)
292 : {
293 : uint32_t v;
294 2001 : memcpy(&v, data + iOffset, sizeof(v));
295 2001 : iOffset += sizeof(v);
296 2001 : if (OGR_SWAP(eByteOrder))
297 : {
298 154 : CPL_SWAP32PTR(&v);
299 : }
300 2001 : return v;
301 : }
302 :
303 : /************************************************************************/
304 : /* ReadWKBPointSequence() */
305 : /************************************************************************/
306 :
307 : template <bool INCLUDE_Z, typename EnvelopeType>
308 772 : static bool ReadWKBPointSequence(const uint8_t *data, size_t size,
309 : OGRwkbByteOrder eByteOrder, int nDim,
310 : bool bHasZ, size_t &iOffset,
311 : EnvelopeType &sEnvelope)
312 : {
313 : const uint32_t nPoints =
314 772 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
315 772 : if (nPoints > (size - iOffset) / (nDim * sizeof(double)))
316 0 : return false;
317 772 : double dfX = 0;
318 772 : double dfY = 0;
319 772 : [[maybe_unused]] double dfZ = 0;
320 14168 : for (uint32_t j = 0; j < nPoints; j++)
321 : {
322 13396 : memcpy(&dfX, data + iOffset, sizeof(double));
323 13396 : memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
324 : if constexpr (INCLUDE_Z)
325 : {
326 52 : if (bHasZ)
327 34 : memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
328 : sizeof(double));
329 : }
330 13396 : iOffset += nDim * sizeof(double);
331 13396 : if (OGR_SWAP(eByteOrder))
332 : {
333 308 : CPL_SWAP64PTR(&dfX);
334 308 : CPL_SWAP64PTR(&dfY);
335 : if constexpr (INCLUDE_Z)
336 : {
337 0 : CPL_SWAP64PTR(&dfZ);
338 : }
339 : }
340 13396 : sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
341 13396 : sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
342 13396 : sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
343 13396 : sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
344 : if constexpr (INCLUDE_Z)
345 : {
346 52 : if (bHasZ)
347 : {
348 34 : sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
349 34 : sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
350 : }
351 : }
352 : }
353 772 : return true;
354 : }
355 :
356 : /************************************************************************/
357 : /* ReadWKBRingSequence() */
358 : /************************************************************************/
359 :
360 : template <bool INCLUDE_Z, typename EnvelopeType>
361 574 : static bool ReadWKBRingSequence(const uint8_t *data, size_t size,
362 : OGRwkbByteOrder eByteOrder, int nDim,
363 : bool bHasZ, size_t &iOffset,
364 : EnvelopeType &sEnvelope)
365 : {
366 574 : const uint32_t nRings = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
367 574 : if (nRings > (size - iOffset) / sizeof(uint32_t))
368 0 : return false;
369 1188 : for (uint32_t i = 0; i < nRings; i++)
370 : {
371 614 : if (iOffset + sizeof(uint32_t) > size)
372 0 : return false;
373 614 : if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
374 : bHasZ, iOffset, sEnvelope))
375 0 : return false;
376 : }
377 574 : return true;
378 : }
379 :
380 : /************************************************************************/
381 : /* OGRWKBGetBoundingBox() */
382 : /************************************************************************/
383 :
384 : constexpr uint32_t WKB_PREFIX_SIZE = 1 + sizeof(uint32_t);
385 : constexpr uint32_t MIN_WKB_SIZE = WKB_PREFIX_SIZE + sizeof(uint32_t);
386 :
387 : template <bool INCLUDE_Z, typename EnvelopeType>
388 958427 : static bool OGRWKBGetBoundingBox(const uint8_t *data, size_t size,
389 : size_t &iOffset, EnvelopeType &sEnvelope,
390 : int nRec)
391 : {
392 958427 : if (size - iOffset < MIN_WKB_SIZE)
393 0 : return false;
394 958427 : const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffset]);
395 958427 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
396 0 : return false;
397 958427 : const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
398 :
399 958427 : OGRwkbGeometryType eGeometryType = wkbUnknown;
400 958427 : OGRReadWKBGeometryType(data + iOffset, wkbVariantIso, &eGeometryType);
401 958427 : iOffset += 5;
402 958427 : const auto eFlatType = wkbFlatten(eGeometryType);
403 958427 : const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeometryType));
404 958427 : const int nDim = 2 + (bHasZ ? 1 : 0) + (OGR_GT_HasM(eGeometryType) ? 1 : 0);
405 :
406 958427 : if (eFlatType == wkbPoint)
407 : {
408 957926 : if (size - iOffset < nDim * sizeof(double))
409 0 : return false;
410 957926 : double dfX = 0;
411 957926 : double dfY = 0;
412 957926 : [[maybe_unused]] double dfZ = 0;
413 957926 : memcpy(&dfX, data + iOffset, sizeof(double));
414 957926 : memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
415 : if constexpr (INCLUDE_Z)
416 : {
417 11 : if (bHasZ)
418 4 : memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
419 : sizeof(double));
420 : }
421 957926 : iOffset += nDim * sizeof(double);
422 957926 : if (OGR_SWAP(eByteOrder))
423 : {
424 22 : CPL_SWAP64PTR(&dfX);
425 22 : CPL_SWAP64PTR(&dfY);
426 : if constexpr (INCLUDE_Z)
427 : {
428 0 : CPL_SWAP64PTR(&dfZ);
429 : }
430 : }
431 957926 : if (std::isnan(dfX))
432 : {
433 : // Point empty
434 6 : sEnvelope = EnvelopeType();
435 : }
436 : else
437 : {
438 957920 : sEnvelope.MinX = dfX;
439 957920 : sEnvelope.MinY = dfY;
440 957920 : sEnvelope.MaxX = dfX;
441 957920 : sEnvelope.MaxY = dfY;
442 : if constexpr (INCLUDE_Z)
443 : {
444 10 : if (bHasZ)
445 : {
446 4 : sEnvelope.MinZ = dfZ;
447 4 : sEnvelope.MaxZ = dfZ;
448 : }
449 : }
450 : }
451 957926 : return true;
452 : }
453 :
454 501 : if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
455 : {
456 88 : sEnvelope = EnvelopeType();
457 :
458 88 : return ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
459 88 : bHasZ, iOffset, sEnvelope);
460 : }
461 :
462 413 : if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
463 : {
464 173 : sEnvelope = EnvelopeType();
465 :
466 173 : return ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
467 173 : bHasZ, iOffset, sEnvelope);
468 : }
469 :
470 240 : if (eFlatType == wkbMultiPoint)
471 : {
472 48 : sEnvelope = EnvelopeType();
473 :
474 48 : uint32_t nParts = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
475 48 : if (nParts >
476 48 : (size - iOffset) / (WKB_PREFIX_SIZE + nDim * sizeof(double)))
477 0 : return false;
478 48 : double dfX = 0;
479 48 : double dfY = 0;
480 48 : [[maybe_unused]] double dfZ = 0;
481 143 : for (uint32_t k = 0; k < nParts; k++)
482 : {
483 95 : iOffset += WKB_PREFIX_SIZE;
484 95 : memcpy(&dfX, data + iOffset, sizeof(double));
485 95 : memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
486 : if constexpr (INCLUDE_Z)
487 : {
488 5 : if (bHasZ)
489 3 : memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
490 : sizeof(double));
491 : }
492 95 : iOffset += nDim * sizeof(double);
493 95 : if (OGR_SWAP(eByteOrder))
494 : {
495 22 : CPL_SWAP64PTR(&dfX);
496 22 : CPL_SWAP64PTR(&dfY);
497 : if constexpr (INCLUDE_Z)
498 : {
499 0 : CPL_SWAP64PTR(&dfZ);
500 : }
501 : }
502 95 : sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
503 95 : sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
504 95 : sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
505 95 : sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
506 : if constexpr (INCLUDE_Z)
507 : {
508 5 : if (bHasZ)
509 : {
510 3 : sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
511 3 : sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
512 : }
513 : }
514 : }
515 48 : return true;
516 : }
517 :
518 192 : if (eFlatType == wkbMultiLineString)
519 : {
520 50 : sEnvelope = EnvelopeType();
521 :
522 : const uint32_t nParts =
523 50 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
524 50 : if (nParts > (size - iOffset) / MIN_WKB_SIZE)
525 0 : return false;
526 120 : for (uint32_t k = 0; k < nParts; k++)
527 : {
528 70 : if (iOffset + MIN_WKB_SIZE > size)
529 0 : return false;
530 70 : iOffset += WKB_PREFIX_SIZE;
531 70 : if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
532 : bHasZ, iOffset, sEnvelope))
533 0 : return false;
534 : }
535 50 : return true;
536 : }
537 :
538 142 : if (eFlatType == wkbMultiPolygon)
539 : {
540 73 : sEnvelope = EnvelopeType();
541 :
542 : const uint32_t nParts =
543 73 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
544 73 : if (nParts > (size - iOffset) / MIN_WKB_SIZE)
545 0 : return false;
546 474 : for (uint32_t k = 0; k < nParts; k++)
547 : {
548 401 : if (iOffset + MIN_WKB_SIZE > size)
549 0 : return false;
550 401 : CPLAssert(data[iOffset] == eByteOrder);
551 401 : iOffset += WKB_PREFIX_SIZE;
552 401 : if (!ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
553 : bHasZ, iOffset, sEnvelope))
554 0 : return false;
555 : }
556 73 : return true;
557 : }
558 :
559 69 : if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
560 4 : eFlatType == wkbCurvePolygon || eFlatType == wkbMultiCurve ||
561 2 : eFlatType == wkbMultiSurface || eFlatType == wkbPolyhedralSurface ||
562 : eFlatType == wkbTIN)
563 : {
564 69 : if (nRec == 128)
565 0 : return false;
566 69 : sEnvelope = EnvelopeType();
567 :
568 : const uint32_t nParts =
569 69 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
570 69 : if (nParts > (size - iOffset) / MIN_WKB_SIZE)
571 0 : return false;
572 69 : EnvelopeType sEnvelopeSubGeom;
573 149 : for (uint32_t k = 0; k < nParts; k++)
574 : {
575 80 : if (!OGRWKBGetBoundingBox<INCLUDE_Z>(data, size, iOffset,
576 : sEnvelopeSubGeom, nRec + 1))
577 0 : return false;
578 80 : sEnvelope.Merge(sEnvelopeSubGeom);
579 : }
580 69 : return true;
581 : }
582 :
583 0 : return false;
584 : }
585 :
586 : /************************************************************************/
587 : /* OGRWKBGetBoundingBox() */
588 : /************************************************************************/
589 :
590 958312 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
591 : OGREnvelope &sEnvelope)
592 : {
593 958312 : size_t iOffset = 0;
594 958312 : return OGRWKBGetBoundingBox<false>(pabyWkb, nWKBSize, iOffset, sEnvelope,
595 1916620 : 0);
596 : }
597 :
598 : /************************************************************************/
599 : /* OGRWKBGetBoundingBox() */
600 : /************************************************************************/
601 :
602 35 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
603 : OGREnvelope3D &sEnvelope)
604 : {
605 35 : size_t iOffset = 0;
606 70 : return OGRWKBGetBoundingBox<true>(pabyWkb, nWKBSize, iOffset, sEnvelope, 0);
607 : }
608 :
609 : /************************************************************************/
610 : /* OGRWKBIntersectsPointSequencePessimistic() */
611 : /************************************************************************/
612 :
613 134 : static bool OGRWKBIntersectsPointSequencePessimistic(
614 : const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
615 : const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
616 : bool &bErrorOut)
617 : {
618 : const uint32_t nPoints =
619 134 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
620 134 : if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
621 : {
622 12 : bErrorOut = true;
623 12 : return false;
624 : }
625 :
626 122 : double dfX = 0;
627 122 : double dfY = 0;
628 434 : for (uint32_t j = 0; j < nPoints; j++)
629 : {
630 381 : memcpy(&dfX, data + iOffsetInOut, sizeof(double));
631 381 : memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
632 381 : iOffsetInOut += nDim * sizeof(double);
633 381 : if (OGR_SWAP(eByteOrder))
634 : {
635 0 : CPL_SWAP64PTR(&dfX);
636 0 : CPL_SWAP64PTR(&dfY);
637 : }
638 381 : if (dfX >= sEnvelope.MinX && dfY >= sEnvelope.MinY &&
639 307 : dfX <= sEnvelope.MaxX && dfY <= sEnvelope.MaxY)
640 : {
641 69 : return true;
642 : }
643 : }
644 :
645 53 : return false;
646 : }
647 :
648 : /************************************************************************/
649 : /* OGRWKBIntersectsRingSequencePessimistic() */
650 : /************************************************************************/
651 :
652 87 : static bool OGRWKBIntersectsRingSequencePessimistic(
653 : const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
654 : const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
655 : bool &bErrorOut)
656 : {
657 : const uint32_t nRings =
658 87 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
659 87 : if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
660 : {
661 16 : bErrorOut = true;
662 16 : return false;
663 : }
664 71 : if (nRings == 0)
665 2 : return false;
666 69 : if (iOffsetInOut + sizeof(uint32_t) > size)
667 : {
668 0 : bErrorOut = true;
669 0 : return false;
670 : }
671 69 : if (OGRWKBIntersectsPointSequencePessimistic(
672 : data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut))
673 : {
674 45 : return true;
675 : }
676 24 : if (bErrorOut)
677 0 : return false;
678 :
679 : // skip inner rings
680 30 : for (uint32_t i = 1; i < nRings; ++i)
681 : {
682 6 : if (iOffsetInOut + sizeof(uint32_t) > size)
683 : {
684 0 : bErrorOut = true;
685 0 : return false;
686 : }
687 : const uint32_t nPoints =
688 6 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
689 6 : if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
690 : {
691 0 : bErrorOut = true;
692 0 : return false;
693 : }
694 6 : iOffsetInOut += sizeof(double) * nPoints * nDim;
695 : }
696 24 : return false;
697 : }
698 :
699 : /************************************************************************/
700 : /* OGRWKBIntersectsPessimistic() */
701 : /************************************************************************/
702 :
703 23369 : static bool OGRWKBIntersectsPessimistic(const GByte *data, const size_t size,
704 : size_t &iOffsetInOut,
705 : const OGREnvelope &sEnvelope,
706 : const int nRec, bool &bErrorOut)
707 : {
708 23369 : if (size - iOffsetInOut < MIN_WKB_SIZE)
709 : {
710 0 : bErrorOut = true;
711 0 : return false;
712 : }
713 23369 : const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
714 23369 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
715 : {
716 0 : bErrorOut = true;
717 0 : return false;
718 : }
719 23369 : const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
720 :
721 23369 : OGRwkbGeometryType eGeometryType = wkbUnknown;
722 23369 : OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
723 23369 : iOffsetInOut += 5;
724 23369 : const auto eFlatType = wkbFlatten(eGeometryType);
725 23369 : const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
726 23369 : (OGR_GT_HasM(eGeometryType) ? 1 : 0);
727 :
728 23369 : if (eFlatType == wkbPoint)
729 : {
730 23070 : if (size - iOffsetInOut < nDim * sizeof(double))
731 8 : return false;
732 23062 : double dfX = 0;
733 23062 : double dfY = 0;
734 23062 : memcpy(&dfX, data + iOffsetInOut, sizeof(double));
735 23062 : memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
736 23062 : iOffsetInOut += nDim * sizeof(double);
737 23062 : if (OGR_SWAP(eByteOrder))
738 : {
739 0 : CPL_SWAP64PTR(&dfX);
740 0 : CPL_SWAP64PTR(&dfY);
741 : }
742 23062 : if (std::isnan(dfX))
743 : {
744 1 : return false;
745 : }
746 : else
747 : {
748 23054 : return dfX >= sEnvelope.MinX && dfX <= sEnvelope.MaxX &&
749 46115 : dfY >= sEnvelope.MinY && dfY <= sEnvelope.MaxY;
750 : }
751 : }
752 :
753 299 : if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
754 : {
755 65 : return OGRWKBIntersectsPointSequencePessimistic(
756 65 : data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
757 : }
758 :
759 234 : if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
760 : {
761 87 : return OGRWKBIntersectsRingSequencePessimistic(
762 87 : data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
763 : }
764 :
765 147 : if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
766 81 : eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection ||
767 65 : eFlatType == wkbCompoundCurve || eFlatType == wkbCurvePolygon ||
768 39 : eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
769 13 : eFlatType == wkbPolyhedralSurface || eFlatType == wkbTIN)
770 : {
771 147 : if (nRec == 128)
772 : {
773 0 : bErrorOut = true;
774 0 : return false;
775 : }
776 : const uint32_t nParts =
777 147 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
778 147 : if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
779 : {
780 80 : bErrorOut = true;
781 80 : return false;
782 : }
783 112 : for (uint32_t k = 0; k < nParts; k++)
784 : {
785 76 : if (OGRWKBIntersectsPessimistic(data, size, iOffsetInOut, sEnvelope,
786 : nRec + 1, bErrorOut))
787 : {
788 31 : return true;
789 : }
790 45 : else if (bErrorOut)
791 : {
792 0 : return false;
793 : }
794 : }
795 36 : return false;
796 : }
797 :
798 0 : bErrorOut = true;
799 0 : return false;
800 : }
801 :
802 : /************************************************************************/
803 : /* OGRWKBIntersectsPessimistic() */
804 : /************************************************************************/
805 :
806 : /* Returns whether the geometry (pabyWkb, nWKBSize) intersects, for sure,
807 : * the passed envelope.
808 : * When it returns true, the geometry intersects the envelope.
809 : * When it returns false, the geometry may or may not intersect the envelope.
810 : */
811 23293 : bool OGRWKBIntersectsPessimistic(const GByte *pabyWkb, size_t nWKBSize,
812 : const OGREnvelope &sEnvelope)
813 : {
814 23293 : size_t iOffsetInOut = 0;
815 23293 : bool bErrorOut = false;
816 23293 : bool bRet = OGRWKBIntersectsPessimistic(pabyWkb, nWKBSize, iOffsetInOut,
817 : sEnvelope, 0, bErrorOut);
818 23293 : if (!bRet && !bErrorOut)
819 : {
820 : // The following assert only holds if there is no trailing data
821 : // after the WKB
822 : // CPLAssert(iOffsetInOut == nWKBSize);
823 : }
824 23293 : return bRet;
825 : }
826 :
827 : /************************************************************************/
828 : /* epsilonEqual() */
829 : /************************************************************************/
830 :
831 : constexpr double EPSILON = 1.0E-5;
832 :
833 48 : static inline bool epsilonEqual(double a, double b, double eps)
834 : {
835 48 : return ::fabs(a - b) < eps;
836 : }
837 :
838 : /************************************************************************/
839 : /* OGRWKBIsClockwiseRing() */
840 : /************************************************************************/
841 :
842 191 : static inline double GetX(const GByte *data, uint32_t i, int nDim,
843 : bool bNeedSwap)
844 : {
845 : double dfX;
846 191 : memcpy(&dfX, data + static_cast<size_t>(i) * nDim * sizeof(double),
847 : sizeof(double));
848 191 : if (bNeedSwap)
849 0 : CPL_SWAP64PTR(&dfX);
850 191 : return dfX;
851 : }
852 :
853 342 : static inline double GetY(const GByte *data, uint32_t i, int nDim,
854 : bool bNeedSwap)
855 : {
856 : double dfY;
857 342 : memcpy(&dfY, data + (static_cast<size_t>(i) * nDim + 1) * sizeof(double),
858 : sizeof(double));
859 342 : if (bNeedSwap)
860 0 : CPL_SWAP64PTR(&dfY);
861 342 : return dfY;
862 : }
863 :
864 19 : static bool OGRWKBIsClockwiseRing(const GByte *data, const uint32_t nPoints,
865 : const int nDim, const bool bNeedSwap)
866 : {
867 : // WARNING: keep in sync OGRLineString::isClockwise(),
868 : // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
869 :
870 19 : bool bUseFallback = false;
871 :
872 : // Find the lowest rightmost vertex.
873 19 : uint32_t v = 0; // Used after for.
874 19 : double vX = GetX(data, v, nDim, bNeedSwap);
875 19 : double vY = GetY(data, v, nDim, bNeedSwap);
876 268 : for (uint32_t i = 1; i < nPoints - 1; i++)
877 : {
878 : // => v < end.
879 249 : const double y = GetY(data, i, nDim, bNeedSwap);
880 249 : if (y < vY)
881 : {
882 78 : v = i;
883 78 : vX = GetX(data, i, nDim, bNeedSwap);
884 78 : vY = y;
885 78 : bUseFallback = false;
886 : }
887 171 : else if (y == vY)
888 : {
889 2 : const double x = GetX(data, i, nDim, bNeedSwap);
890 2 : if (x > vX)
891 : {
892 0 : v = i;
893 0 : vX = x;
894 0 : vY = y;
895 0 : bUseFallback = false;
896 : }
897 2 : else if (x == vX)
898 : {
899 : // Two vertex with same coordinates are the lowest rightmost
900 : // vertex. Cannot use that point as the pivot (#5342).
901 2 : bUseFallback = true;
902 : }
903 : }
904 : }
905 :
906 : // Previous.
907 19 : uint32_t next = (v == 0) ? nPoints - 2 : v - 1;
908 23 : if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
909 4 : epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
910 : {
911 : // Don't try to be too clever by retrying with a next point.
912 : // This can lead to false results as in the case of #3356.
913 0 : bUseFallback = true;
914 : }
915 :
916 19 : const double dx0 = GetX(data, next, nDim, bNeedSwap) - vX;
917 19 : const double dy0 = GetY(data, next, nDim, bNeedSwap) - vY;
918 :
919 : // Following.
920 19 : next = v + 1;
921 19 : if (next >= nPoints - 1)
922 : {
923 1 : next = 0;
924 : }
925 :
926 25 : if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
927 6 : epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
928 : {
929 : // Don't try to be too clever by retrying with a next point.
930 : // This can lead to false results as in the case of #3356.
931 2 : bUseFallback = true;
932 : }
933 :
934 19 : const double dx1 = GetX(data, next, nDim, bNeedSwap) - vX;
935 19 : const double dy1 = GetY(data, next, nDim, bNeedSwap) - vY;
936 :
937 19 : const double crossproduct = dx1 * dy0 - dx0 * dy1;
938 :
939 19 : if (!bUseFallback)
940 : {
941 17 : if (crossproduct > 0) // CCW
942 13 : return false;
943 4 : else if (crossproduct < 0) // CW
944 4 : return true;
945 : }
946 :
947 : // This is a degenerate case: the extent of the polygon is less than EPSILON
948 : // or 2 nearly identical points were found.
949 : // Try with Green Formula as a fallback, but this is not a guarantee
950 : // as we'll probably be affected by numerical instabilities.
951 :
952 2 : double dfSum = GetX(data, 0, nDim, bNeedSwap) *
953 2 : (GetY(data, 1, nDim, bNeedSwap) -
954 2 : GetY(data, nPoints - 1, nDim, bNeedSwap));
955 :
956 12 : for (uint32_t i = 1; i < nPoints - 1; i++)
957 : {
958 10 : dfSum += GetX(data, i, nDim, bNeedSwap) *
959 20 : (GetY(data, i + 1, nDim, bNeedSwap) -
960 10 : GetY(data, i - 1, nDim, bNeedSwap));
961 : }
962 :
963 2 : dfSum += GetX(data, nPoints - 1, nDim, bNeedSwap) *
964 2 : (GetY(data, 0, nDim, bNeedSwap) -
965 2 : GetX(data, nPoints - 2, nDim, bNeedSwap));
966 :
967 2 : return dfSum < 0;
968 : }
969 :
970 : /************************************************************************/
971 : /* OGRWKBFixupCounterClockWiseExternalRing() */
972 : /************************************************************************/
973 :
974 37 : static bool OGRWKBFixupCounterClockWiseExternalRingInternal(
975 : GByte *data, size_t size, size_t &iOffsetInOut, const int nRec)
976 : {
977 37 : if (size - iOffsetInOut < MIN_WKB_SIZE)
978 : {
979 0 : return false;
980 : }
981 37 : const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
982 37 : if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
983 : {
984 0 : return false;
985 : }
986 37 : const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
987 :
988 37 : OGRwkbGeometryType eGeometryType = wkbUnknown;
989 37 : OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
990 37 : iOffsetInOut += 5;
991 37 : const auto eFlatType = wkbFlatten(eGeometryType);
992 37 : const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
993 37 : (OGR_GT_HasM(eGeometryType) ? 1 : 0);
994 :
995 37 : if (eFlatType == wkbPolygon)
996 : {
997 : const uint32_t nRings =
998 16 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
999 16 : if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
1000 : {
1001 0 : return false;
1002 : }
1003 35 : for (uint32_t iRing = 0; iRing < nRings; ++iRing)
1004 : {
1005 19 : if (iOffsetInOut + sizeof(uint32_t) > size)
1006 0 : return false;
1007 : const uint32_t nPoints =
1008 19 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1009 19 : const size_t sizeOfPoint = nDim * sizeof(double);
1010 19 : if (nPoints > (size - iOffsetInOut) / sizeOfPoint)
1011 : {
1012 0 : return false;
1013 : }
1014 :
1015 19 : if (nPoints >= 4)
1016 : {
1017 19 : const bool bIsClockwiseRing = OGRWKBIsClockwiseRing(
1018 19 : data + iOffsetInOut, nPoints, nDim, OGR_SWAP(eByteOrder));
1019 19 : if ((bIsClockwiseRing && iRing == 0) ||
1020 15 : (!bIsClockwiseRing && iRing > 0))
1021 : {
1022 : GByte abyTmp[4 * sizeof(double)];
1023 19 : for (uint32_t i = 0; i < nPoints / 2; ++i)
1024 : {
1025 13 : GByte *pBegin = data + iOffsetInOut + i * sizeOfPoint;
1026 13 : GByte *pEnd = data + iOffsetInOut +
1027 13 : (nPoints - 1 - i) * sizeOfPoint;
1028 13 : memcpy(abyTmp, pBegin, sizeOfPoint);
1029 13 : memcpy(pBegin, pEnd, sizeOfPoint);
1030 13 : memcpy(pEnd, abyTmp, sizeOfPoint);
1031 : }
1032 : }
1033 : }
1034 :
1035 19 : iOffsetInOut += nPoints * sizeOfPoint;
1036 : }
1037 : }
1038 :
1039 37 : if (eFlatType == wkbGeometryCollection || eFlatType == wkbMultiPolygon ||
1040 : eFlatType == wkbMultiSurface)
1041 : {
1042 6 : if (nRec == 128)
1043 : {
1044 0 : return false;
1045 : }
1046 : const uint32_t nParts =
1047 6 : OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1048 6 : if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
1049 : {
1050 0 : return false;
1051 : }
1052 11 : for (uint32_t k = 0; k < nParts; k++)
1053 : {
1054 5 : if (!OGRWKBFixupCounterClockWiseExternalRingInternal(
1055 : data, size, iOffsetInOut, nRec))
1056 : {
1057 0 : return false;
1058 : }
1059 : }
1060 : }
1061 :
1062 37 : return true;
1063 : }
1064 :
1065 : /** Modifies the geometry such that exterior rings of polygons are
1066 : * counter-clockwise oriented and inner rings clockwise oriented.
1067 : */
1068 32 : void OGRWKBFixupCounterClockWiseExternalRing(GByte *pabyWkb, size_t nWKBSize)
1069 : {
1070 32 : size_t iOffsetInOut = 0;
1071 32 : OGRWKBFixupCounterClockWiseExternalRingInternal(
1072 : pabyWkb, nWKBSize, iOffsetInOut, /* nRec = */ 0);
1073 32 : }
1074 :
1075 : /************************************************************************/
1076 : /* OGRAppendBuffer() */
1077 : /************************************************************************/
1078 :
1079 : OGRAppendBuffer::OGRAppendBuffer() = default;
1080 :
1081 : /************************************************************************/
1082 : /* ~OGRAppendBuffer() */
1083 : /************************************************************************/
1084 :
1085 : OGRAppendBuffer::~OGRAppendBuffer() = default;
1086 :
1087 : /************************************************************************/
1088 : /* OGRWKTToWKBTranslator() */
1089 : /************************************************************************/
1090 :
1091 18 : OGRWKTToWKBTranslator::OGRWKTToWKBTranslator(OGRAppendBuffer &oAppendBuffer)
1092 18 : : m_oAppendBuffer(oAppendBuffer)
1093 : {
1094 : #ifndef USE_FAST_FLOAT
1095 : // Test if current locale decimal separator is decimal point
1096 : char szTest[10];
1097 : snprintf(szTest, sizeof(szTest), "%f", 1.5);
1098 : m_bCanUseStrtod = strchr(szTest, '.') != nullptr;
1099 : #endif
1100 18 : CPL_IGNORE_RET_VAL(m_bCanUseStrtod);
1101 18 : }
1102 :
1103 : /************************************************************************/
1104 : /* TranslateWKT() */
1105 : /************************************************************************/
1106 :
1107 138 : size_t OGRWKTToWKBTranslator::TranslateWKT(void *pabyWKTStart, size_t nLength,
1108 : bool bCanAlterByteAfter)
1109 : {
1110 138 : const char *pszPtrStart = static_cast<const char *>(pabyWKTStart);
1111 : // Optimize single-part single-ring multipolygon WKT->WKB translation
1112 138 : if (bCanAlterByteAfter && nLength > strlen("MULTIPOLYGON") &&
1113 28 : EQUALN(pszPtrStart, "MULTIPOLYGON", strlen("MULTIPOLYGON")))
1114 : {
1115 28 : int nCountOpenPar = 0;
1116 28 : size_t nCountComma = 0;
1117 28 : bool bHasZ = false;
1118 28 : bool bHasM = false;
1119 :
1120 28 : char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
1121 28 : const char chBackup = *pszEnd;
1122 28 : *pszEnd = 0;
1123 :
1124 : // Checks that the multipolygon consists of a single part with
1125 : // only an exterior ring.
1126 852 : for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON"); *pszPtr;
1127 : ++pszPtr)
1128 : {
1129 832 : const char ch = *pszPtr;
1130 832 : if (ch == 'Z')
1131 8 : bHasZ = true;
1132 824 : else if (ch == 'M')
1133 8 : bHasM = true;
1134 832 : if (ch == '(')
1135 : {
1136 84 : nCountOpenPar++;
1137 84 : if (nCountOpenPar == 4)
1138 0 : break;
1139 : }
1140 748 : else if (ch == ')')
1141 : {
1142 72 : nCountOpenPar--;
1143 72 : if (nCountOpenPar < 0)
1144 0 : break;
1145 : }
1146 676 : else if (ch == ',')
1147 : {
1148 92 : if (nCountOpenPar < 3)
1149 : {
1150 : // multipart / multi-ring
1151 8 : break;
1152 : }
1153 84 : nCountComma++;
1154 : }
1155 : }
1156 28 : const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
1157 48 : if (nCountOpenPar == 0 && nCountComma > 0 &&
1158 20 : nCountComma < std::numeric_limits<uint32_t>::max())
1159 : {
1160 20 : const uint32_t nVerticesCount =
1161 20 : static_cast<uint32_t>(nCountComma + 1);
1162 20 : const size_t nWKBSize =
1163 : sizeof(GByte) + // Endianness
1164 : sizeof(uint32_t) + // multipolygon WKB geometry type
1165 : sizeof(uint32_t) + // number of parts
1166 : sizeof(GByte) + // Endianness
1167 : sizeof(uint32_t) + // polygon WKB geometry type
1168 : sizeof(uint32_t) + // number of rings
1169 : sizeof(uint32_t) + // number of vertices
1170 20 : nDim * sizeof(double) * nVerticesCount;
1171 : GByte *const pabyCurStart = static_cast<GByte *>(
1172 20 : m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
1173 20 : if (!pabyCurStart)
1174 : {
1175 0 : return static_cast<size_t>(-1);
1176 : }
1177 20 : GByte *pabyCur = pabyCurStart;
1178 : // Multipolygon byte order
1179 : {
1180 20 : *pabyCur = wkbNDR;
1181 20 : pabyCur++;
1182 : }
1183 : // Multipolygon geometry type
1184 : {
1185 20 : uint32_t nWKBGeomType =
1186 20 : wkbMultiPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
1187 20 : CPL_LSBPTR32(&nWKBGeomType);
1188 20 : memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
1189 20 : pabyCur += sizeof(uint32_t);
1190 : }
1191 : // Number of parts
1192 : {
1193 20 : uint32_t nOne = 1;
1194 20 : CPL_LSBPTR32(&nOne);
1195 20 : memcpy(pabyCur, &nOne, sizeof(uint32_t));
1196 20 : pabyCur += sizeof(uint32_t);
1197 : }
1198 : // Polygon byte order
1199 : {
1200 20 : *pabyCur = wkbNDR;
1201 20 : pabyCur++;
1202 : }
1203 : // Polygon geometry type
1204 : {
1205 20 : uint32_t nWKBGeomType =
1206 20 : wkbPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
1207 20 : CPL_LSBPTR32(&nWKBGeomType);
1208 20 : memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
1209 20 : pabyCur += sizeof(uint32_t);
1210 : }
1211 : // Number of rings
1212 : {
1213 20 : uint32_t nOne = 1;
1214 20 : CPL_LSBPTR32(&nOne);
1215 20 : memcpy(pabyCur, &nOne, sizeof(uint32_t));
1216 20 : pabyCur += sizeof(uint32_t);
1217 : }
1218 : // Number of vertices
1219 : {
1220 20 : uint32_t nVerticesCountToWrite = nVerticesCount;
1221 20 : CPL_LSBPTR32(&nVerticesCountToWrite);
1222 20 : memcpy(pabyCur, &nVerticesCountToWrite, sizeof(uint32_t));
1223 20 : pabyCur += sizeof(uint32_t);
1224 : }
1225 20 : uint32_t nDoubleCount = 0;
1226 20 : const uint32_t nExpectedDoubleCount = nVerticesCount * nDim;
1227 616 : for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON");
1228 616 : *pszPtr;
1229 : /* nothing */)
1230 : {
1231 596 : const char ch = *pszPtr;
1232 596 : if (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9'))
1233 : {
1234 224 : nDoubleCount++;
1235 224 : if (nDoubleCount > nExpectedDoubleCount)
1236 : {
1237 0 : break;
1238 : }
1239 : #ifdef USE_FAST_FLOAT
1240 : double dfVal;
1241 224 : auto answer = fast_float::from_chars(pszPtr, pszEnd, dfVal);
1242 224 : if (answer.ec != std::errc())
1243 : {
1244 0 : nDoubleCount = 0;
1245 0 : break;
1246 : }
1247 224 : pszPtr = answer.ptr;
1248 : #else
1249 : char *endptr = nullptr;
1250 : const double dfVal =
1251 : m_bCanUseStrtod ? strtod(pszPtr, &endptr)
1252 : : CPLStrtodDelim(pszPtr, &endptr, '.');
1253 : pszPtr = endptr;
1254 : #endif
1255 224 : CPL_LSBPTR64(&dfVal);
1256 224 : memcpy(pabyCur, &dfVal, sizeof(double));
1257 224 : pabyCur += sizeof(double);
1258 : }
1259 : else
1260 : {
1261 372 : ++pszPtr;
1262 : }
1263 : }
1264 20 : if (nDoubleCount == nExpectedDoubleCount)
1265 : {
1266 20 : CPLAssert(static_cast<size_t>(pabyCur - pabyCurStart) ==
1267 : nWKBSize);
1268 : // cppcheck-suppress selfAssignment
1269 20 : *pszEnd = chBackup;
1270 20 : return nWKBSize;
1271 : }
1272 : else
1273 : {
1274 0 : CPLError(CE_Failure, CPLE_AppDefined,
1275 : "Invalid WKT geometry: %s", pszPtrStart);
1276 : // cppcheck-suppress selfAssignment
1277 0 : *pszEnd = chBackup;
1278 0 : return static_cast<size_t>(-1);
1279 : }
1280 : }
1281 : // cppcheck-suppress selfAssignment
1282 8 : *pszEnd = chBackup;
1283 : }
1284 :
1285 : // General case going through a OGRGeometry
1286 118 : OGRGeometry *poGeometry = nullptr;
1287 118 : if (bCanAlterByteAfter)
1288 : {
1289 : // Slight optimization for all geometries but the final one, to
1290 : // avoid creating a new string each time.
1291 : // We set the ending byte to '\0' and restore it back after parsing
1292 : // the WKT
1293 100 : char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
1294 100 : const char chBackup = *pszEnd;
1295 100 : *pszEnd = 0;
1296 100 : OGRGeometryFactory::createFromWkt(pszPtrStart, nullptr, &poGeometry);
1297 : // cppcheck-suppress selfAssignment
1298 100 : *pszEnd = chBackup;
1299 : }
1300 : else
1301 : {
1302 36 : std::string osTmp;
1303 18 : osTmp.assign(pszPtrStart, nLength);
1304 18 : OGRGeometryFactory::createFromWkt(osTmp.c_str(), nullptr, &poGeometry);
1305 : }
1306 118 : if (!poGeometry)
1307 : {
1308 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid WKT geometry");
1309 0 : return static_cast<size_t>(-1);
1310 : }
1311 118 : const size_t nWKBSize = poGeometry->WkbSize();
1312 : GByte *pabyWKB =
1313 118 : static_cast<GByte *>(m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
1314 118 : if (!pabyWKB)
1315 : {
1316 0 : return static_cast<size_t>(-1);
1317 : }
1318 118 : poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso);
1319 118 : delete poGeometry;
1320 118 : return nWKBSize;
1321 : }
|