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