Coverage Report

Created: 2026-06-30 08:33

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