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 <errno.h>
33 #include <limits.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <time.h>
38 #include <limits>
39 #include <string>
40 #include <vector>
41
42 #include "cpl_conv.h"
43 #include "cpl_error.h"
44 #include "cpl_string.h"
45 #include "cpl_time.h"
46 #include "cpl_vsi.h"
47 #include "filegdbtable_priv.h"
48 #include "ogr_api.h"
49 #include "ogr_core.h"
50 #include "ogr_geometry.h"
51 #include "ogrpgeogeometry.h"
52
53 CPL_CVSID("$Id: filegdbtable.cpp 6b84484fd22d27a4611ab64da86ba221156293f8 2021-08-25 10:30:20 +0200 Even Rouault $")
54
55 #define TEST_BIT(ar, bit) (ar[(bit) / 8] & (1 << ((bit) % 8)))
56 #define BIT_ARRAY_SIZE_IN_BYTES(bitsize) (((bitsize)+7)/8)
57
58 #define UUID_SIZE_IN_BYTES 16
59
60 #define IS_VALID_LAYER_GEOM_TYPE(byVal) ((byVal) <= FGTGT_POLYGON || (byVal) == FGTGT_MULTIPATCH)
61
62 /* Reserve one extra byte in case the last field is a string */
63 /* or 2 for 2 ReadVarIntAndAddNoCheck() in a row */
64 /* or 4 for SkipVarUInt() with nIter = 4 */
65 /* or for 4 ReadVarUInt64NoCheck */
66 #define ZEROES_AFTER_END_OF_BUFFER 4
67
68 constexpr GUInt32 EXT_SHAPE_Z_FLAG = 0x80000000U;
69 constexpr GUInt32 EXT_SHAPE_M_FLAG = 0x40000000U;
70 constexpr GUInt32 EXT_SHAPE_CURVE_FLAG = 0x20000000U;
71
72 constexpr GUInt32 EXT_SHAPE_SEGMENT_ARC = 1;
73 constexpr GUInt32 EXT_SHAPE_SEGMENT_BEZIER = 4;
74 constexpr GUInt32 EXT_SHAPE_SEGMENT_ELLIPSE = 5;
75
76 namespace OpenFileGDB
77 {
78
79 /************************************************************************/
80 /* SanitizeScale() */
81 /************************************************************************/
82
SanitizeScale(double dfVal)83 static double SanitizeScale(double dfVal)
84 {
85 if( dfVal == 0.0 )
86 return std::numeric_limits<double>::min(); // to prevent divide by zero
87 return dfVal;
88 }
89
90 /************************************************************************/
91 /* FileGDBTablePrintError() */
92 /************************************************************************/
93
FileGDBTablePrintError(const char * pszFile,int nLineNumber)94 void FileGDBTablePrintError(const char* pszFile, int nLineNumber)
95 {
96 CPLError( CE_Failure, CPLE_AppDefined, "Error occurred in %s at line %d",
97 pszFile, nLineNumber );
98 }
99
100 /************************************************************************/
101 /* FileGDBTable() */
102 /************************************************************************/
103
FileGDBTable()104 FileGDBTable::FileGDBTable()
105 {
106 Init();
107 }
108
109 /************************************************************************/
110 /* ~FileGDBTable() */
111 /************************************************************************/
112
~FileGDBTable()113 FileGDBTable::~FileGDBTable()
114 {
115 Close();
116 }
117
118 /************************************************************************/
119 /* Init() */
120 /************************************************************************/
121
Init()122 void FileGDBTable::Init()
123 {
124 osFilename = "";
125 fpTable = nullptr;
126 fpTableX = nullptr;
127 nFileSize = 0;
128 memset(&sCurField, 0, sizeof(sCurField));
129 bError = FALSE;
130 nCurRow = -1;
131 nLastCol = -1;
132 pabyIterVals = nullptr;
133 iAccNullable = 0;
134 nRowBlobLength = 0;
135 /* eCurFieldType = OFTInteger; */
136 eTableGeomType = FGTGT_NONE;
137 nValidRecordCount = 0;
138 nTotalRecordCount = 0;
139 iGeomField = -1;
140 nCountNullableFields = 0;
141 nNullableFieldsSizeInBytes = 0;
142 nBufferMaxSize = 0;
143 pabyBuffer = nullptr;
144 nFilterXMin = 0;
145 nFilterXMax = 0;
146 nFilterYMin = 0;
147 nFilterYMax = 0;
148 osObjectIdColName = "";
149 achGUIDBuffer[0] = 0;
150 nChSaved = -1;
151 pabyTablXBlockMap = nullptr;
152 nCountBlocksBeforeIBlockIdx = 0;
153 nCountBlocksBeforeIBlockValue = 0;
154 bHasReadGDBIndexes = FALSE;
155 nOffsetFieldDesc = 0;
156 nFieldDescLength = 0;
157 nTablxOffsetSize = 0;
158 anFeatureOffsets.resize(0);
159 nOffsetHeaderEnd = 0;
160 bHasDeletedFeaturesListed = FALSE;
161 bIsDeleted = FALSE;
162 }
163
164 /************************************************************************/
165 /* Close() */
166 /************************************************************************/
167
Close()168 void FileGDBTable::Close()
169 {
170 if( fpTable )
171 VSIFCloseL(fpTable);
172 fpTable = nullptr;
173
174 if( fpTableX )
175 VSIFCloseL(fpTableX);
176 fpTableX = nullptr;
177
178 CPLFree(pabyBuffer);
179 pabyBuffer = nullptr;
180
181 for(size_t i=0;i<apoFields.size();i++)
182 delete apoFields[i];
183 apoFields.resize(0);
184
185 CPLFree(pabyTablXBlockMap);
186 pabyTablXBlockMap = nullptr;
187
188 for(size_t i=0;i<apoIndexes.size();i++)
189 delete apoIndexes[i];
190 apoIndexes.resize(0);
191
192 Init();
193 }
194
195 /************************************************************************/
196 /* GetFieldIdx() */
197 /************************************************************************/
198
GetFieldIdx(const std::string & osName) const199 int FileGDBTable::GetFieldIdx(const std::string& osName) const
200 {
201 for(size_t i=0;i<apoFields.size();i++)
202 {
203 if( apoFields[i]->GetName() == osName )
204 return (int)i;
205 }
206 return -1;
207 }
208
209 /************************************************************************/
210 /* ReadVarUInt() */
211 /************************************************************************/
212
213 template < class OutType, class ControlType >
ReadVarUInt(GByte * & pabyIter,GByte * pabyEnd,OutType & nOutVal)214 static int ReadVarUInt(GByte*& pabyIter, GByte* pabyEnd, OutType& nOutVal)
215 {
216 const int errorRetValue = FALSE;
217 if( !(ControlType::check_bounds) )
218 {
219 /* nothing */
220 }
221 else if( ControlType::verbose_error )
222 {
223 returnErrorIf(pabyIter >= pabyEnd);
224 }
225 else
226 {
227 if( pabyIter >= pabyEnd )
228 return FALSE;
229 }
230 OutType b = *pabyIter;
231 if( (b & 0x80) == 0 )
232 {
233 pabyIter ++;
234 nOutVal = b;
235 return TRUE;
236 }
237 GByte* pabyLocalIter = pabyIter + 1;
238 int nShift = 7;
239 OutType nVal = ( b & 0x7F );
240 while( true )
241 {
242 if( !(ControlType::check_bounds) )
243 {
244 /* nothing */
245 }
246 else if( ControlType::verbose_error )
247 {
248 returnErrorIf(pabyLocalIter >= pabyEnd);
249 }
250 else
251 {
252 if( pabyLocalIter >= pabyEnd )
253 return FALSE;
254 }
255 b = *pabyLocalIter;
256 pabyLocalIter ++;
257 nVal |= ( b & 0x7F ) << nShift;
258 if( (b & 0x80) == 0 )
259 {
260 pabyIter = pabyLocalIter;
261 nOutVal = nVal;
262 return TRUE;
263 }
264 nShift += 7;
265 // To avoid undefined behavior later when doing << nShift
266 if( nShift >= static_cast<int>(sizeof(OutType)) * 8 )
267 {
268 pabyIter = pabyLocalIter;
269 nOutVal = nVal;
270 returnError();
271 }
272 }
273 }
274
275 struct ControlTypeVerboseErrorTrue
276 {
277 // cppcheck-suppress unusedStructMember
278 static const EMULATED_BOOL check_bounds = true;
279 // cppcheck-suppress unusedStructMember
280 static const EMULATED_BOOL verbose_error = true;
281 };
282
283 struct ControlTypeVerboseErrorFalse
284 {
285 // cppcheck-suppress unusedStructMember
286 static const EMULATED_BOOL check_bounds = true;
287 // cppcheck-suppress unusedStructMember
288 static const EMULATED_BOOL verbose_error = false;
289 };
290
291 struct ControlTypeNone
292 {
293 // cppcheck-suppress unusedStructMember
294 static const EMULATED_BOOL check_bounds = false;
295 // cppcheck-suppress unusedStructMember
296 static const EMULATED_BOOL verbose_error = false;
297 };
298
ReadVarUInt32(GByte * & pabyIter,GByte * pabyEnd,GUInt32 & nOutVal)299 static int ReadVarUInt32(GByte*& pabyIter, GByte* pabyEnd, GUInt32& nOutVal)
300 {
301 return ReadVarUInt<GUInt32, ControlTypeVerboseErrorTrue>(pabyIter, pabyEnd, nOutVal);
302 }
303
ReadVarUInt32NoCheck(GByte * & pabyIter,GUInt32 & nOutVal)304 static void ReadVarUInt32NoCheck(GByte*& pabyIter, GUInt32& nOutVal)
305 {
306 GByte* pabyEnd = nullptr;
307 ReadVarUInt<GUInt32, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
308 }
309
ReadVarUInt32Silent(GByte * & pabyIter,GByte * pabyEnd,GUInt32 & nOutVal)310 static int ReadVarUInt32Silent(GByte*& pabyIter, GByte* pabyEnd, GUInt32& nOutVal)
311 {
312 return ReadVarUInt<GUInt32, ControlTypeVerboseErrorFalse>(pabyIter, pabyEnd, nOutVal);
313 }
314
ReadVarUInt64NoCheck(GByte * & pabyIter,GUIntBig & nOutVal)315 static void ReadVarUInt64NoCheck(GByte*& pabyIter, GUIntBig& nOutVal)
316 {
317 GByte* pabyEnd = nullptr;
318 ReadVarUInt<GUIntBig, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
319 }
320
321 /************************************************************************/
322 /* IsLikelyFeatureAtOffset() */
323 /************************************************************************/
324
IsLikelyFeatureAtOffset(vsi_l_offset nOffset,GUInt32 * pnSize,int * pbDeletedRecord)325 int FileGDBTable::IsLikelyFeatureAtOffset(vsi_l_offset nOffset,
326 GUInt32* pnSize,
327 int* pbDeletedRecord)
328 {
329 VSIFSeekL(fpTable, nOffset, SEEK_SET);
330 GByte abyBuffer[4];
331 if( VSIFReadL(abyBuffer, 4, 1, fpTable) != 1 )
332 return FALSE;
333
334 nRowBlobLength = GetUInt32(abyBuffer, 0);
335 if( nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes ||
336 nRowBlobLength > nFileSize - nOffset ||
337 nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
338 nRowBlobLength > 10 * (nFileSize / nValidRecordCount) )
339 {
340 /* Is it a deleted record ? */
341 if( (nRowBlobLength >> (8 * sizeof(nRowBlobLength) - 1)) != 0 &&
342 nRowBlobLength != 0x80000000U )
343 {
344 nRowBlobLength = (GUInt32) (-(int)nRowBlobLength);
345 if( nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes ||
346 nRowBlobLength > nFileSize - nOffset ||
347 nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
348 nRowBlobLength > 10 * (nFileSize / nValidRecordCount) )
349 return FALSE;
350 else
351 *pbDeletedRecord = TRUE;
352 }
353 else
354 return FALSE;
355 }
356 else
357 *pbDeletedRecord = FALSE;
358
359 if( nRowBlobLength > nBufferMaxSize )
360 {
361 GByte* pabyNewBuffer = (GByte*) VSI_REALLOC_VERBOSE( pabyBuffer,
362 nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER );
363 if( pabyNewBuffer == nullptr )
364 return FALSE;
365
366 pabyBuffer = pabyNewBuffer;
367 nBufferMaxSize = nRowBlobLength;
368 }
369 if( pabyBuffer == nullptr ) return FALSE; /* to please Coverity. Not needed */
370 if( nCountNullableFields > 0 )
371 {
372 if( VSIFReadL(pabyBuffer, nNullableFieldsSizeInBytes, 1, fpTable) != 1 )
373 return FALSE;
374 }
375 size_t i;
376 iAccNullable = 0;
377 int bExactSizeKnown = TRUE;
378 GUInt32 nRequiredLength = nNullableFieldsSizeInBytes;
379 for(i=0;i<apoFields.size();i++)
380 {
381 if( apoFields[i]->bNullable )
382 {
383 int bIsNull = TEST_BIT(pabyBuffer, iAccNullable);
384 iAccNullable ++;
385 if( bIsNull )
386 continue;
387 }
388
389 switch( apoFields[i]->eType )
390 {
391 case FGFT_STRING:
392 case FGFT_XML:
393 case FGFT_GEOMETRY:
394 case FGFT_BINARY:
395 {
396 nRequiredLength += 1; /* varuint32 so at least one byte */
397 bExactSizeKnown = FALSE;
398 break;
399 }
400
401 case FGFT_RASTER:
402 {
403 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(apoFields[i]);
404 if( rasterField->IsManaged() )
405 nRequiredLength += sizeof(GInt32);
406 else
407 nRequiredLength += 1; /* varuint32 so at least one byte */
408 break;
409 }
410
411 case FGFT_INT16: nRequiredLength += sizeof(GInt16); break;
412 case FGFT_INT32: nRequiredLength += sizeof(GInt32); break;
413 case FGFT_FLOAT32: nRequiredLength += sizeof(float); break;
414 case FGFT_FLOAT64: nRequiredLength += sizeof(double); break;
415 case FGFT_DATETIME: nRequiredLength += sizeof(double); break;
416 case FGFT_UUID_1:
417 case FGFT_UUID_2: nRequiredLength += UUID_SIZE_IN_BYTES; break;
418
419 default:
420 CPLAssert(false);
421 break;
422 }
423 if( nRowBlobLength < nRequiredLength )
424 return FALSE;
425 }
426 if( !bExactSizeKnown )
427 {
428 if( VSIFReadL(pabyBuffer + nNullableFieldsSizeInBytes,
429 nRowBlobLength - nNullableFieldsSizeInBytes, 1, fpTable) != 1 )
430 return FALSE;
431
432 iAccNullable = 0;
433 nRequiredLength = nNullableFieldsSizeInBytes;
434 for(i=0;i<apoFields.size();i++)
435 {
436 if( apoFields[i]->bNullable )
437 {
438 int bIsNull = TEST_BIT(pabyBuffer, iAccNullable);
439 iAccNullable ++;
440 if( bIsNull )
441 continue;
442 }
443
444 switch( apoFields[i]->eType )
445 {
446 case FGFT_STRING:
447 case FGFT_XML:
448 {
449 GByte* pabyIter = pabyBuffer + nRequiredLength;
450 GUInt32 nLength;
451 if( !ReadVarUInt32Silent(pabyIter, pabyBuffer + nRowBlobLength, nLength) ||
452 pabyIter - (pabyBuffer + nRequiredLength) > 5 )
453 return FALSE;
454 nRequiredLength = static_cast<GUInt32>(pabyIter - pabyBuffer);
455 if( nLength > nRowBlobLength - nRequiredLength )
456 return FALSE;
457 for( GUInt32 j=0;j<nLength;j++ )
458 {
459 if( pabyIter[j] == 0 )
460 return FALSE;
461 }
462 if( !CPLIsUTF8((const char*)pabyIter, nLength) )
463 return FALSE;
464 nRequiredLength += nLength;
465 break;
466 }
467
468 case FGFT_GEOMETRY:
469 case FGFT_BINARY:
470 {
471 GByte* pabyIter = pabyBuffer + nRequiredLength;
472 GUInt32 nLength;
473 if( !ReadVarUInt32Silent(pabyIter, pabyBuffer + nRowBlobLength, nLength) ||
474 pabyIter - (pabyBuffer + nRequiredLength) > 5 )
475 return FALSE;
476 nRequiredLength = static_cast<GUInt32>(pabyIter - pabyBuffer);
477 if( nLength > nRowBlobLength - nRequiredLength )
478 return FALSE;
479 nRequiredLength += nLength;
480 break;
481 }
482
483 case FGFT_RASTER:
484 {
485 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(apoFields[i]);
486 if( rasterField->IsManaged() )
487 nRequiredLength += sizeof(GInt32);
488 else
489 {
490 GByte* pabyIter = pabyBuffer + nRequiredLength;
491 GUInt32 nLength;
492 if( !ReadVarUInt32Silent(pabyIter, pabyBuffer + nRowBlobLength, nLength) ||
493 pabyIter - (pabyBuffer + nRequiredLength) > 5 )
494 return FALSE;
495 nRequiredLength = static_cast<GUInt32>(pabyIter - pabyBuffer);
496 if( nLength > nRowBlobLength - nRequiredLength )
497 return FALSE;
498 nRequiredLength += nLength;
499 }
500 break;
501 }
502
503 case FGFT_INT16: nRequiredLength += sizeof(GInt16); break;
504 case FGFT_INT32: nRequiredLength += sizeof(GInt32); break;
505 case FGFT_FLOAT32: nRequiredLength += sizeof(float); break;
506 case FGFT_FLOAT64: nRequiredLength += sizeof(double); break;
507 case FGFT_DATETIME: nRequiredLength += sizeof(double); break;
508 case FGFT_UUID_1:
509 case FGFT_UUID_2: nRequiredLength += UUID_SIZE_IN_BYTES; break;
510
511 default:
512 CPLAssert(false);
513 break;
514 }
515 if( nRequiredLength > nRowBlobLength )
516 return FALSE;
517 }
518 }
519
520 *pnSize = 4 + nRequiredLength;
521 return nRequiredLength == nRowBlobLength;
522 }
523
524 /************************************************************************/
525 /* GuessFeatureLocations() */
526 /************************************************************************/
527
528 #define MARK_DELETED(x) ((x) | (((GUIntBig)1) << 63))
529 #define IS_DELETED(x) (((x) & (((GUIntBig)1) << 63)) != 0)
530 #define GET_OFFSET(x) ((x) & ~(((GUIntBig)1) << 63))
531
GuessFeatureLocations()532 int FileGDBTable::GuessFeatureLocations()
533 {
534 VSIFSeekL(fpTable, 0, SEEK_END);
535 nFileSize = VSIFTellL(fpTable);
536
537 int bReportDeletedFeatures =
538 CPLTestBool(CPLGetConfigOption("OPENFILEGDB_REPORT_DELETED_FEATURES", "NO"));
539
540 vsi_l_offset nOffset = 40 + nFieldDescLength;
541
542 if( nOffsetFieldDesc != 40 )
543 {
544 /* Check if there is a deleted field description at offset 40 */
545 GByte abyBuffer[14];
546 VSIFSeekL(fpTable, 40, SEEK_SET);
547 if( VSIFReadL(abyBuffer, 14, 1, fpTable) != 1 )
548 return FALSE;
549 int nSize = GetInt32(abyBuffer, 0);
550 int nVersion = GetInt32(abyBuffer + 4, 0);
551 if( nSize < 0 && nSize > -1024 * 1024 &&
552 (nVersion == 3 || nVersion == 4) &&
553 IS_VALID_LAYER_GEOM_TYPE(abyBuffer[8]) &&
554 abyBuffer[9] == 3 && abyBuffer[10] == 0 && abyBuffer[11] == 0 )
555 {
556 nOffset = 40 + (-nSize);
557 }
558 else
559 {
560 nOffset = 40;
561 }
562 }
563
564 int nInvalidRecords = 0;
565 while(nOffset < nFileSize)
566 {
567 GUInt32 nSize;
568 int bDeletedRecord;
569 if( !IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord) )
570 {
571 nOffset ++;
572 }
573 else
574 {
575 /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)",
576 nOffset, nSize);*/
577 if( bDeletedRecord )
578 {
579 if( bReportDeletedFeatures )
580 {
581 bHasDeletedFeaturesListed = TRUE;
582 anFeatureOffsets.push_back(MARK_DELETED(nOffset));
583 }
584 else
585 {
586 nInvalidRecords ++;
587 anFeatureOffsets.push_back(0);
588 }
589 }
590 else
591 anFeatureOffsets.push_back(nOffset);
592 nOffset += nSize;
593 }
594 }
595 nTotalRecordCount = (int) anFeatureOffsets.size();
596 if( nTotalRecordCount - nInvalidRecords > nValidRecordCount )
597 {
598 if( !bHasDeletedFeaturesListed )
599 {
600 CPLError(CE_Warning, CPLE_AppDefined,
601 "More features found (%d) than declared number of valid features (%d). "
602 "So deleted features will likely be reported.",
603 nTotalRecordCount - nInvalidRecords, nValidRecordCount);
604 }
605 nValidRecordCount = nTotalRecordCount - nInvalidRecords;
606 }
607
608 return nTotalRecordCount > 0;
609 }
610
611 /************************************************************************/
612 /* ReadTableXHeader() */
613 /************************************************************************/
614
ReadTableXHeader()615 int FileGDBTable::ReadTableXHeader()
616 {
617 const int errorRetValue = FALSE;
618 GByte abyHeader[16];
619
620 // Read .gdbtablx file header
621 returnErrorIf(VSIFReadL( abyHeader, 16, 1, fpTableX ) != 1 );
622 GUInt32 n1024Blocks = GetUInt32(abyHeader + 4, 0);
623
624 nTotalRecordCount = GetInt32(abyHeader + 8, 0);
625 if( n1024Blocks == 0 )
626 returnErrorIf(nTotalRecordCount != 0 );
627 else
628 returnErrorIf(nTotalRecordCount < 0 );
629
630 nTablxOffsetSize = GetUInt32(abyHeader + 12, 0);
631 returnErrorIf(nTablxOffsetSize < 4 || nTablxOffsetSize > 6);
632
633 if( n1024Blocks != 0 )
634 {
635 GByte abyTrailer[16];
636
637 VSIFSeekL( fpTableX, nTablxOffsetSize * 1024 * (vsi_l_offset)n1024Blocks + 16, SEEK_SET );
638 returnErrorIf(VSIFReadL( abyTrailer, 16, 1, fpTableX ) != 1 );
639
640 GUInt32 nMagic = GetUInt32(abyTrailer, 0);
641
642 GUInt32 nBitsForBlockMap = GetUInt32(abyTrailer + 4, 0);
643 returnErrorIf(nBitsForBlockMap > INT_MAX / 1024);
644
645 GUInt32 n1024BlocksBis = GetUInt32(abyTrailer + 8, 0);
646 returnErrorIf(n1024BlocksBis != n1024Blocks );
647
648 /* GUInt32 nMagic2 = GetUInt32(abyTrailer + 12, 0); */
649
650 if( nMagic == 0 )
651 {
652 returnErrorIf(nBitsForBlockMap != n1024Blocks );
653 /* returnErrorIf(nMagic2 != 0 ); */
654 }
655 else
656 {
657 returnErrorIf((GUInt32)nTotalRecordCount > nBitsForBlockMap * 1024 );
658 #ifdef DEBUG_VERBOSE
659 CPLDebug("OpenFileGDB", "%s .gdbtablx has block map array",
660 osFilename.c_str());
661 #endif
662
663 // Allocate a bit mask array for blocks of 1024 features.
664 int nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nBitsForBlockMap);
665 pabyTablXBlockMap = (GByte*) VSI_MALLOC_VERBOSE( nSizeInBytes );
666 returnErrorIf(pabyTablXBlockMap == nullptr );
667 returnErrorIf(VSIFReadL( pabyTablXBlockMap, nSizeInBytes, 1, fpTableX ) != 1 );
668 /* returnErrorIf(nMagic2 == 0 ); */
669
670 // Check that the map is consistent with n1024Blocks
671 GUInt32 nCountBlocks = 0;
672 for(GUInt32 i=0;i<nBitsForBlockMap;i++)
673 nCountBlocks += TEST_BIT(pabyTablXBlockMap, i) != 0;
674 returnErrorIf(nCountBlocks != n1024Blocks );
675 }
676 }
677 return TRUE;
678 }
679
680 /************************************************************************/
681 /* ReadUTF16String() */
682 /************************************************************************/
683
ReadUTF16String(const GByte * pabyIter,int nCarCount)684 static std::string ReadUTF16String(const GByte* pabyIter, int nCarCount)
685 {
686 std::wstring osWideStr;
687 for(int j=0;j<nCarCount;j++)
688 osWideStr += pabyIter[2 * j] | (pabyIter[2 * j + 1] << 8);
689 char* pszStr = CPLRecodeFromWChar(osWideStr.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
690 std::string osRet(pszStr);
691 CPLFree(pszStr);
692 return osRet;
693 }
694
695 /************************************************************************/
696 /* Open() */
697 /************************************************************************/
698
Open(const char * pszFilename,const char * pszLayerName)699 int FileGDBTable::Open(const char* pszFilename,
700 const char* pszLayerName)
701 {
702 const int errorRetValue = FALSE;
703 CPLAssert(fpTable == nullptr);
704
705 osFilename = pszFilename;
706 CPLString osFilenameWithLayerName(osFilename);
707 if( pszLayerName )
708 osFilenameWithLayerName += CPLSPrintf(" (layer %s)", pszLayerName);
709
710 fpTable = VSIFOpenL( pszFilename, "rb" );
711 if( fpTable == nullptr )
712 {
713 CPLError(CE_Failure, CPLE_OpenFailed,
714 "Cannot open %s: %s", osFilenameWithLayerName.c_str(),
715 VSIStrerror(errno));
716 return FALSE;
717 }
718
719 // Read .gdtable file header
720 GByte abyHeader[40];
721 returnErrorIf(VSIFReadL( abyHeader, 40, 1, fpTable ) != 1 );
722 nValidRecordCount = GetInt32(abyHeader + 4, 0);
723 returnErrorIf(nValidRecordCount < 0 );
724
725 CPLString osTableXName;
726 if( nValidRecordCount > 0 &&
727 !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_IGNORE_GDBTABLX", "FALSE")) )
728 {
729 osTableXName = CPLFormFilename(CPLGetPath(pszFilename),
730 CPLGetBasename(pszFilename), "gdbtablx");
731 fpTableX = VSIFOpenL( osTableXName, "rb" );
732 if( fpTableX == nullptr )
733 {
734 const char* pszIgnoreGDBTablXAbsence =
735 CPLGetConfigOption("OPENFILEGDB_IGNORE_GDBTABLX_ABSENCE", nullptr);
736 if( pszIgnoreGDBTablXAbsence == nullptr )
737 {
738 CPLError(CE_Warning, CPLE_AppDefined, "%s could not be found. "
739 "Trying to guess feature locations, but this might fail or "
740 "return incorrect results", osTableXName.c_str());
741 }
742 else if( !CPLTestBool(pszIgnoreGDBTablXAbsence) )
743 {
744 returnErrorIf(fpTableX == nullptr );
745 }
746 }
747 else if( !ReadTableXHeader() )
748 return FALSE;
749 }
750
751 if( fpTableX != nullptr )
752 {
753 if(nValidRecordCount > nTotalRecordCount )
754 {
755 if( CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT", "FALSE")) )
756 {
757 /* Potentially unsafe. See #5842 */
758 CPLDebug("OpenFileGDB", "%s: nTotalRecordCount (was %d) forced to nValidRecordCount=%d",
759 osFilenameWithLayerName.c_str(),
760 nTotalRecordCount, nValidRecordCount);
761 nTotalRecordCount = nValidRecordCount;
762 }
763 else
764 {
765 /* By default err on the safe side */
766 CPLError(CE_Warning, CPLE_AppDefined,
767 "File %s declares %d valid records, but %s declares "
768 "only %d total records. Using that later value for safety "
769 "(this possibly ignoring features). "
770 "You can also try setting OPENFILEGDB_IGNORE_GDBTABLX=YES to "
771 "completely ignore the .gdbtablx file (but possibly retrieving "
772 "deleted features), or set OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT=YES "
773 "(but that setting can potentially cause crashes)",
774 osFilenameWithLayerName.c_str(), nValidRecordCount,
775 osTableXName.c_str(), nTotalRecordCount);
776 nValidRecordCount = nTotalRecordCount;
777 }
778 }
779
780 #ifdef DEBUG_VERBOSE
781 else if( nTotalRecordCount != nValidRecordCount )
782 {
783 CPLDebug("OpenFileGDB", "%s: nTotalRecordCount=%d nValidRecordCount=%d",
784 pszFilename,
785 nTotalRecordCount, nValidRecordCount);
786 }
787 #endif
788 }
789
790 nOffsetFieldDesc = GetUInt32(abyHeader + 32, 0) |
791 (static_cast<GUIntBig>(GetUInt32(abyHeader + 36, 0)) << 32);
792
793 #ifdef DEBUG_VERBOSE
794 if( nOffsetFieldDesc != 40 )
795 {
796 CPLDebug("OpenFileGDB", "%s: nOffsetFieldDesc=" CPL_FRMT_GUIB,
797 pszFilename, nOffsetFieldDesc);
798 }
799 #endif
800
801 // Skip to field description section
802 VSIFSeekL( fpTable, nOffsetFieldDesc, SEEK_SET );
803 returnErrorIf(VSIFReadL( abyHeader, 14, 1, fpTable ) != 1 );
804 nFieldDescLength = GetUInt32(abyHeader, 0);
805
806 returnErrorIf(nOffsetFieldDesc >
807 std::numeric_limits<GUIntBig>::max() - nFieldDescLength);
808 nOffsetHeaderEnd = nOffsetFieldDesc + nFieldDescLength;
809
810 returnErrorIf(nFieldDescLength > 10 * 1024 * 1024 || nFieldDescLength < 10 );
811 GByte byTableGeomType = abyHeader[8];
812 if( IS_VALID_LAYER_GEOM_TYPE(byTableGeomType) )
813 eTableGeomType = (FileGDBTableGeometryType) byTableGeomType;
814 else
815 CPLDebug("OpenFileGDB", "Unknown table geometry type: %d", byTableGeomType);
816 const GByte byTableGeomTypeFlags = abyHeader[11];
817 m_bGeomTypeHasM = (byTableGeomTypeFlags & (1 << 6)) != 0;
818 m_bGeomTypeHasZ = (byTableGeomTypeFlags & (1 << 7)) != 0;
819
820 GUInt16 iField, nFields;
821 nFields = GetUInt16(abyHeader + 12, 0);
822
823 /* No interest in guessing a trivial file */
824 returnErrorIf( fpTableX == nullptr && nFields == 0) ;
825
826 GUInt32 nRemaining = nFieldDescLength - 10;
827 nBufferMaxSize = nRemaining;
828 pabyBuffer = (GByte*)VSI_MALLOC_VERBOSE(nBufferMaxSize + ZEROES_AFTER_END_OF_BUFFER);
829 returnErrorIf(pabyBuffer == nullptr );
830 returnErrorIf(VSIFReadL(pabyBuffer, nRemaining, 1, fpTable) != 1 );
831
832 GByte* pabyIter = pabyBuffer;
833 for(iField = 0; iField < nFields; iField ++)
834 {
835 returnErrorIf(nRemaining < 1 );
836 GByte nCarCount = pabyIter[0];
837
838 pabyIter ++;
839 nRemaining --;
840 returnErrorIf(nRemaining < (GUInt32)(2 * nCarCount + 1) );
841 // coverity[tainted_data,tainted_data_argument]
842 std::string osName(ReadUTF16String(pabyIter, nCarCount));
843 pabyIter += 2 * nCarCount;
844 nRemaining -= 2 * nCarCount;
845
846 returnErrorIf(nRemaining < 1 );
847 nCarCount = pabyIter[0];
848 pabyIter ++;
849 nRemaining --;
850 returnErrorIf(nRemaining < (GUInt32)(2 * nCarCount + 1) );
851 // coverity[tainted_data,tainted_data_argument]
852 std::string osAlias(ReadUTF16String(pabyIter, nCarCount));
853 pabyIter += 2 * nCarCount;
854 nRemaining -= 2 * nCarCount;
855
856 returnErrorIf(nRemaining < 1 );
857 GByte byFieldType = pabyIter[0];
858 pabyIter ++;
859 nRemaining --;
860
861 if( byFieldType > FGFT_XML)
862 {
863 CPLDebug("OpenFileGDB", "Unhandled field type : %d", byFieldType);
864 returnError();
865 }
866
867 FileGDBFieldType eType = (FileGDBFieldType) byFieldType;
868 if( eType != FGFT_GEOMETRY && eType != FGFT_RASTER )
869 {
870 GByte flags = 0;
871 int nMaxWidth = 0;
872 GUInt32 defaultValueLength = 0;
873
874 switch( eType )
875 {
876 case FGFT_STRING:
877 {
878 returnErrorIf(nRemaining < 6 );
879 nMaxWidth = GetInt32(pabyIter, 0);
880 returnErrorIf(nMaxWidth < 0);
881 flags = pabyIter[4];
882 pabyIter += 5;
883 nRemaining -= 5;
884 GByte* pabyIterBefore = pabyIter;
885 returnErrorIf(!ReadVarUInt32(pabyIter, pabyIter + nRemaining, defaultValueLength));
886 nRemaining -= static_cast<GUInt32>(pabyIter - pabyIterBefore);
887 break;
888 }
889
890 case FGFT_OBJECTID:
891 case FGFT_BINARY:
892 case FGFT_UUID_1:
893 case FGFT_UUID_2:
894 case FGFT_XML:
895 returnErrorIf(nRemaining < 2 );
896 flags = pabyIter[1];
897 pabyIter += 2;
898 nRemaining -= 2;
899 break;
900
901 default:
902 returnErrorIf(nRemaining < 3 );
903 flags = pabyIter[1];
904 defaultValueLength = pabyIter[2];
905 pabyIter += 3;
906 nRemaining -= 3;
907 break;
908 }
909
910 OGRField sDefault;
911 OGR_RawField_SetUnset(&sDefault);
912 if( (flags & 4) != 0 )
913 {
914 /* Default value */
915 /* Found on PreNIS.gdb/a0000000d.gdbtable */
916 returnErrorIf(nRemaining < defaultValueLength );
917 if( defaultValueLength )
918 {
919 if( eType == FGFT_STRING )
920 {
921 sDefault.String = (char*)CPLMalloc(defaultValueLength+1);
922 memcpy(sDefault.String, pabyIter, defaultValueLength);
923 sDefault.String[defaultValueLength] = 0;
924 }
925 else if( eType == FGFT_INT16 && defaultValueLength == 2 )
926 {
927 sDefault.Integer = GetInt16(pabyIter, 0);
928 sDefault.Set.nMarker2 = 0;
929 sDefault.Set.nMarker3 = 0;
930 }
931 else if( eType == FGFT_INT32 && defaultValueLength == 4 )
932 {
933 sDefault.Integer = GetInt32(pabyIter, 0);
934 sDefault.Set.nMarker2 = 0;
935 sDefault.Set.nMarker3 = 0;
936 }
937 else if( eType == FGFT_FLOAT32 && defaultValueLength == 4 )
938 {
939 sDefault.Real = GetFloat32(pabyIter, 0);
940 }
941 else if( eType == FGFT_FLOAT64 && defaultValueLength == 8 )
942 {
943 sDefault.Real = GetFloat64(pabyIter, 0);
944 }
945 else if( eType == FGFT_DATETIME && defaultValueLength == 8 )
946 {
947 const double dfVal = GetFloat64(pabyIter, 0);
948 FileGDBDoubleDateToOGRDate(dfVal, &sDefault);
949 }
950 }
951
952 pabyIter += defaultValueLength;
953 nRemaining -= defaultValueLength;
954 }
955
956 if( eType == FGFT_OBJECTID )
957 {
958 returnErrorIf(!osObjectIdColName.empty() );
959 osObjectIdColName = osName;
960 continue;
961 }
962
963 FileGDBField* poField = new FileGDBField(this);
964 poField->osName = osName;
965 poField->osAlias = osAlias;
966 poField->eType = eType;
967 poField->bNullable = (flags & 1);
968 poField->nMaxWidth = nMaxWidth;
969 poField->sDefault = sDefault;
970 apoFields.push_back(poField);
971 }
972 else
973 {
974
975 FileGDBRasterField* poRasterField = nullptr;
976 FileGDBGeomField* poField;
977 if( eType == FGFT_GEOMETRY )
978 {
979 returnErrorIf(iGeomField >= 0 );
980 poField = new FileGDBGeomField(this);
981 }
982 else
983 {
984 poRasterField = new FileGDBRasterField(this);
985 poField = poRasterField;
986 }
987
988 poField->osName = osName;
989 poField->osAlias = osAlias;
990 poField->eType = eType;
991 if( eType == FGFT_GEOMETRY )
992 iGeomField = (int)apoFields.size();
993 apoFields.push_back(poField);
994
995 returnErrorIf(nRemaining < 2 );
996 GByte flags = pabyIter[1];
997 poField->bNullable = (flags & 1);
998 pabyIter += 2;
999 nRemaining -= 2;
1000
1001 if( eType == FGFT_RASTER )
1002 {
1003 returnErrorIf(nRemaining < 1 );
1004 nCarCount = pabyIter[0];
1005 pabyIter ++;
1006 nRemaining --;
1007 returnErrorIf(nRemaining < (GUInt32)(2 * nCarCount + 1) );
1008 std::string osRasterColumn(ReadUTF16String(pabyIter, nCarCount));
1009 pabyIter += 2 * nCarCount;
1010 nRemaining -= 2 * nCarCount;
1011 poRasterField->osRasterColumnName = osRasterColumn;
1012 }
1013
1014 returnErrorIf(nRemaining < 2 );
1015 GUInt16 nLengthWKT = GetUInt16(pabyIter, 0);
1016 pabyIter += sizeof(nLengthWKT);
1017 nRemaining -= sizeof(nLengthWKT);
1018
1019 returnErrorIf(nRemaining < (GUInt32)(1 + nLengthWKT) );
1020 poField->osWKT = ReadUTF16String(pabyIter, nLengthWKT/2);
1021 pabyIter += nLengthWKT;
1022 nRemaining -= nLengthWKT;
1023
1024 GByte abyGeomFlags = pabyIter[0];
1025 pabyIter ++;
1026 nRemaining --;
1027 poField->bHasMOriginScaleTolerance = (abyGeomFlags & 2) != 0;
1028 poField->bHasZOriginScaleTolerance = (abyGeomFlags & 4) != 0;
1029
1030 if( eType == FGFT_GEOMETRY || abyGeomFlags > 0 )
1031 {
1032 returnErrorIf(
1033 nRemaining < (GUInt32)(sizeof(double) * ( 4 + (( eType == FGFT_GEOMETRY ) ? 4 : 0) + (poField->bHasMOriginScaleTolerance + poField->bHasZOriginScaleTolerance) * 3 )) );
1034
1035 #define READ_DOUBLE(field) do { \
1036 field = GetFloat64(pabyIter, 0); \
1037 pabyIter += sizeof(double); \
1038 nRemaining -= sizeof(double); } while( false )
1039
1040 READ_DOUBLE(poField->dfXOrigin);
1041 READ_DOUBLE(poField->dfYOrigin);
1042 READ_DOUBLE(poField->dfXYScale);
1043 returnErrorIf( poField->dfXYScale == 0 );
1044
1045 if( poField->bHasMOriginScaleTolerance )
1046 {
1047 READ_DOUBLE(poField->dfMOrigin);
1048 READ_DOUBLE(poField->dfMScale);
1049 }
1050
1051 if( poField->bHasZOriginScaleTolerance )
1052 {
1053 READ_DOUBLE(poField->dfZOrigin);
1054 READ_DOUBLE(poField->dfZScale);
1055 }
1056
1057 READ_DOUBLE(poField->dfXYTolerance);
1058
1059 if( poField->bHasMOriginScaleTolerance )
1060 {
1061 READ_DOUBLE(poField->dfMTolerance);
1062 #ifdef DEBUG_VERBOSE
1063 CPLDebug("OpenFileGDB", "MOrigin = %g, MScale = %g, MTolerance = %g",
1064 poField->dfMOrigin, poField->dfMScale, poField->dfMTolerance);
1065 #endif
1066 }
1067
1068 if( poField->bHasZOriginScaleTolerance )
1069 {
1070 READ_DOUBLE(poField->dfZTolerance);
1071 }
1072 }
1073
1074 if( eType == FGFT_RASTER )
1075 {
1076 returnErrorIf(nRemaining < 1 );
1077 poRasterField->m_bIsManaged = *pabyIter != 0;
1078 pabyIter += 1;
1079 nRemaining -= 1;
1080 }
1081 else
1082 {
1083 returnErrorIf(nRemaining < 4 * sizeof(double) );
1084 READ_DOUBLE(poField->dfXMin);
1085 READ_DOUBLE(poField->dfYMin);
1086 READ_DOUBLE(poField->dfXMax);
1087 READ_DOUBLE(poField->dfYMax);
1088
1089 if( m_bGeomTypeHasZ )
1090 {
1091 returnErrorIf(nRemaining < 2 * sizeof(double) );
1092 READ_DOUBLE(poField->dfZMin);
1093 READ_DOUBLE(poField->dfZMax);
1094 }
1095
1096 if( m_bGeomTypeHasM )
1097 {
1098 returnErrorIf(nRemaining < 2 * sizeof(double) );
1099 READ_DOUBLE(poField->dfMMin);
1100 READ_DOUBLE(poField->dfMMax);
1101 }
1102
1103 returnErrorIf(nRemaining < 5 );
1104 // Skip byte at zero
1105 pabyIter += 1;
1106 nRemaining -= 1;
1107
1108 GUInt32 nGridSizeCount = GetUInt32(pabyIter, 0);
1109 pabyIter += sizeof(nGridSizeCount);
1110 nRemaining -= sizeof(nGridSizeCount);
1111 returnErrorIf(nGridSizeCount == 0 || nGridSizeCount > 3);
1112 returnErrorIf(nRemaining < nGridSizeCount * sizeof(double) );
1113 for( GUInt32 i = 0; i < nGridSizeCount; i++ )
1114 {
1115 double dfGridResolution;
1116 READ_DOUBLE(dfGridResolution);
1117 m_adfSpatialIndexGridResolution.push_back(dfGridResolution);
1118 }
1119 }
1120 }
1121
1122 nCountNullableFields += apoFields.back()->bNullable;
1123 }
1124 nNullableFieldsSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nCountNullableFields);
1125
1126 #ifdef DEBUG_VERBOSE
1127 if( nRemaining > 0 )
1128 {
1129 CPLDebug("OpenFileGDB", "%u remaining (ignored) bytes in field header section",
1130 nRemaining);
1131 }
1132 #endif
1133
1134 if( nValidRecordCount > 0 && fpTableX == nullptr )
1135 return GuessFeatureLocations();
1136
1137 return TRUE;
1138 }
1139
1140 /************************************************************************/
1141 /* SkipVarUInt() */
1142 /************************************************************************/
1143
1144 /* Bound check only valid if nIter <= 4 */
SkipVarUInt(GByte * & pabyIter,GByte * pabyEnd,int nIter=1)1145 static int SkipVarUInt(GByte*& pabyIter, GByte* pabyEnd, int nIter = 1)
1146 {
1147 const int errorRetValue = FALSE;
1148 GByte* pabyLocalIter = pabyIter;
1149 returnErrorIf(pabyLocalIter /*+ nIter - 1*/ >= pabyEnd);
1150 while( nIter -- > 0 )
1151 {
1152 while( true )
1153 {
1154 GByte b = *pabyLocalIter;
1155 pabyLocalIter ++;
1156 if( (b & 0x80) == 0 )
1157 break;
1158 }
1159 }
1160 pabyIter = pabyLocalIter;
1161 return TRUE;
1162 }
1163
1164 /************************************************************************/
1165 /* ReadVarIntAndAddNoCheck() */
1166 /************************************************************************/
1167
1168 CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
ReadVarIntAndAddNoCheck(GByte * & pabyIter,GIntBig & nOutVal)1169 static void ReadVarIntAndAddNoCheck(GByte*& pabyIter, GIntBig& nOutVal)
1170 {
1171 GUInt32 b;
1172
1173 b = *pabyIter;
1174 GUIntBig nVal = (b & 0x3F);
1175 bool bNegative = (b & 0x40) != 0;
1176 if( (b & 0x80) == 0 )
1177 {
1178 pabyIter ++;
1179 if( bNegative )
1180 nOutVal -= nVal;
1181 else
1182 nOutVal += nVal;
1183 return;
1184 }
1185
1186 GByte* pabyLocalIter = pabyIter + 1;
1187 int nShift = 6;
1188 while( true )
1189 {
1190 GUIntBig b64 = *pabyLocalIter;
1191 pabyLocalIter ++;
1192 nVal |= ( b64 & 0x7F ) << nShift;
1193 if( (b64 & 0x80) == 0 )
1194 {
1195 pabyIter = pabyLocalIter;
1196 if( bNegative )
1197 nOutVal -= nVal;
1198 else
1199 nOutVal += nVal;
1200 return;
1201 }
1202 nShift += 7;
1203 // To avoid undefined behavior later when doing << nShift
1204 if( nShift >= static_cast<int>(sizeof(GIntBig)) * 8 )
1205 {
1206 pabyIter = pabyLocalIter;
1207 nOutVal = nVal;
1208 return;
1209 }
1210 }
1211 }
1212
1213 /************************************************************************/
1214 /* GetOffsetInTableForRow() */
1215 /************************************************************************/
1216
GetOffsetInTableForRow(int iRow)1217 vsi_l_offset FileGDBTable::GetOffsetInTableForRow(int iRow)
1218 {
1219 const int errorRetValue = 0;
1220 returnErrorIf(iRow < 0 || iRow >= nTotalRecordCount );
1221
1222 bIsDeleted = FALSE;
1223 if( fpTableX == nullptr )
1224 {
1225 bIsDeleted = IS_DELETED(anFeatureOffsets[iRow]);
1226 return GET_OFFSET(anFeatureOffsets[iRow]);
1227 }
1228
1229 if( pabyTablXBlockMap != nullptr )
1230 {
1231 GUInt32 nCountBlocksBefore = 0;
1232 int iBlock = iRow / 1024;
1233
1234 // Check if the block is not empty
1235 if( TEST_BIT(pabyTablXBlockMap, iBlock) == 0 )
1236 return 0;
1237
1238 // In case of sequential reading, optimization to avoid recomputing
1239 // the number of blocks since the beginning of the map
1240 if( iBlock >= nCountBlocksBeforeIBlockIdx )
1241 {
1242 nCountBlocksBefore = nCountBlocksBeforeIBlockValue;
1243 for(int i=nCountBlocksBeforeIBlockIdx;i<iBlock;i++)
1244 nCountBlocksBefore += TEST_BIT(pabyTablXBlockMap, i) != 0;
1245 }
1246 else
1247 {
1248 nCountBlocksBefore = 0;
1249 for(int i=0;i<iBlock;i++)
1250 nCountBlocksBefore += TEST_BIT(pabyTablXBlockMap, i) != 0;
1251 }
1252 nCountBlocksBeforeIBlockIdx = iBlock;
1253 nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
1254 int iCorrectedRow = nCountBlocksBefore * 1024 + (iRow % 1024);
1255 VSIFSeekL(fpTableX, 16 + static_cast<vsi_l_offset>(nTablxOffsetSize) * iCorrectedRow, SEEK_SET);
1256 }
1257 else
1258 {
1259 VSIFSeekL(fpTableX, 16 + static_cast<vsi_l_offset>(nTablxOffsetSize) * iRow, SEEK_SET);
1260 }
1261
1262 GByte abyBuffer[6];
1263 bError = VSIFReadL(abyBuffer, nTablxOffsetSize, 1, fpTableX) != 1;
1264 returnErrorIf(bError );
1265 vsi_l_offset nOffset;
1266
1267 if( nTablxOffsetSize == 4 )
1268 nOffset = GetUInt32(abyBuffer, 0);
1269 else if( nTablxOffsetSize == 5 )
1270 nOffset = GetUInt32(abyBuffer, 0) | (((vsi_l_offset)abyBuffer[4]) << 32);
1271 else
1272 nOffset = GetUInt32(abyBuffer, 0) | (((vsi_l_offset)abyBuffer[4]) << 32) | (((vsi_l_offset)abyBuffer[5]) << 40);
1273
1274 #ifdef DEBUG_VERBOSE
1275 if( iRow == 0 && nOffset != 0 &&
1276 nOffset != nOffsetHeaderEnd && nOffset != nOffsetHeaderEnd + 4 )
1277 CPLDebug("OpenFileGDB", "%s: first feature offset = " CPL_FRMT_GUIB ". Expected " CPL_FRMT_GUIB,
1278 osFilename.c_str(), nOffset, nOffsetHeaderEnd);
1279 #endif
1280
1281 return nOffset;
1282 }
1283
1284 /************************************************************************/
1285 /* GetAndSelectNextNonEmptyRow() */
1286 /************************************************************************/
1287
GetAndSelectNextNonEmptyRow(int iRow)1288 int FileGDBTable::GetAndSelectNextNonEmptyRow(int iRow)
1289 {
1290 const int errorRetValue = -1;
1291 returnErrorAndCleanupIf(iRow < 0 || iRow >= nTotalRecordCount, nCurRow = -1 );
1292
1293 while( iRow < nTotalRecordCount )
1294 {
1295 if( pabyTablXBlockMap != nullptr && (iRow % 1024) == 0 )
1296 {
1297 int iBlock = iRow / 1024;
1298 if( TEST_BIT(pabyTablXBlockMap, iBlock) == 0 )
1299 {
1300 int nBlocks = (nTotalRecordCount+1023)/1024;
1301 do
1302 {
1303 iBlock ++;
1304 }
1305 while( iBlock < nBlocks &&
1306 TEST_BIT(pabyTablXBlockMap, iBlock) == 0 );
1307
1308 iRow = iBlock * 1024;
1309 if( iRow >= nTotalRecordCount )
1310 return -1;
1311 }
1312 }
1313
1314 if( SelectRow(iRow) )
1315 return iRow;
1316 if( HasGotError() )
1317 return -1;
1318 iRow ++;
1319 }
1320
1321 return -1;
1322 }
1323
1324 /************************************************************************/
1325 /* SelectRow() */
1326 /************************************************************************/
1327
SelectRow(int iRow)1328 int FileGDBTable::SelectRow(int iRow)
1329 {
1330 const int errorRetValue = FALSE;
1331 returnErrorAndCleanupIf(iRow < 0 || iRow >= nTotalRecordCount, nCurRow = -1 );
1332
1333 if( nCurRow != iRow )
1334 {
1335 vsi_l_offset nOffsetTable = GetOffsetInTableForRow(iRow);
1336 if( nOffsetTable == 0 )
1337 {
1338 nCurRow = -1;
1339 return FALSE;
1340 }
1341
1342 VSIFSeekL(fpTable, nOffsetTable, SEEK_SET);
1343 GByte abyBuffer[4];
1344 returnErrorAndCleanupIf(
1345 VSIFReadL(abyBuffer, 4, 1, fpTable) != 1, nCurRow = -1 );
1346
1347 nRowBlobLength = GetUInt32(abyBuffer, 0);
1348 if( bIsDeleted )
1349 {
1350 nRowBlobLength = (GUInt32)(-(int)nRowBlobLength);
1351 }
1352
1353 if( !(apoFields.empty() && nRowBlobLength == 0) )
1354 {
1355 /* CPLDebug("OpenFileGDB", "nRowBlobLength = %u", nRowBlobLength); */
1356 returnErrorAndCleanupIf(
1357 nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes ||
1358 nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER, nCurRow = -1 );
1359
1360 if( nRowBlobLength > nBufferMaxSize )
1361 {
1362 /* For suspicious row blob length, check if we don't go beyond file size */
1363 if( nRowBlobLength > 100 * 1024 * 1024 )
1364 {
1365 if( nFileSize == 0 )
1366 {
1367 VSIFSeekL(fpTable, 0, SEEK_END);
1368 nFileSize = VSIFTellL(fpTable);
1369 VSIFSeekL(fpTable, nOffsetTable + 4, SEEK_SET);
1370 }
1371 returnErrorAndCleanupIf( nOffsetTable + 4 + nRowBlobLength > nFileSize, nCurRow = -1 );
1372 }
1373
1374 GByte* pabyNewBuffer = (GByte*) VSI_REALLOC_VERBOSE( pabyBuffer,
1375 nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER );
1376 returnErrorAndCleanupIf(pabyNewBuffer == nullptr, nCurRow = -1 );
1377
1378 pabyBuffer = pabyNewBuffer;
1379 nBufferMaxSize = nRowBlobLength;
1380 }
1381 returnErrorAndCleanupIf(
1382 VSIFReadL(pabyBuffer, nRowBlobLength, 1, fpTable) != 1, nCurRow = -1 );
1383 /* Protection for 4 ReadVarUInt64NoCheck */
1384 CPL_STATIC_ASSERT(ZEROES_AFTER_END_OF_BUFFER == 4);
1385 pabyBuffer[nRowBlobLength] = 0;
1386 pabyBuffer[nRowBlobLength+1] = 0;
1387 pabyBuffer[nRowBlobLength+2] = 0;
1388 pabyBuffer[nRowBlobLength+3] = 0;
1389 }
1390
1391 nCurRow = iRow;
1392 nLastCol = -1;
1393 pabyIterVals = pabyBuffer + nNullableFieldsSizeInBytes;
1394 iAccNullable = 0;
1395 bError = FALSE;
1396 nChSaved = -1;
1397 }
1398
1399 return TRUE;
1400 }
1401
1402 /************************************************************************/
1403 /* FileGDBDoubleDateToOGRDate() */
1404 /************************************************************************/
1405
FileGDBDoubleDateToOGRDate(double dfVal,OGRField * psField)1406 int FileGDBDoubleDateToOGRDate(double dfVal, OGRField* psField)
1407 {
1408 // 25569: Number of days between 1899/12/30 00:00:00 and 1970/01/01 00:00:00
1409 double dfSeconds = (dfVal - 25569.0) * 3600.0 * 24.0;
1410 if( CPLIsNan(dfSeconds) ||
1411 dfSeconds < static_cast<double>(std::numeric_limits<GIntBig>::min())+1000 ||
1412 dfSeconds > static_cast<double>(std::numeric_limits<GIntBig>::max())-1000 )
1413 {
1414 CPLError(CE_Failure, CPLE_NotSupported,
1415 "FileGDBDoubleDateToOGRDate: Invalid days: %lf", dfVal);
1416 dfSeconds = 0.0;
1417 }
1418
1419 struct tm brokendowntime;
1420 CPLUnixTimeToYMDHMS(static_cast<GIntBig>(dfSeconds), &brokendowntime);
1421
1422 psField->Date.Year = (GInt16)(brokendowntime.tm_year + 1900);
1423 psField->Date.Month = (GByte)brokendowntime.tm_mon + 1;
1424 psField->Date.Day = (GByte)brokendowntime.tm_mday;
1425 psField->Date.Hour = (GByte)brokendowntime.tm_hour;
1426 psField->Date.Minute = (GByte)brokendowntime.tm_min;
1427 psField->Date.Second = (float)brokendowntime.tm_sec;
1428 psField->Date.TZFlag = 0;
1429 psField->Date.Reserved = 0;
1430
1431 return TRUE;
1432 }
1433
1434 /************************************************************************/
1435 /* GetFieldValue() */
1436 /************************************************************************/
1437
GetFieldValue(int iCol)1438 OGRField* FileGDBTable::GetFieldValue(int iCol)
1439 {
1440 OGRField* errorRetValue = nullptr;
1441
1442 returnErrorIf(nCurRow < 0 );
1443 returnErrorIf((GUInt32)iCol >= apoFields.size() );
1444 returnErrorIf(bError );
1445
1446 GByte* pabyEnd = pabyBuffer + nRowBlobLength;
1447
1448 /* In case a string was previously read */
1449 if( nChSaved >= 0 )
1450 {
1451 *pabyIterVals = (GByte)nChSaved;
1452 nChSaved = -1;
1453 }
1454
1455 if( iCol <= nLastCol )
1456 {
1457 nLastCol = -1;
1458 pabyIterVals = pabyBuffer + nNullableFieldsSizeInBytes;
1459 iAccNullable = 0;
1460 }
1461
1462 // Skip previous fields
1463 for( int j = nLastCol + 1; j < iCol; j++ )
1464 {
1465 if( apoFields[j]->bNullable )
1466 {
1467 int bIsNull = TEST_BIT(pabyBuffer, iAccNullable);
1468 iAccNullable ++;
1469 if( bIsNull )
1470 continue;
1471 }
1472
1473 GUInt32 nLength = 0;
1474 CPL_IGNORE_RET_VAL(nLength);
1475 switch( apoFields[j]->eType )
1476 {
1477 case FGFT_STRING:
1478 case FGFT_XML:
1479 case FGFT_GEOMETRY:
1480 case FGFT_BINARY:
1481 {
1482 if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) )
1483 {
1484 bError = TRUE;
1485 returnError();
1486 }
1487 break;
1488 }
1489
1490 case FGFT_RASTER:
1491 {
1492 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(apoFields[j]);
1493 if( rasterField->IsManaged() )
1494 nLength = sizeof(GInt32);
1495 else
1496 {
1497 if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) )
1498 {
1499 bError = TRUE;
1500 returnError();
1501 }
1502 }
1503 break;
1504 }
1505
1506 case FGFT_INT16: nLength = sizeof(GInt16); break;
1507 case FGFT_INT32: nLength = sizeof(GInt32); break;
1508 case FGFT_FLOAT32: nLength = sizeof(float); break;
1509 case FGFT_FLOAT64: nLength = sizeof(double); break;
1510 case FGFT_DATETIME: nLength = sizeof(double); break;
1511 case FGFT_UUID_1:
1512 case FGFT_UUID_2: nLength = UUID_SIZE_IN_BYTES; break;
1513
1514 default:
1515 CPLAssert(false);
1516 break;
1517 }
1518
1519 if( nLength > (GUInt32)(pabyEnd - pabyIterVals) )
1520 {
1521 bError = TRUE;
1522 returnError();
1523 }
1524 pabyIterVals += nLength;
1525 }
1526
1527 nLastCol = iCol;
1528
1529 if( apoFields[iCol]->bNullable )
1530 {
1531 int bIsNull = TEST_BIT(pabyBuffer, iAccNullable);
1532 iAccNullable ++;
1533 if( bIsNull )
1534 {
1535 return nullptr;
1536 }
1537 }
1538
1539 switch( apoFields[iCol]->eType )
1540 {
1541 case FGFT_STRING:
1542 case FGFT_XML:
1543 {
1544 GUInt32 nLength;
1545 if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) )
1546 {
1547 bError = TRUE;
1548 returnError();
1549 }
1550 if( nLength > (GUInt32)(pabyEnd - pabyIterVals) )
1551 {
1552 bError = TRUE;
1553 returnError();
1554 }
1555
1556 /* eCurFieldType = OFTString; */
1557 sCurField.String = (char*) pabyIterVals;
1558 pabyIterVals += nLength;
1559
1560 /* This is a trick to avoid a alloc()+copy(). We null-terminate */
1561 /* after the string, and save the pointer and value to restore */
1562 nChSaved = *pabyIterVals;
1563 *pabyIterVals = '\0';
1564
1565 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow, sCurField.String); */
1566
1567 break;
1568 }
1569
1570 case FGFT_INT16:
1571 {
1572 if( pabyIterVals + sizeof(GInt16) > pabyEnd )
1573 {
1574 bError = TRUE;
1575 returnError();
1576 }
1577
1578 /* eCurFieldType = OFTInteger; */
1579 sCurField.Integer = GetInt16(pabyIterVals, 0);
1580
1581 pabyIterVals += sizeof(GInt16);
1582 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow, sCurField.Integer); */
1583
1584 break;
1585 }
1586
1587 case FGFT_INT32:
1588 {
1589 if( pabyIterVals + sizeof(GInt32) > pabyEnd )
1590 {
1591 bError = TRUE;
1592 returnError();
1593 }
1594
1595 /* eCurFieldType = OFTInteger; */
1596 sCurField.Integer = GetInt32(pabyIterVals, 0);
1597
1598 pabyIterVals += sizeof(GInt32);
1599 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow, sCurField.Integer); */
1600
1601 break;
1602 }
1603
1604 case FGFT_FLOAT32:
1605 {
1606 if( pabyIterVals + sizeof(float) > pabyEnd )
1607 {
1608 bError = TRUE;
1609 returnError();
1610 }
1611
1612 /* eCurFieldType = OFTReal; */
1613 sCurField.Real = GetFloat32(pabyIterVals, 0);
1614
1615 pabyIterVals += sizeof(float);
1616 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow, sCurField.Real); */
1617
1618 break;
1619 }
1620
1621 case FGFT_FLOAT64:
1622 {
1623 if( pabyIterVals + sizeof(double) > pabyEnd )
1624 {
1625 bError = TRUE;
1626 returnError();
1627 }
1628
1629 /* eCurFieldType = OFTReal; */
1630 sCurField.Real = GetFloat64(pabyIterVals, 0);
1631
1632 pabyIterVals += sizeof(double);
1633 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow, sCurField.Real); */
1634
1635 break;
1636 }
1637
1638 case FGFT_DATETIME:
1639 {
1640 if( pabyIterVals + sizeof(double) > pabyEnd )
1641 {
1642 bError = TRUE;
1643 returnError();
1644 }
1645
1646 /* Number of days since 1899/12/30 00:00:00 */
1647 const double dfVal = GetFloat64(pabyIterVals, 0);
1648
1649 FileGDBDoubleDateToOGRDate(dfVal, &sCurField);
1650 /* eCurFieldType = OFTDateTime; */
1651
1652 pabyIterVals += sizeof(double);
1653
1654 break;
1655 }
1656
1657 case FGFT_GEOMETRY:
1658 case FGFT_BINARY:
1659 {
1660 GUInt32 nLength;
1661 if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) )
1662 {
1663 bError = TRUE;
1664 returnError();
1665 }
1666 if( nLength > (GUInt32)(pabyEnd - pabyIterVals) )
1667 {
1668 bError = TRUE;
1669 returnError();
1670 }
1671
1672 /* eCurFieldType = OFTBinary; */
1673 sCurField.Binary.nCount = nLength;
1674 sCurField.Binary.paData = (GByte*) pabyIterVals;
1675
1676 pabyIterVals += nLength;
1677
1678 /* Null terminate binary in case it is used as a string */
1679 nChSaved = *pabyIterVals;
1680 *pabyIterVals = '\0';
1681
1682 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d bytes", iCol, nCurRow, snLength); */
1683
1684 break;
1685 }
1686
1687 case FGFT_RASTER:
1688 {
1689 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(apoFields[iCol]);
1690 if( rasterField->IsManaged() )
1691 {
1692 if( pabyIterVals + sizeof(GInt32) > pabyEnd )
1693 {
1694 bError = TRUE;
1695 returnError();
1696 }
1697
1698 const GInt32 nVal = GetInt32(pabyIterVals, 0);
1699
1700 /* eCurFieldType = OFTIntger; */
1701 sCurField.Integer = nVal;
1702
1703 pabyIterVals += sizeof(GInt32);
1704 }
1705 else
1706 {
1707 GUInt32 nLength;
1708 if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) )
1709 {
1710 bError = TRUE;
1711 returnError();
1712 }
1713 if( nLength > (GUInt32)(pabyEnd - pabyIterVals) )
1714 {
1715 bError = TRUE;
1716 returnError();
1717 }
1718
1719 // coverity[tainted_data,tainted_data_argument]
1720 m_osCacheRasterFieldPath = ReadUTF16String(pabyIterVals, nLength / 2);
1721 pabyIterVals += nLength;
1722
1723 sCurField.String = &m_osCacheRasterFieldPath[0];
1724 }
1725 break;
1726 }
1727
1728 case FGFT_UUID_1:
1729 case FGFT_UUID_2:
1730 {
1731 if( pabyIterVals + UUID_SIZE_IN_BYTES > pabyEnd )
1732 {
1733 bError = TRUE;
1734 returnError();
1735 }
1736
1737 /* eCurFieldType = OFTString; */
1738 sCurField.String = achGUIDBuffer;
1739 /*78563412BC9AF0DE1234567890ABCDEF --> {12345678-9ABC-DEF0-1234-567890ABCDEF} */
1740 snprintf(achGUIDBuffer, sizeof(achGUIDBuffer),
1741 "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
1742 pabyIterVals[3], pabyIterVals[2], pabyIterVals[1], pabyIterVals[0],
1743 pabyIterVals[5], pabyIterVals[4],
1744 pabyIterVals[7], pabyIterVals[6],
1745 pabyIterVals[8], pabyIterVals[9],
1746 pabyIterVals[10], pabyIterVals[11], pabyIterVals[12],
1747 pabyIterVals[13], pabyIterVals[14], pabyIterVals[15]);
1748
1749 pabyIterVals += UUID_SIZE_IN_BYTES;
1750 /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow, sCurField.String); */
1751
1752 break;
1753 }
1754
1755 default:
1756 CPLAssert(false);
1757 break;
1758 }
1759
1760 if( iCol == (int)apoFields.size() - 1 && pabyIterVals < pabyEnd )
1761 {
1762 CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %d",
1763 (int)(pabyEnd - pabyIterVals), nCurRow);
1764 }
1765
1766 return &sCurField;
1767 }
1768
1769 /************************************************************************/
1770 /* GetIndexCount() */
1771 /************************************************************************/
1772
GetIndexCount()1773 int FileGDBTable::GetIndexCount()
1774 {
1775 const int errorRetValue = 0;
1776 if( bHasReadGDBIndexes )
1777 return (int) apoIndexes.size();
1778
1779 bHasReadGDBIndexes = TRUE;
1780
1781 const char* pszIndexesName = CPLFormFilename(CPLGetPath(osFilename.c_str()),
1782 CPLGetBasename(osFilename.c_str()), "gdbindexes");
1783 VSILFILE* fpIndexes = VSIFOpenL( pszIndexesName, "rb" );
1784 VSIStatBufL sStat;
1785 if( fpIndexes == nullptr )
1786 {
1787 if ( VSIStatExL( pszIndexesName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
1788 returnError();
1789 else
1790 return 0;
1791 }
1792
1793 VSIFSeekL(fpIndexes, 0, SEEK_END);
1794 vsi_l_offset l_nFileSize = VSIFTellL(fpIndexes);
1795 returnErrorAndCleanupIf(l_nFileSize > 1024 * 1024, VSIFCloseL(fpIndexes) );
1796
1797 GByte* pabyIdx = (GByte*)VSI_MALLOC_VERBOSE((size_t)l_nFileSize);
1798 returnErrorAndCleanupIf(pabyIdx == nullptr, VSIFCloseL(fpIndexes) );
1799
1800 VSIFSeekL(fpIndexes, 0, SEEK_SET);
1801 int nRead = (int)VSIFReadL( pabyIdx, (size_t)l_nFileSize, 1, fpIndexes );
1802 VSIFCloseL(fpIndexes);
1803 returnErrorAndCleanupIf(nRead != 1, VSIFree(pabyIdx) );
1804
1805 GByte* pabyCur = pabyIdx;
1806 GByte* pabyEnd = pabyIdx + l_nFileSize;
1807 returnErrorAndCleanupIf(pabyEnd - pabyCur < 4, VSIFree(pabyIdx) );
1808 GUInt32 nIndexCount = GetUInt32(pabyCur, 0);
1809 pabyCur += 4;
1810
1811 // FileGDB v9 indexes structure not handled yet. Start with 13 98 85 03
1812 if( nIndexCount == 0x03859813 )
1813 {
1814 CPLDebug("OpenFileGDB", ".gdbindexes v9 not handled yet");
1815 VSIFree(pabyIdx);
1816 return 0;
1817 }
1818 returnErrorAndCleanupIf(nIndexCount >= (size_t)(GetFieldCount() + 1) * 10, VSIFree(pabyIdx) );
1819
1820 GUInt32 i;
1821 for(i=0;i<nIndexCount;i++)
1822 {
1823 returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < sizeof(GUInt32), VSIFree(pabyIdx) );
1824 GUInt32 nIdxNameCarCount = GetUInt32(pabyCur, 0);
1825 pabyCur += sizeof(GUInt32);
1826 returnErrorAndCleanupIf(nIdxNameCarCount > 1024, VSIFree(pabyIdx) );
1827 returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < 2 * nIdxNameCarCount, VSIFree(pabyIdx) );
1828 std::string osIndexName(ReadUTF16String(pabyCur, nIdxNameCarCount));
1829 pabyCur += 2 * nIdxNameCarCount;
1830
1831 // Skip magic fields
1832 pabyCur += 2 + 4 + 2 + 4;
1833
1834 returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < sizeof(GUInt32), VSIFree(pabyIdx) );
1835 GUInt32 nColNameCarCount = GetUInt32(pabyCur, 0);
1836 pabyCur += sizeof(GUInt32);
1837 returnErrorAndCleanupIf(nColNameCarCount > 1024, VSIFree(pabyIdx) );
1838 returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < 2 * nColNameCarCount, VSIFree(pabyIdx) );
1839 std::string osFieldName(ReadUTF16String(pabyCur, nColNameCarCount));
1840 pabyCur += 2 * nColNameCarCount;
1841
1842 // Skip magic field
1843 pabyCur += 2;
1844
1845 FileGDBIndex* poIndex = new FileGDBIndex();
1846 poIndex->osIndexName = osIndexName;
1847 poIndex->osFieldName = osFieldName;
1848 apoIndexes.push_back(poIndex);
1849
1850 if( osFieldName != osObjectIdColName )
1851 {
1852 int nFieldIdx = GetFieldIdx(osFieldName);
1853 if( nFieldIdx < 0 )
1854 {
1855 CPLDebug("OpenFileGDB",
1856 "Index defined for field %s that does not exist",
1857 osFieldName.c_str());
1858 }
1859 else
1860 {
1861 if( apoFields[nFieldIdx]->poIndex != nullptr )
1862 {
1863 CPLDebug("OpenFileGDB",
1864 "There is already one index defined for field %s",
1865 osFieldName.c_str());
1866 }
1867 else
1868 {
1869 apoFields[nFieldIdx]->poIndex = poIndex;
1870 }
1871 }
1872 }
1873 }
1874
1875 VSIFree(pabyIdx);
1876
1877 return (int) apoIndexes.size();
1878 }
1879
1880 /************************************************************************/
1881 /* HasSpatialIndex() */
1882 /************************************************************************/
1883
HasSpatialIndex()1884 bool FileGDBTable::HasSpatialIndex()
1885 {
1886 if( m_nHasSpatialIndex < 0 )
1887 {
1888 const char* pszSpxName = CPLFormFilename(
1889 CPLGetPath(osFilename.c_str()),
1890 CPLGetBasename(osFilename.c_str()), "spx");
1891 VSIStatBufL sStat;
1892 m_nHasSpatialIndex =
1893 ( VSIStatExL( pszSpxName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 );
1894 }
1895 return m_nHasSpatialIndex != FALSE;
1896 }
1897
1898 /************************************************************************/
1899 /* InstallFilterEnvelope() */
1900 /************************************************************************/
1901
1902 #define MAX_GUINTBIG (~((GUIntBig)0))
1903
InstallFilterEnvelope(const OGREnvelope * psFilterEnvelope)1904 void FileGDBTable::InstallFilterEnvelope(const OGREnvelope* psFilterEnvelope)
1905 {
1906 if( psFilterEnvelope != nullptr )
1907 {
1908 CPLAssert( iGeomField >= 0 );
1909 FileGDBGeomField* poGeomField = (FileGDBGeomField*) GetField(iGeomField);
1910
1911 /* We store the bounding box as unscaled coordinates, so that BBOX */
1912 /* intersection is done with integer comparisons */
1913 if( psFilterEnvelope->MinX >= poGeomField->dfXOrigin )
1914 nFilterXMin = (GUIntBig)(0.5 + (psFilterEnvelope->MinX -
1915 poGeomField->dfXOrigin) * poGeomField->dfXYScale);
1916 else
1917 nFilterXMin = 0;
1918 if( psFilterEnvelope->MaxX - poGeomField->dfXOrigin <
1919 static_cast<double>(MAX_GUINTBIG) / poGeomField->dfXYScale )
1920 nFilterXMax = (GUIntBig)(0.5 + (psFilterEnvelope->MaxX -
1921 poGeomField->dfXOrigin) * poGeomField->dfXYScale);
1922 else
1923 nFilterXMax = MAX_GUINTBIG;
1924 if( psFilterEnvelope->MinY >= poGeomField->dfYOrigin )
1925 nFilterYMin = (GUIntBig)(0.5 + (psFilterEnvelope->MinY -
1926 poGeomField->dfYOrigin) * poGeomField->dfXYScale);
1927 else
1928 nFilterYMin = 0;
1929 if( psFilterEnvelope->MaxY - poGeomField->dfYOrigin <
1930 static_cast<double>(MAX_GUINTBIG) / poGeomField->dfXYScale )
1931 nFilterYMax = (GUIntBig)(0.5 + (psFilterEnvelope->MaxY -
1932 poGeomField->dfYOrigin) * poGeomField->dfXYScale);
1933 else
1934 nFilterYMax = MAX_GUINTBIG;
1935 }
1936 else
1937 {
1938 nFilterXMin = 0;
1939 nFilterXMax = 0;
1940 nFilterYMin = 0;
1941 nFilterYMax = 0;
1942 }
1943 }
1944
1945 /************************************************************************/
1946 /* GetFeatureExtent() */
1947 /************************************************************************/
1948
GetFeatureExtent(const OGRField * psField,OGREnvelope * psOutFeatureEnvelope)1949 int FileGDBTable::GetFeatureExtent(const OGRField* psField,
1950 OGREnvelope* psOutFeatureEnvelope)
1951 {
1952 const int errorRetValue = FALSE;
1953 GByte* pabyCur = psField->Binary.paData;
1954 GByte* pabyEnd = pabyCur + psField->Binary.nCount;
1955 GUInt32 nGeomType;
1956 int nToSkip = 0;
1957
1958 CPLAssert( iGeomField >= 0 );
1959 FileGDBGeomField* poGeomField = (FileGDBGeomField*) GetField(iGeomField);
1960
1961 ReadVarUInt32NoCheck(pabyCur, nGeomType);
1962
1963 switch( (nGeomType & 0xff) )
1964 {
1965 case SHPT_NULL:
1966 return FALSE;
1967
1968 case SHPT_POINTZ:
1969 case SHPT_POINTZM:
1970 case SHPT_POINT:
1971 case SHPT_POINTM:
1972 case SHPT_GENERALPOINT:
1973 {
1974 GUIntBig x, y;
1975 ReadVarUInt64NoCheck(pabyCur, x);
1976 x = CPLUnsanitizedAdd<GUIntBig>(x, -1);
1977 ReadVarUInt64NoCheck(pabyCur, y);
1978 y = CPLUnsanitizedAdd<GUIntBig>(y, -1);
1979 psOutFeatureEnvelope->MinX = x / poGeomField->dfXYScale + poGeomField->dfXOrigin;
1980 psOutFeatureEnvelope->MinY = y / poGeomField->dfXYScale + poGeomField->dfYOrigin;
1981 psOutFeatureEnvelope->MaxX = psOutFeatureEnvelope->MinX;
1982 psOutFeatureEnvelope->MaxY = psOutFeatureEnvelope->MinY;
1983 return TRUE;
1984 }
1985
1986 case SHPT_MULTIPOINTZM:
1987 case SHPT_MULTIPOINTZ:
1988 case SHPT_MULTIPOINT:
1989 case SHPT_MULTIPOINTM:
1990 {
1991 break;
1992 }
1993
1994 case SHPT_ARC:
1995 case SHPT_ARCZ:
1996 case SHPT_ARCZM:
1997 case SHPT_ARCM:
1998 case SHPT_POLYGON:
1999 case SHPT_POLYGONZ:
2000 case SHPT_POLYGONZM:
2001 case SHPT_POLYGONM:
2002 {
2003 nToSkip = 1;
2004 break;
2005 }
2006 case SHPT_GENERALPOLYLINE:
2007 case SHPT_GENERALPOLYGON:
2008 {
2009 nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
2010 break;
2011 }
2012
2013 case SHPT_GENERALMULTIPATCH:
2014 case SHPT_MULTIPATCHM:
2015 case SHPT_MULTIPATCH:
2016 {
2017 nToSkip = 2;
2018 break;
2019 }
2020
2021 default:
2022 return FALSE;
2023 }
2024
2025 GUInt32 nPoints;
2026 ReadVarUInt32NoCheck(pabyCur, nPoints);
2027 if( nPoints == 0 )
2028 return TRUE;
2029 returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip) );
2030
2031 GUIntBig vxmin, vymin, vdx, vdy;
2032
2033 returnErrorIf(pabyCur >= pabyEnd);
2034 ReadVarUInt64NoCheck(pabyCur, vxmin);
2035 ReadVarUInt64NoCheck(pabyCur, vymin);
2036 ReadVarUInt64NoCheck(pabyCur, vdx);
2037 ReadVarUInt64NoCheck(pabyCur, vdy);
2038
2039 psOutFeatureEnvelope->MinX = vxmin / poGeomField->dfXYScale + poGeomField->dfXOrigin;
2040 psOutFeatureEnvelope->MinY = vymin / poGeomField->dfXYScale + poGeomField->dfYOrigin;
2041 psOutFeatureEnvelope->MaxX = CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) / poGeomField->dfXYScale + poGeomField->dfXOrigin;
2042 psOutFeatureEnvelope->MaxY = CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) / poGeomField->dfXYScale + poGeomField->dfYOrigin;
2043
2044 return TRUE;
2045 }
2046
2047 /************************************************************************/
2048 /* DoesGeometryIntersectsFilterEnvelope() */
2049 /************************************************************************/
2050
DoesGeometryIntersectsFilterEnvelope(const OGRField * psField)2051 int FileGDBTable::DoesGeometryIntersectsFilterEnvelope(const OGRField* psField)
2052 {
2053 const int errorRetValue = TRUE;
2054 GByte* pabyCur = psField->Binary.paData;
2055 GByte* pabyEnd = pabyCur + psField->Binary.nCount;
2056 GUInt32 nGeomType;
2057 int nToSkip = 0;
2058
2059 ReadVarUInt32NoCheck(pabyCur, nGeomType);
2060
2061 switch( (nGeomType & 0xff) )
2062 {
2063 case SHPT_NULL:
2064 return TRUE;
2065
2066 case SHPT_POINTZ:
2067 case SHPT_POINTZM:
2068 case SHPT_POINT:
2069 case SHPT_POINTM:
2070 case SHPT_GENERALPOINT:
2071 {
2072 GUIntBig x, y;
2073 ReadVarUInt64NoCheck(pabyCur, x);
2074 x --;
2075 if( x < nFilterXMin || x > nFilterXMax )
2076 return FALSE;
2077 ReadVarUInt64NoCheck(pabyCur, y);
2078 y --;
2079 return y >= nFilterYMin && y <= nFilterYMax;
2080 }
2081
2082 case SHPT_MULTIPOINTZM:
2083 case SHPT_MULTIPOINTZ:
2084 case SHPT_MULTIPOINT:
2085 case SHPT_MULTIPOINTM:
2086 {
2087 break;
2088 }
2089
2090 case SHPT_ARC:
2091 case SHPT_ARCZ:
2092 case SHPT_ARCZM:
2093 case SHPT_ARCM:
2094 case SHPT_POLYGON:
2095 case SHPT_POLYGONZ:
2096 case SHPT_POLYGONZM:
2097 case SHPT_POLYGONM:
2098 {
2099 nToSkip = 1;
2100 break;
2101 }
2102
2103 case SHPT_GENERALPOLYLINE:
2104 case SHPT_GENERALPOLYGON:
2105 {
2106 nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
2107 break;
2108 }
2109
2110 case SHPT_GENERALMULTIPATCH:
2111 case SHPT_MULTIPATCHM:
2112 case SHPT_MULTIPATCH:
2113 {
2114 nToSkip = 2;
2115 break;
2116 }
2117
2118 default:
2119 return TRUE;
2120 }
2121
2122 GUInt32 nPoints;
2123 ReadVarUInt32NoCheck(pabyCur, nPoints);
2124 if( nPoints == 0 )
2125 return TRUE;
2126 returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip) );
2127
2128 GUIntBig vxmin, vymin, vdx, vdy;
2129
2130 returnErrorIf(pabyCur >= pabyEnd);
2131 ReadVarUInt64NoCheck(pabyCur, vxmin);
2132 if( vxmin > nFilterXMax )
2133 return FALSE;
2134 ReadVarUInt64NoCheck(pabyCur, vymin);
2135 if( vymin > nFilterYMax )
2136 return FALSE;
2137 ReadVarUInt64NoCheck(pabyCur, vdx);
2138 if( CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) < nFilterXMin )
2139 return FALSE;
2140 ReadVarUInt64NoCheck(pabyCur, vdy);
2141 return CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) >= nFilterYMin;
2142 }
2143
2144 /************************************************************************/
2145 /* FileGDBField() */
2146 /************************************************************************/
2147
FileGDBField(FileGDBTable * poParentIn)2148 FileGDBField::FileGDBField( FileGDBTable* poParentIn ) :
2149 poParent(poParentIn),
2150 eType(FGFT_UNDEFINED),
2151 bNullable(FALSE),
2152 nMaxWidth(0),
2153 poIndex(nullptr)
2154 {
2155 OGR_RawField_SetUnset(&sDefault);
2156 }
2157
2158 /************************************************************************/
2159 /* ~FileGDBField() */
2160 /************************************************************************/
2161
~FileGDBField()2162 FileGDBField::~FileGDBField()
2163 {
2164 if( eType == FGFT_STRING &&
2165 !OGR_RawField_IsUnset(&sDefault) &&
2166 !OGR_RawField_IsNull(&sDefault) )
2167 CPLFree(sDefault.String);
2168 }
2169
2170 /************************************************************************/
2171 /* HasIndex() */
2172 /************************************************************************/
2173
HasIndex()2174 int FileGDBField::HasIndex()
2175 {
2176 poParent->GetIndexCount();
2177 return poIndex != nullptr;
2178 }
2179
2180 /************************************************************************/
2181 /* GetIndex() */
2182 /************************************************************************/
2183
GetIndex()2184 FileGDBIndex *FileGDBField::GetIndex()
2185 {
2186 poParent->GetIndexCount();
2187 return poIndex;
2188 }
2189
2190 /************************************************************************/
2191 /* FileGDBGeomField() */
2192 /************************************************************************/
2193
FileGDBGeomField(FileGDBTable * poParentIn)2194 FileGDBGeomField::FileGDBGeomField( FileGDBTable* poParentIn ) :
2195 FileGDBField(poParentIn)
2196 {}
2197
2198 /************************************************************************/
2199 /* FileGDBOGRGeometryConverterImpl */
2200 /************************************************************************/
2201
2202 class FileGDBOGRGeometryConverterImpl final : public FileGDBOGRGeometryConverter
2203 {
2204 const FileGDBGeomField *poGeomField;
2205 GUInt32 *panPointCount;
2206 GUInt32 nPointCountMax;
2207 #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
2208 int bUseOrganize;
2209 #endif
2210
2211 bool ReadPartDefs( GByte*& pabyCur,
2212 GByte* pabyEnd,
2213 GUInt32& nPoints,
2214 GUInt32& nParts,
2215 GUInt32& nCurves,
2216 bool bHasCurveDesc,
2217 bool bIsMultiPatch );
2218 template <class XYSetter> int ReadXYArray(XYSetter& setter,
2219 GByte*& pabyCur,
2220 GByte* pabyEnd,
2221 GUInt32 nPoints,
2222 GIntBig& dx,
2223 GIntBig& dy);
2224 template <class ZSetter> int ReadZArray(ZSetter& setter,
2225 GByte*& pabyCur,
2226 GByte* pabyEnd,
2227 GUInt32 nPoints,
2228 GIntBig& dz);
2229 template <class MSetter> int ReadMArray(MSetter& setter,
2230 GByte*& pabyCur,
2231 GByte* pabyEnd,
2232 GUInt32 nPoints,
2233 GIntBig& dm);
2234
2235 OGRGeometry* CreateCurveGeometry(
2236 GUInt32 nBaseShapeType,
2237 GUInt32 nParts, GUInt32 nPoints, GUInt32 nCurves,
2238 bool bHasZ, bool bHasM,
2239 GByte*& pabyCur, GByte* pabyEnd );
2240
2241 public:
2242 explicit FileGDBOGRGeometryConverterImpl(
2243 const FileGDBGeomField* poGeomField);
2244 virtual ~FileGDBOGRGeometryConverterImpl();
2245
2246 virtual OGRGeometry* GetAsGeometry(const OGRField* psField) override;
2247 };
2248
2249 /************************************************************************/
2250 /* FileGDBOGRGeometryConverterImpl() */
2251 /************************************************************************/
2252
FileGDBOGRGeometryConverterImpl(const FileGDBGeomField * poGeomFieldIn)2253 FileGDBOGRGeometryConverterImpl::FileGDBOGRGeometryConverterImpl(
2254 const FileGDBGeomField* poGeomFieldIn) :
2255 poGeomField(poGeomFieldIn),
2256 panPointCount(nullptr),
2257 nPointCountMax(0)
2258 #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
2259 ,
2260 bUseOrganize(CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", NULL) != NULL)
2261 #endif
2262 {}
2263
2264 /************************************************************************/
2265 /* ~FileGDBOGRGeometryConverter() */
2266 /************************************************************************/
2267
~FileGDBOGRGeometryConverterImpl()2268 FileGDBOGRGeometryConverterImpl::~FileGDBOGRGeometryConverterImpl()
2269 {
2270 CPLFree(panPointCount);
2271 }
2272
2273 /************************************************************************/
2274 /* ReadPartDefs() */
2275 /************************************************************************/
2276
ReadPartDefs(GByte * & pabyCur,GByte * pabyEnd,GUInt32 & nPoints,GUInt32 & nParts,GUInt32 & nCurves,bool bHasCurveDesc,bool bIsMultiPatch)2277 bool FileGDBOGRGeometryConverterImpl::ReadPartDefs( GByte*& pabyCur,
2278 GByte* pabyEnd,
2279 GUInt32& nPoints,
2280 GUInt32& nParts,
2281 GUInt32& nCurves,
2282 bool bHasCurveDesc,
2283 bool bIsMultiPatch )
2284 {
2285 const bool errorRetValue = false;
2286 returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints));
2287 if( nPoints == 0 )
2288 {
2289 nParts = 0;
2290 nCurves = 0;
2291 return true;
2292 }
2293 returnErrorIf(nPoints > (GUInt32)(pabyEnd - pabyCur) );
2294 if( bIsMultiPatch )
2295 returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd) );
2296 returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nParts));
2297 returnErrorIf(nParts > (GUInt32)(pabyEnd - pabyCur));
2298 returnErrorIf(nParts > static_cast<GUInt32>(INT_MAX) / sizeof(GUInt32));
2299 if( bHasCurveDesc )
2300 {
2301 returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nCurves) );
2302 returnErrorIf(nCurves > (GUInt32)(pabyEnd - pabyCur));
2303 }
2304 else
2305 nCurves = 0;
2306 if( nParts == 0 )
2307 return true;
2308 GUInt32 i;
2309 returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4) );
2310 if( nParts > nPointCountMax )
2311 {
2312 GUInt32* panPointCountNew =
2313 (GUInt32*) VSI_REALLOC_VERBOSE( panPointCount, nParts * sizeof(GUInt32) );
2314 returnErrorIf(panPointCountNew == nullptr );
2315 panPointCount = panPointCountNew;
2316 nPointCountMax = nParts;
2317 }
2318 GUIntBig nSumNPartsM1 = 0;
2319 for(i=0;i<nParts-1;i++)
2320 {
2321 GUInt32 nTmp;
2322 returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nTmp));
2323 returnErrorIf(nTmp > (GUInt32)(pabyEnd - pabyCur) );
2324 panPointCount[i] = nTmp;
2325 nSumNPartsM1 += nTmp;
2326 }
2327 returnErrorIf(nSumNPartsM1 > nPoints );
2328 panPointCount[nParts-1] = (GUInt32)(nPoints - nSumNPartsM1);
2329
2330 return true;
2331 }
2332
2333 /************************************************************************/
2334 /* XYLineStringSetter */
2335 /************************************************************************/
2336
2337 class FileGDBOGRLineString: public OGRLineString
2338 {
2339 public:
FileGDBOGRLineString()2340 FileGDBOGRLineString() {}
2341
GetPoints() const2342 OGRRawPoint * GetPoints() const { return paoPoints; }
2343 };
2344
2345 class FileGDBOGRLinearRing: public OGRLinearRing
2346 {
2347 public:
FileGDBOGRLinearRing()2348 FileGDBOGRLinearRing() {}
2349
GetPoints() const2350 OGRRawPoint * GetPoints() const { return paoPoints; }
2351 };
2352
2353 class XYLineStringSetter
2354 {
2355 OGRRawPoint* paoPoints;
2356 public:
XYLineStringSetter(OGRRawPoint * paoPointsIn)2357 explicit XYLineStringSetter(OGRRawPoint* paoPointsIn) :
2358 paoPoints(paoPointsIn) {}
2359
set(int i,double dfX,double dfY)2360 void set(int i, double dfX, double dfY)
2361 {
2362 paoPoints[i].x = dfX;
2363 paoPoints[i].y = dfY;
2364 }
2365 };
2366
2367 /************************************************************************/
2368 /* XYMultiPointSetter */
2369 /************************************************************************/
2370
2371 class XYMultiPointSetter
2372 {
2373 OGRMultiPoint* poMPoint;
2374 public:
XYMultiPointSetter(OGRMultiPoint * poMPointIn)2375 explicit XYMultiPointSetter(OGRMultiPoint* poMPointIn) :
2376 poMPoint(poMPointIn) {}
2377
set(int i,double dfX,double dfY)2378 void set(int i, double dfX, double dfY)
2379 {
2380 (void)i;
2381 poMPoint->addGeometryDirectly(new OGRPoint(dfX, dfY));
2382 }
2383 };
2384
2385 /************************************************************************/
2386 /* XYArraySetter */
2387 /************************************************************************/
2388
2389 class XYArraySetter
2390 {
2391 double* padfX;
2392 double* padfY;
2393 public:
XYArraySetter(double * padfXIn,double * padfYIn)2394 XYArraySetter(double* padfXIn, double* padfYIn) : padfX(padfXIn), padfY(padfYIn) {}
2395
set(int i,double dfX,double dfY)2396 void set(int i, double dfX, double dfY)
2397 {
2398 padfX[i] = dfX;
2399 padfY[i] = dfY;
2400 }
2401 };
2402
2403 /************************************************************************/
2404 /* ReadXYArray() */
2405 /************************************************************************/
2406
ReadXYArray(XYSetter & setter,GByte * & pabyCur,GByte * pabyEnd,GUInt32 nPoints,GIntBig & dx,GIntBig & dy)2407 template <class XYSetter> int FileGDBOGRGeometryConverterImpl::ReadXYArray(XYSetter& setter,
2408 GByte*& pabyCur,
2409 GByte* pabyEnd,
2410 GUInt32 nPoints,
2411 GIntBig& dx,
2412 GIntBig& dy)
2413 {
2414 const int errorRetValue = FALSE;
2415 GIntBig dxLocal = dx;
2416 GIntBig dyLocal = dy;
2417
2418 for(GUInt32 i = 0; i < nPoints; i++ )
2419 {
2420 returnErrorIf(pabyCur /*+ 1*/ >= pabyEnd);
2421
2422 ReadVarIntAndAddNoCheck(pabyCur, dxLocal);
2423 ReadVarIntAndAddNoCheck(pabyCur, dyLocal);
2424
2425 double dfX = dxLocal / poGeomField->GetXYScale() + poGeomField->GetXOrigin();
2426 double dfY = dyLocal / poGeomField->GetXYScale() + poGeomField->GetYOrigin();
2427 setter.set(i, dfX, dfY);
2428 }
2429
2430 dx = dxLocal;
2431 dy = dyLocal;
2432 return TRUE;
2433 }
2434
2435 /************************************************************************/
2436 /* ZLineStringSetter */
2437 /************************************************************************/
2438
2439 class ZLineStringSetter
2440 {
2441 OGRLineString* poLS;
2442 public:
ZLineStringSetter(OGRLineString * poLSIn)2443 explicit ZLineStringSetter(OGRLineString* poLSIn) : poLS(poLSIn) {}
2444
set(int i,double dfZ)2445 void set(int i, double dfZ)
2446 {
2447 poLS->setZ(i, dfZ);
2448 }
2449 };
2450
2451 /************************************************************************/
2452 /* ZMultiPointSetter */
2453 /************************************************************************/
2454
2455 class ZMultiPointSetter
2456 {
2457 OGRMultiPoint* poMPoint;
2458 public:
ZMultiPointSetter(OGRMultiPoint * poMPointIn)2459 explicit ZMultiPointSetter(OGRMultiPoint* poMPointIn) :
2460 poMPoint(poMPointIn) {}
2461
set(int i,double dfZ)2462 void set(int i, double dfZ)
2463 {
2464 poMPoint->getGeometryRef(i)->setZ(dfZ);
2465 }
2466 };
2467
2468 /************************************************************************/
2469 /* FileGDBArraySetter */
2470 /************************************************************************/
2471
2472 class FileGDBArraySetter
2473 {
2474 double* padfValues;
2475 public:
FileGDBArraySetter(double * padfValuesIn)2476 explicit FileGDBArraySetter(double* padfValuesIn) :
2477 padfValues(padfValuesIn) {}
2478
set(int i,double dfValue)2479 void set(int i, double dfValue)
2480 {
2481 padfValues[i] = dfValue;
2482 }
2483 };
2484
2485 /************************************************************************/
2486 /* ReadZArray() */
2487 /************************************************************************/
2488
ReadZArray(ZSetter & setter,GByte * & pabyCur,GByte * pabyEnd,GUInt32 nPoints,GIntBig & dz)2489 template <class ZSetter> int FileGDBOGRGeometryConverterImpl::ReadZArray(ZSetter& setter,
2490 GByte*& pabyCur,
2491 GByte* pabyEnd,
2492 GUInt32 nPoints,
2493 GIntBig& dz)
2494 {
2495 const int errorRetValue = FALSE;
2496 const double dfZScale = SanitizeScale(poGeomField->GetZScale());
2497 for(GUInt32 i = 0; i < nPoints; i++ )
2498 {
2499 returnErrorIf(pabyCur >= pabyEnd);
2500 ReadVarIntAndAddNoCheck(pabyCur, dz);
2501
2502 double dfZ = dz / dfZScale + poGeomField->GetZOrigin();
2503 setter.set(i, dfZ);
2504 }
2505 return TRUE;
2506 }
2507
2508 /************************************************************************/
2509 /* MLineStringSetter */
2510 /************************************************************************/
2511
2512 class MLineStringSetter
2513 {
2514 OGRLineString* poLS;
2515 public:
MLineStringSetter(OGRLineString * poLSIn)2516 explicit MLineStringSetter(OGRLineString* poLSIn) : poLS(poLSIn) {}
2517
set(int i,double dfM)2518 void set(int i, double dfM)
2519 {
2520 poLS->setM(i, dfM);
2521 }
2522 };
2523
2524 /************************************************************************/
2525 /* MMultiPointSetter */
2526 /************************************************************************/
2527
2528 class MMultiPointSetter
2529 {
2530 OGRMultiPoint* poMPoint;
2531 public:
MMultiPointSetter(OGRMultiPoint * poMPointIn)2532 explicit MMultiPointSetter(OGRMultiPoint* poMPointIn) :
2533 poMPoint(poMPointIn) {}
2534
set(int i,double dfM)2535 void set(int i, double dfM)
2536 {
2537 poMPoint->getGeometryRef(i)->setM(dfM);
2538 }
2539 };
2540
2541 /************************************************************************/
2542 /* ReadMArray() */
2543 /************************************************************************/
2544
ReadMArray(MSetter & setter,GByte * & pabyCur,GByte * pabyEnd,GUInt32 nPoints,GIntBig & dm)2545 template <class MSetter> int FileGDBOGRGeometryConverterImpl::ReadMArray(MSetter& setter,
2546 GByte*& pabyCur,
2547 GByte* pabyEnd,
2548 GUInt32 nPoints,
2549 GIntBig& dm)
2550 {
2551 const int errorRetValue = FALSE;
2552 const double dfMScale = SanitizeScale(poGeomField->GetMScale());
2553 for(GUInt32 i = 0; i < nPoints; i++ )
2554 {
2555 returnErrorIf(pabyCur >= pabyEnd);
2556 ReadVarIntAndAddNoCheck(pabyCur, dm);
2557
2558 double dfM = dm / dfMScale + poGeomField->GetMOrigin();
2559 setter.set(i, dfM);
2560 }
2561 return TRUE;
2562 }
2563
2564 /************************************************************************/
2565 /* CreateCurveGeometry() */
2566 /************************************************************************/
2567
2568 class XYBufferSetter
2569 {
2570 GByte* pabyBuffer;
2571 public:
XYBufferSetter(GByte * pabyBufferIn)2572 explicit XYBufferSetter(GByte* pabyBufferIn) :
2573 pabyBuffer(pabyBufferIn) {}
2574
set(int i,double dfX,double dfY)2575 void set(int i, double dfX, double dfY)
2576 {
2577 CPL_LSBPTR64(&dfX);
2578 memcpy( pabyBuffer + 16 * i, &dfX, 8 );
2579 CPL_LSBPTR64(&dfY);
2580 memcpy( pabyBuffer + 16 * i + 8, &dfY, 8 );
2581 }
2582 };
2583
2584 class ZOrMBufferSetter
2585 {
2586 GByte* pabyBuffer;
2587 public:
ZOrMBufferSetter(GByte * pabyBufferIn)2588 explicit ZOrMBufferSetter(GByte* pabyBufferIn) :
2589 pabyBuffer(pabyBufferIn) {}
2590
set(int i,double dfValue)2591 void set(int i, double dfValue)
2592 {
2593 CPL_LSBPTR64(&dfValue);
2594 memcpy( pabyBuffer + 8 * i, &dfValue, 8 );
2595 }
2596 };
2597
2598 /* We first create an extended shape buffer from the compressed stream */
2599 /* and finally use OGRCreateFromShapeBin() to make a geometry from it */
2600
CreateCurveGeometry(GUInt32 nBaseShapeType,GUInt32 nParts,GUInt32 nPoints,GUInt32 nCurves,bool bHasZ,bool bHasM,GByte * & pabyCur,GByte * pabyEnd)2601 OGRGeometry* FileGDBOGRGeometryConverterImpl::CreateCurveGeometry(
2602 GUInt32 nBaseShapeType,
2603 GUInt32 nParts, GUInt32 nPoints, GUInt32 nCurves,
2604 bool bHasZ, bool bHasM,
2605 GByte*& pabyCur, GByte* pabyEnd )
2606 {
2607 OGRGeometry* errorRetValue = nullptr;
2608 GUInt32 i;
2609 const int nDims = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
2610 GIntBig nMaxSize64 = 44 + 4 * static_cast<GUIntBig>(nParts) +
2611 8 * nDims * static_cast<GUIntBig>(nPoints);
2612 nMaxSize64 += 4; // nCurves
2613 nMaxSize64 += static_cast<GUIntBig>(nCurves) * (4 + /* start index */
2614 4 + /* curve type */
2615 44 /* size of ellipse struct */ );
2616 nMaxSize64 += ((bHasZ ? 1 : 0) + (bHasM ? 1 : 0)) * 16; // space for bounding boxes
2617 if( nMaxSize64 >= INT_MAX )
2618 {
2619 returnError();
2620 }
2621 const int nMaxSize = static_cast<int>(nMaxSize64);
2622 GByte* pabyExtShapeBuffer = (GByte*) VSI_MALLOC_VERBOSE(nMaxSize);
2623 if( pabyExtShapeBuffer == nullptr )
2624 {
2625 VSIFree(pabyExtShapeBuffer);
2626 returnError();
2627 }
2628 GUInt32 nShapeType = nBaseShapeType | EXT_SHAPE_CURVE_FLAG;
2629 if( bHasZ ) nShapeType |= EXT_SHAPE_Z_FLAG;
2630 if( bHasM ) nShapeType |= EXT_SHAPE_M_FLAG;
2631 GUInt32 nTmp;
2632 nTmp = CPL_LSBWORD32(nShapeType);
2633 GByte* pabyShapeTypePtr = pabyExtShapeBuffer;
2634 memcpy( pabyExtShapeBuffer, &nTmp, 4 );
2635 memset( pabyExtShapeBuffer + 4, 0, 32 ); /* bbox: unused */
2636 nTmp = CPL_LSBWORD32(nParts);
2637 memcpy( pabyExtShapeBuffer + 36, &nTmp, 4 );
2638 nTmp = CPL_LSBWORD32(nPoints);
2639 memcpy( pabyExtShapeBuffer + 40, &nTmp, 4 );
2640 GUInt32 nIdx = 0;
2641 for( i=0; i<nParts; i++ )
2642 {
2643 nTmp = CPL_LSBWORD32(nIdx);
2644 nIdx += panPointCount[i];
2645 memcpy( pabyExtShapeBuffer + 44 + 4 * i, &nTmp, 4 );
2646 }
2647 int nOffset = 44 + 4 * nParts;
2648 GIntBig dx = 0;
2649 GIntBig dy = 0;
2650 XYBufferSetter arraySetter(pabyExtShapeBuffer + nOffset);
2651 if( !ReadXYArray<XYBufferSetter>(arraySetter,
2652 pabyCur, pabyEnd, nPoints, dx, dy) )
2653 {
2654 VSIFree(pabyExtShapeBuffer);
2655 returnError();
2656 }
2657 nOffset += 16 * nPoints;
2658
2659 if( bHasZ )
2660 {
2661 memset( pabyExtShapeBuffer + nOffset, 0, 16 ); /* bbox: unused */
2662 nOffset += 16;
2663 GIntBig dz = 0;
2664 ZOrMBufferSetter arrayzSetter(pabyExtShapeBuffer + nOffset);
2665 if( !ReadZArray<ZOrMBufferSetter>(arrayzSetter,
2666 pabyCur, pabyEnd, nPoints, dz) )
2667 {
2668 VSIFree(pabyExtShapeBuffer);
2669 returnError();
2670 }
2671 nOffset += 8 * nPoints;
2672 }
2673
2674 if( bHasM )
2675 {
2676 // It seems that absence of M is marked with a single byte
2677 // with value 66.
2678 if( *pabyCur == 66 )
2679 {
2680 pabyCur ++;
2681 #if 1
2682 // In other code paths of this file, we drop the M component when
2683 // it is at null. The disabled code path would fill it with NaN
2684 // instead.
2685 nShapeType &= ~EXT_SHAPE_M_FLAG;
2686 nTmp = CPL_LSBWORD32(nShapeType);
2687 memcpy( pabyShapeTypePtr, &nTmp, 4 );
2688 #else
2689 memset( pabyExtShapeBuffer + nOffset, 0, 16 ); /* bbox: unused */
2690 nOffset += 16;
2691 const double myNan = std::numeric_limits<double>::quiet_NaN();
2692 for( i = 0; i < nPoints; i++ )
2693 {
2694 memcpy(pabyExtShapeBuffer + nOffset + 8 * i, &myNan, 8);
2695 CPL_LSBPTR64(pabyExtShapeBuffer + nOffset + 8 * i);
2696 }
2697 nOffset += 8 * nPoints;
2698 #endif
2699 }
2700 else
2701 {
2702 memset( pabyExtShapeBuffer + nOffset, 0, 16 ); /* bbox: unused */
2703 nOffset += 16;
2704 ZOrMBufferSetter arraymSetter(pabyExtShapeBuffer + nOffset);
2705 GIntBig dm = 0;
2706 if( !ReadMArray<ZOrMBufferSetter>(arraymSetter,
2707 pabyCur, pabyEnd, nPoints, dm) )
2708 {
2709 VSIFree(pabyExtShapeBuffer);
2710 returnError();
2711 }
2712 nOffset += 8 * nPoints;
2713 }
2714 }
2715
2716 nTmp = CPL_LSBWORD32(nCurves);
2717 memcpy( pabyExtShapeBuffer + nOffset, &nTmp, 4 );
2718 nOffset += 4;
2719 for( i=0; i<nCurves; i++ )
2720 {
2721 // start index
2722 returnErrorAndCleanupIf( !ReadVarUInt32(pabyCur, pabyEnd, nTmp),
2723 VSIFree(pabyExtShapeBuffer) );
2724 CPL_LSBPTR32(&nTmp);
2725 memcpy( pabyExtShapeBuffer + nOffset, &nTmp, 4 );
2726 nOffset += 4;
2727
2728 GUInt32 nCurveType;
2729 returnErrorAndCleanupIf( !ReadVarUInt32(pabyCur, pabyEnd, nCurveType),
2730 VSIFree(pabyExtShapeBuffer) );
2731 nTmp = CPL_LSBWORD32(nCurveType);
2732 memcpy( pabyExtShapeBuffer + nOffset, &nTmp, 4 );
2733 nOffset += 4;
2734
2735 int nStructureSize = 0;
2736 if( nCurveType == EXT_SHAPE_SEGMENT_ARC )
2737 nStructureSize = 2 * 8 + 4;
2738 else if( nCurveType == EXT_SHAPE_SEGMENT_BEZIER )
2739 nStructureSize = 4 * 8;
2740 else if( nCurveType == EXT_SHAPE_SEGMENT_ELLIPSE )
2741 nStructureSize = 5 * 8 + 4;
2742 if( nStructureSize == 0 || pabyCur + nStructureSize > pabyEnd )
2743 {
2744 VSIFree(pabyExtShapeBuffer);
2745 returnError();
2746 }
2747 memcpy( pabyExtShapeBuffer + nOffset, pabyCur, nStructureSize );
2748 pabyCur += nStructureSize;
2749 nOffset += nStructureSize;
2750 }
2751 CPLAssert( nOffset <= nMaxSize );
2752
2753 OGRGeometry* poRet = nullptr;
2754 OGRCreateFromShapeBin(pabyExtShapeBuffer, &poRet, nOffset);
2755 VSIFree(pabyExtShapeBuffer);
2756 return poRet;
2757 }
2758
2759 /************************************************************************/
2760 /* GetAsGeometry() */
2761 /************************************************************************/
2762
GetAsGeometry(const OGRField * psField)2763 OGRGeometry* FileGDBOGRGeometryConverterImpl::GetAsGeometry(const OGRField* psField)
2764 {
2765 OGRGeometry* errorRetValue = nullptr;
2766 GByte* pabyCur = psField->Binary.paData;
2767 GByte* pabyEnd = pabyCur + psField->Binary.nCount;
2768 GUInt32 nGeomType, i, nPoints, nParts, nCurves;
2769 GUIntBig x, y, z;
2770 GIntBig dx, dy, dz;
2771
2772 ReadVarUInt32NoCheck(pabyCur, nGeomType);
2773
2774 bool bHasZ = (nGeomType & EXT_SHAPE_Z_FLAG) != 0;
2775 bool bHasM = (nGeomType & EXT_SHAPE_M_FLAG) != 0;
2776 switch( (nGeomType & 0xff) )
2777 {
2778 case SHPT_NULL:
2779 return nullptr;
2780
2781 case SHPT_POINTZ:
2782 case SHPT_POINTZM:
2783 bHasZ = true; /* go on */
2784 CPL_FALLTHROUGH
2785 case SHPT_POINT:
2786 case SHPT_POINTM:
2787 case SHPT_GENERALPOINT:
2788 {
2789 if( nGeomType == SHPT_POINTM || nGeomType == SHPT_POINTZM )
2790 bHasM = true;
2791
2792 ReadVarUInt64NoCheck(pabyCur, x);
2793 ReadVarUInt64NoCheck(pabyCur, y);
2794
2795 const double dfX =
2796 CPLUnsanitizedAdd<GUIntBig>(x, -1) / poGeomField->GetXYScale() + poGeomField->GetXOrigin();
2797 const double dfY =
2798 CPLUnsanitizedAdd<GUIntBig>(y, -1) / poGeomField->GetXYScale() + poGeomField->GetYOrigin();
2799 if( bHasZ )
2800 {
2801 ReadVarUInt64NoCheck(pabyCur, z);
2802 const double dfZScale = SanitizeScale(poGeomField->GetZScale());
2803 const double dfZ = CPLUnsanitizedAdd<GUIntBig>(z, -1) / dfZScale + poGeomField->GetZOrigin();
2804 if( bHasM )
2805 {
2806 GUIntBig m = 0;
2807 ReadVarUInt64NoCheck(pabyCur, m);
2808 const double dfMScale =
2809 SanitizeScale(poGeomField->GetMScale());
2810 const double dfM =
2811 CPLUnsanitizedAdd<GUIntBig>(m, -1) / dfMScale + poGeomField->GetMOrigin();
2812 return new OGRPoint(dfX, dfY, dfZ, dfM);
2813 }
2814 return new OGRPoint(dfX, dfY, dfZ);
2815 }
2816 else if( bHasM )
2817 {
2818 OGRPoint* poPoint = new OGRPoint(dfX, dfY);
2819 GUIntBig m = 0;
2820 ReadVarUInt64NoCheck(pabyCur, m);
2821 const double dfMScale = SanitizeScale(poGeomField->GetMScale());
2822 const double dfM =
2823 CPLUnsanitizedAdd<GUIntBig>(m, -1) / dfMScale + poGeomField->GetMOrigin();
2824 poPoint->setM(dfM);
2825 return poPoint;
2826 }
2827 else
2828 {
2829 return new OGRPoint(dfX, dfY);
2830 }
2831 break;
2832 }
2833
2834 case SHPT_MULTIPOINTZM:
2835 case SHPT_MULTIPOINTZ:
2836 bHasZ = true; /* go on */
2837 CPL_FALLTHROUGH
2838 case SHPT_MULTIPOINT:
2839 case SHPT_MULTIPOINTM:
2840 {
2841 if( nGeomType == SHPT_MULTIPOINTM || nGeomType == SHPT_MULTIPOINTZM )
2842 bHasM = true;
2843
2844 returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints) );
2845 if( nPoints == 0 )
2846 {
2847 OGRMultiPoint* poMP = new OGRMultiPoint();
2848 if( bHasZ )
2849 poMP->set3D(TRUE);
2850 if( bHasM )
2851 poMP->setMeasured(TRUE);
2852 return poMP;
2853 }
2854
2855 returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4) );
2856
2857 dx = dy = dz = 0;
2858
2859 OGRMultiPoint* poMP = new OGRMultiPoint();
2860 XYMultiPointSetter mpSetter(poMP);
2861 if( !ReadXYArray<XYMultiPointSetter>(mpSetter,
2862 pabyCur, pabyEnd, nPoints, dx, dy) )
2863 {
2864 delete poMP;
2865 returnError();
2866 }
2867
2868 if( bHasZ )
2869 {
2870 poMP->setCoordinateDimension(3);
2871 ZMultiPointSetter mpzSetter(poMP);
2872 if( !ReadZArray<ZMultiPointSetter>(mpzSetter,
2873 pabyCur, pabyEnd, nPoints, dz) )
2874 {
2875 delete poMP;
2876 returnError();
2877 }
2878 }
2879
2880 // It seems that absence of M is marked with a single byte
2881 // with value 66. Be more tolerant and only try to parse the M
2882 // array is there are at least as many remaining bytes as
2883 // expected points
2884 if( bHasM && pabyCur + nPoints <= pabyEnd )
2885 {
2886 poMP->setMeasured(TRUE);
2887 GIntBig dm = 0;
2888 MMultiPointSetter mpmSetter(poMP);
2889 if( !ReadMArray<MMultiPointSetter>(mpmSetter,
2890 pabyCur, pabyEnd, nPoints, dm) )
2891 {
2892 delete poMP;
2893 returnError();
2894 }
2895 }
2896
2897 return poMP;
2898 // cppcheck-suppress duplicateBreak
2899 break;
2900 }
2901
2902 case SHPT_ARCZ:
2903 case SHPT_ARCZM:
2904 bHasZ = true; /* go on */
2905 CPL_FALLTHROUGH
2906 case SHPT_ARC:
2907 case SHPT_ARCM:
2908 case SHPT_GENERALPOLYLINE:
2909 {
2910 if( nGeomType == SHPT_ARCM || nGeomType == SHPT_ARCZM )
2911 bHasM = true;
2912
2913 returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
2914 (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0,
2915 false) );
2916
2917 if( nPoints == 0 || nParts == 0 )
2918 {
2919 OGRLineString* poLS = new OGRLineString();
2920 if( bHasZ )
2921 poLS->set3D(TRUE);
2922 if( bHasM )
2923 poLS->setMeasured(TRUE);
2924 return poLS;
2925 }
2926
2927 if( nCurves )
2928 {
2929 GByte* pabyCurBackup = pabyCur;
2930 OGRGeometry* poRet = CreateCurveGeometry(
2931 SHPT_GENERALPOLYLINE,
2932 nParts, nPoints, nCurves,
2933 bHasZ, bHasM,
2934 pabyCur, pabyEnd );
2935 if( poRet )
2936 return poRet;
2937 // In case something went wrong, go on without curves
2938 pabyCur = pabyCurBackup;
2939 }
2940
2941 OGRMultiLineString* poMLS = nullptr;
2942 FileGDBOGRLineString* poLS = nullptr;
2943 if( nParts > 1 )
2944 {
2945 poMLS = new OGRMultiLineString();
2946 if( bHasZ )
2947 poMLS->setCoordinateDimension(3);
2948 }
2949
2950 dx = dy = dz = 0;
2951 for(i=0;i<nParts;i++)
2952 {
2953 poLS = new FileGDBOGRLineString();
2954 poLS->setNumPoints(panPointCount[i], FALSE);
2955 if( nParts > 1 )
2956 poMLS->addGeometryDirectly(poLS);
2957
2958 XYLineStringSetter lsSetter(poLS->GetPoints());
2959 if( !ReadXYArray<XYLineStringSetter>(lsSetter,
2960 pabyCur, pabyEnd,
2961 panPointCount[i],
2962 dx, dy) )
2963 {
2964 if( nParts > 1 )
2965 delete poMLS;
2966 else
2967 delete poLS;
2968 returnError();
2969 }
2970 }
2971
2972 if( bHasZ )
2973 {
2974 for(i=0;i<nParts;i++)
2975 {
2976 if( nParts > 1 )
2977 poLS = (FileGDBOGRLineString*) poMLS->getGeometryRef(i);
2978
2979 ZLineStringSetter lszSetter(poLS);
2980 if( !ReadZArray<ZLineStringSetter>(lszSetter,
2981 pabyCur, pabyEnd,
2982 panPointCount[i], dz) )
2983 {
2984 if( nParts > 1 )
2985 delete poMLS;
2986 else
2987 delete poLS;
2988 returnError();
2989 }
2990 }
2991 }
2992
2993 if( bHasM )
2994 {
2995 GIntBig dm = 0;
2996 for(i=0;i<nParts;i++)
2997 {
2998 if( nParts > 1 )
2999 poLS = (FileGDBOGRLineString*) poMLS->getGeometryRef(i);
3000
3001 // It seems that absence of M is marked with a single byte
3002 // with value 66. Be more tolerant and only try to parse the M
3003 // array is there are at least as many remaining bytes as
3004 // expected points
3005 if( pabyCur + panPointCount[i] > pabyEnd )
3006 {
3007 if( nParts > 1 )
3008 poMLS->setMeasured(FALSE);
3009 break;
3010 }
3011
3012 MLineStringSetter lsmSetter(poLS);
3013 if( !ReadMArray<MLineStringSetter>(lsmSetter,
3014 pabyCur, pabyEnd,
3015 panPointCount[i], dm) )
3016 {
3017 if( nParts > 1 )
3018 delete poMLS;
3019 else
3020 delete poLS;
3021 returnError();
3022 }
3023 }
3024 }
3025
3026 if( poMLS )
3027 return poMLS;
3028 else
3029 return poLS;
3030
3031 break;
3032 }
3033
3034 case SHPT_POLYGONZ:
3035 case SHPT_POLYGONZM:
3036 bHasZ = true; /* go on */
3037 CPL_FALLTHROUGH
3038 case SHPT_POLYGON:
3039 case SHPT_POLYGONM:
3040 case SHPT_GENERALPOLYGON:
3041 {
3042 if( nGeomType == SHPT_POLYGONM || nGeomType == SHPT_POLYGONZM )
3043 bHasM = true;
3044
3045 returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
3046 (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0,
3047 false) );
3048
3049 if( nPoints == 0 || nParts == 0 )
3050 {
3051 OGRPolygon* poPoly = new OGRPolygon();
3052 if( bHasZ )
3053 poPoly->set3D(TRUE);
3054 if( bHasM )
3055 poPoly->setMeasured(TRUE);
3056 return poPoly;
3057 }
3058
3059 if( nCurves )
3060 {
3061 GByte* pabyCurBackup = pabyCur;
3062 OGRGeometry* poRet = CreateCurveGeometry(
3063 SHPT_GENERALPOLYGON,
3064 nParts, nPoints, nCurves,
3065 bHasZ, bHasM,
3066 pabyCur, pabyEnd );
3067 if( poRet )
3068 return poRet;
3069 // In case something went wrong, go on without curves
3070 pabyCur = pabyCurBackup;
3071 }
3072
3073 OGRLinearRing** papoRings = new OGRLinearRing*[nParts];
3074
3075 dx = dy = dz = 0;
3076 for(i=0;i<nParts;i++)
3077 {
3078 FileGDBOGRLinearRing* poRing = new FileGDBOGRLinearRing();
3079 papoRings[i] = poRing;
3080 poRing->setNumPoints(panPointCount[i], FALSE);
3081
3082 XYLineStringSetter lsSetter(poRing->GetPoints());
3083 if( !ReadXYArray<XYLineStringSetter>(lsSetter,
3084 pabyCur, pabyEnd,
3085 panPointCount[i],
3086 dx, dy) )
3087 {
3088 while( true )
3089 {
3090 delete papoRings[i];
3091 if( i == 0 )
3092 break;
3093 i--;
3094 }
3095 delete[] papoRings;
3096 // For some reason things that papoRings is leaking
3097 // cppcheck-suppress memleak
3098 returnError();
3099 }
3100 }
3101
3102 if( bHasZ )
3103 {
3104 for(i=0;i<nParts;i++)
3105 {
3106 papoRings[i]->setCoordinateDimension(3);
3107
3108 ZLineStringSetter lszSetter(papoRings[i]);
3109 if( !ReadZArray<ZLineStringSetter>(lszSetter,
3110 pabyCur, pabyEnd,
3111 panPointCount[i], dz) )
3112 {
3113 for(i=0;i<nParts;i++)
3114 delete papoRings[i];
3115 delete[] papoRings;
3116 returnError();
3117 }
3118 }
3119 }
3120
3121 if( bHasM )
3122 {
3123 GIntBig dm = 0;
3124 for(i=0;i<nParts;i++)
3125 {
3126 // It seems that absence of M is marked with a single byte
3127 // with value 66. Be more tolerant and only try to parse the M
3128 // array is there are at least as many remaining bytes as
3129 // expected points
3130 if( pabyCur + panPointCount[i] > pabyEnd )
3131 {
3132 while( i != 0 )
3133 {
3134 --i;
3135 papoRings[i]->setMeasured(FALSE);
3136 }
3137 break;
3138 }
3139
3140 papoRings[i]->setMeasured(TRUE);
3141
3142 MLineStringSetter lsmSetter(papoRings[i]);
3143 if( !ReadMArray<MLineStringSetter>(lsmSetter,
3144 pabyCur, pabyEnd,
3145 panPointCount[i], dm) )
3146 {
3147 for(i=0;i<nParts;i++)
3148 delete papoRings[i];
3149 delete[] papoRings;
3150 returnError();
3151 }
3152 }
3153 }
3154
3155 OGRGeometry* poRet = nullptr;
3156 if( nParts == 1 )
3157 {
3158 OGRPolygon* poPoly = new OGRPolygon();
3159 poRet = poPoly;
3160 poPoly->addRingDirectly(papoRings[0]);
3161 }
3162 else
3163 #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
3164 if( bUseOrganize || !(papoRings[0]->isClockwise()) )
3165 #endif
3166 {
3167 /* Slow method : not used by default */
3168 OGRPolygon** papoPolygons = new OGRPolygon*[nParts];
3169 for(i=0;i<nParts;i++)
3170 {
3171 papoPolygons[i] = new OGRPolygon();
3172 papoPolygons[i]->addRingDirectly(papoRings[i]);
3173 }
3174 delete[] papoRings;
3175 papoRings = nullptr;
3176 poRet = OGRGeometryFactory::organizePolygons(
3177 (OGRGeometry**) papoPolygons, nParts, nullptr, nullptr );
3178 delete[] papoPolygons;
3179 }
3180 #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
3181 else
3182 {
3183 /* Inner rings are CCW oriented and follow immediately the outer */
3184 /* ring (that is CW oriented) in which they are included */
3185 OGRMultiPolygon* poMulti = NULL;
3186 OGRPolygon* poPoly = new OGRPolygon();
3187 OGRPolygon* poCur = poPoly;
3188 poRet = poCur;
3189 /* We have already checked that the first ring is CW */
3190 poPoly->addRingDirectly(papoRings[0]);
3191 OGREnvelope sEnvelope;
3192 papoRings[0]->getEnvelope(&sEnvelope);
3193 for(i=1;i<nParts;i++)
3194 {
3195 int bIsCW = papoRings[i]->isClockwise();
3196 if( bIsCW )
3197 {
3198 if( poMulti == NULL )
3199 {
3200 poMulti = new OGRMultiPolygon();
3201 poRet = poMulti;
3202 poMulti->addGeometryDirectly(poCur);
3203 }
3204 OGRPolygon* poPoly = new OGRPolygon();
3205 poCur = poPoly;
3206 poMulti->addGeometryDirectly(poCur);
3207 poPoly->addRingDirectly(papoRings[i]);
3208 papoRings[i]->getEnvelope(&sEnvelope);
3209 }
3210 else
3211 {
3212 poCur->addRingDirectly(papoRings[i]);
3213 OGRPoint oPoint;
3214 papoRings[i]->getPoint(0, &oPoint);
3215 CPLAssert(oPoint.getX() >= sEnvelope.MinX &&
3216 oPoint.getX() <= sEnvelope.MaxX &&
3217 oPoint.getY() >= sEnvelope.MinY &&
3218 oPoint.getY() <= sEnvelope.MaxY);
3219 }
3220 }
3221 }
3222 #endif
3223
3224 delete[] papoRings;
3225 return poRet;
3226 // cppcheck-suppress duplicateBreak
3227 break;
3228 }
3229
3230 case SHPT_MULTIPATCHM:
3231 case SHPT_MULTIPATCH:
3232 bHasZ = true; /* go on */
3233 CPL_FALLTHROUGH
3234 case SHPT_GENERALMULTIPATCH:
3235 {
3236 returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves, false, true ) );
3237
3238 if( nPoints == 0 || nParts == 0 )
3239 {
3240 OGRPolygon* poPoly = new OGRPolygon();
3241 if( bHasZ )
3242 poPoly->setCoordinateDimension(3);
3243 return poPoly;
3244 }
3245 int* panPartType = (int*) VSI_MALLOC_VERBOSE(sizeof(int) * nParts);
3246 int* panPartStart = (int*) VSI_MALLOC_VERBOSE(sizeof(int) * nParts);
3247 double* padfXYZ = (double*) VSI_MALLOC_VERBOSE(3 * sizeof(double) * nPoints);
3248 double* padfX = padfXYZ;
3249 double* padfY = padfXYZ ? padfXYZ + nPoints : nullptr;
3250 double* padfZ = padfXYZ ? padfXYZ + 2 * nPoints : nullptr;
3251 if( panPartType == nullptr || panPartStart == nullptr || padfXYZ == nullptr )
3252 {
3253 VSIFree(panPartType);
3254 VSIFree(panPartStart);
3255 VSIFree(padfXYZ);
3256 returnError();
3257 }
3258 for(i=0;i<nParts;i++)
3259 {
3260 GUInt32 nPartType;
3261 if( !ReadVarUInt32(pabyCur, pabyEnd, nPartType) )
3262 {
3263 VSIFree(panPartType);
3264 VSIFree(panPartStart);
3265 VSIFree(padfXYZ);
3266 returnError();
3267 }
3268 panPartType[i] = (int)nPartType;
3269 }
3270 dx = dy = dz = 0;
3271
3272 XYArraySetter arraySetter(padfX, padfY);
3273 if( !ReadXYArray<XYArraySetter>(arraySetter,
3274 pabyCur, pabyEnd, nPoints, dx, dy) )
3275 {
3276 VSIFree(panPartType);
3277 VSIFree(panPartStart);
3278 VSIFree(padfXYZ);
3279 returnError();
3280 }
3281
3282 if( bHasZ )
3283 {
3284 FileGDBArraySetter arrayzSetter(padfZ);
3285 if( !ReadZArray<FileGDBArraySetter>(arrayzSetter,
3286 pabyCur, pabyEnd, nPoints, dz) )
3287 {
3288 VSIFree(panPartType);
3289 VSIFree(panPartStart);
3290 VSIFree(padfXYZ);
3291 returnError();
3292 }
3293 }
3294 else
3295 {
3296 memset(padfZ, 0, nPoints * sizeof(double));
3297 }
3298
3299 panPartStart[0] = 0;
3300 for( i = 1; i < nParts; ++i )
3301 panPartStart[i] = panPartStart[i-1] + panPointCount[i-1];
3302 // (CID 1404102)
3303 // coverity[overrun-buffer-arg]
3304 OGRGeometry* poRet = OGRCreateFromMultiPatch(
3305 static_cast<int>(nParts),
3306 panPartStart,
3307 panPartType,
3308 static_cast<int>(nPoints),
3309 padfX, padfY, padfZ );
3310
3311 VSIFree(panPartType);
3312 VSIFree(panPartStart);
3313 VSIFree(padfXYZ);
3314
3315 return poRet;
3316 // cppcheck-suppress duplicateBreak
3317 break;
3318 }
3319
3320 default:
3321 CPLDebug("OpenFileGDB", "Unhandled geometry type = %d", (int)nGeomType);
3322 break;
3323 /*
3324 #define SHPT_GENERALMULTIPOINT 53
3325 */
3326 }
3327 return nullptr;
3328 }
3329
3330 /************************************************************************/
3331 /* BuildConverter() */
3332 /************************************************************************/
3333
BuildConverter(const FileGDBGeomField * poGeomField)3334 FileGDBOGRGeometryConverter* FileGDBOGRGeometryConverter::BuildConverter(
3335 const FileGDBGeomField* poGeomField)
3336 {
3337 return new FileGDBOGRGeometryConverterImpl(poGeomField);
3338 }
3339
3340 /************************************************************************/
3341 /* GetGeometryTypeFromESRI() */
3342 /************************************************************************/
3343
3344 static const struct
3345 {
3346 const char *pszStr;
3347 OGRwkbGeometryType eType;
3348 } AssocESRIGeomTypeToOGRGeomType[] =
3349 {
3350 { "esriGeometryPoint", wkbPoint },
3351 { "esriGeometryMultipoint", wkbMultiPoint },
3352 { "esriGeometryLine", wkbMultiLineString },
3353 { "esriGeometryPolyline", wkbMultiLineString },
3354 { "esriGeometryPolygon", wkbMultiPolygon },
3355 { "esriGeometryMultiPatch", wkbUnknown }
3356 };
3357
GetGeometryTypeFromESRI(const char * pszESRIType)3358 OGRwkbGeometryType FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(
3359 const char* pszESRIType)
3360 {
3361 for(size_t i=0;i<sizeof(AssocESRIGeomTypeToOGRGeomType)/
3362 sizeof(AssocESRIGeomTypeToOGRGeomType[0]);i++)
3363 {
3364 if( strcmp(pszESRIType, AssocESRIGeomTypeToOGRGeomType[i].pszStr) == 0 )
3365 return AssocESRIGeomTypeToOGRGeomType[i].eType;
3366 }
3367 CPLDebug("OpenFileGDB", "Unhandled geometry type : %s", pszESRIType);
3368 return wkbUnknown;
3369 }
3370
3371 } /* namespace OpenFileGDB */
3372