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