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