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