Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements reading of FileGDB tables
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "filegdbtable.h"
15 :
16 : #include <algorithm>
17 : #include <cassert>
18 : #include <cinttypes>
19 : #include <cmath>
20 : #include <errno.h>
21 : #include <limits.h>
22 : #include <stddef.h>
23 : #include <stdio.h>
24 : #include <string.h>
25 : #include <time.h>
26 : #include <limits>
27 : #include <string>
28 : #include <vector>
29 :
30 : #include "cpl_conv.h"
31 : #include "cpl_error.h"
32 : #include "cpl_string.h"
33 : #include "cpl_time.h"
34 : #include "cpl_vsi.h"
35 : #include "filegdbtable_priv.h"
36 : #include "ogr_api.h"
37 : #include "ogr_core.h"
38 : #include "ogr_geometry.h"
39 : #include "ogrpgeogeometry.h"
40 : #include "ogr_spatialref.h"
41 :
42 : #define UUID_SIZE_IN_BYTES 16
43 :
44 : #define IS_VALID_LAYER_GEOM_TYPE(byVal) \
45 : ((byVal) <= FGTGT_POLYGON || (byVal) == FGTGT_MULTIPATCH)
46 :
47 : /* Reserve one extra byte in case the last field is a string */
48 : /* or 2 for 2 ReadVarIntAndAddNoCheck() in a row */
49 : /* or 4 for SkipVarUInt() with nIter = 4 */
50 : /* or for 4 ReadVarUInt64NoCheck */
51 : #define ZEROES_AFTER_END_OF_BUFFER 4
52 :
53 : constexpr GUInt32 EXT_SHAPE_Z_FLAG = 0x80000000U;
54 : constexpr GUInt32 EXT_SHAPE_M_FLAG = 0x40000000U;
55 : constexpr GUInt32 EXT_SHAPE_CURVE_FLAG = 0x20000000U;
56 :
57 : constexpr GUInt32 EXT_SHAPE_SEGMENT_ARC = 1;
58 : constexpr GUInt32 EXT_SHAPE_SEGMENT_BEZIER = 4;
59 : constexpr GUInt32 EXT_SHAPE_SEGMENT_ELLIPSE = 5;
60 :
61 : namespace OpenFileGDB
62 : {
63 :
64 : #ifdef _MSC_VER
65 : #pragma warning(push)
66 : #pragma warning(disable : 4800) /* implicit conversion from "type" to bool */
67 : #endif
68 :
69 : FileGDBGeomField::~FileGDBGeomField() = default;
70 :
71 : FileGDBRasterField::~FileGDBRasterField() = default;
72 :
73 : /************************************************************************/
74 : /* SanitizeScale() */
75 : /************************************************************************/
76 :
77 3983 : static double SanitizeScale(double dfVal)
78 : {
79 3983 : if (dfVal == 0.0)
80 0 : return std::numeric_limits<double>::min(); // to prevent divide by zero
81 3983 : return dfVal;
82 : }
83 :
84 : /************************************************************************/
85 : /* FileGDBTablePrintError() */
86 : /************************************************************************/
87 :
88 125 : void FileGDBTablePrintError(const char *pszFile, int nLineNumber)
89 : {
90 125 : CPLError(CE_Failure, CPLE_AppDefined, "Error occurred in %s at line %d",
91 : pszFile, nLineNumber);
92 125 : }
93 :
94 : /************************************************************************/
95 : /* FileGDBTable() */
96 : /************************************************************************/
97 :
98 7387 : FileGDBTable::FileGDBTable()
99 : {
100 7387 : memset(&m_sCurField, 0, sizeof(m_sCurField));
101 7387 : }
102 :
103 : /************************************************************************/
104 : /* ~FileGDBTable() */
105 : /************************************************************************/
106 :
107 7387 : FileGDBTable::~FileGDBTable()
108 : {
109 7387 : Close();
110 7387 : }
111 :
112 : /************************************************************************/
113 : /* Close() */
114 : /************************************************************************/
115 :
116 8263 : void FileGDBTable::Close()
117 : {
118 8263 : Sync();
119 :
120 8263 : if (m_fpTable)
121 7374 : VSIFCloseL(m_fpTable);
122 8263 : m_fpTable = nullptr;
123 :
124 8263 : if (m_fpTableX)
125 7257 : VSIFCloseL(m_fpTableX);
126 8263 : m_fpTableX = nullptr;
127 8263 : }
128 :
129 : /************************************************************************/
130 : /* GetFieldIdx() */
131 : /************************************************************************/
132 :
133 43304 : int FileGDBTable::GetFieldIdx(const std::string &osName) const
134 : {
135 244645 : for (size_t i = 0; i < m_apoFields.size(); i++)
136 : {
137 230137 : if (m_apoFields[i]->GetName() == osName)
138 28796 : return static_cast<int>(i);
139 : }
140 14508 : return -1;
141 : }
142 :
143 : /************************************************************************/
144 : /* ReadVarUInt() */
145 : /************************************************************************/
146 :
147 : template <class OutType, class ControlType>
148 291674 : static int ReadVarUInt(GByte *&pabyIter, GByte *pabyEnd, OutType &nOutVal)
149 : {
150 291674 : const int errorRetValue = FALSE;
151 : if (!(ControlType::check_bounds))
152 : {
153 : /* nothing */
154 : }
155 : else if (ControlType::verbose_error)
156 : {
157 218638 : returnErrorIf(pabyIter >= pabyEnd);
158 : }
159 : else
160 : {
161 913 : if (pabyIter >= pabyEnd)
162 0 : return FALSE;
163 : }
164 291674 : OutType b = *pabyIter;
165 291674 : if ((b & 0x80) == 0)
166 : {
167 232737 : pabyIter++;
168 232737 : nOutVal = b;
169 232737 : return TRUE;
170 : }
171 58937 : GByte *pabyLocalIter = pabyIter + 1;
172 58937 : int nShift = 7;
173 58937 : OutType nVal = (b & 0x7F);
174 : while (true)
175 : {
176 191561 : if (!(ControlType::check_bounds))
177 : {
178 : /* nothing */
179 : }
180 67 : else if (ControlType::verbose_error)
181 : {
182 18505 : returnErrorIf(pabyLocalIter >= pabyEnd);
183 : }
184 : else
185 : {
186 91 : if (pabyLocalIter >= pabyEnd)
187 0 : return FALSE;
188 : }
189 250565 : b = *pabyLocalIter;
190 250565 : pabyLocalIter++;
191 250565 : nVal |= (b & 0x7F) << nShift;
192 250565 : if ((b & 0x80) == 0)
193 : {
194 58937 : pabyIter = pabyLocalIter;
195 58937 : nOutVal = nVal;
196 58937 : return TRUE;
197 : }
198 191628 : nShift += 7;
199 : // To avoid undefined behavior later when doing << nShift
200 191628 : if (nShift >= static_cast<int>(sizeof(OutType)) * 8)
201 : {
202 0 : pabyIter = pabyLocalIter;
203 0 : nOutVal = nVal;
204 0 : returnError();
205 : }
206 : }
207 : }
208 :
209 : struct ControlTypeVerboseErrorTrue
210 : {
211 : // cppcheck-suppress unusedStructMember
212 : static const bool check_bounds = true;
213 : // cppcheck-suppress unusedStructMember
214 : static const bool verbose_error = true;
215 : };
216 :
217 : struct ControlTypeVerboseErrorFalse
218 : {
219 : // cppcheck-suppress unusedStructMember
220 : static const bool check_bounds = true;
221 : // cppcheck-suppress unusedStructMember
222 : static const bool verbose_error = false;
223 : };
224 :
225 : struct ControlTypeNone
226 : {
227 : // cppcheck-suppress unusedStructMember
228 : static const bool check_bounds = false;
229 : // cppcheck-suppress unusedStructMember
230 : static const bool verbose_error = false;
231 : };
232 :
233 218638 : static int ReadVarUInt32(GByte *&pabyIter, GByte *pabyEnd, GUInt32 &nOutVal)
234 : {
235 218638 : return ReadVarUInt<GUInt32, ControlTypeVerboseErrorTrue>(pabyIter, pabyEnd,
236 218638 : nOutVal);
237 : }
238 :
239 32049 : static void ReadVarUInt32NoCheck(GByte *&pabyIter, GUInt32 &nOutVal)
240 : {
241 32049 : GByte *pabyEnd = nullptr;
242 32049 : ReadVarUInt<GUInt32, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
243 32049 : }
244 :
245 913 : static int ReadVarUInt32Silent(GByte *&pabyIter, GByte *pabyEnd,
246 : GUInt32 &nOutVal)
247 : {
248 913 : return ReadVarUInt<GUInt32, ControlTypeVerboseErrorFalse>(pabyIter, pabyEnd,
249 913 : nOutVal);
250 : }
251 :
252 40074 : static void ReadVarUInt64NoCheck(GByte *&pabyIter, GUIntBig &nOutVal)
253 : {
254 40074 : GByte *pabyEnd = nullptr;
255 40074 : ReadVarUInt<GUIntBig, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
256 40074 : }
257 :
258 : /************************************************************************/
259 : /* IsLikelyFeatureAtOffset() */
260 : /************************************************************************/
261 :
262 3438 : int FileGDBTable::IsLikelyFeatureAtOffset(vsi_l_offset nOffset, GUInt32 *pnSize,
263 : int *pbDeletedRecord)
264 : {
265 3438 : VSIFSeekL(m_fpTable, nOffset, SEEK_SET);
266 : GByte abyBuffer[4];
267 3438 : if (VSIFReadL(abyBuffer, 4, 1, m_fpTable) != 1)
268 3 : return FALSE;
269 :
270 3435 : m_nRowBlobLength = GetUInt32(abyBuffer, 0);
271 3435 : if (m_nRowBlobLength < static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
272 3286 : m_nRowBlobLength > m_nFileSize - nOffset ||
273 631 : m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
274 631 : m_nRowBlobLength > 10 * (m_nFileSize / m_nValidRecordCount))
275 : {
276 : /* Is it a deleted record ? */
277 2828 : if ((m_nRowBlobLength >> (8 * sizeof(m_nRowBlobLength) - 1)) != 0 &&
278 210 : m_nRowBlobLength != 0x80000000U)
279 : {
280 210 : m_nRowBlobLength =
281 210 : static_cast<GUInt32>(-static_cast<int>(m_nRowBlobLength));
282 210 : if (m_nRowBlobLength <
283 210 : static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
284 210 : m_nRowBlobLength > m_nFileSize - nOffset ||
285 53 : m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
286 53 : m_nRowBlobLength > 10 * (m_nFileSize / m_nValidRecordCount))
287 157 : return FALSE;
288 : else
289 53 : *pbDeletedRecord = TRUE;
290 : }
291 : else
292 2618 : return FALSE;
293 : }
294 : else
295 607 : *pbDeletedRecord = FALSE;
296 :
297 660 : m_nRowBufferMaxSize = std::max(m_nRowBlobLength, m_nRowBufferMaxSize);
298 660 : if (m_abyBuffer.size() < m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER)
299 : {
300 : try
301 : {
302 4 : m_abyBuffer.resize(m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER);
303 : }
304 0 : catch (const std::exception &e)
305 : {
306 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
307 0 : return FALSE;
308 : }
309 : }
310 660 : if (m_nCountNullableFields > 0)
311 : {
312 610 : if (VSIFReadL(m_abyBuffer.data(), m_nNullableFieldsSizeInBytes, 1,
313 610 : m_fpTable) != 1)
314 0 : return FALSE;
315 : }
316 660 : m_iAccNullable = 0;
317 660 : int bExactSizeKnown = TRUE;
318 660 : GUInt32 nRequiredLength = m_nNullableFieldsSizeInBytes;
319 5007 : for (int i = 0; i < static_cast<int>(m_apoFields.size()); i++)
320 : {
321 4365 : if (m_apoFields[i]->m_bNullable)
322 : {
323 3404 : int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
324 3404 : m_iAccNullable++;
325 3404 : if (bIsNull)
326 805 : continue;
327 : }
328 :
329 3560 : switch (m_apoFields[i]->m_eType)
330 : {
331 0 : case FGFT_UNDEFINED:
332 0 : CPLAssert(false);
333 : break;
334 :
335 660 : case FGFT_OBJECTID:
336 660 : break;
337 :
338 1196 : case FGFT_STRING:
339 : case FGFT_XML:
340 : case FGFT_GEOMETRY:
341 : case FGFT_BINARY:
342 : {
343 1196 : nRequiredLength += 1; /* varuint32 so at least one byte */
344 1196 : bExactSizeKnown = FALSE;
345 1196 : break;
346 : }
347 :
348 0 : case FGFT_RASTER:
349 : {
350 : const FileGDBRasterField *rasterField =
351 0 : cpl::down_cast<const FileGDBRasterField *>(
352 0 : m_apoFields[i].get());
353 0 : if (rasterField->GetRasterType() ==
354 : FileGDBRasterField::Type::MANAGED)
355 0 : nRequiredLength += sizeof(GInt32);
356 : else
357 0 : nRequiredLength += 1; /* varuint32 so at least one byte */
358 0 : break;
359 : }
360 :
361 95 : case FGFT_INT16:
362 95 : nRequiredLength += sizeof(GInt16);
363 95 : break;
364 677 : case FGFT_INT32:
365 677 : nRequiredLength += sizeof(GInt32);
366 677 : break;
367 95 : case FGFT_FLOAT32:
368 95 : nRequiredLength += sizeof(float);
369 95 : break;
370 436 : case FGFT_FLOAT64:
371 436 : nRequiredLength += sizeof(double);
372 436 : break;
373 95 : case FGFT_DATETIME:
374 : case FGFT_DATE:
375 : case FGFT_TIME:
376 95 : nRequiredLength += sizeof(double);
377 95 : break;
378 306 : case FGFT_GUID:
379 : case FGFT_GLOBALID:
380 306 : nRequiredLength += UUID_SIZE_IN_BYTES;
381 306 : break;
382 0 : case FGFT_INT64:
383 0 : nRequiredLength += sizeof(int64_t);
384 0 : break;
385 0 : case FGFT_DATETIME_WITH_OFFSET:
386 0 : nRequiredLength += sizeof(double) + sizeof(int16_t);
387 0 : break;
388 : }
389 3560 : if (m_nRowBlobLength < nRequiredLength)
390 18 : return FALSE;
391 : }
392 642 : if (!bExactSizeKnown)
393 : {
394 289 : if (VSIFReadL(m_abyBuffer.data() + m_nNullableFieldsSizeInBytes,
395 289 : m_nRowBlobLength - m_nNullableFieldsSizeInBytes, 1,
396 289 : m_fpTable) != 1)
397 0 : return FALSE;
398 :
399 289 : m_iAccNullable = 0;
400 289 : nRequiredLength = m_nNullableFieldsSizeInBytes;
401 3132 : for (int i = 0; i < static_cast<int>(m_apoFields.size()); i++)
402 : {
403 2905 : if (m_apoFields[i]->m_bNullable)
404 : {
405 2348 : int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
406 2348 : m_iAccNullable++;
407 2348 : if (bIsNull)
408 639 : continue;
409 : }
410 :
411 2266 : switch (m_apoFields[i]->m_eType)
412 : {
413 0 : case FGFT_UNDEFINED:
414 0 : CPLAssert(false);
415 : break;
416 :
417 279 : case FGFT_OBJECTID:
418 279 : break;
419 :
420 603 : case FGFT_STRING:
421 : case FGFT_XML:
422 : {
423 603 : GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
424 : GUInt32 nLength;
425 1809 : if (!ReadVarUInt32Silent(
426 603 : pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
427 1206 : nLength) ||
428 603 : pabyIter - (m_abyBuffer.data() + nRequiredLength) > 5)
429 50 : return FALSE;
430 603 : nRequiredLength =
431 603 : static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
432 603 : if (nLength > m_nRowBlobLength - nRequiredLength)
433 22 : return FALSE;
434 157797 : for (GUInt32 j = 0; j < nLength; j++)
435 : {
436 157240 : if (pabyIter[j] == 0)
437 24 : return FALSE;
438 : }
439 557 : if (!CPLIsUTF8(reinterpret_cast<const char *>(pabyIter),
440 : nLength))
441 4 : return FALSE;
442 553 : nRequiredLength += nLength;
443 553 : break;
444 : }
445 :
446 310 : case FGFT_GEOMETRY:
447 : case FGFT_BINARY:
448 : {
449 310 : GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
450 : GUInt32 nLength;
451 930 : if (!ReadVarUInt32Silent(
452 310 : pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
453 620 : nLength) ||
454 310 : pabyIter - (m_abyBuffer.data() + nRequiredLength) > 5)
455 12 : return FALSE;
456 310 : nRequiredLength =
457 310 : static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
458 310 : if (nLength > m_nRowBlobLength - nRequiredLength)
459 12 : return FALSE;
460 298 : nRequiredLength += nLength;
461 298 : break;
462 : }
463 :
464 0 : case FGFT_RASTER:
465 : {
466 : const FileGDBRasterField *rasterField =
467 0 : cpl::down_cast<const FileGDBRasterField *>(
468 0 : m_apoFields[i].get());
469 0 : if (rasterField->GetRasterType() ==
470 : FileGDBRasterField::Type::MANAGED)
471 0 : nRequiredLength += sizeof(GInt32);
472 : else
473 : {
474 0 : GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
475 : GUInt32 nLength;
476 0 : if (!ReadVarUInt32Silent(
477 0 : pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
478 0 : nLength) ||
479 0 : pabyIter - (m_abyBuffer.data() + nRequiredLength) >
480 : 5)
481 0 : return FALSE;
482 0 : nRequiredLength =
483 0 : static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
484 0 : if (nLength > m_nRowBlobLength - nRequiredLength)
485 0 : return FALSE;
486 0 : nRequiredLength += nLength;
487 : }
488 0 : break;
489 : }
490 :
491 95 : case FGFT_INT16:
492 95 : nRequiredLength += sizeof(GInt16);
493 95 : break;
494 411 : case FGFT_INT32:
495 411 : nRequiredLength += sizeof(GInt32);
496 411 : break;
497 95 : case FGFT_FLOAT32:
498 95 : nRequiredLength += sizeof(float);
499 95 : break;
500 95 : case FGFT_FLOAT64:
501 95 : nRequiredLength += sizeof(double);
502 95 : break;
503 95 : case FGFT_DATETIME:
504 : case FGFT_DATE:
505 : case FGFT_TIME:
506 95 : nRequiredLength += sizeof(double);
507 95 : break;
508 283 : case FGFT_GUID:
509 : case FGFT_GLOBALID:
510 283 : nRequiredLength += UUID_SIZE_IN_BYTES;
511 283 : break;
512 0 : case FGFT_INT64:
513 0 : nRequiredLength += sizeof(int64_t);
514 0 : break;
515 0 : case FGFT_DATETIME_WITH_OFFSET:
516 0 : nRequiredLength += sizeof(double) + sizeof(int16_t);
517 0 : break;
518 : }
519 2204 : if (nRequiredLength > m_nRowBlobLength)
520 0 : return FALSE;
521 : }
522 : }
523 :
524 580 : *pnSize = 4 + nRequiredLength;
525 580 : return nRequiredLength == m_nRowBlobLength;
526 : }
527 :
528 : /************************************************************************/
529 : /* GuessFeatureLocations() */
530 : /************************************************************************/
531 :
532 : #define MARK_DELETED(x) ((x) | (static_cast<GUIntBig>(1) << 63))
533 : #define IS_DELETED(x) (((x) & (static_cast<GUIntBig>(1) << 63)) != 0)
534 : #define GET_OFFSET(x) ((x) & ~(static_cast<GUIntBig>(1) << 63))
535 :
536 38 : bool FileGDBTable::GuessFeatureLocations()
537 : {
538 38 : VSIFSeekL(m_fpTable, 0, SEEK_END);
539 38 : m_nFileSize = VSIFTellL(m_fpTable);
540 :
541 38 : int bReportDeletedFeatures = CPLTestBool(
542 38 : CPLGetConfigOption("OPENFILEGDB_REPORT_DELETED_FEATURES", "NO"));
543 :
544 38 : vsi_l_offset nOffset = 40 + m_nFieldDescLength;
545 :
546 38 : if (m_nOffsetFieldDesc != 40)
547 : {
548 : /* Check if there is a deleted field description at offset 40 */
549 : GByte abyBuffer[14];
550 1 : VSIFSeekL(m_fpTable, 40, SEEK_SET);
551 1 : if (VSIFReadL(abyBuffer, 14, 1, m_fpTable) != 1)
552 0 : return FALSE;
553 1 : int nSize = GetInt32(abyBuffer, 0);
554 1 : int nVersion = GetInt32(abyBuffer + 4, 0);
555 1 : if (nSize < 0 && nSize > -1024 * 1024 &&
556 0 : (nVersion == 3 || nVersion == 4) &&
557 0 : IS_VALID_LAYER_GEOM_TYPE(abyBuffer[8]) && abyBuffer[9] == 3 &&
558 0 : abyBuffer[10] == 0 && abyBuffer[11] == 0)
559 : {
560 0 : nOffset = 40 + (-nSize);
561 : }
562 : else
563 : {
564 1 : nOffset = 40;
565 : }
566 : }
567 :
568 38 : int64_t nInvalidRecords = 0;
569 : try
570 : {
571 3476 : while (nOffset < m_nFileSize)
572 : {
573 : GUInt32 nSize;
574 : int bDeletedRecord;
575 3438 : if (!IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord))
576 : {
577 2869 : nOffset++;
578 : }
579 : else
580 : {
581 : /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)",
582 : nOffset, nSize);*/
583 569 : if (bDeletedRecord)
584 : {
585 9 : if (bReportDeletedFeatures)
586 : {
587 0 : m_bHasDeletedFeaturesListed = TRUE;
588 0 : m_anFeatureOffsets.push_back(MARK_DELETED(nOffset));
589 : }
590 : else
591 : {
592 9 : nInvalidRecords++;
593 9 : m_anFeatureOffsets.push_back(0);
594 : }
595 : }
596 : else
597 560 : m_anFeatureOffsets.push_back(nOffset);
598 569 : nOffset += nSize;
599 : }
600 : }
601 : }
602 0 : catch (const std::bad_alloc &)
603 : {
604 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
605 : "Out of memory in FileGDBTable::GuessFeatureLocations()");
606 0 : return false;
607 : }
608 38 : m_nTotalRecordCount = static_cast<int64_t>(m_anFeatureOffsets.size());
609 38 : if (m_nTotalRecordCount - nInvalidRecords > m_nValidRecordCount)
610 : {
611 0 : if (!m_bHasDeletedFeaturesListed)
612 : {
613 0 : CPLError(CE_Warning, CPLE_AppDefined,
614 : "More features found (%" PRId64
615 : ") than declared number of valid "
616 : "features ((%" PRId64 "). "
617 : "So deleted features will likely be reported.",
618 0 : m_nTotalRecordCount - nInvalidRecords,
619 : m_nValidRecordCount);
620 : }
621 0 : m_nValidRecordCount = m_nTotalRecordCount - nInvalidRecords;
622 : }
623 :
624 38 : return m_nTotalRecordCount > 0;
625 : }
626 :
627 : /************************************************************************/
628 : /* ReadTableXHeaderV3() */
629 : /************************************************************************/
630 :
631 5220 : bool FileGDBTable::ReadTableXHeaderV3()
632 : {
633 5220 : const bool errorRetValue = false;
634 : GByte abyHeader[16];
635 :
636 : // Read .gdbtablx file header
637 5220 : returnErrorIf(VSIFReadL(abyHeader, 16, 1, m_fpTableX) != 1);
638 :
639 5220 : const int nGDBTablxVersion = GetUInt32(abyHeader, 0);
640 5220 : if (nGDBTablxVersion != static_cast<int>(m_eGDBTableVersion))
641 : {
642 0 : CPLError(CE_Failure, CPLE_AppDefined,
643 : ".gdbtablx version is %d whereas it should be %d",
644 0 : nGDBTablxVersion, static_cast<int>(m_eGDBTableVersion));
645 0 : return false;
646 : }
647 :
648 5220 : m_n1024BlocksPresent = GetUInt32(abyHeader + 4, 0);
649 :
650 5220 : m_nTotalRecordCount = GetInt32(abyHeader + 8, 0);
651 5220 : if (m_n1024BlocksPresent == 0)
652 498 : returnErrorIf(m_nTotalRecordCount != 0);
653 : else
654 4722 : returnErrorIf(m_nTotalRecordCount < 0);
655 :
656 5219 : m_nTablxOffsetSize = GetUInt32(abyHeader + 12, 0);
657 5219 : returnErrorIf(m_nTablxOffsetSize < 4 || m_nTablxOffsetSize > 6);
658 :
659 5218 : m_nOffsetTableXTrailer =
660 5218 : 16 + m_nTablxOffsetSize * 1024 *
661 5218 : static_cast<vsi_l_offset>(m_n1024BlocksPresent);
662 5218 : if (m_n1024BlocksPresent != 0)
663 : {
664 : GByte abyTrailer[16];
665 :
666 4720 : VSIFSeekL(m_fpTableX, m_nOffsetTableXTrailer, SEEK_SET);
667 4725 : returnErrorIf(VSIFReadL(abyTrailer, 16, 1, m_fpTableX) != 1);
668 :
669 4719 : GUInt32 nBitmapInt32Words = GetUInt32(abyTrailer, 0);
670 :
671 4719 : GUInt32 nBitsForBlockMap = GetUInt32(abyTrailer + 4, 0);
672 4719 : returnErrorIf(nBitsForBlockMap > 1 + INT_MAX / 1024);
673 :
674 4718 : GUInt32 n1024BlocksBis = GetUInt32(abyTrailer + 8, 0);
675 4718 : returnErrorIf(n1024BlocksBis != m_n1024BlocksPresent);
676 :
677 : /* GUInt32 nLeadingNonZero32BitWords = GetUInt32(abyTrailer + 12, 0); */
678 :
679 4716 : if (nBitmapInt32Words == 0)
680 : {
681 4692 : returnErrorIf(nBitsForBlockMap != m_n1024BlocksPresent);
682 : /* returnErrorIf(nLeadingNonZero32BitWords != 0 ); */
683 : }
684 : else
685 : {
686 24 : returnErrorIf(static_cast<GUInt32>(m_nTotalRecordCount) >
687 : nBitsForBlockMap * 1024);
688 : #ifdef DEBUG_VERBOSE
689 : CPLDebug("OpenFileGDB", "%s .gdbtablx has block map array",
690 : m_osFilename.c_str());
691 : #endif
692 :
693 : // Allocate a bit mask array for blocks of 1024 features.
694 24 : uint32_t nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nBitsForBlockMap);
695 : try
696 : {
697 24 : m_abyTablXBlockMap.resize(nSizeInBytes);
698 : }
699 0 : catch (const std::exception &e)
700 : {
701 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
702 0 : "Cannot allocate m_abyTablXBlockMap: %s", e.what());
703 0 : return false;
704 : }
705 24 : returnErrorIf(VSIFReadL(m_abyTablXBlockMap.data(), nSizeInBytes, 1,
706 : m_fpTableX) != 1);
707 : /* returnErrorIf(nMagic2 == 0 ); */
708 :
709 : // Check that the map is consistent with m_n1024BlocksPresent
710 23 : GUInt32 nCountBlocks = 0;
711 4204160 : for (GUInt32 i = 0; i < nBitsForBlockMap; i++)
712 4204140 : nCountBlocks += TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
713 23 : returnErrorIf(nCountBlocks != m_n1024BlocksPresent);
714 : }
715 : }
716 5212 : return true;
717 : }
718 :
719 : /************************************************************************/
720 : /* ReadTableXHeaderV4() */
721 : /************************************************************************/
722 :
723 17 : bool FileGDBTable::ReadTableXHeaderV4()
724 : {
725 17 : const bool errorRetValue = false;
726 : GByte abyHeader[16];
727 :
728 : // Read .gdbtablx file header
729 17 : returnErrorIf(VSIFReadL(abyHeader, 16, 1, m_fpTableX) != 1);
730 :
731 17 : const int nGDBTablxVersion = GetUInt32(abyHeader, 0);
732 17 : if (nGDBTablxVersion != static_cast<int>(m_eGDBTableVersion))
733 : {
734 0 : CPLError(CE_Failure, CPLE_AppDefined,
735 : ".gdbtablx version is %d whereas it should be %d",
736 0 : nGDBTablxVersion, static_cast<int>(m_eGDBTableVersion));
737 0 : return false;
738 : }
739 :
740 17 : m_n1024BlocksPresent = GetUInt64(abyHeader + 4, 0);
741 :
742 17 : m_nTablxOffsetSize = GetUInt32(abyHeader + 12, 0);
743 17 : returnErrorIf(m_nTablxOffsetSize < 4 || m_nTablxOffsetSize > 6);
744 :
745 17 : returnErrorIf(m_n1024BlocksPresent >
746 : (std::numeric_limits<vsi_l_offset>::max() - 16) /
747 : (m_nTablxOffsetSize * 1024));
748 :
749 17 : m_nOffsetTableXTrailer =
750 17 : 16 + m_nTablxOffsetSize * 1024 *
751 17 : static_cast<vsi_l_offset>(m_n1024BlocksPresent);
752 17 : if (m_n1024BlocksPresent != 0)
753 : {
754 : GByte abyTrailer[12];
755 :
756 17 : VSIFSeekL(m_fpTableX, m_nOffsetTableXTrailer, SEEK_SET);
757 17 : returnErrorIf(VSIFReadL(abyTrailer, 12, 1, m_fpTableX) != 1);
758 :
759 17 : m_nTotalRecordCount = GetUInt64(abyTrailer, 0);
760 :
761 : // Cf https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec#trailing-section-16-bytes--variable-number-
762 : // for all below magic numbers and byte sequences
763 17 : GUInt32 nSizeBitmapSection = GetUInt32(abyTrailer + 8, 0);
764 17 : if (nSizeBitmapSection == 0)
765 : {
766 : // no bitmap. Fine
767 : }
768 14 : else if (nSizeBitmapSection == 22 + 32768 + 52 &&
769 12 : m_nTotalRecordCount <= 32768 * 1024 * 8)
770 : {
771 : try
772 : {
773 10 : std::vector<GByte> abyBitmapSection(nSizeBitmapSection);
774 10 : returnErrorIf(VSIFReadL(abyBitmapSection.data(),
775 : abyBitmapSection.size(), 1,
776 : m_fpTableX) != 1);
777 10 : if (memcmp(abyBitmapSection.data(), "\x01\x00\x01\x00\x00\x00",
778 20 : 6) == 0 &&
779 10 : memcmp(abyBitmapSection.data() + 22 + 32768,
780 : "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
781 : 12) == 0)
782 : {
783 : m_abyTablXBlockMap.insert(
784 0 : m_abyTablXBlockMap.end(), abyBitmapSection.data() + 22,
785 6 : abyBitmapSection.data() + 22 + 32768);
786 : }
787 : else
788 : {
789 4 : m_bReliableObjectID = false;
790 : }
791 : }
792 0 : catch (const std::exception &e)
793 : {
794 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
795 0 : "Cannot allocate m_abyTablXBlockMap: %s", e.what());
796 0 : return false;
797 10 : }
798 : }
799 : else
800 : {
801 4 : m_bReliableObjectID = false;
802 : }
803 17 : if (!m_bReliableObjectID)
804 : {
805 8 : m_nTotalRecordCount = 1024 * m_n1024BlocksPresent;
806 8 : CPLError(CE_Warning, CPLE_AppDefined,
807 : "Due to partial reverse engineering of the format, "
808 : "ObjectIDs will not be accurate and attribute and spatial "
809 : "indices cannot be used on %s",
810 : m_osFilenameWithLayerName.c_str());
811 : }
812 : }
813 17 : return true;
814 : }
815 :
816 : /************************************************************************/
817 : /* Open() */
818 : /************************************************************************/
819 :
820 5356 : bool FileGDBTable::Open(const char *pszFilename, bool bUpdate,
821 : const char *pszLayerName)
822 : {
823 5356 : const bool errorRetValue = false;
824 5356 : CPLAssert(m_fpTable == nullptr);
825 :
826 5356 : m_bUpdate = bUpdate;
827 :
828 5356 : m_osFilename = pszFilename;
829 5356 : m_osFilenameWithLayerName = m_osFilename;
830 5356 : if (pszLayerName)
831 856 : m_osFilenameWithLayerName += CPLSPrintf(" (layer %s)", pszLayerName);
832 :
833 5356 : m_fpTable = VSIFOpenL(pszFilename, m_bUpdate ? "r+b" : "rb");
834 5356 : if (m_fpTable == nullptr)
835 : {
836 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s: %s",
837 2 : m_osFilenameWithLayerName.c_str(), VSIStrerror(errno));
838 2 : return false;
839 : }
840 :
841 : // Read .gdbtable file header
842 : GByte abyHeader[40];
843 5354 : returnErrorIf(VSIFReadL(abyHeader, 40, 1, m_fpTable) != 1);
844 :
845 5354 : int nGDBTableVersion = GetInt32(abyHeader, 0);
846 5354 : if (nGDBTableVersion == 3)
847 : {
848 5336 : m_eGDBTableVersion = GDBTableVersion::V3;
849 : }
850 18 : else if (nGDBTableVersion == 4)
851 : {
852 18 : m_eGDBTableVersion = GDBTableVersion::V4;
853 18 : if (m_bUpdate)
854 : {
855 1 : CPLError(CE_Failure, CPLE_NotSupported,
856 : "Version 4 of the FileGeodatabase format is not supported "
857 : "for update.");
858 1 : return false;
859 : }
860 : }
861 : else
862 : {
863 0 : CPLError(CE_Failure, CPLE_NotSupported,
864 : "Version %u of the FileGeodatabase format is not supported.",
865 : nGDBTableVersion);
866 0 : return false;
867 : }
868 :
869 5353 : if (m_eGDBTableVersion == GDBTableVersion::V3)
870 : {
871 5336 : m_nValidRecordCount = GetInt32(abyHeader + 4, 0);
872 5336 : returnErrorIf(m_nValidRecordCount < 0);
873 : }
874 : else
875 : {
876 17 : m_nValidRecordCount = GetInt64(abyHeader + 16, 0);
877 17 : returnErrorIf(m_nValidRecordCount < 0);
878 : }
879 :
880 5352 : m_nHeaderBufferMaxSize = GetInt32(abyHeader + 8, 0);
881 :
882 10704 : std::string osTableXName;
883 8823 : if (m_bUpdate || (m_nValidRecordCount > 0 &&
884 3471 : !CPLTestBool(CPLGetConfigOption(
885 : "OPENFILEGDB_IGNORE_GDBTABLX", "false"))))
886 : {
887 15711 : osTableXName = CPLFormFilenameSafe(
888 10474 : CPLGetPathSafe(pszFilename).c_str(),
889 15711 : CPLGetBasenameSafe(pszFilename).c_str(), "gdbtablx");
890 5237 : m_fpTableX = VSIFOpenL(osTableXName.c_str(), m_bUpdate ? "r+b" : "rb");
891 5237 : if (m_fpTableX == nullptr)
892 : {
893 0 : if (m_bUpdate)
894 : {
895 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s: %s",
896 0 : osTableXName.c_str(), VSIStrerror(errno));
897 0 : return false;
898 : }
899 0 : const char *pszIgnoreGDBTablXAbsence = CPLGetConfigOption(
900 : "OPENFILEGDB_IGNORE_GDBTABLX_ABSENCE", nullptr);
901 0 : if (pszIgnoreGDBTablXAbsence == nullptr)
902 : {
903 0 : CPLError(
904 : CE_Warning, CPLE_AppDefined,
905 : "%s could not be found. "
906 : "Trying to guess feature locations, but this might fail or "
907 : "return incorrect results",
908 : osTableXName.c_str());
909 : }
910 0 : else if (!CPLTestBool(pszIgnoreGDBTablXAbsence))
911 : {
912 0 : returnErrorIf(m_fpTableX == nullptr);
913 : }
914 : }
915 10457 : else if (m_eGDBTableVersion == GDBTableVersion::V3 &&
916 5220 : !ReadTableXHeaderV3())
917 8 : return false;
918 5246 : else if (m_eGDBTableVersion == GDBTableVersion::V4 &&
919 17 : !ReadTableXHeaderV4())
920 0 : return false;
921 : }
922 :
923 5344 : if (m_fpTableX != nullptr)
924 : {
925 5229 : if (m_nValidRecordCount > m_nTotalRecordCount)
926 : {
927 6 : if (CPLTestBool(CPLGetConfigOption(
928 : "OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT", "false")))
929 : {
930 : /* Potentially unsafe. See #5842 */
931 0 : CPLDebug("OpenFileGDB",
932 : "%s: nTotalRecordCount (was %" PRId64 ") forced to "
933 : "nValidRecordCount=%" PRId64,
934 : m_osFilenameWithLayerName.c_str(), m_nTotalRecordCount,
935 : m_nValidRecordCount);
936 0 : m_nTotalRecordCount = m_nValidRecordCount;
937 : }
938 : else
939 : {
940 : /* By default err on the safe side */
941 6 : CPLError(
942 : CE_Warning, CPLE_AppDefined,
943 : "File %s declares %" PRId64
944 : " valid records, but %s declares "
945 : "only %" PRId64
946 : " total records. Using that later value for safety "
947 : "(this possibly ignoring features). "
948 : "You can also try setting OPENFILEGDB_IGNORE_GDBTABLX=YES "
949 : "to "
950 : "completely ignore the .gdbtablx file (but possibly "
951 : "retrieving "
952 : "deleted features), or set "
953 : "OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT=YES "
954 : "(but that setting can potentially cause crashes)",
955 : m_osFilenameWithLayerName.c_str(), m_nValidRecordCount,
956 : osTableXName.c_str(), m_nTotalRecordCount);
957 6 : m_nValidRecordCount = m_nTotalRecordCount;
958 : }
959 : }
960 :
961 : #ifdef DEBUG_VERBOSE
962 : else if (m_nTotalRecordCount != m_nValidRecordCount)
963 : {
964 : CPLDebug("OpenFileGDB",
965 : "%s: nTotalRecordCount=%" PRId64
966 : " nValidRecordCount=%" PRId64,
967 : pszFilename, m_nTotalRecordCount, m_nValidRecordCount);
968 : }
969 : #endif
970 : }
971 :
972 5344 : m_nOffsetFieldDesc = GetUInt64(abyHeader + 32, 0);
973 :
974 : #ifdef DEBUG_VERBOSE
975 : if (m_nOffsetFieldDesc != 40)
976 : {
977 : CPLDebug("OpenFileGDB", "%s: nOffsetFieldDesc=" CPL_FRMT_GUIB,
978 : pszFilename, m_nOffsetFieldDesc);
979 : }
980 : #endif
981 :
982 5344 : if (m_bUpdate)
983 : {
984 1804 : VSIFSeekL(m_fpTable, 0, SEEK_END);
985 1804 : m_nFileSize = VSIFTellL(m_fpTable);
986 : }
987 :
988 : // Skip to field description section
989 5344 : VSIFSeekL(m_fpTable, m_nOffsetFieldDesc, SEEK_SET);
990 5344 : returnErrorIf(VSIFReadL(abyHeader, 14, 1, m_fpTable) != 1);
991 5343 : m_nFieldDescLength = GetUInt32(abyHeader, 0);
992 :
993 5343 : const auto nSecondaryHeaderVersion = GetUInt32(abyHeader + 4, 0);
994 : // nSecondaryHeaderVersion == 6 is used in table arcgis_pro_32_types.gdb/a0000000b.gdbtable (big_int)
995 : // Not sure why...
996 5343 : if (m_bUpdate && nSecondaryHeaderVersion != 4 &&
997 : nSecondaryHeaderVersion != 6) // FileGDB v10
998 : {
999 0 : CPLError(CE_Failure, CPLE_NotSupported,
1000 : "Version %u of the secondary header of the FileGeodatabase "
1001 : "format is not supported for update.",
1002 : nSecondaryHeaderVersion);
1003 0 : return false;
1004 : }
1005 5343 : m_bIsV9 = (nSecondaryHeaderVersion == 3);
1006 :
1007 5343 : returnErrorIf(m_nOffsetFieldDesc >
1008 : std::numeric_limits<GUIntBig>::max() - m_nFieldDescLength);
1009 :
1010 5343 : returnErrorIf(m_nFieldDescLength > 10 * 1024 * 1024 ||
1011 : m_nFieldDescLength < 10);
1012 5341 : GByte byTableGeomType = abyHeader[8];
1013 5341 : if (IS_VALID_LAYER_GEOM_TYPE(byTableGeomType))
1014 5341 : m_eTableGeomType =
1015 5341 : static_cast<FileGDBTableGeometryType>(byTableGeomType);
1016 : else
1017 0 : CPLDebug("OpenFileGDB", "Unknown table geometry type: %d",
1018 : byTableGeomType);
1019 5341 : m_bStringsAreUTF8 = (abyHeader[9] & 0x1) != 0;
1020 5341 : const GByte byTableGeomTypeFlags = abyHeader[11];
1021 5341 : m_bGeomTypeHasM = (byTableGeomTypeFlags & (1 << 6)) != 0;
1022 5341 : m_bGeomTypeHasZ = (byTableGeomTypeFlags & (1 << 7)) != 0;
1023 :
1024 : GUInt16 iField, nFields;
1025 5341 : nFields = GetUInt16(abyHeader + 12, 0);
1026 :
1027 : /* No interest in guessing a trivial file */
1028 5341 : returnErrorIf(m_fpTableX == nullptr && nFields == 0);
1029 :
1030 5341 : GUInt32 nRemaining = m_nFieldDescLength - 10;
1031 5341 : m_nRowBufferMaxSize = nRemaining;
1032 : try
1033 : {
1034 5341 : m_abyBuffer.resize(m_nRowBufferMaxSize + ZEROES_AFTER_END_OF_BUFFER);
1035 : }
1036 0 : catch (const std::exception &e)
1037 : {
1038 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1039 0 : returnError();
1040 : }
1041 5341 : returnErrorIf(VSIFReadL(m_abyBuffer.data(), nRemaining, 1, m_fpTable) != 1);
1042 :
1043 5340 : GByte *pabyIter = m_abyBuffer.data();
1044 63329 : for (iField = 0; iField < nFields; iField++)
1045 : {
1046 58013 : returnErrorIf(nRemaining < 1);
1047 58001 : GByte nCarCount = pabyIter[0];
1048 :
1049 58001 : pabyIter++;
1050 58001 : nRemaining--;
1051 58001 : returnErrorIf(nCarCount > nRemaining / 2);
1052 57998 : std::string osName(ReadUTF16String(pabyIter, nCarCount));
1053 57998 : pabyIter += 2 * nCarCount;
1054 57998 : nRemaining -= 2 * nCarCount;
1055 :
1056 57998 : returnErrorIf(nRemaining < 1);
1057 57998 : nCarCount = pabyIter[0];
1058 57998 : pabyIter++;
1059 57998 : nRemaining--;
1060 57998 : returnErrorIf(nCarCount > nRemaining / 2);
1061 57995 : std::string osAlias(ReadUTF16String(pabyIter, nCarCount));
1062 57995 : pabyIter += 2 * nCarCount;
1063 57995 : nRemaining -= 2 * nCarCount;
1064 :
1065 57995 : returnErrorIf(nRemaining < 1);
1066 57995 : GByte byFieldType = pabyIter[0];
1067 57995 : pabyIter++;
1068 57995 : nRemaining--;
1069 :
1070 57995 : if (byFieldType > FGFT_DATETIME_WITH_OFFSET)
1071 : {
1072 3 : CPLDebug("OpenFileGDB", "Unhandled field type : %d", byFieldType);
1073 3 : returnError();
1074 : }
1075 :
1076 57992 : FileGDBFieldType eType = static_cast<FileGDBFieldType>(byFieldType);
1077 57992 : if (eType != FGFT_GEOMETRY && eType != FGFT_RASTER)
1078 : {
1079 55296 : GByte flags = 0;
1080 55296 : int nMaxWidth = 0;
1081 55296 : GUInt32 defaultValueLength = 0;
1082 :
1083 55296 : switch (eType)
1084 : {
1085 15644 : case FGFT_STRING:
1086 : {
1087 15647 : returnErrorIf(nRemaining < 6);
1088 15644 : nMaxWidth = GetInt32(pabyIter, 0);
1089 15644 : returnErrorIf(nMaxWidth < 0);
1090 15643 : flags = pabyIter[4];
1091 15643 : pabyIter += 5;
1092 15643 : nRemaining -= 5;
1093 15643 : GByte *pabyIterBefore = pabyIter;
1094 15643 : returnErrorIf(!ReadVarUInt32(
1095 : pabyIter, pabyIter + nRemaining, defaultValueLength));
1096 15643 : nRemaining -=
1097 15643 : static_cast<GUInt32>(pabyIter - pabyIterBefore);
1098 15643 : break;
1099 : }
1100 :
1101 21553 : case FGFT_OBJECTID:
1102 : case FGFT_BINARY:
1103 : case FGFT_GUID:
1104 : case FGFT_GLOBALID:
1105 : case FGFT_XML:
1106 21553 : returnErrorIf(nRemaining < 2);
1107 21553 : flags = pabyIter[1];
1108 21553 : pabyIter += 2;
1109 21553 : nRemaining -= 2;
1110 21553 : break;
1111 :
1112 18099 : default:
1113 18099 : returnErrorIf(nRemaining < 3);
1114 18099 : flags = pabyIter[1];
1115 18099 : defaultValueLength = pabyIter[2];
1116 18099 : pabyIter += 3;
1117 18099 : nRemaining -= 3;
1118 18099 : break;
1119 : }
1120 :
1121 : OGRField sDefault;
1122 55295 : OGR_RawField_SetUnset(&sDefault);
1123 : // Default value
1124 : // Found on PreNIS.gdb/a0000000d.gdbtable
1125 55295 : if (defaultValueLength > 0)
1126 : {
1127 96 : returnErrorIf(nRemaining < defaultValueLength);
1128 94 : if (eType == FGFT_STRING)
1129 : {
1130 14 : if (m_bStringsAreUTF8)
1131 : {
1132 12 : sDefault.String = static_cast<char *>(
1133 12 : CPLMalloc(defaultValueLength + 1));
1134 12 : memcpy(sDefault.String, pabyIter, defaultValueLength);
1135 12 : sDefault.String[defaultValueLength] = 0;
1136 : }
1137 : else
1138 : {
1139 : m_osTempString =
1140 2 : ReadUTF16String(pabyIter, defaultValueLength / 2);
1141 2 : sDefault.String = CPLStrdup(m_osTempString.c_str());
1142 : }
1143 : }
1144 80 : else if (eType == FGFT_INT16 && defaultValueLength == 2)
1145 : {
1146 9 : sDefault.Integer = GetInt16(pabyIter, 0);
1147 9 : sDefault.Set.nMarker2 = 0;
1148 9 : sDefault.Set.nMarker3 = 0;
1149 : }
1150 71 : else if (eType == FGFT_INT32 && defaultValueLength == 4)
1151 : {
1152 18 : sDefault.Integer = GetInt32(pabyIter, 0);
1153 18 : sDefault.Set.nMarker2 = 0;
1154 18 : sDefault.Set.nMarker3 = 0;
1155 : }
1156 53 : else if (eType == FGFT_FLOAT32 && defaultValueLength == 4)
1157 : {
1158 7 : sDefault.Real = GetFloat32(pabyIter, 0);
1159 : }
1160 46 : else if (eType == FGFT_FLOAT64 && defaultValueLength == 8)
1161 : {
1162 11 : sDefault.Real = GetFloat64(pabyIter, 0);
1163 : }
1164 35 : else if ((eType == FGFT_DATETIME || eType == FGFT_DATE) &&
1165 18 : defaultValueLength == 8)
1166 : {
1167 18 : const double dfVal = GetFloat64(pabyIter, 0);
1168 18 : FileGDBDoubleDateToOGRDate(dfVal, true, &sDefault);
1169 : }
1170 17 : else if (eType == FGFT_TIME && defaultValueLength == 8)
1171 : {
1172 6 : const double dfVal = GetFloat64(pabyIter, 0);
1173 6 : FileGDBDoubleTimeToOGRTime(dfVal, &sDefault);
1174 : }
1175 11 : else if (eType == FGFT_INT64 && defaultValueLength == 8)
1176 : {
1177 4 : sDefault.Integer64 = GetInt64(pabyIter, 0);
1178 4 : sDefault.Set.nMarker3 = 0;
1179 : }
1180 7 : else if (eType == FGFT_DATETIME_WITH_OFFSET &&
1181 7 : defaultValueLength == sizeof(double) + sizeof(int16_t))
1182 : {
1183 7 : const double dfVal = GetFloat64(pabyIter, 0);
1184 : const int16_t nUTCOffset =
1185 7 : GetInt16(pabyIter + sizeof(double), 0);
1186 7 : FileGDBDateTimeWithOffsetToOGRDate(dfVal, nUTCOffset,
1187 : &sDefault);
1188 : }
1189 :
1190 94 : pabyIter += defaultValueLength;
1191 94 : nRemaining -= defaultValueLength;
1192 : }
1193 :
1194 55293 : if (eType == FGFT_OBJECTID)
1195 : {
1196 5315 : returnErrorIf(flags != FileGDBField::MASK_REQUIRED);
1197 5315 : returnErrorIf(m_iObjectIdField >= 0);
1198 5315 : m_iObjectIdField = static_cast<int>(m_apoFields.size());
1199 : }
1200 :
1201 110586 : auto poField = std::make_unique<FileGDBField>(this);
1202 55293 : poField->m_osName = std::move(osName);
1203 55293 : poField->m_osAlias = std::move(osAlias);
1204 55293 : poField->m_eType = eType;
1205 55293 : poField->m_bNullable = (flags & FileGDBField::MASK_NULLABLE) != 0;
1206 55293 : poField->m_bRequired = (flags & FileGDBField::MASK_REQUIRED) != 0;
1207 55293 : poField->m_bEditable = (flags & FileGDBField::MASK_EDITABLE) != 0;
1208 55293 : poField->m_nMaxWidth = nMaxWidth;
1209 55293 : poField->m_sDefault = sDefault;
1210 110586 : m_apoFields.emplace_back(std::move(poField));
1211 : }
1212 : else
1213 : {
1214 :
1215 2696 : FileGDBRasterField *poRasterField = nullptr;
1216 : FileGDBGeomField *poField;
1217 2696 : if (eType == FGFT_GEOMETRY)
1218 : {
1219 2693 : returnErrorIf(m_iGeomField >= 0);
1220 2693 : poField = new FileGDBGeomField(this);
1221 : }
1222 : else
1223 : {
1224 3 : poRasterField = new FileGDBRasterField(this);
1225 3 : poField = poRasterField;
1226 : }
1227 :
1228 2696 : poField->m_osName = std::move(osName);
1229 2696 : poField->m_osAlias = std::move(osAlias);
1230 2696 : poField->m_eType = eType;
1231 2696 : if (eType == FGFT_GEOMETRY)
1232 2693 : m_iGeomField = static_cast<int>(m_apoFields.size());
1233 : m_apoFields.emplace_back(
1234 2696 : std::unique_ptr<FileGDBGeomField>(poField));
1235 :
1236 2696 : returnErrorIf(nRemaining < 2);
1237 2696 : GByte flags = pabyIter[1];
1238 2696 : poField->m_bNullable = (flags & 1) != 0;
1239 2696 : pabyIter += 2;
1240 2696 : nRemaining -= 2;
1241 :
1242 2696 : if (eType == FGFT_RASTER)
1243 : {
1244 3 : returnErrorIf(nRemaining < 1);
1245 3 : nCarCount = pabyIter[0];
1246 3 : pabyIter++;
1247 3 : nRemaining--;
1248 3 : returnErrorIf(nRemaining <
1249 : static_cast<GUInt32>(2 * nCarCount + 1));
1250 : poRasterField->m_osRasterColumnName =
1251 3 : ReadUTF16String(pabyIter, nCarCount);
1252 3 : pabyIter += 2 * nCarCount;
1253 3 : nRemaining -= 2 * nCarCount;
1254 : }
1255 :
1256 2696 : returnErrorIf(nRemaining < 2);
1257 2696 : GUInt16 nLengthWKT = GetUInt16(pabyIter, 0);
1258 2696 : pabyIter += sizeof(nLengthWKT);
1259 2696 : nRemaining -= sizeof(nLengthWKT);
1260 :
1261 2696 : returnErrorIf(nRemaining < static_cast<GUInt32>(1 + nLengthWKT));
1262 2696 : poField->m_osWKT = ReadUTF16String(pabyIter, nLengthWKT / 2);
1263 2696 : pabyIter += nLengthWKT;
1264 2696 : nRemaining -= nLengthWKT;
1265 :
1266 2696 : GByte abyGeomFlags = pabyIter[0];
1267 2696 : pabyIter++;
1268 2696 : nRemaining--;
1269 2696 : poField->m_bHasMOriginScaleTolerance = (abyGeomFlags & 2) != 0;
1270 2696 : poField->m_bHasZOriginScaleTolerance = (abyGeomFlags & 4) != 0;
1271 :
1272 2696 : if (eType == FGFT_GEOMETRY || abyGeomFlags > 0)
1273 : {
1274 2695 : returnErrorIf(nRemaining <
1275 : static_cast<GUInt32>(
1276 : sizeof(double) *
1277 : (4 + ((eType == FGFT_GEOMETRY) ? 4 : 0) +
1278 : (poField->m_bHasMOriginScaleTolerance +
1279 : poField->m_bHasZOriginScaleTolerance) *
1280 : 3)));
1281 :
1282 : #define READ_DOUBLE(field) \
1283 : do \
1284 : { \
1285 : field = GetFloat64(pabyIter, 0); \
1286 : pabyIter += sizeof(double); \
1287 : nRemaining -= sizeof(double); \
1288 : } while (false)
1289 :
1290 2695 : READ_DOUBLE(poField->m_dfXOrigin);
1291 2695 : READ_DOUBLE(poField->m_dfYOrigin);
1292 2695 : READ_DOUBLE(poField->m_dfXYScale);
1293 2695 : returnErrorIf(poField->m_dfXYScale == 0);
1294 :
1295 2695 : if (poField->m_bHasMOriginScaleTolerance)
1296 : {
1297 2685 : READ_DOUBLE(poField->m_dfMOrigin);
1298 2685 : READ_DOUBLE(poField->m_dfMScale);
1299 : }
1300 :
1301 2695 : if (poField->m_bHasZOriginScaleTolerance)
1302 : {
1303 2692 : READ_DOUBLE(poField->m_dfZOrigin);
1304 2692 : READ_DOUBLE(poField->m_dfZScale);
1305 : }
1306 :
1307 2695 : READ_DOUBLE(poField->m_dfXYTolerance);
1308 :
1309 2695 : if (poField->m_bHasMOriginScaleTolerance)
1310 : {
1311 2685 : READ_DOUBLE(poField->m_dfMTolerance);
1312 : #ifdef DEBUG_VERBOSE
1313 : CPLDebug("OpenFileGDB",
1314 : "MOrigin = %g, MScale = %g, MTolerance = %g",
1315 : poField->m_dfMOrigin, poField->m_dfMScale,
1316 : poField->m_dfMTolerance);
1317 : #endif
1318 : }
1319 :
1320 2695 : if (poField->m_bHasZOriginScaleTolerance)
1321 : {
1322 2692 : READ_DOUBLE(poField->m_dfZTolerance);
1323 : }
1324 : }
1325 :
1326 2696 : if (eType == FGFT_RASTER)
1327 : {
1328 3 : returnErrorIf(nRemaining < 1);
1329 3 : if (*pabyIter == 0)
1330 0 : poRasterField->m_eRasterType =
1331 : FileGDBRasterField::Type::EXTERNAL;
1332 3 : else if (*pabyIter == 1)
1333 3 : poRasterField->m_eRasterType =
1334 : FileGDBRasterField::Type::MANAGED;
1335 0 : else if (*pabyIter == 2)
1336 0 : poRasterField->m_eRasterType =
1337 : FileGDBRasterField::Type::INLINE;
1338 : else
1339 : {
1340 0 : CPLError(CE_Warning, CPLE_NotSupported,
1341 0 : "Unknown raster field type %d", *pabyIter);
1342 : }
1343 3 : pabyIter += 1;
1344 3 : nRemaining -= 1;
1345 : }
1346 : else
1347 : {
1348 2693 : returnErrorIf(nRemaining < 4 * sizeof(double));
1349 2693 : m_nGeomFieldBBoxSubOffset =
1350 2693 : static_cast<uint32_t>(pabyIter - m_abyBuffer.data()) + 14;
1351 2693 : READ_DOUBLE(poField->m_dfXMin);
1352 2693 : READ_DOUBLE(poField->m_dfYMin);
1353 2693 : READ_DOUBLE(poField->m_dfXMax);
1354 2693 : READ_DOUBLE(poField->m_dfYMax);
1355 :
1356 : #ifdef PARANOID_CHECK
1357 : const auto nCurrentPos = VSIFTellL(m_fpTable);
1358 : VSIFSeekL(m_fpTable,
1359 : m_nOffsetFieldDesc + m_nGeomFieldBBoxSubOffset,
1360 : SEEK_SET);
1361 : double dfXMinFromFile;
1362 : VSIFReadL(&dfXMinFromFile, 1, sizeof(dfXMinFromFile),
1363 : m_fpTable);
1364 : fprintf(stderr, "%f %f\n", dfXMinFromFile, /*ok*/
1365 : poField->m_dfXMin);
1366 : double dfYMinFromFile;
1367 : VSIFReadL(&dfYMinFromFile, 1, sizeof(dfYMinFromFile),
1368 : m_fpTable);
1369 : fprintf(stderr, "%f %f\n", dfYMinFromFile, /*ok*/
1370 : poField->m_dfYMin);
1371 : double dfXMaxFromFile;
1372 : VSIFReadL(&dfXMaxFromFile, 1, sizeof(dfXMaxFromFile),
1373 : m_fpTable);
1374 : fprintf(stderr, "%f %f\n", dfXMaxFromFile, /*ok*/
1375 : poField->m_dfXMax);
1376 : double dfYMaxFromFile;
1377 : VSIFReadL(&dfYMaxFromFile, 1, sizeof(dfYMaxFromFile),
1378 : m_fpTable);
1379 : fprintf(stderr, "%f %f\n", dfYMaxFromFile, /*ok*/
1380 : poField->m_dfYMax);
1381 : VSIFSeekL(m_fpTable, nCurrentPos, SEEK_SET);
1382 : #endif
1383 2693 : if (m_bGeomTypeHasZ)
1384 : {
1385 204 : returnErrorIf(nRemaining < 2 * sizeof(double));
1386 204 : READ_DOUBLE(poField->m_dfZMin);
1387 204 : READ_DOUBLE(poField->m_dfZMax);
1388 : }
1389 :
1390 2693 : if (m_bGeomTypeHasM)
1391 : {
1392 85 : returnErrorIf(nRemaining < 2 * sizeof(double));
1393 85 : READ_DOUBLE(poField->m_dfMMin);
1394 85 : READ_DOUBLE(poField->m_dfMMax);
1395 : }
1396 :
1397 2693 : returnErrorIf(nRemaining < 5);
1398 : // Skip byte at zero
1399 2693 : pabyIter += 1;
1400 2693 : nRemaining -= 1;
1401 :
1402 2693 : GUInt32 nGridSizeCount = GetUInt32(pabyIter, 0);
1403 2693 : pabyIter += sizeof(nGridSizeCount);
1404 2693 : nRemaining -= sizeof(nGridSizeCount);
1405 2693 : returnErrorIf(nGridSizeCount == 0 || nGridSizeCount > 3);
1406 2693 : returnErrorIf(nRemaining < nGridSizeCount * sizeof(double));
1407 2693 : m_nGeomFieldSpatialIndexGridResSubOffset =
1408 2693 : static_cast<uint32_t>(pabyIter - m_abyBuffer.data()) + 14;
1409 9710 : for (GUInt32 i = 0; i < nGridSizeCount; i++)
1410 : {
1411 : double dfGridResolution;
1412 7017 : READ_DOUBLE(dfGridResolution);
1413 7017 : m_adfSpatialIndexGridResolution.push_back(dfGridResolution);
1414 : }
1415 : poField->m_adfSpatialIndexGridResolution =
1416 2693 : m_adfSpatialIndexGridResolution;
1417 : }
1418 : }
1419 :
1420 57989 : m_nCountNullableFields +=
1421 57989 : static_cast<int>(m_apoFields.back()->m_bNullable);
1422 : }
1423 5328 : m_nNullableFieldsSizeInBytes =
1424 5328 : BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
1425 :
1426 : #ifdef DEBUG_VERBOSE
1427 : if (nRemaining > 0)
1428 : {
1429 : CPLDebug("OpenFileGDB",
1430 : "%u remaining (ignored) bytes in field header section",
1431 : nRemaining);
1432 : }
1433 : #endif
1434 :
1435 5328 : if (m_nValidRecordCount > 0 && m_fpTableX == nullptr)
1436 38 : return GuessFeatureLocations();
1437 :
1438 5290 : return true;
1439 : }
1440 :
1441 : /************************************************************************/
1442 : /* SkipVarUInt() */
1443 : /************************************************************************/
1444 :
1445 : /* Bound check only valid if nIter <= 4 */
1446 7836 : static int SkipVarUInt(GByte *&pabyIter, GByte *pabyEnd, int nIter = 1)
1447 : {
1448 7836 : const int errorRetValue = FALSE;
1449 7836 : GByte *pabyLocalIter = pabyIter;
1450 7836 : returnErrorIf(pabyLocalIter /*+ nIter - 1*/ >= pabyEnd);
1451 35242 : while (nIter-- > 0)
1452 : {
1453 : while (true)
1454 : {
1455 142071 : GByte b = *pabyLocalIter;
1456 142071 : pabyLocalIter++;
1457 142071 : if ((b & 0x80) == 0)
1458 27406 : break;
1459 114665 : }
1460 : }
1461 7836 : pabyIter = pabyLocalIter;
1462 7836 : return TRUE;
1463 : }
1464 :
1465 : /************************************************************************/
1466 : /* ReadVarIntAndAddNoCheck() */
1467 : /************************************************************************/
1468 :
1469 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
1470 115694 : static void ReadVarIntAndAddNoCheck(GByte *&pabyIter, GIntBig &nOutVal)
1471 : {
1472 : GUInt32 b;
1473 :
1474 115694 : b = *pabyIter;
1475 115694 : GUIntBig nVal = (b & 0x3F);
1476 115694 : bool bNegative = (b & 0x40) != 0;
1477 115694 : if ((b & 0x80) == 0)
1478 : {
1479 38317 : pabyIter++;
1480 38317 : if (bNegative)
1481 10 : nOutVal -= nVal;
1482 : else
1483 38307 : nOutVal += nVal;
1484 38317 : return;
1485 : }
1486 :
1487 77377 : GByte *pabyLocalIter = pabyIter + 1;
1488 77377 : int nShift = 6;
1489 : while (true)
1490 : {
1491 275873 : GUIntBig b64 = *pabyLocalIter;
1492 275873 : pabyLocalIter++;
1493 275873 : nVal |= (b64 & 0x7F) << nShift;
1494 275873 : if ((b64 & 0x80) == 0)
1495 : {
1496 77377 : pabyIter = pabyLocalIter;
1497 77377 : if (bNegative)
1498 26035 : nOutVal -= nVal;
1499 : else
1500 51342 : nOutVal += nVal;
1501 77377 : return;
1502 : }
1503 198496 : nShift += 7;
1504 : // To avoid undefined behavior later when doing << nShift
1505 198496 : if (nShift >= static_cast<int>(sizeof(GIntBig)) * 8)
1506 : {
1507 0 : pabyIter = pabyLocalIter;
1508 0 : nOutVal = nVal;
1509 0 : return;
1510 : }
1511 198496 : }
1512 : }
1513 :
1514 : /************************************************************************/
1515 : /* GetOffsetInTableForRow() */
1516 : /************************************************************************/
1517 :
1518 : vsi_l_offset
1519 1877560 : FileGDBTable::GetOffsetInTableForRow(int64_t iRow,
1520 : vsi_l_offset *pnOffsetInTableX)
1521 : {
1522 1877560 : const int errorRetValue = 0;
1523 1877560 : if (pnOffsetInTableX)
1524 2707 : *pnOffsetInTableX = 0;
1525 1877560 : returnErrorIf(iRow < 0 || iRow >= m_nTotalRecordCount);
1526 :
1527 1877560 : m_bIsDeleted = false;
1528 1877560 : if (m_fpTableX == nullptr)
1529 : {
1530 147 : m_bIsDeleted =
1531 147 : IS_DELETED(m_anFeatureOffsets[static_cast<size_t>(iRow)]);
1532 147 : return GET_OFFSET(m_anFeatureOffsets[static_cast<size_t>(iRow)]);
1533 : }
1534 :
1535 : vsi_l_offset nOffsetInTableX;
1536 1877410 : if (!m_abyTablXBlockMap.empty())
1537 : {
1538 1564230 : GUInt32 nCountBlocksBefore = 0;
1539 1564230 : const int iBlock = static_cast<int>(iRow / 1024);
1540 :
1541 : // Check if the block is not empty
1542 1564230 : if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0)
1543 1479690 : return 0;
1544 :
1545 : // In case of sequential reading, optimization to avoid recomputing
1546 : // the number of blocks since the beginning of the map
1547 84538 : if (iBlock >= m_nCountBlocksBeforeIBlockIdx)
1548 : {
1549 84531 : nCountBlocksBefore = m_nCountBlocksBeforeIBlockValue;
1550 4291420 : for (int i = m_nCountBlocksBeforeIBlockIdx; i < iBlock; i++)
1551 4206890 : nCountBlocksBefore +=
1552 4206890 : TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
1553 : }
1554 : else
1555 : {
1556 7 : nCountBlocksBefore = 0;
1557 28 : for (int i = 0; i < iBlock; i++)
1558 21 : nCountBlocksBefore +=
1559 21 : TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
1560 : }
1561 84538 : m_nCountBlocksBeforeIBlockIdx = iBlock;
1562 84538 : m_nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
1563 84538 : const int64_t iCorrectedRow =
1564 84538 : static_cast<int64_t>(nCountBlocksBefore) * 1024 + (iRow % 1024);
1565 84538 : nOffsetInTableX =
1566 84538 : 16 + static_cast<vsi_l_offset>(m_nTablxOffsetSize) * iCorrectedRow;
1567 : }
1568 : else
1569 : {
1570 313182 : nOffsetInTableX =
1571 313182 : 16 + static_cast<vsi_l_offset>(m_nTablxOffsetSize) * iRow;
1572 : }
1573 :
1574 397720 : if (pnOffsetInTableX)
1575 2707 : *pnOffsetInTableX = nOffsetInTableX;
1576 397720 : VSIFSeekL(m_fpTableX, nOffsetInTableX, SEEK_SET);
1577 :
1578 : GByte abyBuffer[6];
1579 397720 : m_bError = VSIFReadL(abyBuffer, m_nTablxOffsetSize, 1, m_fpTableX) != 1;
1580 397720 : returnErrorIf(m_bError);
1581 :
1582 397720 : const vsi_l_offset nOffset = ReadFeatureOffset(abyBuffer);
1583 :
1584 : #ifdef DEBUG_VERBOSE
1585 : const auto nOffsetHeaderEnd = m_nOffsetFieldDesc + m_nFieldDescLength;
1586 : if (iRow == 0 && nOffset != 0 && nOffset != nOffsetHeaderEnd &&
1587 : nOffset != nOffsetHeaderEnd + 4)
1588 : CPLDebug("OpenFileGDB",
1589 : "%s: first feature offset = " CPL_FRMT_GUIB
1590 : ". Expected " CPL_FRMT_GUIB,
1591 : m_osFilename.c_str(), nOffset, nOffsetHeaderEnd);
1592 : #endif
1593 :
1594 397720 : return nOffset;
1595 : }
1596 :
1597 : /************************************************************************/
1598 : /* ReadFeatureOffset() */
1599 : /************************************************************************/
1600 :
1601 447997 : uint64_t FileGDBTable::ReadFeatureOffset(const GByte *pabyBuffer)
1602 : {
1603 447997 : uint64_t nOffset = 0;
1604 447997 : memcpy(&nOffset, pabyBuffer, m_nTablxOffsetSize);
1605 447997 : CPL_LSBPTR64(&nOffset);
1606 447997 : return nOffset;
1607 : }
1608 :
1609 : /************************************************************************/
1610 : /* GetAndSelectNextNonEmptyRow() */
1611 : /************************************************************************/
1612 :
1613 34941 : int64_t FileGDBTable::GetAndSelectNextNonEmptyRow(int64_t iRow)
1614 : {
1615 34941 : const int64_t errorRetValue = -1;
1616 34941 : returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount,
1617 : m_nCurRow = -1);
1618 :
1619 330778 : while (iRow < m_nTotalRecordCount)
1620 : {
1621 330699 : if (!m_abyTablXBlockMap.empty() && (iRow % 1024) == 0)
1622 : {
1623 138 : int iBlock = static_cast<int>(iRow / 1024);
1624 138 : if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0)
1625 : {
1626 111 : int nBlocks =
1627 111 : static_cast<int>(DIV_ROUND_UP(m_nTotalRecordCount, 1024));
1628 4247210 : do
1629 : {
1630 4247320 : iBlock++;
1631 8494650 : } while (iBlock < nBlocks &&
1632 4247320 : TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0);
1633 :
1634 111 : iRow = static_cast<int64_t>(iBlock) * 1024;
1635 111 : if (iRow >= m_nTotalRecordCount)
1636 0 : return -1;
1637 : }
1638 : }
1639 :
1640 330699 : if (SelectRow(iRow))
1641 34862 : return iRow;
1642 295837 : if (HasGotError())
1643 0 : return -1;
1644 295837 : iRow++;
1645 : }
1646 :
1647 79 : return -1;
1648 : }
1649 :
1650 : /************************************************************************/
1651 : /* SelectRow() */
1652 : /************************************************************************/
1653 :
1654 1876180 : bool FileGDBTable::SelectRow(int64_t iRow, bool bWarnOnlyOnDeletedRows)
1655 : {
1656 1876180 : const int errorRetValue = FALSE;
1657 1876180 : returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount,
1658 : m_nCurRow = -1);
1659 :
1660 1876180 : if (m_nCurRow != iRow)
1661 : {
1662 1874750 : vsi_l_offset nOffsetTable = GetOffsetInTableForRow(iRow);
1663 1874750 : if (nOffsetTable == 0)
1664 : {
1665 1792690 : m_nCurRow = -1;
1666 1792690 : return FALSE;
1667 : }
1668 :
1669 82062 : VSIFSeekL(m_fpTable, nOffsetTable, SEEK_SET);
1670 : GByte abyBuffer[4];
1671 82062 : returnErrorAndCleanupIf(VSIFReadL(abyBuffer, 4, 1, m_fpTable) != 1,
1672 : m_nCurRow = -1);
1673 :
1674 82062 : m_nRowBlobLength = GetUInt32(abyBuffer, 0);
1675 82062 : if (m_bIsDeleted)
1676 : {
1677 0 : m_nRowBlobLength =
1678 0 : static_cast<GUInt32>(-static_cast<int>(m_nRowBlobLength));
1679 : }
1680 :
1681 82062 : if (m_nRowBlobLength > 0)
1682 : {
1683 : #ifdef DEBUG_VERBOSE
1684 : CPLDebug("OpenFileGDB",
1685 : "%s: iRow = %" PRId64 ", m_nRowBlobLength = %u",
1686 : m_osFilename.c_str(), iRow, m_nRowBlobLength);
1687 : #endif
1688 81906 : if ((m_nRowBlobLength >> 31) != 0)
1689 : {
1690 0 : CPLError(
1691 : bWarnOnlyOnDeletedRows ? CE_Warning : CE_Failure,
1692 : CPLE_AppDefined,
1693 : "Feature %" PRId64
1694 : " of %s appears to be deleted, but index is out of sync",
1695 : iRow + 1, m_osFilename.c_str());
1696 0 : m_nCurRow = -1;
1697 0 : return false;
1698 : }
1699 81906 : returnErrorAndCleanupIf(
1700 : m_nRowBlobLength <
1701 : static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
1702 : m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER,
1703 : m_nCurRow = -1);
1704 :
1705 81906 : if (m_nRowBlobLength > m_nHeaderBufferMaxSize)
1706 : {
1707 10 : if (CPLTestBool(CPLGetConfigOption(
1708 : "OGR_OPENFILEGDB_ERROR_ON_INCONSISTENT_BUFFER_MAX_SIZE",
1709 : "NO")))
1710 : {
1711 0 : CPLError(CE_Failure, CPLE_AppDefined,
1712 : "Invalid row length (%u) on feature %" PRId64
1713 : " compared "
1714 : "to the maximum size in the header (%u)",
1715 : m_nRowBlobLength, iRow + 1,
1716 : m_nHeaderBufferMaxSize);
1717 0 : m_nCurRow = -1;
1718 0 : return errorRetValue;
1719 : }
1720 : else
1721 : {
1722 : // Versions of the driver before commit
1723 : // fdf39012788b1110b3bf0ae6b8422a528f0ae8b6 didn't
1724 : // properly update the m_nHeaderBufferMaxSize field
1725 : // when updating an existing feature when the new version
1726 : // takes more space than the previous version.
1727 : // OpenFileGDB doesn't care but Esri software (FileGDB SDK
1728 : // or ArcMap/ArcGis) do, leading to issues such as
1729 : // https://github.com/qgis/QGIS/issues/57536
1730 :
1731 10 : CPLDebug("OpenFileGDB",
1732 : "Invalid row length (%u) on feature %" PRId64
1733 : " compared "
1734 : "to the maximum size in the header (%u)",
1735 : m_nRowBlobLength, iRow + 1,
1736 : m_nHeaderBufferMaxSize);
1737 :
1738 10 : if (m_bUpdate)
1739 : {
1740 3 : if (!m_bHasWarnedAboutHeaderRepair)
1741 : {
1742 1 : m_bHasWarnedAboutHeaderRepair = true;
1743 1 : CPLError(CE_Warning, CPLE_AppDefined,
1744 : "A corruption in the header of %s has "
1745 : "been detected. It is going to be "
1746 : "repaired to be properly read by other "
1747 : "software.",
1748 : m_osFilename.c_str());
1749 :
1750 1 : m_bDirtyHeader = true;
1751 :
1752 : // Invalidate existing indices, as the corrupted
1753 : // m_nHeaderBufferMaxSize value may have cause
1754 : // Esri software to generate corrupted indices.
1755 1 : m_bDirtyIndices = true;
1756 :
1757 : // Compute file size
1758 1 : VSIFSeekL(m_fpTable, 0, SEEK_END);
1759 1 : m_nFileSize = VSIFTellL(m_fpTable);
1760 1 : VSIFSeekL(m_fpTable, nOffsetTable + 4, SEEK_SET);
1761 : }
1762 : }
1763 : else
1764 : {
1765 7 : if (!m_bHasWarnedAboutHeaderRepair)
1766 : {
1767 5 : m_bHasWarnedAboutHeaderRepair = true;
1768 5 : CPLError(CE_Warning, CPLE_AppDefined,
1769 : "A corruption in the header of %s has "
1770 : "been detected. It would need to be "
1771 : "repaired to be properly read by other "
1772 : "software, either by using ogr2ogr to "
1773 : "generate a new dataset, or by opening "
1774 : "this dataset in update mode and reading "
1775 : "all its records.",
1776 : m_osFilename.c_str());
1777 : }
1778 : }
1779 :
1780 10 : m_nHeaderBufferMaxSize = m_nRowBlobLength;
1781 : }
1782 : }
1783 :
1784 81906 : if (m_nRowBlobLength > m_nRowBufferMaxSize)
1785 : {
1786 : /* For suspicious row blob length, check if we don't go beyond
1787 : * file size */
1788 1512 : if (m_nRowBlobLength > 100 * 1024 * 1024)
1789 : {
1790 1 : if (m_nFileSize == 0)
1791 : {
1792 1 : VSIFSeekL(m_fpTable, 0, SEEK_END);
1793 1 : m_nFileSize = VSIFTellL(m_fpTable);
1794 1 : VSIFSeekL(m_fpTable, nOffsetTable + 4, SEEK_SET);
1795 : }
1796 1 : if (nOffsetTable + 4 + m_nRowBlobLength > m_nFileSize)
1797 : {
1798 1 : CPLError(CE_Failure, CPLE_AppDefined,
1799 : "Invalid row length (%u) on feature %" PRId64,
1800 : m_nRowBlobLength, iRow + 1);
1801 1 : m_nCurRow = -1;
1802 1 : return errorRetValue;
1803 : }
1804 : }
1805 1511 : m_nRowBufferMaxSize = m_nRowBlobLength;
1806 : }
1807 :
1808 81905 : if (m_abyBuffer.size() <
1809 81905 : m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER)
1810 : {
1811 : try
1812 : {
1813 3891 : m_abyBuffer.resize(m_nRowBlobLength +
1814 : ZEROES_AFTER_END_OF_BUFFER);
1815 : }
1816 0 : catch (const std::exception &e)
1817 : {
1818 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1819 0 : returnErrorAndCleanupIf(true, m_nCurRow = -1);
1820 : }
1821 : }
1822 :
1823 81905 : returnErrorAndCleanupIf(VSIFReadL(m_abyBuffer.data(),
1824 : m_nRowBlobLength, 1,
1825 : m_fpTable) != 1,
1826 : m_nCurRow = -1);
1827 : /* Protection for 4 ReadVarUInt64NoCheck */
1828 81902 : CPL_STATIC_ASSERT(ZEROES_AFTER_END_OF_BUFFER == 4);
1829 81902 : m_abyBuffer[m_nRowBlobLength] = 0;
1830 81902 : m_abyBuffer[m_nRowBlobLength + 1] = 0;
1831 81902 : m_abyBuffer[m_nRowBlobLength + 2] = 0;
1832 81902 : m_abyBuffer[m_nRowBlobLength + 3] = 0;
1833 : }
1834 :
1835 82058 : m_nCurRow = iRow;
1836 82058 : m_nLastCol = -1;
1837 82058 : m_pabyIterVals = m_abyBuffer.data() + m_nNullableFieldsSizeInBytes;
1838 82058 : m_iAccNullable = 0;
1839 82058 : m_bError = FALSE;
1840 82058 : m_nChSaved = -1;
1841 : }
1842 :
1843 83483 : return TRUE;
1844 : }
1845 :
1846 : /************************************************************************/
1847 : /* FileGDBDoubleDateToOGRDate() */
1848 : /************************************************************************/
1849 :
1850 7747 : int FileGDBDoubleDateToOGRDate(double dfVal, bool bHighPrecision,
1851 : OGRField *psField)
1852 : {
1853 : // 25569: Number of days between 1899/12/30 00:00:00 and 1970/01/01 00:00:00
1854 7747 : double dfSeconds = (dfVal - 25569.0) * 3600.0 * 24.0;
1855 7747 : if (std::isnan(dfSeconds) ||
1856 : dfSeconds <
1857 15494 : static_cast<double>(std::numeric_limits<GIntBig>::min()) + 1000 ||
1858 : dfSeconds >
1859 7747 : static_cast<double>(std::numeric_limits<GIntBig>::max()) - 1000)
1860 : {
1861 0 : CPLError(CE_Failure, CPLE_NotSupported,
1862 : "FileGDBDoubleDateToOGRDate: Invalid days: %lf", dfVal);
1863 0 : dfSeconds = 0.0;
1864 : }
1865 7747 : if (!bHighPrecision)
1866 7693 : dfSeconds = std::floor(dfSeconds + 0.5);
1867 54 : else if (fmod(dfSeconds, 1.0) > 1 - 1e-4)
1868 18 : dfSeconds = std::floor(dfSeconds + 0.5);
1869 :
1870 : struct tm brokendowntime;
1871 7747 : CPLUnixTimeToYMDHMS(static_cast<GIntBig>(dfSeconds), &brokendowntime);
1872 :
1873 7747 : psField->Date.Year = static_cast<GInt16>(brokendowntime.tm_year + 1900);
1874 7747 : psField->Date.Month = static_cast<GByte>(brokendowntime.tm_mon + 1);
1875 7747 : psField->Date.Day = static_cast<GByte>(brokendowntime.tm_mday);
1876 7747 : psField->Date.Hour = static_cast<GByte>(brokendowntime.tm_hour);
1877 7747 : psField->Date.Minute = static_cast<GByte>(brokendowntime.tm_min);
1878 7747 : double dfSec = brokendowntime.tm_sec;
1879 7747 : if (bHighPrecision)
1880 : {
1881 54 : dfSec += fmod(dfSeconds, 1.0);
1882 : }
1883 7747 : psField->Date.Second = static_cast<float>(dfSec);
1884 7747 : psField->Date.TZFlag = 0;
1885 7747 : psField->Date.Reserved = 0;
1886 :
1887 7747 : return TRUE;
1888 : }
1889 :
1890 : /************************************************************************/
1891 : /* FileGDBDoubleTimeToOGRTime() */
1892 : /************************************************************************/
1893 :
1894 28 : int FileGDBDoubleTimeToOGRTime(double dfVal, OGRField *psField)
1895 : {
1896 28 : double dfSeconds = dfVal * 3600.0 * 24.0;
1897 28 : if (std::isnan(dfSeconds) || dfSeconds < 0 || dfSeconds > 86400)
1898 : {
1899 0 : CPLError(CE_Failure, CPLE_NotSupported,
1900 : "FileGDBDoubleTimeToOGRTime: Invalid time: %lf", dfVal);
1901 0 : dfSeconds = 0.0;
1902 : }
1903 :
1904 28 : psField->Date.Year = 0;
1905 28 : psField->Date.Month = 0;
1906 28 : psField->Date.Day = 0;
1907 28 : psField->Date.Hour = static_cast<GByte>(dfSeconds / 3600);
1908 28 : psField->Date.Minute =
1909 28 : static_cast<GByte>((static_cast<int>(dfSeconds) % 3600) / 60);
1910 28 : psField->Date.Second = static_cast<float>(fmod(dfSeconds, 60));
1911 28 : psField->Date.TZFlag = 0;
1912 28 : psField->Date.Reserved = 0;
1913 :
1914 28 : return TRUE;
1915 : }
1916 :
1917 : /************************************************************************/
1918 : /* FileGDBDateTimeWithOffsetToOGRDate() */
1919 : /************************************************************************/
1920 :
1921 31 : int FileGDBDateTimeWithOffsetToOGRDate(double dfVal, int16_t nUTCOffset,
1922 : OGRField *psField)
1923 : {
1924 31 : int ret = FileGDBDoubleDateToOGRDate(dfVal, true, psField);
1925 31 : if (nUTCOffset >= -14 * 60 && nUTCOffset <= 14 * 60)
1926 : {
1927 31 : psField->Date.TZFlag = static_cast<GByte>(100 + nUTCOffset / 15);
1928 : }
1929 : else
1930 0 : ret = FALSE;
1931 31 : return ret;
1932 : }
1933 :
1934 : /************************************************************************/
1935 : /* GetAllFieldValues() */
1936 : /************************************************************************/
1937 :
1938 69 : std::vector<OGRField> FileGDBTable::GetAllFieldValues()
1939 : {
1940 : std::vector<OGRField> asFields(m_apoFields.size(),
1941 69 : FileGDBField::UNSET_FIELD);
1942 869 : for (int i = 0; i < static_cast<int>(m_apoFields.size()); ++i)
1943 : {
1944 800 : const OGRField *psField = GetFieldValue(i);
1945 490 : if (psField && !OGR_RawField_IsNull(psField) &&
1946 2047 : !OGR_RawField_IsUnset(psField) &&
1947 757 : (m_apoFields[i]->GetType() == FGFT_STRING ||
1948 491 : m_apoFields[i]->GetType() == FGFT_XML ||
1949 409 : m_apoFields[i]->GetType() == FGFT_GLOBALID ||
1950 185 : m_apoFields[i]->GetType() == FGFT_GUID))
1951 : {
1952 344 : asFields[i].String = CPLStrdup(psField->String);
1953 : }
1954 146 : else if (psField && !OGR_RawField_IsNull(psField) &&
1955 877 : !OGR_RawField_IsUnset(psField) &&
1956 275 : (m_apoFields[i]->GetType() == FGFT_BINARY ||
1957 129 : m_apoFields[i]->GetType() == FGFT_GEOMETRY))
1958 : {
1959 34 : asFields[i].Binary.paData =
1960 17 : static_cast<GByte *>(CPLMalloc(psField->Binary.nCount));
1961 17 : asFields[i].Binary.nCount = psField->Binary.nCount;
1962 17 : memcpy(asFields[i].Binary.paData, psField->Binary.paData,
1963 17 : asFields[i].Binary.nCount);
1964 : }
1965 439 : else if (psField && !(m_apoFields[i]->GetType() == FGFT_RASTER))
1966 : {
1967 129 : asFields[i] = *psField;
1968 : }
1969 : }
1970 69 : return asFields;
1971 : }
1972 :
1973 : /************************************************************************/
1974 : /* FreeAllFieldValues() */
1975 : /************************************************************************/
1976 :
1977 69 : void FileGDBTable::FreeAllFieldValues(std::vector<OGRField> &asFields)
1978 : {
1979 869 : for (int i = 0; i < static_cast<int>(m_apoFields.size()); ++i)
1980 : {
1981 800 : if (!OGR_RawField_IsNull(&asFields[i]) &&
1982 1557 : !OGR_RawField_IsUnset(&asFields[i]) &&
1983 757 : (m_apoFields[i]->GetType() == FGFT_STRING ||
1984 491 : m_apoFields[i]->GetType() == FGFT_XML ||
1985 409 : m_apoFields[i]->GetType() == FGFT_GLOBALID ||
1986 185 : m_apoFields[i]->GetType() == FGFT_GUID))
1987 : {
1988 344 : CPLFree(asFields[i].String);
1989 344 : asFields[i].String = nullptr;
1990 : }
1991 456 : else if (!OGR_RawField_IsNull(&asFields[i]) &&
1992 731 : !OGR_RawField_IsUnset(&asFields[i]) &&
1993 275 : (m_apoFields[i]->GetType() == FGFT_BINARY ||
1994 129 : m_apoFields[i]->GetType() == FGFT_GEOMETRY))
1995 : {
1996 17 : CPLFree(asFields[i].Binary.paData);
1997 17 : asFields[i].Binary.paData = nullptr;
1998 : }
1999 : }
2000 69 : }
2001 :
2002 : /************************************************************************/
2003 : /* GetFieldValue() */
2004 : /************************************************************************/
2005 :
2006 230680 : const OGRField *FileGDBTable::GetFieldValue(int iCol)
2007 : {
2008 230680 : OGRField *errorRetValue = nullptr;
2009 :
2010 230680 : returnErrorIf(m_nCurRow < 0);
2011 230680 : returnErrorIf(static_cast<GUInt32>(iCol) >= m_apoFields.size());
2012 230680 : returnErrorIf(m_bError);
2013 :
2014 230680 : GByte *pabyEnd = m_abyBuffer.data() + m_nRowBlobLength;
2015 :
2016 : /* In case a string was previously read */
2017 230680 : if (m_nChSaved >= 0)
2018 : {
2019 57209 : *m_pabyIterVals = static_cast<GByte>(m_nChSaved);
2020 57209 : m_nChSaved = -1;
2021 : }
2022 :
2023 230680 : if (iCol <= m_nLastCol)
2024 : {
2025 10358 : m_nLastCol = -1;
2026 10358 : m_pabyIterVals = m_abyBuffer.data() + m_nNullableFieldsSizeInBytes;
2027 10358 : m_iAccNullable = 0;
2028 : }
2029 :
2030 : // Skip previous fields
2031 460910 : for (int j = m_nLastCol + 1; j < iCol; j++)
2032 : {
2033 230230 : if (m_apoFields[j]->m_bNullable)
2034 : {
2035 126936 : int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
2036 126936 : m_iAccNullable++;
2037 126936 : if (bIsNull)
2038 44102 : continue;
2039 : }
2040 :
2041 186128 : GUInt32 nLength = 0;
2042 186128 : CPL_IGNORE_RET_VAL(nLength);
2043 186128 : switch (m_apoFields[j]->m_eType)
2044 : {
2045 0 : case FGFT_UNDEFINED:
2046 0 : CPLAssert(false);
2047 : break;
2048 :
2049 63575 : case FGFT_OBJECTID:
2050 63575 : break;
2051 :
2052 69045 : case FGFT_STRING:
2053 : case FGFT_XML:
2054 : case FGFT_GEOMETRY:
2055 : case FGFT_BINARY:
2056 : {
2057 69045 : if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
2058 : {
2059 0 : m_bError = TRUE;
2060 0 : returnError();
2061 : }
2062 69045 : break;
2063 : }
2064 :
2065 0 : case FGFT_RASTER:
2066 : {
2067 : const FileGDBRasterField *rasterField =
2068 0 : cpl::down_cast<const FileGDBRasterField *>(
2069 0 : m_apoFields[j].get());
2070 0 : if (rasterField->GetRasterType() ==
2071 : FileGDBRasterField::Type::MANAGED)
2072 0 : nLength = sizeof(GInt32);
2073 : else
2074 : {
2075 0 : if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
2076 : {
2077 0 : m_bError = TRUE;
2078 0 : returnError();
2079 : }
2080 : }
2081 0 : break;
2082 : }
2083 :
2084 12 : case FGFT_INT16:
2085 12 : nLength = sizeof(GInt16);
2086 12 : break;
2087 14186 : case FGFT_INT32:
2088 14186 : nLength = sizeof(GInt32);
2089 14186 : break;
2090 8 : case FGFT_FLOAT32:
2091 8 : nLength = sizeof(float);
2092 8 : break;
2093 211 : case FGFT_FLOAT64:
2094 211 : nLength = sizeof(double);
2095 211 : break;
2096 27 : case FGFT_DATETIME:
2097 : case FGFT_DATE:
2098 : case FGFT_TIME:
2099 27 : nLength = sizeof(double);
2100 27 : break;
2101 39055 : case FGFT_GUID:
2102 : case FGFT_GLOBALID:
2103 39055 : nLength = UUID_SIZE_IN_BYTES;
2104 39055 : break;
2105 0 : case FGFT_INT64:
2106 0 : nLength = sizeof(int64_t);
2107 0 : break;
2108 9 : case FGFT_DATETIME_WITH_OFFSET:
2109 9 : nLength += sizeof(double) + sizeof(int16_t);
2110 9 : break;
2111 : }
2112 :
2113 186128 : if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
2114 : {
2115 0 : m_bError = TRUE;
2116 0 : returnError();
2117 : }
2118 186128 : m_pabyIterVals += nLength;
2119 : }
2120 :
2121 230680 : m_nLastCol = iCol;
2122 :
2123 230680 : if (m_apoFields[iCol]->m_bNullable)
2124 : {
2125 203184 : int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
2126 203184 : m_iAccNullable++;
2127 203184 : if (bIsNull)
2128 : {
2129 27169 : return nullptr;
2130 : }
2131 : }
2132 :
2133 203511 : switch (m_apoFields[iCol]->m_eType)
2134 : {
2135 0 : case FGFT_UNDEFINED:
2136 0 : CPLAssert(false);
2137 : break;
2138 :
2139 69 : case FGFT_OBJECTID:
2140 69 : return nullptr;
2141 :
2142 64132 : case FGFT_STRING:
2143 : case FGFT_XML:
2144 : {
2145 : GUInt32 nLength;
2146 64132 : if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
2147 : {
2148 0 : m_bError = TRUE;
2149 68 : returnError();
2150 : }
2151 64132 : if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
2152 : {
2153 68 : m_bError = TRUE;
2154 68 : returnError();
2155 : }
2156 :
2157 64064 : if (m_bStringsAreUTF8 || m_apoFields[iCol]->m_eType != FGFT_STRING)
2158 : {
2159 : /* eCurFieldType = OFTString; */
2160 64062 : m_sCurField.String = reinterpret_cast<char *>(m_pabyIterVals);
2161 64062 : m_pabyIterVals += nLength;
2162 :
2163 : /* This is a trick to avoid a alloc()+copy(). We null-terminate
2164 : */
2165 : /* after the string, and save the pointer and value to restore
2166 : */
2167 64062 : m_nChSaved = *m_pabyIterVals;
2168 64062 : *m_pabyIterVals = '\0';
2169 : }
2170 : else
2171 : {
2172 2 : m_osTempString = ReadUTF16String(m_pabyIterVals, nLength / 2);
2173 2 : m_sCurField.String = &m_osTempString[0];
2174 2 : m_pabyIterVals += nLength;
2175 : }
2176 :
2177 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow,
2178 : * sCurField.String); */
2179 :
2180 64064 : break;
2181 : }
2182 :
2183 7661 : case FGFT_INT16:
2184 : {
2185 7661 : if (m_pabyIterVals + sizeof(GInt16) > pabyEnd)
2186 : {
2187 0 : m_bError = TRUE;
2188 0 : returnError();
2189 : }
2190 :
2191 : /* eCurFieldType = OFTInteger; */
2192 7661 : m_sCurField.Integer = GetInt16(m_pabyIterVals, 0);
2193 :
2194 7661 : m_pabyIterVals += sizeof(GInt16);
2195 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow,
2196 : * sCurField.Integer); */
2197 :
2198 7661 : break;
2199 : }
2200 :
2201 19764 : case FGFT_INT32:
2202 : {
2203 19764 : if (m_pabyIterVals + sizeof(GInt32) > pabyEnd)
2204 : {
2205 0 : m_bError = TRUE;
2206 0 : returnError();
2207 : }
2208 :
2209 : /* eCurFieldType = OFTInteger; */
2210 19764 : m_sCurField.Integer = GetInt32(m_pabyIterVals, 0);
2211 :
2212 19764 : m_pabyIterVals += sizeof(GInt32);
2213 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow,
2214 : * sCurField.Integer); */
2215 :
2216 19764 : break;
2217 : }
2218 :
2219 7655 : case FGFT_FLOAT32:
2220 : {
2221 7655 : if (m_pabyIterVals + sizeof(float) > pabyEnd)
2222 : {
2223 0 : m_bError = TRUE;
2224 0 : returnError();
2225 : }
2226 :
2227 : /* eCurFieldType = OFTReal; */
2228 7655 : m_sCurField.Real = GetFloat32(m_pabyIterVals, 0);
2229 :
2230 7655 : m_pabyIterVals += sizeof(float);
2231 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow,
2232 : * sCurField.Real); */
2233 :
2234 7655 : break;
2235 : }
2236 :
2237 27664 : case FGFT_FLOAT64:
2238 : {
2239 27664 : if (m_pabyIterVals + sizeof(double) > pabyEnd)
2240 : {
2241 0 : m_bError = TRUE;
2242 0 : returnError();
2243 : }
2244 :
2245 : /* eCurFieldType = OFTReal; */
2246 27664 : m_sCurField.Real = GetFloat64(m_pabyIterVals, 0);
2247 :
2248 27664 : m_pabyIterVals += sizeof(double);
2249 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow,
2250 : * sCurField.Real); */
2251 :
2252 27664 : break;
2253 : }
2254 :
2255 7695 : case FGFT_DATETIME:
2256 : case FGFT_DATE:
2257 : {
2258 7695 : if (m_pabyIterVals + sizeof(double) > pabyEnd)
2259 : {
2260 0 : m_bError = TRUE;
2261 0 : returnError();
2262 : }
2263 :
2264 : /* Number of days since 1899/12/30 00:00:00 */
2265 7695 : const double dfVal = GetFloat64(m_pabyIterVals, 0);
2266 :
2267 7695 : if (m_apoFields[iCol]->m_bReadAsDouble)
2268 : {
2269 5 : m_sCurField.Real = dfVal;
2270 : }
2271 : else
2272 : {
2273 7690 : FileGDBDoubleDateToOGRDate(
2274 7690 : dfVal, m_apoFields[iCol]->IsHighPrecision(), &m_sCurField);
2275 : /* eCurFieldType = OFTDateTime; */
2276 : }
2277 :
2278 7695 : m_pabyIterVals += sizeof(double);
2279 :
2280 7695 : break;
2281 : }
2282 :
2283 51006 : case FGFT_GEOMETRY:
2284 : case FGFT_BINARY:
2285 : {
2286 : GUInt32 nLength;
2287 51006 : if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
2288 : {
2289 0 : m_bError = TRUE;
2290 0 : returnError();
2291 : }
2292 51006 : if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
2293 : {
2294 0 : m_bError = TRUE;
2295 0 : returnError();
2296 : }
2297 :
2298 : /* eCurFieldType = OFTBinary; */
2299 51006 : m_sCurField.Binary.nCount = nLength;
2300 51006 : m_sCurField.Binary.paData = const_cast<GByte *>(m_pabyIterVals);
2301 :
2302 51006 : m_pabyIterVals += nLength;
2303 :
2304 : /* Null terminate binary in case it is used as a string */
2305 51006 : m_nChSaved = *m_pabyIterVals;
2306 51006 : *m_pabyIterVals = '\0';
2307 :
2308 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d bytes", iCol,
2309 : * nCurRow, snLength); */
2310 :
2311 51006 : break;
2312 : }
2313 :
2314 0 : case FGFT_RASTER:
2315 : {
2316 : const FileGDBRasterField *rasterField =
2317 0 : cpl::down_cast<const FileGDBRasterField *>(
2318 0 : m_apoFields[iCol].get());
2319 0 : if (rasterField->GetRasterType() ==
2320 : FileGDBRasterField::Type::MANAGED)
2321 : {
2322 0 : if (m_pabyIterVals + sizeof(GInt32) > pabyEnd)
2323 : {
2324 0 : m_bError = TRUE;
2325 0 : returnError();
2326 : }
2327 :
2328 0 : const GInt32 nVal = GetInt32(m_pabyIterVals, 0);
2329 :
2330 : /* eCurFieldType = OFTIntger; */
2331 0 : m_sCurField.Integer = nVal;
2332 :
2333 0 : m_pabyIterVals += sizeof(GInt32);
2334 : }
2335 : else
2336 : {
2337 : GUInt32 nLength;
2338 0 : if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
2339 : {
2340 0 : m_bError = TRUE;
2341 0 : returnError();
2342 : }
2343 0 : if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
2344 : {
2345 0 : m_bError = TRUE;
2346 0 : returnError();
2347 : }
2348 :
2349 0 : if (rasterField->GetRasterType() ==
2350 : FileGDBRasterField::Type::EXTERNAL)
2351 : {
2352 : // coverity[tainted_data,tainted_data_argument]
2353 : m_osCacheRasterFieldPath =
2354 0 : ReadUTF16String(m_pabyIterVals, nLength / 2);
2355 0 : m_sCurField.String = &m_osCacheRasterFieldPath[0];
2356 0 : m_pabyIterVals += nLength;
2357 : }
2358 : else
2359 : {
2360 : /* eCurFieldType = OFTBinary; */
2361 0 : m_sCurField.Binary.nCount = nLength;
2362 0 : m_sCurField.Binary.paData =
2363 0 : const_cast<GByte *>(m_pabyIterVals);
2364 :
2365 0 : m_pabyIterVals += nLength;
2366 :
2367 : /* Null terminate binary in case it is used as a string */
2368 0 : m_nChSaved = *m_pabyIterVals;
2369 0 : *m_pabyIterVals = '\0';
2370 : }
2371 : }
2372 0 : break;
2373 : }
2374 :
2375 17806 : case FGFT_GUID:
2376 : case FGFT_GLOBALID:
2377 : {
2378 17806 : if (m_pabyIterVals + UUID_SIZE_IN_BYTES > pabyEnd)
2379 : {
2380 0 : m_bError = TRUE;
2381 0 : returnError();
2382 : }
2383 :
2384 : /* eCurFieldType = OFTString; */
2385 17806 : m_sCurField.String = m_achGUIDBuffer;
2386 : /*78563412BC9AF0DE1234567890ABCDEF -->
2387 : * {12345678-9ABC-DEF0-1234-567890ABCDEF} */
2388 17806 : snprintf(m_achGUIDBuffer, sizeof(m_achGUIDBuffer),
2389 : "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%"
2390 : "02X%02X%02X%02X}",
2391 17806 : m_pabyIterVals[3], m_pabyIterVals[2], m_pabyIterVals[1],
2392 17806 : m_pabyIterVals[0], m_pabyIterVals[5], m_pabyIterVals[4],
2393 17806 : m_pabyIterVals[7], m_pabyIterVals[6], m_pabyIterVals[8],
2394 17806 : m_pabyIterVals[9], m_pabyIterVals[10], m_pabyIterVals[11],
2395 17806 : m_pabyIterVals[12], m_pabyIterVals[13], m_pabyIterVals[14],
2396 17806 : m_pabyIterVals[15]);
2397 :
2398 17806 : m_pabyIterVals += UUID_SIZE_IN_BYTES;
2399 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow,
2400 : * sCurField.String); */
2401 :
2402 17806 : break;
2403 : }
2404 :
2405 9 : case FGFT_INT64:
2406 : {
2407 9 : if (m_pabyIterVals + sizeof(int64_t) > pabyEnd)
2408 : {
2409 0 : m_bError = TRUE;
2410 0 : returnError();
2411 : }
2412 :
2413 : /* eCurFieldType = OFTInteger; */
2414 9 : m_sCurField.Integer64 = GetInt64(m_pabyIterVals, 0);
2415 :
2416 9 : m_pabyIterVals += sizeof(int64_t);
2417 : /* CPLDebug("OpenFileGDB", "Field %d, row %d: " CPL_FRMT_GIB, iCol, nCurRow,
2418 : * sCurField.Integer64); */
2419 :
2420 9 : break;
2421 : }
2422 :
2423 23 : case FGFT_TIME:
2424 : {
2425 23 : if (m_pabyIterVals + sizeof(double) > pabyEnd)
2426 : {
2427 0 : m_bError = TRUE;
2428 0 : returnError();
2429 : }
2430 :
2431 : /* Fraction of day */
2432 23 : const double dfVal = GetFloat64(m_pabyIterVals, 0);
2433 :
2434 23 : if (m_apoFields[iCol]->m_bReadAsDouble)
2435 : {
2436 3 : m_sCurField.Real = dfVal;
2437 : }
2438 : else
2439 : {
2440 20 : FileGDBDoubleTimeToOGRTime(dfVal, &m_sCurField);
2441 : /* eCurFieldType = OFTTime; */
2442 : }
2443 :
2444 23 : m_pabyIterVals += sizeof(double);
2445 :
2446 23 : break;
2447 : }
2448 :
2449 27 : case FGFT_DATETIME_WITH_OFFSET:
2450 : {
2451 27 : if (m_pabyIterVals + sizeof(double) + sizeof(int16_t) > pabyEnd)
2452 : {
2453 0 : m_bError = TRUE;
2454 0 : returnError();
2455 : }
2456 :
2457 : /* Number of days since 1899/12/30 00:00:00 */
2458 27 : const double dfVal = GetFloat64(m_pabyIterVals, 0);
2459 27 : m_pabyIterVals += sizeof(double);
2460 27 : const int16_t nUTCOffset = GetInt16(m_pabyIterVals, 0);
2461 27 : m_pabyIterVals += sizeof(int16_t);
2462 :
2463 27 : if (m_apoFields[iCol]->m_bReadAsDouble)
2464 : {
2465 3 : m_sCurField.Real = dfVal - nUTCOffset * 60.0 / 86400.0;
2466 : }
2467 : else
2468 : {
2469 24 : FileGDBDateTimeWithOffsetToOGRDate(dfVal, nUTCOffset,
2470 : &m_sCurField);
2471 : /* eCurFieldType = OFTDateTime; */
2472 : }
2473 :
2474 27 : break;
2475 : }
2476 : }
2477 :
2478 226089 : if (iCol == static_cast<int>(m_apoFields.size()) - 1 &&
2479 22715 : m_pabyIterVals < pabyEnd)
2480 : {
2481 1 : CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %" PRId64,
2482 1 : static_cast<int>(pabyEnd - m_pabyIterVals), m_nCurRow);
2483 : }
2484 :
2485 203374 : return &m_sCurField;
2486 : }
2487 :
2488 : /************************************************************************/
2489 : /* GetIndexCount() */
2490 : /************************************************************************/
2491 :
2492 5708 : int FileGDBTable::GetIndexCount()
2493 : {
2494 5708 : const int errorRetValue = 0;
2495 5708 : if (m_bHasReadGDBIndexes)
2496 3910 : return static_cast<int>(m_apoIndexes.size());
2497 :
2498 1798 : m_bHasReadGDBIndexes = TRUE;
2499 :
2500 : const std::string osIndexesName = CPLFormFilenameSafe(
2501 3596 : CPLGetPathSafe(m_osFilename.c_str()).c_str(),
2502 5394 : CPLGetBasenameSafe(m_osFilename.c_str()).c_str(), "gdbindexes");
2503 1798 : VSILFILE *fpIndexes = VSIFOpenL(osIndexesName.c_str(), "rb");
2504 : VSIStatBufL sStat;
2505 1798 : if (fpIndexes == nullptr)
2506 : {
2507 1560 : if (VSIStatExL(osIndexesName.c_str(), &sStat, VSI_STAT_EXISTS_FLAG) ==
2508 : 0)
2509 0 : returnError();
2510 : else
2511 1560 : return 0;
2512 : }
2513 :
2514 238 : VSIFSeekL(fpIndexes, 0, SEEK_END);
2515 238 : vsi_l_offset nFileSize = VSIFTellL(fpIndexes);
2516 238 : returnErrorAndCleanupIf(nFileSize > 1024 * 1024, VSIFCloseL(fpIndexes));
2517 :
2518 : GByte *pabyIdx = static_cast<GByte *>(
2519 238 : VSI_MALLOC_VERBOSE(static_cast<size_t>(nFileSize)));
2520 238 : returnErrorAndCleanupIf(pabyIdx == nullptr, VSIFCloseL(fpIndexes));
2521 :
2522 238 : VSIFSeekL(fpIndexes, 0, SEEK_SET);
2523 : int nRead = static_cast<int>(
2524 238 : VSIFReadL(pabyIdx, static_cast<size_t>(nFileSize), 1, fpIndexes));
2525 238 : VSIFCloseL(fpIndexes);
2526 238 : returnErrorAndCleanupIf(nRead != 1, VSIFree(pabyIdx));
2527 :
2528 238 : GByte *pabyCur = pabyIdx;
2529 238 : GByte *pabyEnd = pabyIdx + nFileSize;
2530 238 : returnErrorAndCleanupIf(pabyEnd - pabyCur < 4, VSIFree(pabyIdx));
2531 238 : GUInt32 nIndexCount = GetUInt32(pabyCur, 0);
2532 238 : pabyCur += 4;
2533 :
2534 : // FileGDB v9 indexes structure not handled yet. Start with 13 98 85 03
2535 238 : if (nIndexCount == 0x03859813)
2536 : {
2537 44 : VSIFree(pabyIdx);
2538 :
2539 : // Hard code detection of blk_key_index on raster layers
2540 44 : const int iBlockKeyFieldIdx = GetFieldIdx("block_key");
2541 44 : if (iBlockKeyFieldIdx >= 0)
2542 : {
2543 : const std::string osAtxFilename = CPLResetExtensionSafe(
2544 2 : m_osFilename.c_str(), "blk_key_index.atx");
2545 2 : if (VSIStatExL(osAtxFilename.c_str(), &sStat,
2546 2 : VSI_STAT_EXISTS_FLAG) == 0)
2547 : {
2548 2 : auto poIndex = std::make_unique<FileGDBIndex>();
2549 2 : poIndex->m_osIndexName = "blk_key_index";
2550 2 : poIndex->m_osExpression = "block_key";
2551 2 : m_apoFields[iBlockKeyFieldIdx]->m_poIndex = poIndex.get();
2552 2 : m_apoIndexes.push_back(std::move(poIndex));
2553 2 : return 1;
2554 : }
2555 : }
2556 :
2557 42 : CPLDebug("OpenFileGDB", ".gdbindexes v9 not handled yet");
2558 42 : return 0;
2559 : }
2560 :
2561 194 : returnErrorAndCleanupIf(nIndexCount >=
2562 : static_cast<size_t>(GetFieldCount() + 1) * 10,
2563 : VSIFree(pabyIdx));
2564 :
2565 : GUInt32 i;
2566 866 : for (i = 0; i < nIndexCount; i++)
2567 : {
2568 713 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2569 : sizeof(GUInt32),
2570 : VSIFree(pabyIdx));
2571 693 : const GUInt32 nIdxNameCharCount = GetUInt32(pabyCur, 0);
2572 693 : pabyCur += sizeof(GUInt32);
2573 693 : returnErrorAndCleanupIf(nIdxNameCharCount > 1024, VSIFree(pabyIdx));
2574 688 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2575 : 2 * nIdxNameCharCount,
2576 : VSIFree(pabyIdx));
2577 683 : std::string osIndexName(ReadUTF16String(pabyCur, nIdxNameCharCount));
2578 683 : pabyCur += 2 * nIdxNameCharCount;
2579 :
2580 : // 4 "magic fields"
2581 683 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2582 : sizeof(GUInt16) + sizeof(GUInt32) +
2583 : sizeof(GUInt16) + sizeof(GUInt32),
2584 : VSIFree(pabyIdx));
2585 : // const GUInt16 nMagic1 = GetUInt16(pabyCur, 0);
2586 683 : const GUInt32 nMagic2 = GetUInt32(pabyCur + sizeof(GUInt16), 0);
2587 : const GUInt16 nMagic3 =
2588 683 : GetUInt16(pabyCur + sizeof(GUInt16) + sizeof(GUInt32), 0);
2589 683 : if (!((nMagic2 == 2 && nMagic3 == 0) ||
2590 94 : (nMagic2 == 4 && nMagic3 == 0) ||
2591 191 : (nMagic2 == 16 && nMagic3 == 65535)))
2592 : {
2593 : // Cf files a00000029.gdbindexes, a000000ea.gdbindexes, a000000ed.gdbindexes,
2594 : // a000000f8.gdbindexes, a000000fb.gdbindexes, a00000103.gdbindexes
2595 : // from https://github.com/OSGeo/gdal/issues/11295#issuecomment-2491158506
2596 3 : CPLDebug("OpenFileGDB", "Reading %s", osIndexesName.c_str());
2597 3 : CPLDebug(
2598 : "OpenFileGDB",
2599 : "Strange (deleted?) index descriptor at index %u of name %s", i,
2600 : osIndexName.c_str());
2601 :
2602 : // Skip magic fields
2603 3 : pabyCur += sizeof(GUInt16);
2604 :
2605 3 : const GUInt32 nColNameCharCount = nMagic2;
2606 3 : pabyCur += sizeof(GUInt32);
2607 3 : returnErrorAndCleanupIf(nColNameCharCount > 1024, VSIFree(pabyIdx));
2608 3 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2609 : 2 * nColNameCharCount,
2610 : VSIFree(pabyIdx));
2611 3 : pabyCur += 2 * nColNameCharCount;
2612 :
2613 : // Skip magic field
2614 3 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2615 : sizeof(GUInt16),
2616 : VSIFree(pabyIdx));
2617 3 : pabyCur += sizeof(GUInt16);
2618 :
2619 3 : continue;
2620 : }
2621 :
2622 : // Skip magic fields
2623 680 : pabyCur += sizeof(GUInt16) + sizeof(GUInt32) + sizeof(GUInt16) +
2624 : sizeof(GUInt32);
2625 :
2626 680 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2627 : sizeof(GUInt32),
2628 : VSIFree(pabyIdx));
2629 680 : const GUInt32 nColNameCharCount = GetUInt32(pabyCur, 0);
2630 680 : pabyCur += sizeof(GUInt32);
2631 680 : returnErrorAndCleanupIf(nColNameCharCount > 1024, VSIFree(pabyIdx));
2632 675 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2633 : 2 * nColNameCharCount,
2634 : VSIFree(pabyIdx));
2635 : const std::string osExpression(
2636 670 : ReadUTF16String(pabyCur, nColNameCharCount));
2637 670 : pabyCur += 2 * nColNameCharCount;
2638 :
2639 : // Skip magic field
2640 670 : returnErrorAndCleanupIf(static_cast<size_t>(pabyEnd - pabyCur) <
2641 : sizeof(GUInt16),
2642 : VSIFree(pabyIdx));
2643 670 : pabyCur += sizeof(GUInt16);
2644 :
2645 1340 : auto poIndex = std::make_unique<FileGDBIndex>();
2646 670 : poIndex->m_osIndexName = std::move(osIndexName);
2647 670 : poIndex->m_osExpression = osExpression;
2648 :
2649 1340 : if (m_iObjectIdField < 0 ||
2650 670 : osExpression != m_apoFields[m_iObjectIdField]->GetName())
2651 : {
2652 962 : const auto osFieldName = poIndex->GetFieldName();
2653 481 : int nFieldIdx = GetFieldIdx(osFieldName);
2654 481 : if (nFieldIdx < 0)
2655 : {
2656 2 : CPLDebug("OpenFileGDB",
2657 : "Index defined for field %s that does not exist",
2658 : osFieldName.c_str());
2659 : }
2660 : else
2661 : {
2662 479 : if (m_apoFields[nFieldIdx]->m_poIndex != nullptr)
2663 : {
2664 0 : CPLDebug("OpenFileGDB",
2665 : "There is already one index defined for field %s",
2666 : osFieldName.c_str());
2667 : }
2668 : else
2669 : {
2670 479 : m_apoFields[nFieldIdx]->m_poIndex = poIndex.get();
2671 : }
2672 : }
2673 : }
2674 :
2675 670 : m_apoIndexes.push_back(std::move(poIndex));
2676 : }
2677 :
2678 173 : VSIFree(pabyIdx);
2679 :
2680 173 : return static_cast<int>(m_apoIndexes.size());
2681 : }
2682 :
2683 : /************************************************************************/
2684 : /* HasSpatialIndex() */
2685 : /************************************************************************/
2686 :
2687 906 : bool FileGDBTable::HasSpatialIndex()
2688 : {
2689 906 : if (m_nHasSpatialIndex < 0)
2690 : {
2691 : const std::string osSpxName = CPLFormFilenameSafe(
2692 1014 : CPLGetPathSafe(m_osFilename.c_str()).c_str(),
2693 1014 : CPLGetBasenameSafe(m_osFilename.c_str()).c_str(), "spx");
2694 : VSIStatBufL sStat;
2695 507 : m_nHasSpatialIndex =
2696 507 : (VSIStatExL(osSpxName.c_str(), &sStat, VSI_STAT_EXISTS_FLAG) == 0);
2697 : }
2698 906 : return m_nHasSpatialIndex != FALSE;
2699 : }
2700 :
2701 : /************************************************************************/
2702 : /* InstallFilterEnvelope() */
2703 : /************************************************************************/
2704 :
2705 : #define MAX_GUINTBIG std::numeric_limits<GUIntBig>::max()
2706 :
2707 3068 : void FileGDBTable::InstallFilterEnvelope(const OGREnvelope *psFilterEnvelope)
2708 : {
2709 3068 : if (psFilterEnvelope != nullptr)
2710 : {
2711 1470 : CPLAssert(m_iGeomField >= 0);
2712 : FileGDBGeomField *poGeomField =
2713 1470 : cpl::down_cast<FileGDBGeomField *>(GetField(m_iGeomField));
2714 :
2715 : /* We store the bounding box as unscaled coordinates, so that BBOX */
2716 : /* intersection is done with integer comparisons */
2717 1470 : if (psFilterEnvelope->MinX >= poGeomField->m_dfXOrigin)
2718 1470 : m_nFilterXMin = static_cast<GUIntBig>(
2719 1470 : 0.5 + (psFilterEnvelope->MinX - poGeomField->m_dfXOrigin) *
2720 1470 : poGeomField->m_dfXYScale);
2721 : else
2722 0 : m_nFilterXMin = 0;
2723 2940 : if (psFilterEnvelope->MaxX - poGeomField->m_dfXOrigin <
2724 1470 : static_cast<double>(MAX_GUINTBIG) / poGeomField->m_dfXYScale)
2725 1470 : m_nFilterXMax = static_cast<GUIntBig>(
2726 1470 : 0.5 + (psFilterEnvelope->MaxX - poGeomField->m_dfXOrigin) *
2727 1470 : poGeomField->m_dfXYScale);
2728 : else
2729 0 : m_nFilterXMax = MAX_GUINTBIG;
2730 1470 : if (psFilterEnvelope->MinY >= poGeomField->m_dfYOrigin)
2731 1470 : m_nFilterYMin = static_cast<GUIntBig>(
2732 1470 : 0.5 + (psFilterEnvelope->MinY - poGeomField->m_dfYOrigin) *
2733 1470 : poGeomField->m_dfXYScale);
2734 : else
2735 0 : m_nFilterYMin = 0;
2736 2940 : if (psFilterEnvelope->MaxY - poGeomField->m_dfYOrigin <
2737 1470 : static_cast<double>(MAX_GUINTBIG) / poGeomField->m_dfXYScale)
2738 1470 : m_nFilterYMax = static_cast<GUIntBig>(
2739 1470 : 0.5 + (psFilterEnvelope->MaxY - poGeomField->m_dfYOrigin) *
2740 1470 : poGeomField->m_dfXYScale);
2741 : else
2742 0 : m_nFilterYMax = MAX_GUINTBIG;
2743 : }
2744 : else
2745 : {
2746 1598 : m_nFilterXMin = 0;
2747 1598 : m_nFilterXMax = 0;
2748 1598 : m_nFilterYMin = 0;
2749 1598 : m_nFilterYMax = 0;
2750 : }
2751 3068 : }
2752 :
2753 : /************************************************************************/
2754 : /* GetMinMaxProjYForSpatialIndex() */
2755 : /************************************************************************/
2756 :
2757 : // ESRI software seems to have an extremely weird behavior regarding spatial
2758 : // indexing of geometries.
2759 : // When a projected CRS is associated with a layer, the northing of geometries
2760 : // is clamped, using the returned (dfYMin,dfYMax) values of this method.
2761 : // When creating the .spx file, if the maximum Y of a geometry is > dfYMax, then
2762 : // the geometry must be shifted along the Y axis so that its maximum value is
2763 : // dfYMax
2764 489 : void FileGDBTable::GetMinMaxProjYForSpatialIndex(double &dfYMin,
2765 : double &dfYMax) const
2766 : {
2767 489 : dfYMin = -std::numeric_limits<double>::max();
2768 489 : dfYMax = std::numeric_limits<double>::max();
2769 489 : const auto poGeomField = GetGeomField();
2770 489 : if (poGeomField == nullptr)
2771 478 : return;
2772 489 : const auto &osWKT = poGeomField->GetWKT();
2773 489 : OGRSpatialReference oSRS;
2774 661 : if (osWKT.empty() || osWKT[0] == '{' ||
2775 172 : oSRS.importFromWkt(osWKT.c_str()) != OGRERR_NONE)
2776 317 : return;
2777 172 : if (!oSRS.IsProjected())
2778 161 : return;
2779 11 : const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
2780 11 : if (pszProjection == nullptr)
2781 0 : return;
2782 : double dfMinLat;
2783 : double dfMaxLat;
2784 :
2785 : // Determined through experimentation, e.g with the `find_srs_latitude_limits.py` script.
2786 11 : if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
2787 : {
2788 11 : dfMinLat = -90;
2789 11 : dfMaxLat = 90;
2790 : }
2791 0 : else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
2792 0 : EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
2793 : {
2794 0 : dfMinLat = -89.9;
2795 0 : dfMaxLat = 89.9;
2796 : }
2797 : else
2798 : {
2799 : // TODO? add other projection methods
2800 0 : return;
2801 : }
2802 :
2803 : auto poSRSLongLat =
2804 11 : std::unique_ptr<OGRSpatialReference>(oSRS.CloneGeogCS());
2805 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
2806 11 : OGRCreateCoordinateTransformation(poSRSLongLat.get(), &oSRS));
2807 11 : if (!poCT)
2808 0 : return;
2809 : {
2810 11 : double x = 0;
2811 11 : double y = dfMinLat;
2812 11 : if (poCT->Transform(1, &x, &y))
2813 11 : dfYMin = y;
2814 : }
2815 : {
2816 11 : double x = 0;
2817 11 : double y = dfMaxLat;
2818 11 : if (poCT->Transform(1, &x, &y))
2819 11 : dfYMax = y;
2820 : }
2821 : }
2822 :
2823 : /************************************************************************/
2824 : /* GetFeatureExtent() */
2825 : /************************************************************************/
2826 :
2827 326 : int FileGDBTable::GetFeatureExtent(const OGRField *psField,
2828 : OGREnvelope *psOutFeatureEnvelope)
2829 : {
2830 326 : const int errorRetValue = FALSE;
2831 326 : GByte *pabyCur = psField->Binary.paData;
2832 326 : GByte *pabyEnd = pabyCur + psField->Binary.nCount;
2833 : GUInt32 nGeomType;
2834 326 : int nToSkip = 0;
2835 :
2836 326 : CPLAssert(m_iGeomField >= 0);
2837 : FileGDBGeomField *poGeomField =
2838 326 : cpl::down_cast<FileGDBGeomField *>(GetField(m_iGeomField));
2839 :
2840 326 : ReadVarUInt32NoCheck(pabyCur, nGeomType);
2841 :
2842 326 : switch ((nGeomType & 0xff))
2843 : {
2844 0 : case SHPT_NULL:
2845 0 : return FALSE;
2846 :
2847 33 : case SHPT_POINTZ:
2848 : case SHPT_POINTZM:
2849 : case SHPT_POINT:
2850 : case SHPT_POINTM:
2851 : case SHPT_GENERALPOINT:
2852 : {
2853 : GUIntBig x, y;
2854 33 : ReadVarUInt64NoCheck(pabyCur, x);
2855 33 : x = CPLUnsanitizedAdd<GUIntBig>(x, -1);
2856 33 : ReadVarUInt64NoCheck(pabyCur, y);
2857 33 : y = CPLUnsanitizedAdd<GUIntBig>(y, -1);
2858 33 : psOutFeatureEnvelope->MinX =
2859 33 : x / poGeomField->m_dfXYScale + poGeomField->m_dfXOrigin;
2860 33 : psOutFeatureEnvelope->MinY =
2861 33 : y / poGeomField->m_dfXYScale + poGeomField->m_dfYOrigin;
2862 33 : psOutFeatureEnvelope->MaxX = psOutFeatureEnvelope->MinX;
2863 33 : psOutFeatureEnvelope->MaxY = psOutFeatureEnvelope->MinY;
2864 33 : return TRUE;
2865 : }
2866 :
2867 1 : case SHPT_MULTIPOINTZM:
2868 : case SHPT_MULTIPOINTZ:
2869 : case SHPT_MULTIPOINT:
2870 : case SHPT_MULTIPOINTM:
2871 : {
2872 1 : break;
2873 : }
2874 :
2875 279 : case SHPT_ARC:
2876 : case SHPT_ARCZ:
2877 : case SHPT_ARCZM:
2878 : case SHPT_ARCM:
2879 : case SHPT_POLYGON:
2880 : case SHPT_POLYGONZ:
2881 : case SHPT_POLYGONZM:
2882 : case SHPT_POLYGONM:
2883 : {
2884 279 : nToSkip = 1;
2885 279 : break;
2886 : }
2887 12 : case SHPT_GENERALPOLYLINE:
2888 : case SHPT_GENERALPOLYGON:
2889 : {
2890 12 : nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
2891 12 : break;
2892 : }
2893 :
2894 1 : case SHPT_GENERALMULTIPATCH:
2895 : case SHPT_MULTIPATCHM:
2896 : case SHPT_MULTIPATCH:
2897 : {
2898 1 : nToSkip = 2;
2899 1 : break;
2900 : }
2901 :
2902 0 : default:
2903 0 : return FALSE;
2904 : }
2905 :
2906 : GUInt32 nPoints;
2907 293 : ReadVarUInt32NoCheck(pabyCur, nPoints);
2908 293 : if (nPoints == 0)
2909 16 : return TRUE;
2910 277 : returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip));
2911 :
2912 : GUIntBig vxmin, vymin, vdx, vdy;
2913 :
2914 277 : returnErrorIf(pabyCur >= pabyEnd);
2915 277 : ReadVarUInt64NoCheck(pabyCur, vxmin);
2916 277 : ReadVarUInt64NoCheck(pabyCur, vymin);
2917 277 : ReadVarUInt64NoCheck(pabyCur, vdx);
2918 277 : ReadVarUInt64NoCheck(pabyCur, vdy);
2919 :
2920 277 : psOutFeatureEnvelope->MinX =
2921 277 : vxmin / poGeomField->m_dfXYScale + poGeomField->m_dfXOrigin;
2922 277 : psOutFeatureEnvelope->MinY =
2923 277 : vymin / poGeomField->m_dfXYScale + poGeomField->m_dfYOrigin;
2924 277 : psOutFeatureEnvelope->MaxX =
2925 277 : CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) / poGeomField->m_dfXYScale +
2926 277 : poGeomField->m_dfXOrigin;
2927 277 : psOutFeatureEnvelope->MaxY =
2928 277 : CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) / poGeomField->m_dfXYScale +
2929 277 : poGeomField->m_dfYOrigin;
2930 :
2931 277 : return TRUE;
2932 : }
2933 :
2934 : /************************************************************************/
2935 : /* DoesGeometryIntersectsFilterEnvelope() */
2936 : /************************************************************************/
2937 :
2938 15199 : int FileGDBTable::DoesGeometryIntersectsFilterEnvelope(const OGRField *psField)
2939 : {
2940 15199 : const int errorRetValue = TRUE;
2941 15199 : GByte *pabyCur = psField->Binary.paData;
2942 15199 : GByte *pabyEnd = pabyCur + psField->Binary.nCount;
2943 : GUInt32 nGeomType;
2944 15199 : int nToSkip = 0;
2945 :
2946 15199 : ReadVarUInt32NoCheck(pabyCur, nGeomType);
2947 :
2948 15199 : switch ((nGeomType & 0xff))
2949 : {
2950 0 : case SHPT_NULL:
2951 0 : return TRUE;
2952 :
2953 14721 : case SHPT_POINTZ:
2954 : case SHPT_POINTZM:
2955 : case SHPT_POINT:
2956 : case SHPT_POINTM:
2957 : case SHPT_GENERALPOINT:
2958 : {
2959 : GUIntBig x, y;
2960 14721 : ReadVarUInt64NoCheck(pabyCur, x);
2961 14721 : if (x == 0) // POINT EMPTY
2962 0 : return FALSE;
2963 14721 : x--;
2964 14721 : if (x < m_nFilterXMin || x > m_nFilterXMax)
2965 11044 : return FALSE;
2966 3677 : ReadVarUInt64NoCheck(pabyCur, y);
2967 3677 : y--;
2968 3677 : return y >= m_nFilterYMin && y <= m_nFilterYMax;
2969 : }
2970 :
2971 8 : case SHPT_MULTIPOINTZM:
2972 : case SHPT_MULTIPOINTZ:
2973 : case SHPT_MULTIPOINT:
2974 : case SHPT_MULTIPOINTM:
2975 : {
2976 8 : break;
2977 : }
2978 :
2979 455 : case SHPT_ARC:
2980 : case SHPT_ARCZ:
2981 : case SHPT_ARCZM:
2982 : case SHPT_ARCM:
2983 : case SHPT_POLYGON:
2984 : case SHPT_POLYGONZ:
2985 : case SHPT_POLYGONZM:
2986 : case SHPT_POLYGONM:
2987 : {
2988 455 : nToSkip = 1;
2989 455 : break;
2990 : }
2991 :
2992 0 : case SHPT_GENERALPOLYLINE:
2993 : case SHPT_GENERALPOLYGON:
2994 : {
2995 0 : nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
2996 0 : break;
2997 : }
2998 :
2999 15 : case SHPT_GENERALMULTIPATCH:
3000 : case SHPT_MULTIPATCHM:
3001 : case SHPT_MULTIPATCH:
3002 : {
3003 15 : nToSkip = 2;
3004 15 : break;
3005 : }
3006 :
3007 0 : default:
3008 0 : return TRUE;
3009 : }
3010 :
3011 : GUInt32 nPoints;
3012 478 : ReadVarUInt32NoCheck(pabyCur, nPoints);
3013 478 : if (nPoints == 0)
3014 0 : return TRUE;
3015 478 : returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip));
3016 :
3017 : GUIntBig vxmin, vymin, vdx, vdy;
3018 :
3019 478 : returnErrorIf(pabyCur >= pabyEnd);
3020 478 : ReadVarUInt64NoCheck(pabyCur, vxmin);
3021 478 : if (vxmin > m_nFilterXMax)
3022 98 : return FALSE;
3023 380 : ReadVarUInt64NoCheck(pabyCur, vymin);
3024 380 : if (vymin > m_nFilterYMax)
3025 42 : return FALSE;
3026 338 : ReadVarUInt64NoCheck(pabyCur, vdx);
3027 338 : if (CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) < m_nFilterXMin)
3028 0 : return FALSE;
3029 338 : ReadVarUInt64NoCheck(pabyCur, vdy);
3030 338 : return CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) >= m_nFilterYMin;
3031 : }
3032 :
3033 : /************************************************************************/
3034 : /* FileGDBField::UNSET_FIELD */
3035 : /************************************************************************/
3036 :
3037 1848 : static OGRField GetUnsetField()
3038 : {
3039 : OGRField sUnsetField;
3040 1848 : OGR_RawField_SetUnset(&sUnsetField);
3041 1848 : return sUnsetField;
3042 : }
3043 :
3044 : const OGRField FileGDBField::UNSET_FIELD = GetUnsetField();
3045 :
3046 : /************************************************************************/
3047 : /* FileGDBField() */
3048 : /************************************************************************/
3049 :
3050 57989 : FileGDBField::FileGDBField(FileGDBTable *poParentIn) : m_poParent(poParentIn)
3051 : {
3052 57989 : OGR_RawField_SetUnset(&m_sDefault);
3053 57989 : }
3054 :
3055 : /************************************************************************/
3056 : /* FileGDBField() */
3057 : /************************************************************************/
3058 :
3059 14464 : FileGDBField::FileGDBField(const std::string &osName,
3060 : const std::string &osAlias, FileGDBFieldType eType,
3061 : bool bNullable, bool bRequired, bool bEditable,
3062 14464 : int nMaxWidth, const OGRField &sDefault)
3063 : : m_osName(osName), m_osAlias(osAlias), m_eType(eType),
3064 : m_bNullable(bNullable), m_bRequired(bRequired), m_bEditable(bEditable),
3065 14464 : m_nMaxWidth(nMaxWidth)
3066 : {
3067 14464 : if (m_eType == FGFT_OBJECTID || m_eType == FGFT_GLOBALID)
3068 : {
3069 2263 : CPLAssert(!m_bNullable);
3070 2263 : CPLAssert(m_bRequired);
3071 2263 : CPLAssert(!m_bEditable);
3072 : }
3073 :
3074 14480 : if (m_eType == FGFT_STRING && !OGR_RawField_IsUnset(&sDefault) &&
3075 16 : !OGR_RawField_IsNull(&sDefault))
3076 : {
3077 16 : m_sDefault.String = CPLStrdup(sDefault.String);
3078 : }
3079 : else
3080 : {
3081 14448 : m_sDefault = sDefault;
3082 : }
3083 14464 : }
3084 :
3085 : /************************************************************************/
3086 : /* ~FileGDBField() */
3087 : /************************************************************************/
3088 :
3089 141730 : FileGDBField::~FileGDBField()
3090 : {
3091 72483 : if (m_eType == FGFT_STRING && !OGR_RawField_IsUnset(&m_sDefault) &&
3092 30 : !OGR_RawField_IsNull(&m_sDefault))
3093 30 : CPLFree(m_sDefault.String);
3094 141730 : }
3095 :
3096 : /************************************************************************/
3097 : /* HasIndex() */
3098 : /************************************************************************/
3099 :
3100 1296 : int FileGDBField::HasIndex()
3101 : {
3102 1296 : m_poParent->GetIndexCount();
3103 1296 : return m_poIndex != nullptr;
3104 : }
3105 :
3106 : /************************************************************************/
3107 : /* GetIndex() */
3108 : /************************************************************************/
3109 :
3110 716 : FileGDBIndex *FileGDBField::GetIndex()
3111 : {
3112 716 : m_poParent->GetIndexCount();
3113 716 : return m_poIndex;
3114 : }
3115 :
3116 : /************************************************************************/
3117 : /* getESRI_NAN() */
3118 : /************************************************************************/
3119 :
3120 1848 : static double getESRI_NAN()
3121 : {
3122 : // Use exact same quiet NaN value as generated by the ESRI SDK, just
3123 : // for the purpose of ensuring binary identical output for some tests.
3124 : // I doubt this matter much which NaN is generated for usage.
3125 : // The reason is that std::numeric_limits<double>::quiet_NaN() on my
3126 : // platform has not the least significant bit set.
3127 1848 : constexpr uint64_t nNAN = (static_cast<uint64_t>(0x7FF80000U) << 32) | 1;
3128 : double v;
3129 1848 : memcpy(&v, &nNAN, sizeof(v));
3130 1848 : return v;
3131 : }
3132 :
3133 : const double FileGDBGeomField::ESRI_NAN = getESRI_NAN();
3134 :
3135 : /************************************************************************/
3136 : /* FileGDBGeomField() */
3137 : /************************************************************************/
3138 :
3139 2696 : FileGDBGeomField::FileGDBGeomField(FileGDBTable *poParentIn)
3140 2696 : : FileGDBField(poParentIn)
3141 : {
3142 2696 : }
3143 :
3144 : /************************************************************************/
3145 : /* FileGDBGeomField() */
3146 : /************************************************************************/
3147 :
3148 480 : FileGDBGeomField::FileGDBGeomField(
3149 : const std::string &osName, const std::string &osAlias, bool bNullable,
3150 : const std::string &osWKT, double dfXOrigin, double dfYOrigin,
3151 : double dfXYScale, double dfXYTolerance,
3152 480 : const std::vector<double> &adfSpatialIndexGridResolution)
3153 : : FileGDBField(osName, osAlias, FGFT_GEOMETRY, bNullable,
3154 : /* bRequired = */ true, /* bEditable = */ true, 0,
3155 : FileGDBField::UNSET_FIELD),
3156 : m_osWKT(osWKT), m_dfXOrigin(dfXOrigin), m_dfYOrigin(dfYOrigin),
3157 : m_dfXYScale(dfXYScale), m_dfXYTolerance(dfXYTolerance),
3158 480 : m_adfSpatialIndexGridResolution(adfSpatialIndexGridResolution)
3159 : {
3160 480 : }
3161 :
3162 : /************************************************************************/
3163 : /* SetXYMinMax() */
3164 : /************************************************************************/
3165 :
3166 4082 : void FileGDBGeomField::SetXYMinMax(double dfXMin, double dfYMin, double dfXMax,
3167 : double dfYMax)
3168 : {
3169 4082 : m_dfXMin = dfXMin;
3170 4082 : m_dfYMin = dfYMin;
3171 4082 : m_dfXMax = dfXMax;
3172 4082 : m_dfYMax = dfYMax;
3173 4082 : }
3174 :
3175 : /************************************************************************/
3176 : /* SetZMinMax() */
3177 : /************************************************************************/
3178 :
3179 4080 : void FileGDBGeomField::SetZMinMax(double dfZMin, double dfZMax)
3180 : {
3181 4080 : m_dfZMin = dfZMin;
3182 4080 : m_dfZMax = dfZMax;
3183 4080 : }
3184 :
3185 : /************************************************************************/
3186 : /* SetMMinMax() */
3187 : /************************************************************************/
3188 :
3189 0 : void FileGDBGeomField::SetMMinMax(double dfMMin, double dfMMax)
3190 : {
3191 0 : m_dfMMin = dfMMin;
3192 0 : m_dfMMax = dfMMax;
3193 0 : }
3194 :
3195 : /************************************************************************/
3196 : /* SetZOriginScaleTolerance() */
3197 : /************************************************************************/
3198 :
3199 480 : void FileGDBGeomField::SetZOriginScaleTolerance(double dfZOrigin,
3200 : double dfZScale,
3201 : double dfZTolerance)
3202 : {
3203 480 : m_bHasZOriginScaleTolerance = TRUE;
3204 480 : m_dfZOrigin = dfZOrigin;
3205 480 : m_dfZScale = dfZScale;
3206 480 : m_dfZTolerance = dfZTolerance;
3207 480 : }
3208 :
3209 : /************************************************************************/
3210 : /* SetMOriginScaleTolerance() */
3211 : /************************************************************************/
3212 :
3213 480 : void FileGDBGeomField::SetMOriginScaleTolerance(double dfMOrigin,
3214 : double dfMScale,
3215 : double dfMTolerance)
3216 : {
3217 480 : m_bHasMOriginScaleTolerance = TRUE;
3218 480 : m_dfMOrigin = dfMOrigin;
3219 480 : m_dfMScale = dfMScale;
3220 480 : m_dfMTolerance = dfMTolerance;
3221 480 : }
3222 :
3223 : FileGDBOGRGeometryConverter::~FileGDBOGRGeometryConverter() = default;
3224 :
3225 : /************************************************************************/
3226 : /* FileGDBOGRGeometryConverterImpl */
3227 : /************************************************************************/
3228 :
3229 : class FileGDBOGRGeometryConverterImpl final : public FileGDBOGRGeometryConverter
3230 : {
3231 : const FileGDBGeomField *poGeomField;
3232 : GUInt32 *panPointCount = nullptr;
3233 : GUInt32 nPointCountMax = 0;
3234 : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
3235 : int bUseOrganize = 0;
3236 : #endif
3237 :
3238 : bool ReadPartDefs(GByte *&pabyCur, GByte *pabyEnd, GUInt32 &nPoints,
3239 : GUInt32 &nParts, GUInt32 &nCurves, bool bHasCurveDesc,
3240 : bool bIsMultiPatch);
3241 : template <class XYSetter>
3242 : int ReadXYArray(XYSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
3243 : GUInt32 nPoints, GIntBig &dx, GIntBig &dy);
3244 : template <class ZSetter>
3245 : int ReadZArray(ZSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
3246 : GUInt32 nPoints, GIntBig &dz);
3247 : template <class MSetter>
3248 : int ReadMArray(MSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
3249 : GUInt32 nPoints, GIntBig &dm);
3250 :
3251 : OGRGeometry *CreateCurveGeometry(GUInt32 nBaseShapeType, GUInt32 nParts,
3252 : GUInt32 nPoints, GUInt32 nCurves,
3253 : bool bHasZ, bool bHasM, GByte *&pabyCur,
3254 : GByte *pabyEnd);
3255 :
3256 : FileGDBOGRGeometryConverterImpl(const FileGDBOGRGeometryConverterImpl &) =
3257 : delete;
3258 : FileGDBOGRGeometryConverterImpl &
3259 : operator=(const FileGDBOGRGeometryConverterImpl &) = delete;
3260 :
3261 : public:
3262 : explicit FileGDBOGRGeometryConverterImpl(
3263 : const FileGDBGeomField *poGeomField);
3264 : ~FileGDBOGRGeometryConverterImpl() override;
3265 :
3266 : OGRGeometry *GetAsGeometry(const OGRField *psField) override;
3267 : };
3268 :
3269 : /************************************************************************/
3270 : /* FileGDBOGRGeometryConverterImpl() */
3271 : /************************************************************************/
3272 :
3273 970 : FileGDBOGRGeometryConverterImpl::FileGDBOGRGeometryConverterImpl(
3274 970 : const FileGDBGeomField *poGeomFieldIn)
3275 970 : : poGeomField(poGeomFieldIn)
3276 : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
3277 : ,
3278 : bUseOrganize(CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", NULL) != NULL)
3279 : #endif
3280 : {
3281 970 : }
3282 :
3283 : /************************************************************************/
3284 : /* ~FileGDBOGRGeometryConverter() */
3285 : /************************************************************************/
3286 :
3287 1940 : FileGDBOGRGeometryConverterImpl::~FileGDBOGRGeometryConverterImpl()
3288 : {
3289 970 : CPLFree(panPointCount);
3290 1940 : }
3291 :
3292 : /************************************************************************/
3293 : /* ReadPartDefs() */
3294 : /************************************************************************/
3295 :
3296 5633 : bool FileGDBOGRGeometryConverterImpl::ReadPartDefs(
3297 : GByte *&pabyCur, GByte *pabyEnd, GUInt32 &nPoints, GUInt32 &nParts,
3298 : GUInt32 &nCurves, bool bHasCurveDesc, bool bIsMultiPatch)
3299 : {
3300 5633 : const bool errorRetValue = false;
3301 5633 : returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints));
3302 5633 : if (nPoints == 0)
3303 : {
3304 10 : nParts = 0;
3305 10 : nCurves = 0;
3306 10 : return true;
3307 : }
3308 5623 : returnErrorIf(nPoints > static_cast<size_t>(pabyEnd - pabyCur));
3309 5623 : if (bIsMultiPatch)
3310 564 : returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd));
3311 5623 : returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nParts));
3312 5623 : returnErrorIf(nParts > static_cast<size_t>(pabyEnd - pabyCur));
3313 5623 : returnErrorIf(nParts > static_cast<GUInt32>(INT_MAX) / sizeof(GUInt32) - 1);
3314 5623 : if (bHasCurveDesc)
3315 : {
3316 71 : returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nCurves));
3317 71 : returnErrorIf(nCurves > static_cast<size_t>(pabyEnd - pabyCur));
3318 : }
3319 : else
3320 5552 : nCurves = 0;
3321 5623 : if (nParts == 0)
3322 0 : return true;
3323 : GUInt32 i;
3324 5623 : returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4));
3325 5623 : if (nParts > nPointCountMax)
3326 : {
3327 : GUInt32 *panPointCountNew = static_cast<GUInt32 *>(
3328 315 : VSI_REALLOC_VERBOSE(panPointCount, nParts * sizeof(GUInt32)));
3329 315 : returnErrorIf(panPointCountNew == nullptr);
3330 315 : panPointCount = panPointCountNew;
3331 315 : nPointCountMax = nParts;
3332 : }
3333 5623 : GUIntBig nSumNPartsM1 = 0;
3334 9146 : for (i = 0; i < nParts - 1; i++)
3335 : {
3336 : GUInt32 nTmp;
3337 3523 : returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nTmp));
3338 3523 : returnErrorIf(nTmp > static_cast<size_t>(pabyEnd - pabyCur));
3339 3523 : panPointCount[i] = nTmp;
3340 3523 : nSumNPartsM1 += nTmp;
3341 : }
3342 5623 : returnErrorIf(nSumNPartsM1 > nPoints);
3343 5623 : panPointCount[nParts - 1] = static_cast<GUInt32>(nPoints - nSumNPartsM1);
3344 :
3345 5623 : return true;
3346 : }
3347 :
3348 : /************************************************************************/
3349 : /* XYLineStringSetter */
3350 : /************************************************************************/
3351 :
3352 4620 : class FileGDBOGRLineString final : public OGRLineString
3353 : {
3354 : public:
3355 2310 : FileGDBOGRLineString() = default;
3356 :
3357 : ~FileGDBOGRLineString() override;
3358 :
3359 2310 : OGRRawPoint *GetPoints() const
3360 : {
3361 2310 : return paoPoints;
3362 : }
3363 : };
3364 :
3365 : FileGDBOGRLineString::~FileGDBOGRLineString() = default;
3366 :
3367 7846 : class FileGDBOGRLinearRing final : public OGRLinearRing
3368 : {
3369 : public:
3370 3923 : FileGDBOGRLinearRing() = default;
3371 :
3372 : ~FileGDBOGRLinearRing() override;
3373 :
3374 3923 : OGRRawPoint *GetPoints() const
3375 : {
3376 3923 : return paoPoints;
3377 : }
3378 : };
3379 :
3380 : FileGDBOGRLinearRing::~FileGDBOGRLinearRing() = default;
3381 :
3382 : class XYLineStringSetter
3383 : {
3384 : OGRRawPoint *paoPoints;
3385 :
3386 : public:
3387 6233 : explicit XYLineStringSetter(OGRRawPoint *paoPointsIn)
3388 6233 : : paoPoints(paoPointsIn)
3389 : {
3390 6233 : }
3391 :
3392 33186 : void set(int i, double dfX, double dfY)
3393 : {
3394 33186 : paoPoints[i].x = dfX;
3395 33186 : paoPoints[i].y = dfY;
3396 33186 : }
3397 : };
3398 :
3399 : /************************************************************************/
3400 : /* XYMultiPointSetter */
3401 : /************************************************************************/
3402 :
3403 : class XYMultiPointSetter
3404 : {
3405 : OGRMultiPoint *poMPoint;
3406 :
3407 : public:
3408 894 : explicit XYMultiPointSetter(OGRMultiPoint *poMPointIn)
3409 894 : : poMPoint(poMPointIn)
3410 : {
3411 894 : }
3412 :
3413 1794 : void set(int i, double dfX, double dfY)
3414 : {
3415 : (void)i;
3416 1794 : poMPoint->addGeometryDirectly(new OGRPoint(dfX, dfY));
3417 1794 : }
3418 : };
3419 :
3420 : /************************************************************************/
3421 : /* XYArraySetter */
3422 : /************************************************************************/
3423 :
3424 : class XYArraySetter
3425 : {
3426 : double *padfX;
3427 : double *padfY;
3428 :
3429 : public:
3430 564 : XYArraySetter(double *padfXIn, double *padfYIn)
3431 564 : : padfX(padfXIn), padfY(padfYIn)
3432 : {
3433 564 : }
3434 :
3435 11816 : void set(int i, double dfX, double dfY)
3436 : {
3437 11816 : padfX[i] = dfX;
3438 11816 : padfY[i] = dfY;
3439 11816 : }
3440 : };
3441 :
3442 : /************************************************************************/
3443 : /* ReadXYArray() */
3444 : /************************************************************************/
3445 :
3446 : template <class XYSetter>
3447 7762 : int FileGDBOGRGeometryConverterImpl::ReadXYArray(XYSetter &setter,
3448 : GByte *&pabyCur,
3449 : GByte *pabyEnd,
3450 : GUInt32 nPoints, GIntBig &dx,
3451 : GIntBig &dy)
3452 : {
3453 7762 : const int errorRetValue = FALSE;
3454 7762 : GIntBig dxLocal = dx;
3455 7762 : GIntBig dyLocal = dy;
3456 :
3457 55081 : for (GUInt32 i = 0; i < nPoints; i++)
3458 : {
3459 47319 : returnErrorIf(pabyCur /*+ 1*/ >= pabyEnd);
3460 :
3461 47319 : ReadVarIntAndAddNoCheck(pabyCur, dxLocal);
3462 47319 : ReadVarIntAndAddNoCheck(pabyCur, dyLocal);
3463 :
3464 47319 : double dfX =
3465 47319 : dxLocal / poGeomField->GetXYScale() + poGeomField->GetXOrigin();
3466 47319 : double dfY =
3467 47319 : dyLocal / poGeomField->GetXYScale() + poGeomField->GetYOrigin();
3468 47319 : setter.set(i, dfX, dfY);
3469 : }
3470 :
3471 7762 : dx = dxLocal;
3472 7762 : dy = dyLocal;
3473 7762 : return TRUE;
3474 : }
3475 :
3476 : /************************************************************************/
3477 : /* ZLineStringSetter */
3478 : /************************************************************************/
3479 :
3480 : class ZLineStringSetter
3481 : {
3482 : OGRLineString *poLS;
3483 :
3484 : public:
3485 2127 : explicit ZLineStringSetter(OGRLineString *poLSIn) : poLS(poLSIn)
3486 : {
3487 2127 : }
3488 :
3489 7220 : void set(int i, double dfZ)
3490 : {
3491 7220 : poLS->setZ(i, dfZ);
3492 7220 : }
3493 : };
3494 :
3495 : /************************************************************************/
3496 : /* ZMultiPointSetter */
3497 : /************************************************************************/
3498 :
3499 : class ZMultiPointSetter
3500 : {
3501 : OGRMultiPoint *poMPoint;
3502 :
3503 : public:
3504 446 : explicit ZMultiPointSetter(OGRMultiPoint *poMPointIn) : poMPoint(poMPointIn)
3505 : {
3506 446 : }
3507 :
3508 896 : void set(int i, double dfZ)
3509 : {
3510 896 : poMPoint->getGeometryRef(i)->setZ(dfZ);
3511 896 : }
3512 : };
3513 :
3514 : /************************************************************************/
3515 : /* FileGDBArraySetter */
3516 : /************************************************************************/
3517 :
3518 : class FileGDBArraySetter
3519 : {
3520 : double *padfValues;
3521 :
3522 : public:
3523 564 : explicit FileGDBArraySetter(double *padfValuesIn) : padfValues(padfValuesIn)
3524 : {
3525 564 : }
3526 :
3527 11816 : void set(int i, double dfValue)
3528 : {
3529 11816 : padfValues[i] = dfValue;
3530 11816 : }
3531 : };
3532 :
3533 : /************************************************************************/
3534 : /* ReadZArray() */
3535 : /************************************************************************/
3536 :
3537 : template <class ZSetter>
3538 3153 : int FileGDBOGRGeometryConverterImpl::ReadZArray(ZSetter &setter,
3539 : GByte *&pabyCur, GByte *pabyEnd,
3540 : GUInt32 nPoints, GIntBig &dz)
3541 : {
3542 3153 : const int errorRetValue = FALSE;
3543 3153 : const double dfZScale = SanitizeScale(poGeomField->GetZScale());
3544 23159 : for (GUInt32 i = 0; i < nPoints; i++)
3545 : {
3546 20006 : returnErrorIf(pabyCur >= pabyEnd);
3547 20006 : ReadVarIntAndAddNoCheck(pabyCur, dz);
3548 :
3549 20006 : double dfZ = dz / dfZScale + poGeomField->GetZOrigin();
3550 20006 : setter.set(i, dfZ);
3551 : }
3552 3153 : return TRUE;
3553 : }
3554 :
3555 : /************************************************************************/
3556 : /* MLineStringSetter */
3557 : /************************************************************************/
3558 :
3559 : class MLineStringSetter
3560 : {
3561 : OGRLineString *poLS;
3562 :
3563 : public:
3564 244 : explicit MLineStringSetter(OGRLineString *poLSIn) : poLS(poLSIn)
3565 : {
3566 244 : }
3567 :
3568 868 : void set(int i, double dfM)
3569 : {
3570 868 : poLS->setM(i, dfM);
3571 868 : }
3572 : };
3573 :
3574 : /************************************************************************/
3575 : /* MMultiPointSetter */
3576 : /************************************************************************/
3577 :
3578 : class MMultiPointSetter
3579 : {
3580 : OGRMultiPoint *poMPoint;
3581 :
3582 : public:
3583 56 : explicit MMultiPointSetter(OGRMultiPoint *poMPointIn) : poMPoint(poMPointIn)
3584 : {
3585 56 : }
3586 :
3587 118 : void set(int i, double dfM)
3588 : {
3589 118 : poMPoint->getGeometryRef(i)->setM(dfM);
3590 118 : }
3591 : };
3592 :
3593 : /************************************************************************/
3594 : /* ReadMArray() */
3595 : /************************************************************************/
3596 :
3597 : template <class MSetter>
3598 314 : int FileGDBOGRGeometryConverterImpl::ReadMArray(MSetter &setter,
3599 : GByte *&pabyCur, GByte *pabyEnd,
3600 : GUInt32 nPoints, GIntBig &dm)
3601 : {
3602 314 : const int errorRetValue = FALSE;
3603 314 : const double dfMScale = SanitizeScale(poGeomField->GetMScale());
3604 1364 : for (GUInt32 i = 0; i < nPoints; i++)
3605 : {
3606 1050 : returnErrorIf(pabyCur >= pabyEnd);
3607 1050 : ReadVarIntAndAddNoCheck(pabyCur, dm);
3608 :
3609 1050 : double dfM = dm / dfMScale + poGeomField->GetMOrigin();
3610 1050 : setter.set(i, dfM);
3611 : }
3612 314 : return TRUE;
3613 : }
3614 :
3615 : /************************************************************************/
3616 : /* CreateCurveGeometry() */
3617 : /************************************************************************/
3618 :
3619 : class XYBufferSetter
3620 : {
3621 : GByte *pabyBuffer;
3622 :
3623 : public:
3624 71 : explicit XYBufferSetter(GByte *pabyBufferIn) : pabyBuffer(pabyBufferIn)
3625 : {
3626 71 : }
3627 :
3628 523 : void set(int i, double dfX, double dfY)
3629 : {
3630 523 : CPL_LSBPTR64(&dfX);
3631 523 : memcpy(pabyBuffer + 16 * i, &dfX, 8);
3632 523 : CPL_LSBPTR64(&dfY);
3633 523 : memcpy(pabyBuffer + 16 * i + 8, &dfY, 8);
3634 523 : }
3635 : };
3636 :
3637 : class ZOrMBufferSetter
3638 : {
3639 : GByte *pabyBuffer;
3640 :
3641 : public:
3642 30 : explicit ZOrMBufferSetter(GByte *pabyBufferIn) : pabyBuffer(pabyBufferIn)
3643 : {
3644 30 : }
3645 :
3646 138 : void set(int i, double dfValue)
3647 : {
3648 138 : CPL_LSBPTR64(&dfValue);
3649 138 : memcpy(pabyBuffer + 8 * i, &dfValue, 8);
3650 138 : }
3651 : };
3652 :
3653 : /* We first create an extended shape buffer from the compressed stream */
3654 : /* and finally use OGRCreateFromShapeBin() to make a geometry from it */
3655 :
3656 71 : OGRGeometry *FileGDBOGRGeometryConverterImpl::CreateCurveGeometry(
3657 : GUInt32 nBaseShapeType, GUInt32 nParts, GUInt32 nPoints, GUInt32 nCurves,
3658 : bool bHasZ, bool bHasM, GByte *&pabyCur, GByte *pabyEnd)
3659 : {
3660 71 : OGRGeometry *errorRetValue = nullptr;
3661 : GUInt32 i;
3662 71 : const int nDims = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
3663 71 : GIntBig nMaxSize64 = 44 + 4 * static_cast<GUIntBig>(nParts) +
3664 71 : 8 * nDims * static_cast<GUIntBig>(nPoints);
3665 71 : nMaxSize64 += 4; // nCurves
3666 71 : nMaxSize64 +=
3667 71 : static_cast<GUIntBig>(nCurves) * (4 + /* start index */
3668 : 4 + /* curve type */
3669 : 44 /* size of ellipse struct */);
3670 71 : nMaxSize64 +=
3671 71 : ((bHasZ ? 1 : 0) + (bHasM ? 1 : 0)) * 16; // space for bounding boxes
3672 71 : if (nMaxSize64 >= INT_MAX)
3673 : {
3674 0 : returnError();
3675 : }
3676 71 : const int nMaxSize = static_cast<int>(nMaxSize64 & INT_MAX);
3677 : // coverity[overflow_sink]
3678 : GByte *pabyExtShapeBuffer =
3679 71 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nMaxSize));
3680 71 : if (pabyExtShapeBuffer == nullptr)
3681 : {
3682 0 : VSIFree(pabyExtShapeBuffer);
3683 0 : returnError();
3684 : }
3685 71 : GUInt32 nShapeType = nBaseShapeType | EXT_SHAPE_CURVE_FLAG;
3686 71 : if (bHasZ)
3687 16 : nShapeType |= EXT_SHAPE_Z_FLAG;
3688 71 : if (bHasM)
3689 16 : nShapeType |= EXT_SHAPE_M_FLAG;
3690 : GUInt32 nTmp;
3691 71 : nTmp = CPL_LSBWORD32(nShapeType);
3692 71 : GByte *pabyShapeTypePtr = pabyExtShapeBuffer;
3693 71 : memcpy(pabyExtShapeBuffer, &nTmp, 4);
3694 71 : memset(pabyExtShapeBuffer + 4, 0, 32); /* bbox: unused */
3695 71 : nTmp = CPL_LSBWORD32(nParts);
3696 71 : memcpy(pabyExtShapeBuffer + 36, &nTmp, 4);
3697 71 : nTmp = CPL_LSBWORD32(nPoints);
3698 71 : memcpy(pabyExtShapeBuffer + 40, &nTmp, 4);
3699 71 : GUInt32 nIdx = 0;
3700 170 : for (i = 0; i < nParts; i++)
3701 : {
3702 99 : nTmp = CPL_LSBWORD32(nIdx);
3703 99 : nIdx += panPointCount[i];
3704 99 : memcpy(pabyExtShapeBuffer + 44 + 4 * i, &nTmp, 4);
3705 : }
3706 71 : int nOffset = 44 + 4 * nParts;
3707 71 : GIntBig dx = 0;
3708 71 : GIntBig dy = 0;
3709 71 : XYBufferSetter arraySetter(pabyExtShapeBuffer + nOffset);
3710 71 : if (!ReadXYArray<XYBufferSetter>(arraySetter, pabyCur, pabyEnd, nPoints, dx,
3711 : dy))
3712 : {
3713 0 : VSIFree(pabyExtShapeBuffer);
3714 0 : returnError();
3715 : }
3716 71 : nOffset += 16 * nPoints;
3717 :
3718 71 : if (bHasZ)
3719 : {
3720 16 : memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
3721 16 : nOffset += 16;
3722 16 : GIntBig dz = 0;
3723 16 : ZOrMBufferSetter arrayzSetter(pabyExtShapeBuffer + nOffset);
3724 16 : if (!ReadZArray<ZOrMBufferSetter>(arrayzSetter, pabyCur, pabyEnd,
3725 : nPoints, dz))
3726 : {
3727 0 : VSIFree(pabyExtShapeBuffer);
3728 0 : returnError();
3729 : }
3730 16 : nOffset += 8 * nPoints;
3731 : }
3732 :
3733 71 : if (bHasM)
3734 : {
3735 : // It seems that absence of M is marked with a single byte
3736 : // with value 66.
3737 16 : if (*pabyCur == 66)
3738 : {
3739 2 : pabyCur++;
3740 : #if 1
3741 : // In other code paths of this file, we drop the M component when
3742 : // it is at null. The disabled code path would fill it with NaN
3743 : // instead.
3744 2 : nShapeType &= ~EXT_SHAPE_M_FLAG;
3745 2 : nTmp = CPL_LSBWORD32(nShapeType);
3746 2 : memcpy(pabyShapeTypePtr, &nTmp, 4);
3747 : #else
3748 : memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
3749 : nOffset += 16;
3750 : const double myNan = std::numeric_limits<double>::quiet_NaN();
3751 : for (i = 0; i < nPoints; i++)
3752 : {
3753 : memcpy(pabyExtShapeBuffer + nOffset + 8 * i, &myNan, 8);
3754 : CPL_LSBPTR64(pabyExtShapeBuffer + nOffset + 8 * i);
3755 : }
3756 : nOffset += 8 * nPoints;
3757 : #endif
3758 : }
3759 : else
3760 : {
3761 14 : memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
3762 14 : nOffset += 16;
3763 14 : ZOrMBufferSetter arraymSetter(pabyExtShapeBuffer + nOffset);
3764 14 : GIntBig dm = 0;
3765 14 : if (!ReadMArray<ZOrMBufferSetter>(arraymSetter, pabyCur, pabyEnd,
3766 : nPoints, dm))
3767 : {
3768 0 : VSIFree(pabyExtShapeBuffer);
3769 0 : returnError();
3770 : }
3771 14 : nOffset += 8 * nPoints;
3772 : }
3773 : }
3774 :
3775 71 : nTmp = CPL_LSBWORD32(nCurves);
3776 71 : memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
3777 71 : nOffset += 4;
3778 198 : for (i = 0; i < nCurves; i++)
3779 : {
3780 : // start index
3781 127 : returnErrorAndCleanupIf(!ReadVarUInt32(pabyCur, pabyEnd, nTmp),
3782 : VSIFree(pabyExtShapeBuffer));
3783 127 : CPL_LSBPTR32(&nTmp);
3784 127 : memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
3785 127 : nOffset += 4;
3786 :
3787 : GUInt32 nCurveType;
3788 127 : returnErrorAndCleanupIf(!ReadVarUInt32(pabyCur, pabyEnd, nCurveType),
3789 : VSIFree(pabyExtShapeBuffer));
3790 127 : nTmp = CPL_LSBWORD32(nCurveType);
3791 127 : memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
3792 127 : nOffset += 4;
3793 :
3794 127 : int nStructureSize = 0;
3795 127 : if (nCurveType == EXT_SHAPE_SEGMENT_ARC)
3796 101 : nStructureSize = 2 * 8 + 4;
3797 26 : else if (nCurveType == EXT_SHAPE_SEGMENT_BEZIER)
3798 19 : nStructureSize = 4 * 8;
3799 7 : else if (nCurveType == EXT_SHAPE_SEGMENT_ELLIPSE)
3800 7 : nStructureSize = 5 * 8 + 4;
3801 127 : if (nStructureSize == 0 || pabyCur + nStructureSize > pabyEnd)
3802 : {
3803 0 : VSIFree(pabyExtShapeBuffer);
3804 0 : returnError();
3805 : }
3806 127 : memcpy(pabyExtShapeBuffer + nOffset, pabyCur, nStructureSize);
3807 127 : pabyCur += nStructureSize;
3808 127 : nOffset += nStructureSize;
3809 : }
3810 71 : CPLAssert(nOffset <= nMaxSize);
3811 :
3812 71 : OGRGeometry *poRet = nullptr;
3813 71 : OGRCreateFromShapeBin(pabyExtShapeBuffer, &poRet, nOffset,
3814 : /* pszOrganizePolygonsMethod = */ "DEFAULT");
3815 71 : VSIFree(pabyExtShapeBuffer);
3816 71 : return poRet;
3817 : }
3818 :
3819 : /************************************************************************/
3820 : /* GetAsGeometry() */
3821 : /************************************************************************/
3822 :
3823 : OGRGeometry *
3824 15753 : FileGDBOGRGeometryConverterImpl::GetAsGeometry(const OGRField *psField)
3825 : {
3826 15753 : OGRGeometry *errorRetValue = nullptr;
3827 15753 : GByte *pabyCur = psField->Binary.paData;
3828 15753 : GByte *pabyEnd = pabyCur + psField->Binary.nCount;
3829 : GUInt32 nGeomType, i, nPoints, nParts, nCurves;
3830 : GUIntBig x, y, z;
3831 : GIntBig dx, dy, dz;
3832 :
3833 15753 : ReadVarUInt32NoCheck(pabyCur, nGeomType);
3834 :
3835 15753 : bool bHasZ = (nGeomType & EXT_SHAPE_Z_FLAG) != 0;
3836 15753 : bool bHasM = (nGeomType & EXT_SHAPE_M_FLAG) != 0;
3837 15753 : switch ((nGeomType & 0xff))
3838 : {
3839 0 : case SHPT_NULL:
3840 0 : return nullptr;
3841 :
3842 458 : case SHPT_POINTZ:
3843 : case SHPT_POINTZM:
3844 458 : bHasZ = true; /* go on */
3845 : [[fallthrough]];
3846 9226 : case SHPT_POINT:
3847 : case SHPT_POINTM:
3848 : case SHPT_GENERALPOINT:
3849 : {
3850 9226 : if (nGeomType == SHPT_POINTM || nGeomType == SHPT_POINTZM)
3851 58 : bHasM = true;
3852 :
3853 9226 : ReadVarUInt64NoCheck(pabyCur, x);
3854 9226 : ReadVarUInt64NoCheck(pabyCur, y);
3855 :
3856 18444 : const double dfX = x == 0 ? std::numeric_limits<double>::quiet_NaN()
3857 9218 : : (x - 1U) / poGeomField->GetXYScale() +
3858 9218 : poGeomField->GetXOrigin();
3859 18444 : const double dfY = y == 0 ? std::numeric_limits<double>::quiet_NaN()
3860 9218 : : (y - 1U) / poGeomField->GetXYScale() +
3861 9218 : poGeomField->GetYOrigin();
3862 9226 : if (bHasZ)
3863 : {
3864 458 : ReadVarUInt64NoCheck(pabyCur, z);
3865 458 : const double dfZScale = SanitizeScale(poGeomField->GetZScale());
3866 : const double dfZ =
3867 912 : z == 0 ? std::numeric_limits<double>::quiet_NaN()
3868 454 : : (z - 1U) / dfZScale + poGeomField->GetZOrigin();
3869 458 : if (bHasM)
3870 : {
3871 30 : GUIntBig m = 0;
3872 30 : ReadVarUInt64NoCheck(pabyCur, m);
3873 : const double dfMScale =
3874 30 : SanitizeScale(poGeomField->GetMScale());
3875 30 : if (m == 0)
3876 : {
3877 : return new OGRPoint(
3878 : dfX, dfY, dfZ,
3879 2 : std::numeric_limits<double>::quiet_NaN());
3880 : }
3881 : else
3882 : {
3883 28 : assert(m >= 1U);
3884 : const double dfM =
3885 28 : (m - 1U) / dfMScale + poGeomField->GetMOrigin();
3886 28 : return new OGRPoint(dfX, dfY, dfZ, dfM);
3887 : }
3888 : }
3889 428 : return new OGRPoint(dfX, dfY, dfZ);
3890 : }
3891 8768 : else if (bHasM)
3892 : {
3893 28 : OGRPoint *poPoint = new OGRPoint(dfX, dfY);
3894 28 : GUIntBig m = 0;
3895 28 : ReadVarUInt64NoCheck(pabyCur, m);
3896 28 : const double dfMScale = SanitizeScale(poGeomField->GetMScale());
3897 : const double dfM =
3898 54 : m == 0 ? std::numeric_limits<double>::quiet_NaN()
3899 26 : : (m - 1U) / dfMScale + poGeomField->GetMOrigin();
3900 28 : poPoint->setM(dfM);
3901 28 : return poPoint;
3902 : }
3903 8740 : return new OGRPoint(dfX, dfY);
3904 : }
3905 :
3906 446 : case SHPT_MULTIPOINTZM:
3907 : case SHPT_MULTIPOINTZ:
3908 446 : bHasZ = true; /* go on */
3909 : [[fallthrough]];
3910 894 : case SHPT_MULTIPOINT:
3911 : case SHPT_MULTIPOINTM:
3912 : {
3913 894 : if (nGeomType == SHPT_MULTIPOINTM || nGeomType == SHPT_MULTIPOINTZM)
3914 56 : bHasM = true;
3915 :
3916 894 : returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints));
3917 894 : if (nPoints == 0)
3918 : {
3919 0 : OGRMultiPoint *poMP = new OGRMultiPoint();
3920 0 : if (bHasZ)
3921 0 : poMP->set3D(TRUE);
3922 0 : if (bHasM)
3923 0 : poMP->setMeasured(TRUE);
3924 0 : return poMP;
3925 : }
3926 :
3927 894 : returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4));
3928 :
3929 894 : dx = dy = dz = 0;
3930 :
3931 894 : OGRMultiPoint *poMP = new OGRMultiPoint();
3932 894 : XYMultiPointSetter mpSetter(poMP);
3933 894 : if (!ReadXYArray<XYMultiPointSetter>(mpSetter, pabyCur, pabyEnd,
3934 : nPoints, dx, dy))
3935 : {
3936 0 : delete poMP;
3937 0 : returnError();
3938 : }
3939 :
3940 894 : if (bHasZ)
3941 : {
3942 446 : poMP->setCoordinateDimension(3);
3943 446 : ZMultiPointSetter mpzSetter(poMP);
3944 446 : if (!ReadZArray<ZMultiPointSetter>(mpzSetter, pabyCur, pabyEnd,
3945 : nPoints, dz))
3946 : {
3947 0 : delete poMP;
3948 0 : returnError();
3949 : }
3950 : }
3951 :
3952 : // It seems that absence of M is marked with a single byte
3953 : // with value 66. Be more tolerant and only try to parse the M
3954 : // array is there are at least as many remaining bytes as
3955 : // expected points
3956 894 : if (bHasM && pabyCur + nPoints <= pabyEnd)
3957 : {
3958 56 : poMP->setMeasured(TRUE);
3959 56 : GIntBig dm = 0;
3960 56 : MMultiPointSetter mpmSetter(poMP);
3961 56 : if (!ReadMArray<MMultiPointSetter>(mpmSetter, pabyCur, pabyEnd,
3962 : nPoints, dm))
3963 : {
3964 0 : delete poMP;
3965 0 : returnError();
3966 : }
3967 : }
3968 :
3969 894 : return poMP;
3970 : }
3971 :
3972 1006 : case SHPT_ARCZ:
3973 : case SHPT_ARCZM:
3974 1006 : bHasZ = true; /* go on */
3975 : [[fallthrough]];
3976 2056 : case SHPT_ARC:
3977 : case SHPT_ARCM:
3978 : case SHPT_GENERALPOLYLINE:
3979 : {
3980 2056 : if (nGeomType == SHPT_ARCM || nGeomType == SHPT_ARCZM)
3981 115 : bHasM = true;
3982 :
3983 2056 : returnErrorIf(
3984 : !ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
3985 : (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0, false));
3986 :
3987 2056 : if (nPoints == 0 || nParts == 0)
3988 : {
3989 4 : OGRLineString *poLS = new OGRLineString();
3990 4 : if (bHasZ)
3991 2 : poLS->set3D(TRUE);
3992 4 : if (bHasM)
3993 2 : poLS->setMeasured(TRUE);
3994 4 : return poLS;
3995 : }
3996 :
3997 2052 : if (nCurves)
3998 : {
3999 30 : GByte *pabyCurBackup = pabyCur;
4000 30 : OGRGeometry *poRet = CreateCurveGeometry(
4001 : SHPT_GENERALPOLYLINE, nParts, nPoints, nCurves, bHasZ,
4002 : bHasM, pabyCur, pabyEnd);
4003 30 : if (poRet)
4004 30 : return poRet;
4005 : // In case something went wrong, go on without curves
4006 0 : pabyCur = pabyCurBackup;
4007 : }
4008 :
4009 2022 : OGRMultiLineString *poMLS = nullptr;
4010 2022 : FileGDBOGRLineString *poLS = nullptr;
4011 2022 : if (nParts > 1)
4012 : {
4013 288 : poMLS = new OGRMultiLineString();
4014 288 : if (bHasZ)
4015 137 : poMLS->set3D(TRUE);
4016 288 : if (bHasM)
4017 6 : poMLS->setMeasured(TRUE);
4018 : }
4019 :
4020 2022 : dx = dy = dz = 0;
4021 4332 : for (i = 0; i < nParts; i++)
4022 : {
4023 2310 : poLS = new FileGDBOGRLineString();
4024 2310 : poLS->setNumPoints(panPointCount[i], FALSE);
4025 2310 : if (nParts > 1)
4026 576 : poMLS->addGeometryDirectly(poLS);
4027 :
4028 2310 : XYLineStringSetter lsSetter(poLS->GetPoints());
4029 2310 : if (!ReadXYArray<XYLineStringSetter>(lsSetter, pabyCur, pabyEnd,
4030 2310 : panPointCount[i], dx, dy))
4031 : {
4032 0 : if (nParts > 1)
4033 0 : delete poMLS;
4034 : else
4035 0 : delete poLS;
4036 0 : returnError();
4037 : }
4038 : }
4039 :
4040 2022 : if (bHasZ)
4041 : {
4042 2145 : for (i = 0; i < nParts; i++)
4043 : {
4044 1141 : if (nParts > 1)
4045 274 : poLS = cpl::down_cast<FileGDBOGRLineString *>(
4046 : poMLS->getGeometryRef(i));
4047 :
4048 1141 : ZLineStringSetter lszSetter(poLS);
4049 1141 : if (!ReadZArray<ZLineStringSetter>(
4050 1141 : lszSetter, pabyCur, pabyEnd, panPointCount[i], dz))
4051 : {
4052 0 : if (nParts > 1)
4053 0 : delete poMLS;
4054 : else
4055 0 : delete poLS;
4056 0 : returnError();
4057 : }
4058 : }
4059 : }
4060 :
4061 2022 : if (bHasM)
4062 : {
4063 113 : GIntBig dm = 0;
4064 231 : for (i = 0; i < nParts; i++)
4065 : {
4066 119 : if (nParts > 1)
4067 12 : poLS = cpl::down_cast<FileGDBOGRLineString *>(
4068 : poMLS->getGeometryRef(i));
4069 :
4070 : // It seems that absence of M is marked with a single byte
4071 : // with value 66. Be more tolerant and only try to parse the
4072 : // M array is there are at least as many remaining bytes as
4073 : // expected points
4074 119 : if (pabyCur + panPointCount[i] > pabyEnd)
4075 : {
4076 1 : if (nParts > 1)
4077 0 : poMLS->setMeasured(FALSE);
4078 1 : break;
4079 : }
4080 :
4081 118 : MLineStringSetter lsmSetter(poLS);
4082 118 : if (!ReadMArray<MLineStringSetter>(
4083 118 : lsmSetter, pabyCur, pabyEnd, panPointCount[i], dm))
4084 : {
4085 0 : if (nParts > 1)
4086 0 : delete poMLS;
4087 : else
4088 0 : delete poLS;
4089 0 : returnError();
4090 : }
4091 : }
4092 : }
4093 :
4094 2022 : return poMLS ? static_cast<OGRGeometry *>(poMLS)
4095 2022 : : static_cast<OGRGeometry *>(poLS);
4096 : }
4097 :
4098 982 : case SHPT_POLYGONZ:
4099 : case SHPT_POLYGONZM:
4100 982 : bHasZ = true; /* go on */
4101 : [[fallthrough]];
4102 3013 : case SHPT_POLYGON:
4103 : case SHPT_POLYGONM:
4104 : case SHPT_GENERALPOLYGON:
4105 : {
4106 3013 : if (nGeomType == SHPT_POLYGONM || nGeomType == SHPT_POLYGONZM)
4107 124 : bHasM = true;
4108 :
4109 3013 : returnErrorIf(
4110 : !ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
4111 : (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0, false));
4112 :
4113 3013 : if (nPoints == 0 || nParts == 0)
4114 : {
4115 6 : OGRPolygon *poPoly = new OGRPolygon();
4116 6 : if (bHasZ)
4117 2 : poPoly->set3D(TRUE);
4118 6 : if (bHasM)
4119 2 : poPoly->setMeasured(TRUE);
4120 6 : return poPoly;
4121 : }
4122 :
4123 3007 : if (nCurves)
4124 : {
4125 41 : GByte *pabyCurBackup = pabyCur;
4126 41 : OGRGeometry *poRet = CreateCurveGeometry(
4127 : SHPT_GENERALPOLYGON, nParts, nPoints, nCurves, bHasZ, bHasM,
4128 : pabyCur, pabyEnd);
4129 41 : if (poRet)
4130 41 : return poRet;
4131 : // In case something went wrong, go on without curves
4132 0 : pabyCur = pabyCurBackup;
4133 : }
4134 :
4135 5932 : std::vector<std::unique_ptr<OGRLinearRing>> apoRings;
4136 2966 : apoRings.reserve(nParts);
4137 :
4138 2966 : dx = dy = dz = 0;
4139 6889 : for (i = 0; i < nParts; i++)
4140 : {
4141 3923 : auto poRing = std::make_unique<FileGDBOGRLinearRing>();
4142 3923 : poRing->setNumPoints(panPointCount[i], FALSE);
4143 :
4144 3923 : XYLineStringSetter lsSetter(poRing->GetPoints());
4145 3923 : if (!ReadXYArray<XYLineStringSetter>(lsSetter, pabyCur, pabyEnd,
4146 3923 : panPointCount[i], dx, dy))
4147 : {
4148 : // For some reason things that papoRings is leaking
4149 : // cppcheck-suppress memleak
4150 0 : returnError();
4151 : }
4152 3923 : apoRings.push_back(std::move(poRing));
4153 : }
4154 :
4155 2966 : if (bHasZ)
4156 : {
4157 1966 : for (i = 0; i < nParts; i++)
4158 : {
4159 986 : apoRings[i]->setCoordinateDimension(3);
4160 :
4161 986 : ZLineStringSetter lszSetter(apoRings[i].get());
4162 986 : if (!ReadZArray<ZLineStringSetter>(
4163 986 : lszSetter, pabyCur, pabyEnd, panPointCount[i], dz))
4164 : {
4165 0 : returnError();
4166 : }
4167 : }
4168 : }
4169 :
4170 2966 : if (bHasM)
4171 : {
4172 122 : GIntBig dm = 0;
4173 248 : for (i = 0; i < nParts; i++)
4174 : {
4175 : // It seems that absence of M is marked with a single byte
4176 : // with value 66. Be more tolerant and only try to parse the
4177 : // M array is there are at least as many remaining bytes as
4178 : // expected points
4179 128 : if (pabyCur + panPointCount[i] > pabyEnd)
4180 : {
4181 2 : while (i != 0)
4182 : {
4183 0 : --i;
4184 0 : apoRings[i]->setMeasured(FALSE);
4185 : }
4186 2 : break;
4187 : }
4188 :
4189 126 : apoRings[i]->setMeasured(TRUE);
4190 :
4191 126 : MLineStringSetter lsmSetter(apoRings[i].get());
4192 126 : if (!ReadMArray<MLineStringSetter>(
4193 126 : lsmSetter, pabyCur, pabyEnd, panPointCount[i], dm))
4194 : {
4195 0 : returnError();
4196 : }
4197 : }
4198 : }
4199 :
4200 2966 : std::unique_ptr<OGRGeometry> poRet;
4201 2966 : if (nParts == 1)
4202 : {
4203 4964 : auto poPoly = std::make_unique<OGRPolygon>();
4204 2482 : poPoly->addRing(std::move(apoRings[0]));
4205 2482 : poRet = std::move(poPoly);
4206 : }
4207 : else
4208 : // Note: ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING is *not* defined
4209 : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
4210 : if (bUseOrganize || !(apoRings[0]->isClockwise()))
4211 : #endif
4212 : {
4213 : /* Slow method: we do a rather expensive topological analysis of
4214 : * the rings to figure out which ones are inner rings from outer
4215 : * rings, and to which outer ring an inner ring belongs too.
4216 : * In most cases, inner rings are CCW oriented and follow
4217 : * immediately the outer ring in which they are included,
4218 : * (that situation is the commented code in the below else
4219 : * branch).
4220 : * In nearly all cases, inner rings are CCW and outer rings
4221 : * are CW oriented, so we could call organizePolygons() with
4222 : * the relatively lightweight METHOD=ONLY_CCW strategy (which
4223 : * is what the shapefile drivers does at time of writing).
4224 : * Unfortunately in https://github.com/OSGeo/gdal/issues/1369,
4225 : * we found likely broken datasets where a polygon with inner
4226 : * rings has its exterior ring with wrong orientation, hence
4227 : * we use the slowest but bullet-proof method.
4228 : */
4229 484 : std::vector<std::unique_ptr<OGRGeometry>> apoPolys;
4230 484 : apoPolys.reserve(nParts);
4231 1925 : for (i = 0; i < nParts; i++)
4232 : {
4233 1441 : auto poPoly = std::make_unique<OGRPolygon>();
4234 1441 : poPoly->addRing(std::move(apoRings[i]));
4235 1441 : apoPolys.push_back(std::move(poPoly));
4236 : }
4237 484 : poRet = OGRGeometryFactory::organizePolygons(apoPolys);
4238 : }
4239 : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
4240 : else
4241 : {
4242 : /* Inner rings are CCW oriented and follow immediately the outer
4243 : */
4244 : /* ring (that is CW oriented) in which they are included */
4245 : std::unique_ptr<OGRMultiPolygon> poMulti;
4246 : auto poCur = std::make_unique<OGRPolygon>();
4247 : /* We have already checked that the first ring is CW */
4248 : OGREnvelope sEnvelope;
4249 : apoRings[0]->getEnvelope(&sEnvelope);
4250 : poCur->addRing(std::move(apoRings[0]));
4251 : for (i = 1; i < nParts; i++)
4252 : {
4253 : const int bIsCW = apoRings[i]->isClockwise();
4254 : if (bIsCW)
4255 : {
4256 : if (poMulti == nullptr)
4257 : {
4258 : poMulti = std::make_unique<OGRMultiPolygon>();
4259 : poMulti->addGeometry(std::move(poCur));
4260 : }
4261 : poCur = std::make_unique<OGRPolygon>();
4262 : apoRings[i]->getEnvelope(&sEnvelope);
4263 : poCur->addRing(std::move(apoRings[i]));
4264 : poMulti->addGeometry(std::move(poCur));
4265 : }
4266 : else
4267 : {
4268 : OGRPoint oPoint;
4269 : apoRings[i]->getPoint(0, &oPoint);
4270 : if (poCur)
4271 : {
4272 : poCur->addRing(std::move(apoRings[i]));
4273 : }
4274 : else
4275 : {
4276 : poMulti
4277 : ->getGeometryRef(poMulti->getNumGeometries() -
4278 : 1)
4279 : ->addRing(std::move(apoRings[i]));
4280 : }
4281 : CPLAssert(oPoint.getX() >= sEnvelope.MinX &&
4282 : oPoint.getX() <= sEnvelope.MaxX &&
4283 : oPoint.getY() >= sEnvelope.MinY &&
4284 : oPoint.getY() <= sEnvelope.MaxY);
4285 : }
4286 : }
4287 : if (poCur)
4288 : poRet = std::move(poCur);
4289 : else
4290 : poRet = std::move(poMulti);
4291 : }
4292 : #endif
4293 :
4294 2966 : return poRet.release();
4295 : }
4296 :
4297 284 : case SHPT_MULTIPATCHM:
4298 : case SHPT_MULTIPATCH:
4299 284 : bHasZ = true; /* go on */
4300 : [[fallthrough]];
4301 564 : case SHPT_GENERALMULTIPATCH:
4302 : {
4303 564 : returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts,
4304 : nCurves, false, true));
4305 :
4306 564 : if (nPoints == 0 || nParts == 0)
4307 : {
4308 0 : OGRPolygon *poPoly = new OGRPolygon();
4309 0 : if (bHasZ)
4310 0 : poPoly->setCoordinateDimension(3);
4311 0 : return poPoly;
4312 : }
4313 : int *panPartType =
4314 564 : static_cast<int *>(VSI_MALLOC_VERBOSE(sizeof(int) * nParts));
4315 : // The + 1 is to add an extra element, not actually used, but
4316 : // to please Coverity Scan
4317 : int *panPartStart = static_cast<int *>(
4318 564 : VSI_MALLOC_VERBOSE(sizeof(int) * (nParts + 1)));
4319 : double *padfXYZ = static_cast<double *>(
4320 564 : VSI_MALLOC_VERBOSE(3 * sizeof(double) * nPoints));
4321 564 : double *padfX = padfXYZ;
4322 564 : double *padfY = padfXYZ ? padfXYZ + nPoints : nullptr;
4323 564 : double *padfZ = padfXYZ ? padfXYZ + 2 * nPoints : nullptr;
4324 564 : if (panPartType == nullptr || panPartStart == nullptr ||
4325 : padfXYZ == nullptr)
4326 : {
4327 0 : VSIFree(panPartType);
4328 0 : VSIFree(panPartStart);
4329 0 : VSIFree(padfXYZ);
4330 0 : returnError();
4331 : }
4332 3378 : for (i = 0; i < nParts; i++)
4333 : {
4334 : GUInt32 nPartType;
4335 2814 : if (!ReadVarUInt32(pabyCur, pabyEnd, nPartType))
4336 : {
4337 0 : VSIFree(panPartType);
4338 0 : VSIFree(panPartStart);
4339 0 : VSIFree(padfXYZ);
4340 0 : returnError();
4341 : }
4342 2814 : panPartType[i] = static_cast<int>(nPartType);
4343 : }
4344 564 : dx = dy = dz = 0;
4345 :
4346 564 : XYArraySetter arraySetter(padfX, padfY);
4347 564 : if (!ReadXYArray<XYArraySetter>(arraySetter, pabyCur, pabyEnd,
4348 : nPoints, dx, dy))
4349 : {
4350 0 : VSIFree(panPartType);
4351 0 : VSIFree(panPartStart);
4352 0 : VSIFree(padfXYZ);
4353 0 : returnError();
4354 : }
4355 :
4356 564 : if (bHasZ)
4357 : {
4358 564 : FileGDBArraySetter arrayzSetter(padfZ);
4359 564 : if (!ReadZArray<FileGDBArraySetter>(arrayzSetter, pabyCur,
4360 : pabyEnd, nPoints, dz))
4361 : {
4362 0 : VSIFree(panPartType);
4363 0 : VSIFree(panPartStart);
4364 0 : VSIFree(padfXYZ);
4365 0 : returnError();
4366 : }
4367 : }
4368 : else
4369 : {
4370 0 : memset(padfZ, 0, nPoints * sizeof(double));
4371 : }
4372 :
4373 564 : panPartStart[0] = 0;
4374 2814 : for (i = 1; i < nParts; ++i)
4375 2250 : panPartStart[i] = panPartStart[i - 1] + panPointCount[i - 1];
4376 : // Not used, but avoids a Coverity Scan warning
4377 564 : panPartStart[nParts] = nPoints;
4378 564 : OGRGeometry *poRet = OGRCreateFromMultiPatch(
4379 : static_cast<int>(nParts), panPartStart, panPartType,
4380 : static_cast<int>(nPoints), padfX, padfY, padfZ);
4381 :
4382 564 : VSIFree(panPartType);
4383 564 : VSIFree(panPartStart);
4384 564 : VSIFree(padfXYZ);
4385 :
4386 564 : return poRet;
4387 : }
4388 :
4389 0 : default:
4390 0 : CPLDebug("OpenFileGDB", "Unhandled geometry type = %d",
4391 : static_cast<int>(nGeomType));
4392 0 : break;
4393 : /*
4394 : #define SHPT_GENERALMULTIPOINT 53
4395 : */
4396 : }
4397 0 : return nullptr;
4398 : }
4399 :
4400 : /************************************************************************/
4401 : /* BuildConverter() */
4402 : /************************************************************************/
4403 :
4404 : FileGDBOGRGeometryConverter *
4405 970 : FileGDBOGRGeometryConverter::BuildConverter(const FileGDBGeomField *poGeomField)
4406 : {
4407 970 : return new FileGDBOGRGeometryConverterImpl(poGeomField);
4408 : }
4409 :
4410 : /************************************************************************/
4411 : /* GetGeometryTypeFromESRI() */
4412 : /************************************************************************/
4413 :
4414 : static const struct
4415 : {
4416 : const char *pszStr;
4417 : OGRwkbGeometryType eType;
4418 : } AssocESRIGeomTypeToOGRGeomType[] = {
4419 : {"esriGeometryPoint", wkbPoint},
4420 : {"esriGeometryMultipoint", wkbMultiPoint},
4421 : {"esriGeometryLine", wkbMultiLineString},
4422 : {"esriGeometryPolyline", wkbMultiLineString},
4423 : {"esriGeometryPolygon", wkbMultiPolygon},
4424 : {"esriGeometryMultiPatch", wkbUnknown}};
4425 :
4426 : OGRwkbGeometryType
4427 2800 : FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(const char *pszESRIType)
4428 : {
4429 9784 : for (size_t i = 0; i < sizeof(AssocESRIGeomTypeToOGRGeomType) /
4430 : sizeof(AssocESRIGeomTypeToOGRGeomType[0]);
4431 : i++)
4432 : {
4433 9784 : if (strcmp(pszESRIType, AssocESRIGeomTypeToOGRGeomType[i].pszStr) == 0)
4434 2800 : return AssocESRIGeomTypeToOGRGeomType[i].eType;
4435 : }
4436 0 : CPLDebug("OpenFileGDB", "Unhandled geometry type : %s", pszESRIType);
4437 0 : return wkbUnknown;
4438 : }
4439 :
4440 : #ifdef _MSC_VER
4441 : #pragma warning(pop)
4442 : #endif
4443 :
4444 : } /* namespace OpenFileGDB */
|