Coverage Report

Created: 2025-06-09 08:44

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