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