LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/flatgeobuf - ogrflatgeobuflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1180 1480 79.7 %
Date: 2026-05-06 15:03:12 Functions: 37 39 94.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  FlatGeobuf driver
       4             :  * Purpose:  Implements OGRFlatGeobufLayer class.
       5             :  * Author:   Björn Harrtell <bjorn at wololo dot org>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018-2020, Björn Harrtell <bjorn at wololo dot org>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogrsf_frmts.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : #include "cpl_conv.h"
      16             : #include "cpl_json.h"
      17             : #include "cpl_http.h"
      18             : #include "cpl_time.h"
      19             : #include "ogr_p.h"
      20             : #include "ograrrowarrayhelper.h"
      21             : #include "ogrlayerarrow.h"
      22             : #include "ogr_recordbatch.h"
      23             : 
      24             : #include "ogr_flatgeobuf.h"
      25             : #include "cplerrors.h"
      26             : #include "geometryreader.h"
      27             : #include "geometrywriter.h"
      28             : 
      29             : #include <algorithm>
      30             : #include <cmath>
      31             : #include <limits>
      32             : #include <new>
      33             : #include <stdexcept>
      34             : 
      35             : using namespace flatbuffers;
      36             : using namespace FlatGeobuf;
      37             : using namespace ogr_flatgeobuf;
      38             : 
      39           0 : static OGRErr CPLErrorMemoryAllocation(const char *message)
      40             : {
      41           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Could not allocate memory: %s",
      42             :              message);
      43           0 :     return OGRERR_NOT_ENOUGH_MEMORY;
      44             : }
      45             : 
      46           0 : static OGRErr CPLErrorIO(const char *message)
      47             : {
      48           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unexpected I/O failure: %s",
      49             :              message);
      50           0 :     return OGRERR_FAILURE;
      51             : }
      52             : 
      53         153 : OGRFlatGeobufLayer::OGRFlatGeobufLayer(const Header *poHeader, GByte *headerBuf,
      54             :                                        const char *pszFilename, VSILFILE *poFp,
      55         153 :                                        uint64_t offset)
      56             : {
      57         153 :     m_poHeader = poHeader;
      58         153 :     CPLAssert(poHeader);
      59         153 :     m_headerBuf = headerBuf;
      60         153 :     CPLAssert(pszFilename);
      61         153 :     if (pszFilename)
      62         153 :         m_osFilename = pszFilename;
      63         153 :     m_poFp = poFp;
      64         153 :     m_offsetFeatures = offset;
      65         153 :     m_offset = offset;
      66         153 :     m_create = false;
      67             : 
      68         153 :     m_featuresCount = m_poHeader->features_count();
      69         153 :     m_geometryType = m_poHeader->geometry_type();
      70         153 :     m_indexNodeSize = m_poHeader->index_node_size();
      71         153 :     m_hasZ = m_poHeader->has_z();
      72         153 :     m_hasM = m_poHeader->has_m();
      73         153 :     m_hasT = m_poHeader->has_t();
      74         153 :     const auto envelope = m_poHeader->envelope();
      75         274 :     if (envelope && envelope->size() == 4 && std::isfinite((*envelope)[0]) &&
      76         563 :         std::isfinite((*envelope)[1]) && std::isfinite((*envelope)[2]) &&
      77         136 :         std::isfinite((*envelope)[3]))
      78             :     {
      79         136 :         m_sExtent.MinX = (*envelope)[0];
      80         136 :         m_sExtent.MinY = (*envelope)[1];
      81         136 :         m_sExtent.MaxX = (*envelope)[2];
      82         136 :         m_sExtent.MaxY = (*envelope)[3];
      83             :     }
      84             : 
      85         153 :     CPLDebugOnly("FlatGeobuf", "geometryType: %d, hasZ: %d, hasM: %d, hasT: %d",
      86             :                  (int)m_geometryType, m_hasZ, m_hasM, m_hasT);
      87             : 
      88         153 :     const auto crs = m_poHeader->crs();
      89         153 :     if (crs != nullptr)
      90             :     {
      91          17 :         m_poSRS = new OGRSpatialReference();
      92          17 :         m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      93          17 :         const auto org = crs->org();
      94          17 :         const auto code = crs->code();
      95          17 :         const auto crs_wkt = crs->wkt();
      96          34 :         CPLString wkt = crs_wkt ? crs_wkt->c_str() : "";
      97          17 :         double dfCoordEpoch = 0;
      98          17 :         if (STARTS_WITH_CI(wkt.c_str(), "COORDINATEMETADATA["))
      99             :         {
     100           3 :             size_t nPos = std::string::npos;
     101             :             // We don't want to match FRAMEEPOCH[
     102           0 :             for (const char *pszEpoch :
     103           3 :                  {",EPOCH[", " EPOCH[", "\tEPOCH[", "\nEPOCH[", "\rEPOCH["})
     104             :             {
     105           3 :                 nPos = wkt.ifind(pszEpoch);
     106           3 :                 if (nPos != std::string::npos)
     107           3 :                     break;
     108             :             }
     109           3 :             if (nPos != std::string::npos)
     110             :             {
     111           3 :                 dfCoordEpoch = CPLAtof(wkt.c_str() + nPos + strlen(",EPOCH["));
     112           3 :                 wkt.resize(nPos);
     113           3 :                 wkt = wkt.substr(strlen("COORDINATEMETADATA["));
     114             :             }
     115             :         }
     116             : 
     117          17 :         if ((org == nullptr || EQUAL(org->c_str(), "EPSG")) && code != 0)
     118             :         {
     119          12 :             m_poSRS->importFromEPSG(code);
     120             :         }
     121           5 :         else if (org && code != 0)
     122             :         {
     123           2 :             CPLString osCode;
     124           1 :             osCode.Printf("%s:%d", org->c_str(), code);
     125           1 :             if (m_poSRS->SetFromUserInput(
     126             :                     osCode.c_str(),
     127             :                     OGRSpatialReference::
     128           1 :                         SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE &&
     129           0 :                 !wkt.empty())
     130             :             {
     131           0 :                 m_poSRS->importFromWkt(wkt.c_str());
     132           1 :             }
     133             :         }
     134           4 :         else if (!wkt.empty())
     135             :         {
     136           3 :             m_poSRS->importFromWkt(wkt.c_str());
     137             :         }
     138             : 
     139          17 :         if (dfCoordEpoch > 0)
     140           3 :             m_poSRS->SetCoordinateEpoch(dfCoordEpoch);
     141             :     }
     142             : 
     143         153 :     m_eGType = getOGRwkbGeometryType();
     144             : 
     145         153 :     if (const auto title = poHeader->title())
     146           2 :         SetMetadataItem("TITLE", title->c_str());
     147             : 
     148         153 :     if (const auto description = poHeader->description())
     149           2 :         SetMetadataItem("DESCRIPTION", description->c_str());
     150             : 
     151         153 :     if (const auto metadata = poHeader->metadata())
     152             :     {
     153          10 :         CPLJSONDocument oDoc;
     154          10 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     155          15 :         if (oDoc.LoadMemory(metadata->c_str()) &&
     156          10 :             oDoc.GetRoot().GetType() == CPLJSONObject::Type::Object)
     157             :         {
     158          12 :             for (const auto &oItem : oDoc.GetRoot().GetChildren())
     159             :             {
     160           7 :                 if (oItem.GetType() == CPLJSONObject::Type::String)
     161             :                 {
     162           7 :                     SetMetadataItem(oItem.GetName().c_str(),
     163          14 :                                     oItem.ToString().c_str());
     164             :                 }
     165             :             }
     166             :         }
     167             :     }
     168             : 
     169             :     const char *pszName =
     170         153 :         m_poHeader->name() ? m_poHeader->name()->c_str() : "unknown";
     171         153 :     m_poFeatureDefn = new OGRFeatureDefn(pszName);
     172         153 :     SetDescription(m_poFeatureDefn->GetName());
     173         153 :     m_poFeatureDefn->SetGeomType(wkbNone);
     174             :     auto poGeomFieldDefn =
     175         306 :         std::make_unique<OGRGeomFieldDefn>(nullptr, m_eGType);
     176         153 :     if (m_poSRS != nullptr)
     177          17 :         poGeomFieldDefn->SetSpatialRef(m_poSRS);
     178         153 :     m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     179         153 :     readColumns();
     180         153 :     m_poFeatureDefn->Reference();
     181         153 : }
     182             : 
     183         175 : OGRFlatGeobufLayer::OGRFlatGeobufLayer(
     184             :     GDALDataset *poDS, const char *pszLayerName, const char *pszFilename,
     185             :     const OGRSpatialReference *poSpatialRef, OGRwkbGeometryType eGType,
     186             :     bool bCreateSpatialIndexAtClose, VSILFILE *poFpWrite,
     187         175 :     std::string &osTempFile, CSLConstList papszOptions)
     188             :     : m_eGType(eGType), m_poDS(poDS), m_create(true),
     189             :       m_bCreateSpatialIndexAtClose(bCreateSpatialIndexAtClose),
     190             :       m_poFpWrite(poFpWrite), m_aosCreationOption(papszOptions),
     191         175 :       m_osTempFile(osTempFile)
     192             : {
     193         175 :     if (pszLayerName)
     194         175 :         m_osLayerName = pszLayerName;
     195         175 :     if (pszFilename)
     196         175 :         m_osFilename = pszFilename;
     197         175 :     m_geometryType = GeometryWriter::translateOGRwkbGeometryType(eGType);
     198         175 :     if wkbHasZ (eGType)
     199          59 :         m_hasZ = true;
     200         175 :     if wkbHasM (eGType)
     201          42 :         m_hasM = true;
     202         175 :     if (poSpatialRef)
     203          10 :         m_poSRS = poSpatialRef->Clone();
     204             : 
     205         175 :     CPLDebugOnly("FlatGeobuf", "geometryType: %d, hasZ: %d, hasM: %d, hasT: %d",
     206             :                  (int)m_geometryType, m_hasZ, m_hasM, m_hasT);
     207             : 
     208         175 :     SetMetadataItem(OLMD_FID64, "YES");
     209             : 
     210         175 :     m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
     211         175 :     SetDescription(m_poFeatureDefn->GetName());
     212         175 :     m_poFeatureDefn->SetGeomType(eGType);
     213         175 :     m_poFeatureDefn->Reference();
     214         175 : }
     215             : 
     216         154 : OGRwkbGeometryType OGRFlatGeobufLayer::getOGRwkbGeometryType()
     217             : {
     218         154 :     OGRwkbGeometryType ogrType = OGRwkbGeometryType::wkbUnknown;
     219         154 :     if (static_cast<int>(m_geometryType) <= 17)
     220         154 :         ogrType = (OGRwkbGeometryType)m_geometryType;
     221         154 :     if (m_hasZ)
     222          45 :         ogrType = wkbSetZ(ogrType);
     223         154 :     if (m_hasM)
     224          42 :         ogrType = wkbSetM(ogrType);
     225         154 :     return ogrType;
     226             : }
     227             : 
     228      131434 : static ColumnType toColumnType(const char *pszFieldName, OGRFieldType type,
     229             :                                OGRFieldSubType subType)
     230             : {
     231      131434 :     switch (type)
     232             :     {
     233      131139 :         case OGRFieldType::OFTInteger:
     234      262270 :             return subType == OFSTBoolean ? ColumnType::Bool
     235      131131 :                    : subType == OFSTInt16 ? ColumnType::Short
     236      131139 :                                           : ColumnType::Int;
     237          29 :         case OGRFieldType::OFTInteger64:
     238          29 :             return ColumnType::Long;
     239          84 :         case OGRFieldType::OFTReal:
     240          84 :             return subType == OFSTFloat32 ? ColumnType::Float
     241          84 :                                           : ColumnType::Double;
     242         102 :         case OGRFieldType::OFTString:
     243         102 :             return ColumnType::String;
     244          33 :         case OGRFieldType::OFTDate:
     245          33 :             return ColumnType::DateTime;
     246           0 :         case OGRFieldType::OFTTime:
     247           0 :             return ColumnType::DateTime;
     248          39 :         case OGRFieldType::OFTDateTime:
     249          39 :             return ColumnType::DateTime;
     250           8 :         case OGRFieldType::OFTBinary:
     251           8 :             return ColumnType::Binary;
     252           0 :         default:
     253           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     254             :                      "toColumnType: %s field is of type %s, which is not "
     255             :                      "handled natively. Falling back to String.",
     256             :                      pszFieldName, OGRFieldDefn::GetFieldTypeName(type));
     257             :     }
     258           0 :     return ColumnType::String;
     259             : }
     260             : 
     261       65677 : static OGRFieldType toOGRFieldType(ColumnType type, OGRFieldSubType &eSubType)
     262             : {
     263       65677 :     eSubType = OFSTNone;
     264       65677 :     switch (type)
     265             :     {
     266           1 :         case ColumnType::Byte:
     267           1 :             return OGRFieldType::OFTInteger;
     268           1 :         case ColumnType::UByte:
     269           1 :             return OGRFieldType::OFTInteger;
     270           5 :         case ColumnType::Bool:
     271           5 :             eSubType = OFSTBoolean;
     272           5 :             return OGRFieldType::OFTInteger;
     273           5 :         case ColumnType::Short:
     274           5 :             eSubType = OFSTInt16;
     275           5 :             return OGRFieldType::OFTInteger;
     276           1 :         case ColumnType::UShort:
     277           1 :             return OGRFieldType::OFTInteger;
     278       65548 :         case ColumnType::Int:
     279       65548 :             return OGRFieldType::OFTInteger;
     280           1 :         case ColumnType::UInt:
     281           1 :             return OGRFieldType::OFTInteger64;
     282          22 :         case ColumnType::Long:
     283          22 :             return OGRFieldType::OFTInteger64;
     284           1 :         case ColumnType::ULong:
     285           1 :             return OGRFieldType::OFTReal;
     286           5 :         case ColumnType::Float:
     287           5 :             eSubType = OFSTFloat32;
     288           5 :             return OGRFieldType::OFTReal;
     289          28 :         case ColumnType::Double:
     290          28 :             return OGRFieldType::OFTReal;
     291          46 :         case ColumnType::String:
     292          46 :             return OGRFieldType::OFTString;
     293           1 :         case ColumnType::Json:
     294           1 :             return OGRFieldType::OFTString;
     295           7 :         case ColumnType::DateTime:
     296           7 :             return OGRFieldType::OFTDateTime;
     297           5 :         case ColumnType::Binary:
     298           5 :             return OGRFieldType::OFTBinary;
     299             :     }
     300           0 :     return OGRFieldType::OFTString;
     301             : }
     302             : 
     303             : const std::vector<Offset<Column>>
     304         322 : OGRFlatGeobufLayer::writeColumns(FlatBufferBuilder &fbb)
     305             : {
     306         322 :     std::vector<Offset<Column>> columns;
     307      131756 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     308             :     {
     309      131434 :         const auto field = m_poFeatureDefn->GetFieldDefn(i);
     310      131434 :         const auto name = field->GetNameRef();
     311             :         const auto columnType =
     312      131434 :             toColumnType(name, field->GetType(), field->GetSubType());
     313      131434 :         auto title = field->GetAlternativeNameRef();
     314      131434 :         if (EQUAL(title, ""))
     315      131432 :             title = nullptr;
     316      131434 :         const std::string &osComment = field->GetComment();
     317             :         const char *description =
     318      131434 :             !osComment.empty() ? osComment.c_str() : nullptr;
     319      131434 :         auto width = -1;
     320      131434 :         auto precision = -1;
     321      131434 :         auto scale = field->GetPrecision();
     322      131434 :         if (scale == 0)
     323      131427 :             scale = -1;
     324      131434 :         if (columnType == ColumnType::Float || columnType == ColumnType::Double)
     325          84 :             precision = field->GetWidth();
     326             :         else
     327      131350 :             width = field->GetWidth();
     328      131434 :         auto nullable = CPL_TO_BOOL(field->IsNullable());
     329      131434 :         auto unique = CPL_TO_BOOL(field->IsUnique());
     330      131434 :         auto primaryKey = false;
     331             :         // CPLDebugOnly("FlatGeobuf", "Create column %s (index %d)", name, i);
     332             :         const auto column =
     333             :             CreateColumnDirect(fbb, name, columnType, title, description, width,
     334      131434 :                                precision, scale, nullable, unique, primaryKey);
     335      131434 :         columns.push_back(column);
     336             :         // CPLDebugOnly("FlatGeobuf", "DEBUG writeColumns: Created column %s
     337             :         // added as index %d", name, i);
     338             :     }
     339         322 :     CPLDebugOnly("FlatGeobuf", "Created %lu columns for writing",
     340             :                  static_cast<long unsigned int>(columns.size()));
     341         322 :     return columns;
     342             : }
     343             : 
     344         153 : void OGRFlatGeobufLayer::readColumns()
     345             : {
     346         153 :     const auto columns = m_poHeader->columns();
     347         153 :     if (columns == nullptr)
     348           3 :         return;
     349       65827 :     for (uint32_t i = 0; i < columns->size(); i++)
     350             :     {
     351       65677 :         const auto column = columns->Get(i);
     352       65677 :         const auto type = column->type();
     353       65677 :         const auto name = column->name()->c_str();
     354             :         const auto title =
     355       65677 :             column->title() != nullptr ? column->title()->c_str() : nullptr;
     356       65677 :         const auto width = column->width();
     357       65677 :         const auto precision = column->precision();
     358       65677 :         const auto scale = column->scale();
     359       65677 :         const auto nullable = column->nullable();
     360       65677 :         const auto unique = column->unique();
     361       65677 :         OGRFieldSubType eSubType = OFSTNone;
     362       65677 :         const auto ogrType = toOGRFieldType(column->type(), eSubType);
     363      131354 :         OGRFieldDefn field(name, ogrType);
     364       65677 :         field.SetSubType(eSubType);
     365       65677 :         field.SetAlternativeName(title);
     366       65677 :         if (column->description())
     367           1 :             field.SetComment(column->description()->str());
     368       65677 :         if (width != -1 && type != ColumnType::Float &&
     369             :             type != ColumnType::Double)
     370       65614 :             field.SetWidth(width);
     371       65677 :         if (precision != -1)
     372          26 :             field.SetWidth(precision);
     373       65677 :         field.SetPrecision(scale != -1 ? scale : 0);
     374       65677 :         field.SetNullable(nullable);
     375       65677 :         field.SetUnique(unique);
     376       65677 :         m_poFeatureDefn->AddFieldDefn(&field);
     377             :         // CPLDebugOnly("FlatGeobuf", "DEBUG readColumns: Read column %s added
     378             :         // as index %d", name, i);
     379             :     }
     380         150 :     CPLDebugOnly("FlatGeobuf",
     381             :                  "Read %lu columns and added to feature definition",
     382             :                  static_cast<long unsigned int>(columns->size()));
     383             : }
     384             : 
     385         322 : void OGRFlatGeobufLayer::writeHeader(VSILFILE *poFp, uint64_t featuresCount,
     386             :                                      std::vector<double> *extentVector)
     387             : {
     388             :     size_t c;
     389         322 :     c = VSIFWriteL(&magicbytes, sizeof(magicbytes), 1, poFp);
     390         322 :     CPLDebugOnly("FlatGeobuf", "Wrote magicbytes (%lu bytes)",
     391             :                  static_cast<long unsigned int>(c * sizeof(magicbytes)));
     392         322 :     m_writeOffset += sizeof(magicbytes);
     393             : 
     394         644 :     FlatBufferBuilder fbb;
     395         322 :     fbb.TrackMinAlign(8);
     396         644 :     auto columns = writeColumns(fbb);
     397             : 
     398         322 :     flatbuffers::Offset<Crs> crs = 0;
     399         322 :     if (m_poSRS)
     400             :     {
     401          13 :         int nAuthorityCode = 0;
     402          13 :         const char *pszAuthorityName = m_poSRS->GetAuthorityName();
     403          13 :         if (pszAuthorityName == nullptr || strlen(pszAuthorityName) == 0)
     404             :         {
     405             :             // Try to force identify an EPSG code.
     406           2 :             m_poSRS->AutoIdentifyEPSG();
     407             : 
     408           2 :             pszAuthorityName = m_poSRS->GetAuthorityName();
     409           2 :             if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
     410             :             {
     411           0 :                 const char *pszAuthorityCode = m_poSRS->GetAuthorityCode();
     412           0 :                 if (pszAuthorityCode != nullptr && strlen(pszAuthorityCode) > 0)
     413             :                 {
     414             :                     /* Import 'clean' SRS */
     415           0 :                     m_poSRS->importFromEPSG(atoi(pszAuthorityCode));
     416             : 
     417           0 :                     pszAuthorityName = m_poSRS->GetAuthorityName();
     418             :                 }
     419             :             }
     420             :         }
     421          13 :         if (pszAuthorityName != nullptr && strlen(pszAuthorityName) > 0)
     422             :         {
     423             :             // For the root authority name 'EPSG', the authority code
     424             :             // should always be integral
     425          11 :             nAuthorityCode = atoi(m_poSRS->GetAuthorityCode());
     426             :         }
     427             : 
     428             :         // Translate SRS to WKT.
     429          13 :         char *pszWKT = nullptr;
     430          13 :         const char *const apszOptionsWkt[] = {"FORMAT=WKT2_2019", nullptr};
     431          13 :         m_poSRS->exportToWkt(&pszWKT, apszOptionsWkt);
     432          13 :         if (pszWKT && pszWKT[0] == '\0')
     433             :         {
     434           0 :             CPLFree(pszWKT);
     435           0 :             pszWKT = nullptr;
     436             :         }
     437             : 
     438          13 :         if (pszWKT && m_poSRS->GetCoordinateEpoch() > 0)
     439             :         {
     440             :             std::string osCoordinateEpoch =
     441           4 :                 CPLSPrintf("%f", m_poSRS->GetCoordinateEpoch());
     442           2 :             if (osCoordinateEpoch.find('.') != std::string::npos)
     443             :             {
     444          12 :                 while (osCoordinateEpoch.back() == '0')
     445          10 :                     osCoordinateEpoch.pop_back();
     446             :             }
     447             : 
     448           2 :             std::string osWKT("COORDINATEMETADATA[");
     449           2 :             osWKT += pszWKT;
     450           2 :             osWKT += ",EPOCH[";
     451           2 :             osWKT += osCoordinateEpoch;
     452           2 :             osWKT += "]]";
     453           2 :             CPLFree(pszWKT);
     454           2 :             pszWKT = CPLStrdup(osWKT.c_str());
     455             :         }
     456             : 
     457          13 :         if (pszWKT && !CPLIsUTF8(pszWKT, -1))
     458             :         {
     459           0 :             char *pszWKTtmp = CPLForceToASCII(pszWKT, -1, '?');
     460           0 :             CPLFree(pszWKT);
     461           0 :             pszWKT = pszWKTtmp;
     462             :         }
     463             :         crs = CreateCrsDirect(fbb, pszAuthorityName, nAuthorityCode,
     464          13 :                               m_poSRS->GetName(), nullptr, pszWKT);
     465          13 :         CPLFree(pszWKT);
     466             :     }
     467             : 
     468         644 :     std::string osTitle(m_aosCreationOption.FetchNameValueDef("TITLE", ""));
     469             :     std::string osDescription(
     470         644 :         m_aosCreationOption.FetchNameValueDef("DESCRIPTION", ""));
     471         644 :     std::string osMetadata;
     472         322 :     CPLJSONObject oMetadataJSONObj;
     473         322 :     bool bEmptyMetadata = true;
     474         644 :     for (GDALMajorObject *poContainer :
     475             :          {static_cast<GDALMajorObject *>(this),
     476             :           static_cast<GDALMajorObject *>(
     477         966 :               m_poDS && m_poDS->GetLayerCount() == 1 ? m_poDS : nullptr)})
     478             :     {
     479         644 :         if (poContainer)
     480             :         {
     481         640 :             if (CSLConstList papszMD = poContainer->GetMetadata())
     482             :             {
     483         652 :                 for (CSLConstList papszIter = papszMD; *papszIter; ++papszIter)
     484             :                 {
     485         330 :                     char *pszKey = nullptr;
     486             :                     const char *pszValue =
     487         330 :                         CPLParseNameValue(*papszIter, &pszKey);
     488         330 :                     if (pszKey && pszValue && !EQUAL(pszKey, OLMD_FID64))
     489             :                     {
     490          17 :                         if (EQUAL(pszKey, "TITLE"))
     491             :                         {
     492           2 :                             if (osTitle.empty())
     493           2 :                                 osTitle = pszValue;
     494             :                         }
     495          15 :                         else if (EQUAL(pszKey, "DESCRIPTION"))
     496             :                         {
     497           2 :                             if (osDescription.empty())
     498           2 :                                 osDescription = pszValue;
     499             :                         }
     500             :                         else
     501             :                         {
     502          13 :                             bEmptyMetadata = false;
     503          13 :                             oMetadataJSONObj.Add(pszKey, pszValue);
     504             :                         }
     505             :                     }
     506         330 :                     CPLFree(pszKey);
     507             :                 }
     508             :             }
     509             :         }
     510             :     }
     511         322 :     if (!bEmptyMetadata)
     512             :     {
     513             :         osMetadata =
     514           9 :             oMetadataJSONObj.Format(CPLJSONObject::PrettyFormat::Plain);
     515             :     }
     516             : 
     517             :     const auto header = CreateHeaderDirect(
     518         322 :         fbb, m_osLayerName.c_str(), extentVector, m_geometryType, m_hasZ,
     519         322 :         m_hasM, m_hasT, m_hasTM, &columns, featuresCount, m_indexNodeSize, crs,
     520         326 :         osTitle.empty() ? nullptr : osTitle.c_str(),
     521         326 :         osDescription.empty() ? nullptr : osDescription.c_str(),
     522         970 :         osMetadata.empty() ? nullptr : osMetadata.c_str());
     523         322 :     fbb.FinishSizePrefixed(header);
     524         322 :     c = VSIFWriteL(fbb.GetBufferPointer(), 1, fbb.GetSize(), poFp);
     525         322 :     CPLDebugOnly("FlatGeobuf", "Wrote header (%lu bytes)",
     526             :                  static_cast<long unsigned int>(c));
     527         322 :     m_writeOffset += c;
     528         322 : }
     529             : 
     530         182 : static bool SupportsSeekWhileWriting(const std::string &osFilename)
     531             : {
     532         337 :     return (!STARTS_WITH(osFilename.c_str(), "/vsi")) ||
     533         337 :            STARTS_WITH(osFilename.c_str(), "/vsimem/");
     534             : }
     535             : 
     536         175 : bool OGRFlatGeobufLayer::CreateFinalFile()
     537             : {
     538             :     // no spatial index requested, we are (almost) done
     539         175 :     if (!m_bCreateSpatialIndexAtClose)
     540             :     {
     541          17 :         if (m_poFpWrite == nullptr || !SupportsSeekWhileWriting(m_osFilename))
     542             :         {
     543           1 :             return true;
     544             :         }
     545             : 
     546             :         // Rewrite header
     547          16 :         VSIFSeekL(m_poFpWrite, 0, SEEK_SET);
     548          16 :         m_writeOffset = 0;
     549          16 :         std::vector<double> extentVector;
     550          16 :         if (!m_sExtent.IsInit())
     551             :         {
     552           3 :             extentVector.resize(4, std::numeric_limits<double>::quiet_NaN());
     553             :         }
     554             :         else
     555             :         {
     556          13 :             extentVector.push_back(m_sExtent.MinX);
     557          13 :             extentVector.push_back(m_sExtent.MinY);
     558          13 :             extentVector.push_back(m_sExtent.MaxX);
     559          13 :             extentVector.push_back(m_sExtent.MaxY);
     560             :         }
     561          16 :         writeHeader(m_poFpWrite, m_featuresCount, &extentVector);
     562             :         // Sanity check to verify that the dummy header and the real header
     563             :         // have the same size.
     564          16 :         if (m_featuresCount)
     565             :         {
     566          15 :             CPLAssert(m_writeOffset == m_offsetAfterHeader);
     567             :         }
     568          16 :         CPL_IGNORE_RET_VAL(m_writeOffset);  // otherwise checkers might tell the
     569             :                                             // member is not used
     570          16 :         return true;
     571             :     }
     572             : 
     573         158 :     m_poFp = VSIFOpenL(m_osFilename.c_str(), "wb");
     574         158 :     if (m_poFp == nullptr)
     575             :     {
     576           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s:\n%s",
     577           0 :                  m_osFilename.c_str(), VSIStrerror(errno));
     578           0 :         return false;
     579             :     }
     580             : 
     581             :     // check if something has been written, if not write empty layer and bail
     582         158 :     if (m_writeOffset == 0 || m_featuresCount == 0)
     583             :     {
     584          41 :         CPLDebugOnly("FlatGeobuf", "Writing empty layer");
     585          41 :         writeHeader(m_poFp, 0, nullptr);
     586          41 :         return true;
     587             :     }
     588             : 
     589         117 :     CPLDebugOnly("FlatGeobuf", "Writing second pass sorted by spatial index");
     590             : 
     591         117 :     const uint64_t nTempFileSize = m_writeOffset;
     592         117 :     m_writeOffset = 0;
     593         117 :     m_indexNodeSize = 16;
     594             : 
     595             :     size_t c;
     596             : 
     597         117 :     if (m_featuresCount >= std::numeric_limits<size_t>::max() / 8)
     598             :     {
     599           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     600             :                  "Too many features for this architecture");
     601           0 :         return false;
     602             :     }
     603             : 
     604         117 :     NodeItem extent = calcExtent(m_featureItems);
     605         234 :     auto extentVector = extent.toVector();
     606             : 
     607         117 :     writeHeader(m_poFp, m_featuresCount, &extentVector);
     608             : 
     609         117 :     CPLDebugOnly("FlatGeobuf", "Sorting items for Packed R-tree");
     610         117 :     hilbertSort(m_featureItems);
     611         117 :     CPLDebugOnly("FlatGeobuf", "Calc new feature offsets");
     612         117 :     uint64_t featureOffset = 0;
     613         282 :     for (auto &item : m_featureItems)
     614             :     {
     615         165 :         item.nodeItem.offset = featureOffset;
     616         165 :         featureOffset += item.size;
     617             :     }
     618         117 :     CPLDebugOnly("FlatGeobuf", "Creating Packed R-tree");
     619         117 :     c = 0;
     620             :     try
     621             :     {
     622         117 :         const auto fillNodeItems = [this](NodeItem *dest)
     623             :         {
     624         117 :             size_t i = 0;
     625         282 :             for (const auto &featureItem : m_featureItems)
     626             :             {
     627         165 :                 dest[i] = featureItem.nodeItem;
     628         165 :                 ++i;
     629             :             }
     630         234 :         };
     631         117 :         PackedRTree tree(fillNodeItems, m_featureItems.size(), extent);
     632         117 :         CPLDebugOnly("FlatGeobuf", "PackedRTree extent %f, %f, %f, %f",
     633             :                      extentVector[0], extentVector[1], extentVector[2],
     634             :                      extentVector[3]);
     635         117 :         tree.streamWrite([this, &c](uint8_t *data, size_t size)
     636         117 :                          { c += VSIFWriteL(data, 1, size, m_poFp); });
     637             :     }
     638           0 :     catch (const std::exception &e)
     639             :     {
     640           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Create: %s", e.what());
     641           0 :         return false;
     642             :     }
     643         117 :     CPLDebugOnly("FlatGeobuf", "Wrote tree (%lu bytes)",
     644             :                  static_cast<long unsigned int>(c));
     645         117 :     m_writeOffset += c;
     646             : 
     647         117 :     CPLDebugOnly("FlatGeobuf", "Writing feature buffers at offset %lu",
     648             :                  static_cast<long unsigned int>(m_writeOffset));
     649             : 
     650         117 :     c = 0;
     651             : 
     652             :     // For temporary files not in memory, we use a batch strategy to write the
     653             :     // final file. That is to say we try to separate reads in the source
     654             :     // temporary file and writes in the target file as much as possible, and by
     655             :     // reading source features in increasing offset within a batch.
     656             :     const bool bUseBatchStrategy =
     657         117 :         !STARTS_WITH(m_osTempFile.c_str(), "/vsimem/");
     658         117 :     if (bUseBatchStrategy)
     659             :     {
     660             :         const uint32_t nMaxBufferSize = std::max(
     661           7 :             m_maxFeatureSize,
     662          14 :             static_cast<uint32_t>(std::min(
     663           7 :                 static_cast<uint64_t>(100 * 1024 * 1024), nTempFileSize)));
     664           7 :         if (ensureFeatureBuf(nMaxBufferSize) != OGRERR_NONE)
     665           0 :             return false;
     666           7 :         uint32_t offsetInBuffer = 0;
     667             : 
     668             :         struct BatchItem
     669             :         {
     670             :             size_t featureIdx;  // index of m_featureItems[]
     671             :             uint32_t offsetInBuffer;
     672             :         };
     673             : 
     674           7 :         std::vector<BatchItem> batch;
     675             : 
     676          97 :         const auto flushBatch = [this, &batch, &offsetInBuffer]()
     677             :         {
     678             :             // Sort by increasing source offset
     679           7 :             std::sort(batch.begin(), batch.end(),
     680          88 :                       [this](const BatchItem &a, const BatchItem &b)
     681             :                       {
     682          44 :                           return m_featureItems[a.featureIdx].offset <
     683          44 :                                  m_featureItems[b.featureIdx].offset;
     684             :                       });
     685             : 
     686             :             // Read source features
     687          23 :             for (const auto &batchItem : batch)
     688             :             {
     689          16 :                 const auto &item = m_featureItems[batchItem.featureIdx];
     690          16 :                 if (VSIFSeekL(m_poFpWrite, item.offset, SEEK_SET) == -1)
     691             :                 {
     692           0 :                     CPLErrorIO("seeking to temp feature location");
     693           0 :                     return false;
     694             :                 }
     695          32 :                 if (VSIFReadL(m_featureBuf + batchItem.offsetInBuffer, 1,
     696          16 :                               item.size, m_poFpWrite) != item.size)
     697             :                 {
     698           0 :                     CPLErrorIO("reading temp feature");
     699           0 :                     return false;
     700             :                 }
     701             :             }
     702             : 
     703             :             // Write target features
     704          14 :             if (offsetInBuffer > 0 &&
     705           7 :                 VSIFWriteL(m_featureBuf, 1, offsetInBuffer, m_poFp) !=
     706           7 :                     offsetInBuffer)
     707             :             {
     708           0 :                 CPLErrorIO("writing feature");
     709           0 :                 return false;
     710             :             }
     711             : 
     712           7 :             batch.clear();
     713           7 :             offsetInBuffer = 0;
     714           7 :             return true;
     715           7 :         };
     716             : 
     717          23 :         for (size_t i = 0; i < m_featuresCount; i++)
     718             :         {
     719          16 :             const auto &featureItem = m_featureItems[i];
     720          16 :             const auto featureSize = featureItem.size;
     721             : 
     722          16 :             if (offsetInBuffer + featureSize > m_featureBufSize)
     723             :             {
     724           0 :                 if (!flushBatch())
     725             :                 {
     726           0 :                     return false;
     727             :                 }
     728             :             }
     729             : 
     730             :             BatchItem bachItem;
     731          16 :             bachItem.offsetInBuffer = offsetInBuffer;
     732          16 :             bachItem.featureIdx = i;
     733          16 :             batch.emplace_back(bachItem);
     734          16 :             offsetInBuffer += featureSize;
     735          16 :             c += featureSize;
     736             :         }
     737             : 
     738           7 :         if (!flushBatch())
     739             :         {
     740           0 :             return false;
     741             :         }
     742             :     }
     743             :     else
     744             :     {
     745         110 :         const auto err = ensureFeatureBuf(m_maxFeatureSize);
     746         110 :         if (err != OGRERR_NONE)
     747           0 :             return false;
     748             : 
     749         259 :         for (const auto &featureItem : m_featureItems)
     750             :         {
     751         149 :             const auto featureSize = featureItem.size;
     752             : 
     753             :             // CPLDebugOnly("FlatGeobuf", "featureItem.offset: %lu",
     754             :             // static_cast<long unsigned int>(featureItem.offset));
     755             :             // CPLDebugOnly("FlatGeobuf", "featureSize: %d", featureSize);
     756         149 :             if (VSIFSeekL(m_poFpWrite, featureItem.offset, SEEK_SET) == -1)
     757             :             {
     758           0 :                 CPLErrorIO("seeking to temp feature location");
     759           0 :                 return false;
     760             :             }
     761         149 :             if (VSIFReadL(m_featureBuf, 1, featureSize, m_poFpWrite) !=
     762         149 :                 featureSize)
     763             :             {
     764           0 :                 CPLErrorIO("reading temp feature");
     765           0 :                 return false;
     766             :             }
     767         149 :             if (VSIFWriteL(m_featureBuf, 1, featureSize, m_poFp) != featureSize)
     768             :             {
     769           0 :                 CPLErrorIO("writing feature");
     770           0 :                 return false;
     771             :             }
     772         149 :             c += featureSize;
     773             :         }
     774             :     }
     775             : 
     776         117 :     CPLDebugOnly("FlatGeobuf", "Wrote feature buffers (%lu bytes)",
     777             :                  static_cast<long unsigned int>(c));
     778         117 :     m_writeOffset += c;
     779             : 
     780         117 :     CPLDebugOnly("FlatGeobuf", "Now at offset %lu",
     781             :                  static_cast<long unsigned int>(m_writeOffset));
     782             : 
     783         117 :     return true;
     784             : }
     785             : 
     786         656 : OGRFlatGeobufLayer::~OGRFlatGeobufLayer()
     787             : {
     788         328 :     OGRFlatGeobufLayer::Close();
     789             : 
     790         328 :     if (m_poFeatureDefn)
     791         328 :         m_poFeatureDefn->Release();
     792             : 
     793         328 :     if (m_poSRS)
     794          27 :         m_poSRS->Release();
     795             : 
     796         328 :     if (m_featureBuf)
     797         254 :         VSIFree(m_featureBuf);
     798             : 
     799         328 :     if (m_headerBuf)
     800         153 :         VSIFree(m_headerBuf);
     801         656 : }
     802             : 
     803         653 : CPLErr OGRFlatGeobufLayer::Close(GDALProgressFunc, void *)
     804             : {
     805         653 :     CPLErr eErr = CE_None;
     806             : 
     807         653 :     if (m_create)
     808             :     {
     809         175 :         if (!CreateFinalFile())
     810           0 :             eErr = CE_Failure;
     811         175 :         m_create = false;
     812             :     }
     813             : 
     814         653 :     if (m_poFp)
     815             :     {
     816         311 :         if (VSIFCloseL(m_poFp) != 0)
     817           0 :             eErr = CE_Failure;
     818         311 :         m_poFp = nullptr;
     819             :     }
     820             : 
     821         653 :     if (m_poFpWrite)
     822             :     {
     823         175 :         if (VSIFCloseL(m_poFpWrite) != 0)
     824           0 :             eErr = CE_Failure;
     825         175 :         m_poFpWrite = nullptr;
     826             :     }
     827             : 
     828         653 :     if (!m_osTempFile.empty())
     829             :     {
     830         175 :         VSIUnlink(m_osTempFile.c_str());
     831         175 :         m_osTempFile.clear();
     832             :     }
     833             : 
     834         653 :     return eErr;
     835             : }
     836             : 
     837           9 : OGRErr OGRFlatGeobufLayer::readFeatureOffset(uint64_t index,
     838             :                                              uint64_t &featureOffset)
     839             : {
     840             :     try
     841             :     {
     842             :         const auto treeSize =
     843           9 :             PackedRTree::size(m_featuresCount, m_indexNodeSize);
     844             :         const auto levelBounds =
     845          18 :             PackedRTree::generateLevelBounds(m_featuresCount, m_indexNodeSize);
     846             :         const auto bottomLevelOffset =
     847           9 :             m_offset - treeSize +
     848           9 :             (levelBounds.front().first * sizeof(NodeItem));
     849           9 :         const auto nodeItemOffset =
     850           9 :             bottomLevelOffset + (index * sizeof(NodeItem));
     851           9 :         const auto featureOffsetOffset = nodeItemOffset + (sizeof(double) * 4);
     852           9 :         if (VSIFSeekL(m_poFp, featureOffsetOffset, SEEK_SET) == -1)
     853           0 :             return CPLErrorIO("seeking feature offset");
     854           9 :         if (VSIFReadL(&featureOffset, sizeof(uint64_t), 1, m_poFp) != 1)
     855           0 :             return CPLErrorIO("reading feature offset");
     856           9 :         CPL_LSBPTR64(&featureOffset);
     857           9 :         return OGRERR_NONE;
     858             :     }
     859           0 :     catch (const std::exception &e)
     860             :     {
     861           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     862           0 :                  "Failed to calculate tree size: %s", e.what());
     863           0 :         return OGRERR_FAILURE;
     864             :     }
     865             : }
     866             : 
     867          14 : OGRFeature *OGRFlatGeobufLayer::GetFeature(GIntBig nFeatureId)
     868             : {
     869          14 :     if (m_indexNodeSize == 0)
     870             :     {
     871           0 :         return OGRLayer::GetFeature(nFeatureId);
     872             :     }
     873             :     else
     874             :     {
     875          14 :         if (nFeatureId < 0 ||
     876          12 :             static_cast<uint64_t>(nFeatureId) >= m_featuresCount)
     877             :         {
     878           5 :             return nullptr;
     879             :         }
     880           9 :         ResetReading();
     881           9 :         m_ignoreSpatialFilter = true;
     882           9 :         m_ignoreAttributeFilter = true;
     883             :         uint64_t featureOffset;
     884           9 :         const auto err = readFeatureOffset(nFeatureId, featureOffset);
     885           9 :         if (err != OGRERR_NONE)
     886             :         {
     887           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     888             :                      "Unexpected error reading feature offset from id");
     889           0 :             return nullptr;
     890             :         }
     891           9 :         m_offset = m_offsetFeatures + featureOffset;
     892           9 :         OGRFeature *poFeature = GetNextFeature();
     893           9 :         if (poFeature != nullptr)
     894           9 :             poFeature->SetFID(nFeatureId);
     895           9 :         ResetReading();
     896           9 :         return poFeature;
     897             :     }
     898             : }
     899             : 
     900         668 : OGRErr OGRFlatGeobufLayer::readIndex()
     901             : {
     902         668 :     if (m_queriedSpatialIndex || !m_poFilterGeom)
     903         488 :         return OGRERR_NONE;
     904         360 :     if (m_sFilterEnvelope.IsInit() && m_sExtent.IsInit() &&
     905         114 :         m_sFilterEnvelope.MinX <= m_sExtent.MinX &&
     906          84 :         m_sFilterEnvelope.MinY <= m_sExtent.MinY &&
     907         432 :         m_sFilterEnvelope.MaxX >= m_sExtent.MaxX &&
     908          72 :         m_sFilterEnvelope.MaxY >= m_sExtent.MaxY)
     909          72 :         return OGRERR_NONE;
     910         108 :     const auto indexNodeSize = m_poHeader->index_node_size();
     911         108 :     if (indexNodeSize == 0)
     912          74 :         return OGRERR_NONE;
     913          34 :     const auto featuresCount = m_poHeader->features_count();
     914          34 :     if (featuresCount == 0)
     915           0 :         return OGRERR_NONE;
     916             : 
     917          34 :     if (VSIFSeekL(m_poFp, static_cast<vsi_l_offset>(sizeof(magicbytes)),
     918          34 :                   SEEK_SET) == -1)  // skip magic bytes
     919           0 :         return CPLErrorIO("seeking past magic bytes");
     920             :     uoffset_t headerSize;
     921          34 :     if (VSIFReadL(&headerSize, sizeof(uoffset_t), 1, m_poFp) != 1)
     922           0 :         return CPLErrorIO("reading header size");
     923          34 :     CPL_LSBPTR32(&headerSize);
     924             : 
     925             :     try
     926             :     {
     927             :         const auto treeSize =
     928          34 :             indexNodeSize > 0 ? PackedRTree::size(featuresCount) : 0;
     929          34 :         if (treeSize > 0 && m_poFilterGeom && !m_ignoreSpatialFilter)
     930             :         {
     931          33 :             CPLDebugOnly("FlatGeobuf", "Attempting spatial index query");
     932          33 :             OGREnvelope env;
     933          33 :             m_poFilterGeom->getEnvelope(&env);
     934          33 :             NodeItem n{env.MinX, env.MinY, env.MaxX, env.MaxY, 0};
     935          33 :             CPLDebugOnly("FlatGeobuf", "Spatial index search on %f,%f,%f,%f",
     936             :                          env.MinX, env.MinY, env.MaxX, env.MaxY);
     937          33 :             const vsi_l_offset treeOffset =
     938          33 :                 sizeof(magicbytes) + sizeof(uoffset_t) + headerSize;
     939             :             const auto readNode =
     940         122 :                 [this, treeOffset](uint8_t *buf, size_t i, size_t s)
     941             :             {
     942          61 :                 if (VSIFSeekL(m_poFp, treeOffset + i, SEEK_SET) == -1)
     943           0 :                     throw std::runtime_error("I/O seek failure");
     944          61 :                 if (VSIFReadL(buf, 1, s, m_poFp) != s)
     945           0 :                     throw std::runtime_error("I/O read file");
     946          61 :             };
     947          66 :             m_foundItems = PackedRTree::streamSearch(
     948          33 :                 featuresCount, indexNodeSize, n, readNode);
     949          33 :             m_featuresCount = m_foundItems.size();
     950          33 :             CPLDebugOnly("FlatGeobuf",
     951             :                          "%lu features found in spatial index search",
     952             :                          static_cast<long unsigned int>(m_featuresCount));
     953             : 
     954          33 :             m_queriedSpatialIndex = true;
     955             :         }
     956             :     }
     957           0 :     catch (const std::exception &e)
     958             :     {
     959           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     960           0 :                  "readIndex: Unexpected failure: %s", e.what());
     961           0 :         return OGRERR_FAILURE;
     962             :     }
     963             : 
     964          34 :     return OGRERR_NONE;
     965             : }
     966             : 
     967          31 : GIntBig OGRFlatGeobufLayer::GetFeatureCount(int bForce)
     968             : {
     969          31 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr ||
     970          17 :         m_featuresCount == 0)
     971          17 :         return OGRLayer::GetFeatureCount(bForce);
     972             :     else
     973          14 :         return m_featuresCount;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                           ParseDateTime()                            */
     978             : /************************************************************************/
     979             : 
     980          25 : static inline bool ParseDateTime(std::string_view sInput, OGRField *psField)
     981             : {
     982          48 :     return OGRParseDateTimeYYYYMMDDTHHMMSSZ(sInput, psField) ||
     983          48 :            OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(sInput, psField);
     984             : }
     985             : 
     986         655 : OGRFeature *OGRFlatGeobufLayer::GetNextFeature()
     987             : {
     988         655 :     if (m_create)
     989          16 :         return nullptr;
     990             : 
     991             :     while (true)
     992             :     {
     993         709 :         if (m_featuresCount > 0 && m_featuresPos >= m_featuresCount)
     994             :         {
     995          81 :             CPLDebugOnly("FlatGeobuf", "GetNextFeature: iteration end at %lu",
     996             :                          static_cast<long unsigned int>(m_featuresPos));
     997         639 :             return nullptr;
     998             :         }
     999             : 
    1000         628 :         if (readIndex() != OGRERR_NONE)
    1001             :         {
    1002           0 :             return nullptr;
    1003             :         }
    1004             : 
    1005         628 :         if (m_queriedSpatialIndex && m_featuresCount == 0)
    1006             :         {
    1007           5 :             CPLDebugOnly("FlatGeobuf", "GetNextFeature: no features found");
    1008           5 :             return nullptr;
    1009             :         }
    1010             : 
    1011         623 :         auto poFeature = std::make_unique<OGRFeature>(m_poFeatureDefn);
    1012         623 :         if (parseFeature(poFeature.get()) != OGRERR_NONE)
    1013             :         {
    1014           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    1015             :                      "Fatal error parsing feature");
    1016           4 :             return nullptr;
    1017             :         }
    1018             : 
    1019         619 :         if (VSIFEofL(m_poFp) || VSIFErrorL(m_poFp))
    1020             :         {
    1021          11 :             CPLDebug("FlatGeobuf", "GetNextFeature: iteration end due to EOF");
    1022          11 :             return nullptr;
    1023             :         }
    1024             : 
    1025         608 :         m_featuresPos++;
    1026             : 
    1027         241 :         if ((m_poFilterGeom == nullptr || m_ignoreSpatialFilter ||
    1028        1418 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    1029         570 :             (m_poAttrQuery == nullptr || m_ignoreAttributeFilter ||
    1030          71 :              m_poAttrQuery->Evaluate(poFeature.get())))
    1031         538 :             return poFeature.release();
    1032          70 :     }
    1033             : }
    1034             : 
    1035         842 : OGRErr OGRFlatGeobufLayer::ensureFeatureBuf(uint32_t featureSize)
    1036             : {
    1037         842 :     if (m_featureBufSize == 0)
    1038             :     {
    1039         254 :         const auto newBufSize = std::max(1024U * 32U, featureSize);
    1040         254 :         CPLDebugOnly("FlatGeobuf", "ensureFeatureBuf: newBufSize: %d",
    1041             :                      newBufSize);
    1042         254 :         m_featureBuf = static_cast<GByte *>(VSIMalloc(newBufSize));
    1043         254 :         if (m_featureBuf == nullptr)
    1044           0 :             return CPLErrorMemoryAllocation("initial feature buffer");
    1045         254 :         m_featureBufSize = newBufSize;
    1046             :     }
    1047         588 :     else if (m_featureBufSize < featureSize)
    1048             :     {
    1049             :         // Do not increase this x2 factor without modifying
    1050             :         // feature_max_buffer_size
    1051           0 :         const auto newBufSize = std::max(m_featureBufSize * 2, featureSize);
    1052           0 :         CPLDebugOnly("FlatGeobuf", "ensureFeatureBuf: newBufSize: %d",
    1053             :                      newBufSize);
    1054             :         const auto featureBuf =
    1055           0 :             static_cast<GByte *>(VSIRealloc(m_featureBuf, newBufSize));
    1056           0 :         if (featureBuf == nullptr)
    1057           0 :             return CPLErrorMemoryAllocation("feature buffer resize");
    1058           0 :         m_featureBuf = featureBuf;
    1059           0 :         m_featureBufSize = newBufSize;
    1060             :     }
    1061         842 :     return OGRERR_NONE;
    1062             : }
    1063             : 
    1064         623 : OGRErr OGRFlatGeobufLayer::parseFeature(OGRFeature *poFeature)
    1065             : {
    1066             :     GIntBig fid;
    1067         623 :     auto seek = false;
    1068         623 :     if (m_queriedSpatialIndex && !m_ignoreSpatialFilter)
    1069             :     {
    1070         108 :         const auto item = m_foundItems[m_featuresPos];
    1071         108 :         m_offset = m_offsetFeatures + item.offset;
    1072         108 :         fid = item.index;
    1073         108 :         seek = true;
    1074             :     }
    1075             :     else
    1076             :     {
    1077         515 :         fid = m_featuresPos;
    1078             :     }
    1079         623 :     poFeature->SetFID(fid);
    1080             : 
    1081             :     // CPLDebugOnly("FlatGeobuf", "m_featuresPos: %lu", static_cast<long
    1082             :     // unsigned int>(m_featuresPos));
    1083             : 
    1084         623 :     if (m_featuresPos == 0)
    1085         232 :         seek = true;
    1086             : 
    1087         623 :     if (seek && VSIFSeekL(m_poFp, m_offset, SEEK_SET) == -1)
    1088             :     {
    1089           0 :         if (VSIFEofL(m_poFp))
    1090           0 :             return OGRERR_NONE;
    1091           0 :         return CPLErrorIO("seeking to feature location");
    1092             :     }
    1093             :     uint32_t featureSize;
    1094         623 :     if (VSIFReadL(&featureSize, sizeof(featureSize), 1, m_poFp) != 1)
    1095             :     {
    1096          11 :         if (VSIFEofL(m_poFp))
    1097          11 :             return OGRERR_NONE;
    1098           0 :         return CPLErrorIO("reading feature size");
    1099             :     }
    1100         612 :     CPL_LSBPTR32(&featureSize);
    1101             : 
    1102             :     // Sanity check to avoid allocated huge amount of memory on corrupted
    1103             :     // feature
    1104         612 :     if (featureSize > 100 * 1024 * 1024)
    1105             :     {
    1106           0 :         if (featureSize > feature_max_buffer_size)
    1107           0 :             return CPLErrorInvalidSize("feature");
    1108             : 
    1109           0 :         if (m_nFileSize == 0)
    1110             :         {
    1111             :             VSIStatBufL sStatBuf;
    1112           0 :             if (VSIStatL(m_osFilename.c_str(), &sStatBuf) == 0)
    1113             :             {
    1114           0 :                 m_nFileSize = sStatBuf.st_size;
    1115             :             }
    1116             :         }
    1117           0 :         if (m_offset + featureSize > m_nFileSize)
    1118             :         {
    1119           0 :             return CPLErrorIO("reading feature size");
    1120             :         }
    1121             :     }
    1122             : 
    1123         612 :     const auto err = ensureFeatureBuf(featureSize);
    1124         612 :     if (err != OGRERR_NONE)
    1125           0 :         return err;
    1126         612 :     if (VSIFReadL(m_featureBuf, 1, featureSize, m_poFp) != featureSize)
    1127           0 :         return CPLErrorIO("reading feature");
    1128         612 :     m_offset += featureSize + sizeof(featureSize);
    1129             : 
    1130         612 :     if (m_bVerifyBuffers)
    1131             :     {
    1132         612 :         Verifier v(m_featureBuf, featureSize);
    1133         612 :         const auto ok = VerifyFeatureBuffer(v);
    1134         612 :         if (!ok)
    1135             :         {
    1136           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Buffer verification failed");
    1137           0 :             CPLDebugOnly("FlatGeobuf", "m_offset: %lu",
    1138             :                          static_cast<long unsigned int>(m_offset));
    1139           0 :             CPLDebugOnly("FlatGeobuf", "m_featuresPos: %lu",
    1140             :                          static_cast<long unsigned int>(m_featuresPos));
    1141           0 :             CPLDebugOnly("FlatGeobuf", "featureSize: %d", featureSize);
    1142           0 :             return OGRERR_CORRUPT_DATA;
    1143             :         }
    1144             :     }
    1145             : 
    1146         612 :     const auto feature = GetRoot<Feature>(m_featureBuf);
    1147         612 :     const auto geometry = feature->geometry();
    1148         612 :     if (!m_poFeatureDefn->IsGeometryIgnored() && geometry != nullptr)
    1149             :     {
    1150         598 :         auto geometryType = m_geometryType;
    1151         598 :         if (geometryType == GeometryType::Unknown)
    1152           6 :             geometryType = geometry->type();
    1153             :         OGRGeometry *poOGRGeometry =
    1154         598 :             GeometryReader(geometry, geometryType, m_hasZ, m_hasM).read();
    1155         598 :         if (poOGRGeometry == nullptr)
    1156             :         {
    1157           4 :             CPLError(CE_Failure, CPLE_AppDefined, "Failed to read geometry");
    1158           4 :             return OGRERR_CORRUPT_DATA;
    1159             :         }
    1160             :         // #ifdef DEBUG
    1161             :         //             char *wkt;
    1162             :         //             poOGRGeometry->exportToWkt(&wkt);
    1163             :         //             CPLDebugOnly("FlatGeobuf", "readGeometry as wkt: %s",
    1164             :         //             wkt);
    1165             :         // #endif
    1166         594 :         if (m_poSRS != nullptr)
    1167         329 :             poOGRGeometry->assignSpatialReference(m_poSRS);
    1168         594 :         poFeature->SetGeometryDirectly(poOGRGeometry);
    1169             :     }
    1170             : 
    1171         608 :     const auto properties = feature->properties();
    1172         608 :     if (properties != nullptr)
    1173             :     {
    1174         511 :         const auto data = properties->data();
    1175         511 :         const auto size = properties->size();
    1176             : 
    1177             :         // CPLDebugOnly("FlatGeobuf", "DEBUG parseFeature: size: %lu",
    1178             :         // static_cast<long unsigned int>(size));
    1179             : 
    1180             :         // CPLDebugOnly("FlatGeobuf", "properties->size: %d", size);
    1181         511 :         uoffset_t offset = 0;
    1182             :         // size must be at least large enough to contain
    1183             :         // a single column index and smallest value type
    1184         511 :         if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t)))
    1185           0 :             return CPLErrorInvalidSize("property value");
    1186       67331 :         while (offset + 1 < size)
    1187             :         {
    1188       66820 :             if (offset + sizeof(uint16_t) > size)
    1189           0 :                 return CPLErrorInvalidSize("property value");
    1190             :             uint16_t i;
    1191       66820 :             memcpy(&i, data + offset, sizeof(i));
    1192       66820 :             CPL_LSBPTR16(&i);
    1193             :             // CPLDebugOnly("FlatGeobuf", "DEBUG parseFeature: i: %hu", i);
    1194       66820 :             offset += sizeof(uint16_t);
    1195             :             // CPLDebugOnly("FlatGeobuf", "DEBUG parseFeature: offset: %du",
    1196             :             // offset);
    1197             :             //  TODO: use columns from feature if defined
    1198       66820 :             const auto columns = m_poHeader->columns();
    1199       66820 :             if (columns == nullptr)
    1200             :             {
    1201           0 :                 CPLErrorInvalidPointer("columns");
    1202           0 :                 return OGRERR_CORRUPT_DATA;
    1203             :             }
    1204       66820 :             if (i >= columns->size())
    1205             :             {
    1206           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1207             :                          "Column index %hu out of range", i);
    1208           0 :                 return OGRERR_CORRUPT_DATA;
    1209             :             }
    1210       66820 :             const auto column = columns->Get(i);
    1211       66820 :             const auto type = column->type();
    1212       66820 :             const auto isIgnored = poFeature->GetFieldDefnRef(i)->IsIgnored();
    1213       66820 :             const auto ogrField = poFeature->GetRawFieldRef(i);
    1214       66820 :             if (!OGR_RawField_IsUnset(ogrField))
    1215             :             {
    1216           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1217             :                          "Field %d set more than once", i);
    1218           0 :                 return OGRERR_CORRUPT_DATA;
    1219             :             }
    1220             : 
    1221       66820 :             switch (type)
    1222             :             {
    1223           5 :                 case ColumnType::Bool:
    1224           5 :                     if (offset + sizeof(unsigned char) > size)
    1225           0 :                         return CPLErrorInvalidSize("bool value");
    1226           5 :                     if (!isIgnored)
    1227             :                     {
    1228           5 :                         ogrField->Integer = *(data + offset);
    1229             :                     }
    1230           5 :                     offset += sizeof(unsigned char);
    1231           5 :                     break;
    1232             : 
    1233           1 :                 case ColumnType::Byte:
    1234           1 :                     if (offset + sizeof(signed char) > size)
    1235           0 :                         return CPLErrorInvalidSize("byte value");
    1236           1 :                     if (!isIgnored)
    1237             :                     {
    1238           1 :                         ogrField->Integer =
    1239           1 :                             *reinterpret_cast<const signed char *>(data +
    1240           1 :                                                                    offset);
    1241             :                     }
    1242           1 :                     offset += sizeof(signed char);
    1243           1 :                     break;
    1244             : 
    1245           1 :                 case ColumnType::UByte:
    1246           1 :                     if (offset + sizeof(unsigned char) > size)
    1247           0 :                         return CPLErrorInvalidSize("ubyte value");
    1248           1 :                     if (!isIgnored)
    1249             :                     {
    1250           1 :                         ogrField->Integer =
    1251           1 :                             *reinterpret_cast<const unsigned char *>(data +
    1252           1 :                                                                      offset);
    1253             :                     }
    1254           1 :                     offset += sizeof(unsigned char);
    1255           1 :                     break;
    1256             : 
    1257           5 :                 case ColumnType::Short:
    1258           5 :                     if (offset + sizeof(int16_t) > size)
    1259           0 :                         return CPLErrorInvalidSize("short value");
    1260           5 :                     if (!isIgnored)
    1261             :                     {
    1262             :                         short s;
    1263           5 :                         memcpy(&s, data + offset, sizeof(int16_t));
    1264           5 :                         CPL_LSBPTR16(&s);
    1265           5 :                         ogrField->Integer = s;
    1266             :                     }
    1267           5 :                     offset += sizeof(int16_t);
    1268           5 :                     break;
    1269             : 
    1270           1 :                 case ColumnType::UShort:
    1271           1 :                     if (offset + sizeof(uint16_t) > size)
    1272           0 :                         return CPLErrorInvalidSize("ushort value");
    1273           1 :                     if (!isIgnored)
    1274             :                     {
    1275             :                         uint16_t s;
    1276           1 :                         memcpy(&s, data + offset, sizeof(uint16_t));
    1277           1 :                         CPL_LSBPTR16(&s);
    1278           1 :                         ogrField->Integer = s;
    1279             :                     }
    1280           1 :                     offset += sizeof(uint16_t);
    1281           1 :                     break;
    1282             : 
    1283       65552 :                 case ColumnType::Int:
    1284       65552 :                     if (offset + sizeof(int32_t) > size)
    1285           0 :                         return CPLErrorInvalidSize("int32 value");
    1286       65552 :                     if (!isIgnored)
    1287             :                     {
    1288       65552 :                         memcpy(&ogrField->Integer, data + offset,
    1289             :                                sizeof(int32_t));
    1290       65552 :                         CPL_LSBPTR32(&ogrField->Integer);
    1291             :                     }
    1292       65552 :                     offset += sizeof(int32_t);
    1293       65552 :                     break;
    1294             : 
    1295           1 :                 case ColumnType::UInt:
    1296           1 :                     if (offset + sizeof(uint32_t) > size)
    1297           0 :                         return CPLErrorInvalidSize("uint value");
    1298           1 :                     if (!isIgnored)
    1299             :                     {
    1300             :                         uint32_t v;
    1301           1 :                         memcpy(&v, data + offset, sizeof(int32_t));
    1302           1 :                         CPL_LSBPTR32(&v);
    1303           1 :                         ogrField->Integer64 = v;
    1304             :                     }
    1305           1 :                     offset += sizeof(int32_t);
    1306           1 :                     break;
    1307             : 
    1308         457 :                 case ColumnType::Long:
    1309         457 :                     if (offset + sizeof(int64_t) > size)
    1310           0 :                         return CPLErrorInvalidSize("int64 value");
    1311         457 :                     if (!isIgnored)
    1312             :                     {
    1313         457 :                         memcpy(&ogrField->Integer64, data + offset,
    1314             :                                sizeof(int64_t));
    1315         457 :                         CPL_LSBPTR64(&ogrField->Integer64);
    1316             :                     }
    1317         457 :                     offset += sizeof(int64_t);
    1318         457 :                     break;
    1319             : 
    1320           1 :                 case ColumnType::ULong:
    1321           1 :                     if (offset + sizeof(uint64_t) > size)
    1322           0 :                         return CPLErrorInvalidSize("uint64 value");
    1323           1 :                     if (!isIgnored)
    1324             :                     {
    1325             :                         uint64_t v;
    1326           1 :                         memcpy(&v, data + offset, sizeof(v));
    1327           1 :                         CPL_LSBPTR64(&v);
    1328           1 :                         ogrField->Real = static_cast<double>(v);
    1329             :                     }
    1330           1 :                     offset += sizeof(int64_t);
    1331           1 :                     break;
    1332             : 
    1333           4 :                 case ColumnType::Float:
    1334           4 :                     if (offset + sizeof(float) > size)
    1335           0 :                         return CPLErrorInvalidSize("float value");
    1336           4 :                     if (!isIgnored)
    1337             :                     {
    1338             :                         float f;
    1339           4 :                         memcpy(&f, data + offset, sizeof(float));
    1340           4 :                         CPL_LSBPTR32(&f);
    1341           4 :                         ogrField->Real = f;
    1342             :                     }
    1343           4 :                     offset += sizeof(float);
    1344           4 :                     break;
    1345             : 
    1346         377 :                 case ColumnType::Double:
    1347         377 :                     if (offset + sizeof(double) > size)
    1348           0 :                         return CPLErrorInvalidSize("double value");
    1349         377 :                     if (!isIgnored)
    1350             :                     {
    1351         367 :                         memcpy(&ogrField->Real, data + offset, sizeof(double));
    1352         367 :                         CPL_LSBPTR64(&ogrField->Real);
    1353             :                     }
    1354         377 :                     offset += sizeof(double);
    1355         377 :                     break;
    1356             : 
    1357         405 :                 case ColumnType::String:
    1358             :                 case ColumnType::Json:
    1359             :                 {
    1360         405 :                     if (offset + sizeof(uint32_t) > size)
    1361           0 :                         return CPLErrorInvalidSize("string length");
    1362             :                     uint32_t len;
    1363         405 :                     memcpy(&len, data + offset, sizeof(int32_t));
    1364         405 :                     CPL_LSBPTR32(&len);
    1365         405 :                     offset += sizeof(uint32_t);
    1366         405 :                     if (len > size - offset)
    1367           0 :                         return CPLErrorInvalidSize("string value");
    1368         405 :                     if (!isIgnored)
    1369             :                     {
    1370             :                         char *str =
    1371         401 :                             static_cast<char *>(VSI_MALLOC_VERBOSE(len + 1));
    1372         401 :                         if (str == nullptr)
    1373           0 :                             return CPLErrorMemoryAllocation("string value");
    1374         401 :                         memcpy(str, data + offset, len);
    1375         401 :                         str[len] = '\0';
    1376         401 :                         ogrField->String = str;
    1377             :                     }
    1378         405 :                     offset += len;
    1379         405 :                     break;
    1380             :                 }
    1381             : 
    1382           5 :                 case ColumnType::DateTime:
    1383             :                 {
    1384           5 :                     if (offset + sizeof(uint32_t) > size)
    1385           0 :                         return CPLErrorInvalidSize("datetime length ");
    1386             :                     uint32_t len;
    1387           5 :                     memcpy(&len, data + offset, sizeof(int32_t));
    1388           5 :                     CPL_LSBPTR32(&len);
    1389           5 :                     offset += sizeof(uint32_t);
    1390           5 :                     if (len > size - offset || len > 32)
    1391           0 :                         return CPLErrorInvalidSize("datetime value");
    1392           5 :                     if (!isIgnored)
    1393             :                     {
    1394           5 :                         if (!ParseDateTime(
    1395             :                                 std::string_view(reinterpret_cast<const char *>(
    1396           5 :                                                      data + offset),
    1397             :                                                  len),
    1398             :                                 ogrField))
    1399             :                         {
    1400             :                             char str[32 + 1];
    1401           0 :                             memcpy(str, data + offset, len);
    1402           0 :                             str[len] = '\0';
    1403           0 :                             if (!OGRParseDate(str, ogrField, 0))
    1404             :                             {
    1405           0 :                                 OGR_RawField_SetUnset(ogrField);
    1406             :                             }
    1407             :                         }
    1408             :                     }
    1409           5 :                     offset += len;
    1410           5 :                     break;
    1411             :                 }
    1412             : 
    1413           5 :                 case ColumnType::Binary:
    1414             :                 {
    1415           5 :                     if (offset + sizeof(uint32_t) > size)
    1416           0 :                         return CPLErrorInvalidSize("binary length");
    1417             :                     uint32_t len;
    1418           5 :                     memcpy(&len, data + offset, sizeof(int32_t));
    1419           5 :                     CPL_LSBPTR32(&len);
    1420           5 :                     offset += sizeof(uint32_t);
    1421           5 :                     if (len > static_cast<uint32_t>(INT_MAX) ||
    1422           5 :                         len > size - offset)
    1423           0 :                         return CPLErrorInvalidSize("binary value");
    1424           5 :                     if (!isIgnored)
    1425             :                     {
    1426             :                         GByte *binary = static_cast<GByte *>(
    1427           5 :                             VSI_MALLOC_VERBOSE(len ? len : 1));
    1428           5 :                         if (binary == nullptr)
    1429           0 :                             return CPLErrorMemoryAllocation("string value");
    1430           5 :                         memcpy(binary, data + offset, len);
    1431           5 :                         ogrField->Binary.nCount = static_cast<int>(len);
    1432           5 :                         ogrField->Binary.paData = binary;
    1433             :                     }
    1434           5 :                     offset += len;
    1435           5 :                     break;
    1436             :                 }
    1437             :             }
    1438             :         }
    1439             :     }
    1440         608 :     return OGRERR_NONE;
    1441             : }
    1442             : 
    1443             : /************************************************************************/
    1444             : /*                         GetNextArrowArray()                          */
    1445             : /************************************************************************/
    1446             : 
    1447          70 : int OGRFlatGeobufLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    1448             :                                           struct ArrowArray *out_array)
    1449             : {
    1450         137 :     if (!m_poSharedArrowArrayStreamPrivateData->m_anQueriedFIDs.empty() ||
    1451          67 :         CPLTestBool(
    1452             :             CPLGetConfigOption("OGR_FLATGEOBUF_STREAM_BASE_IMPL", "NO")))
    1453             :     {
    1454           3 :         return OGRLayer::GetNextArrowArray(stream, out_array);
    1455             :     }
    1456             : 
    1457          67 : begin:
    1458          77 :     int errorErrno = EIO;
    1459          77 :     memset(out_array, 0, sizeof(*out_array));
    1460             : 
    1461          77 :     if (m_create)
    1462           1 :         return EINVAL;
    1463             : 
    1464          76 :     if (m_bEOF || (m_featuresCount > 0 && m_featuresPos >= m_featuresCount))
    1465             :     {
    1466          36 :         return 0;
    1467             :     }
    1468             : 
    1469          40 :     if (readIndex() != OGRERR_NONE)
    1470           0 :         return EIO;
    1471             : 
    1472             :     OGRArrowArrayHelper sHelper(
    1473             :         nullptr,  // dataset pointer. only used for field domains (not used by
    1474             :                   // FlatGeobuf)
    1475          40 :         m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
    1476          40 :     if (out_array->release == nullptr)
    1477             :     {
    1478           0 :         return ENOMEM;
    1479             :     }
    1480             : 
    1481          40 :     std::vector<bool> abSetFields(sHelper.m_nFieldCount);
    1482             : 
    1483             :     struct tm brokenDown;
    1484          40 :     memset(&brokenDown, 0, sizeof(brokenDown));
    1485             : 
    1486          40 :     int iFeat = 0;
    1487          40 :     bool bEOFOrError = true;
    1488             : 
    1489          40 :     if (m_queriedSpatialIndex && m_featuresCount == 0)
    1490             :     {
    1491           0 :         CPLDebugOnly("FlatGeobuf", "GetNextFeature: no features found");
    1492           0 :         sHelper.m_nMaxBatchSize = 0;
    1493             :     }
    1494             : 
    1495          40 :     const GIntBig nFeatureIdxStart = m_featuresPos;
    1496          40 :     const bool bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
    1497             :         GAS_OPT_DATETIME_AS_STRING, false);
    1498             : 
    1499          40 :     const uint32_t nMemLimit = OGRArrowArrayHelper::GetMemLimit();
    1500         153 :     while (iFeat < sHelper.m_nMaxBatchSize)
    1501             :     {
    1502         142 :         bEOFOrError = true;
    1503         142 :         if (m_featuresCount > 0 && m_featuresPos >= m_featuresCount)
    1504             :         {
    1505          29 :             CPLDebugOnly("FlatGeobuf", "GetNextFeature: iteration end at %lu",
    1506             :                          static_cast<long unsigned int>(m_featuresPos));
    1507          29 :             break;
    1508             :         }
    1509             : 
    1510             :         GIntBig fid;
    1511         113 :         auto seek = false;
    1512         113 :         if (m_queriedSpatialIndex && !m_ignoreSpatialFilter)
    1513             :         {
    1514          17 :             const auto item = m_foundItems[m_featuresPos];
    1515          17 :             m_offset = m_offsetFeatures + item.offset;
    1516          17 :             fid = item.index;
    1517          17 :             seek = true;
    1518             :         }
    1519             :         else
    1520             :         {
    1521          96 :             fid = m_featuresPos;
    1522             :         }
    1523             : 
    1524         113 :         if (sHelper.m_panFIDValues)
    1525         108 :             sHelper.m_panFIDValues[iFeat] = fid;
    1526             : 
    1527         113 :         if (m_featuresPos == 0)
    1528          36 :             seek = true;
    1529             : 
    1530         113 :         if (seek && VSIFSeekL(m_poFp, m_offset, SEEK_SET) == -1)
    1531             :         {
    1532           0 :             break;
    1533             :         }
    1534             :         uint32_t featureSize;
    1535         113 :         if (VSIFReadL(&featureSize, sizeof(featureSize), 1, m_poFp) != 1)
    1536             :         {
    1537           0 :             if (VSIFEofL(m_poFp))
    1538           0 :                 break;
    1539           0 :             CPLErrorIO("reading feature size");
    1540           0 :             goto error;
    1541             :         }
    1542         113 :         CPL_LSBPTR32(&featureSize);
    1543             : 
    1544             :         // Sanity check to avoid allocated huge amount of memory on corrupted
    1545             :         // feature
    1546         113 :         if (featureSize > 100 * 1024 * 1024)
    1547             :         {
    1548           0 :             if (featureSize > feature_max_buffer_size)
    1549             :             {
    1550           0 :                 CPLErrorInvalidSize("feature");
    1551           0 :                 goto error;
    1552             :             }
    1553             : 
    1554           0 :             if (m_nFileSize == 0)
    1555             :             {
    1556             :                 VSIStatBufL sStatBuf;
    1557           0 :                 if (VSIStatL(m_osFilename.c_str(), &sStatBuf) == 0)
    1558             :                 {
    1559           0 :                     m_nFileSize = sStatBuf.st_size;
    1560             :                 }
    1561             :             }
    1562           0 :             if (m_offset + featureSize > m_nFileSize)
    1563             :             {
    1564           0 :                 CPLErrorIO("reading feature size");
    1565           0 :                 goto error;
    1566             :             }
    1567             :         }
    1568             : 
    1569         113 :         const auto err = ensureFeatureBuf(featureSize);
    1570         113 :         if (err != OGRERR_NONE)
    1571           0 :             goto error;
    1572         113 :         if (VSIFReadL(m_featureBuf, 1, featureSize, m_poFp) != featureSize)
    1573             :         {
    1574           0 :             CPLErrorIO("reading feature");
    1575           0 :             goto error;
    1576             :         }
    1577         113 :         m_offset += featureSize + sizeof(featureSize);
    1578             : 
    1579         113 :         if (m_bVerifyBuffers)
    1580             :         {
    1581         113 :             Verifier v(m_featureBuf, featureSize);
    1582         113 :             const auto ok = VerifyFeatureBuffer(v);
    1583         113 :             if (!ok)
    1584             :             {
    1585           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1586             :                          "Buffer verification failed");
    1587           0 :                 CPLDebugOnly("FlatGeobuf", "m_offset: %lu",
    1588             :                              static_cast<long unsigned int>(m_offset));
    1589           0 :                 CPLDebugOnly("FlatGeobuf", "m_featuresPos: %lu",
    1590             :                              static_cast<long unsigned int>(m_featuresPos));
    1591           0 :                 CPLDebugOnly("FlatGeobuf", "featureSize: %d", featureSize);
    1592           0 :                 goto error;
    1593             :             }
    1594             :         }
    1595             : 
    1596         113 :         const auto feature = GetRoot<Feature>(m_featureBuf);
    1597         113 :         const auto geometry = feature->geometry();
    1598         113 :         const auto properties = feature->properties();
    1599         113 :         if (!m_poFeatureDefn->IsGeometryIgnored() && geometry != nullptr)
    1600             :         {
    1601         107 :             auto geometryType = m_geometryType;
    1602         107 :             if (geometryType == GeometryType::Unknown)
    1603           4 :                 geometryType = geometry->type();
    1604             :             auto poOGRGeometry = std::unique_ptr<OGRGeometry>(
    1605         107 :                 GeometryReader(geometry, geometryType, m_hasZ, m_hasM).read());
    1606         107 :             if (poOGRGeometry == nullptr)
    1607             :             {
    1608           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1609             :                          "Failed to read geometry");
    1610           0 :                 goto error;
    1611             :             }
    1612             : 
    1613         107 :             if (!FilterGeometry(poOGRGeometry.get()))
    1614           6 :                 goto end_of_loop;
    1615             : 
    1616         101 :             const int iArrowField = sHelper.m_mapOGRGeomFieldToArrowField[0];
    1617         101 :             const size_t nWKBSize = poOGRGeometry->WkbSize();
    1618             : 
    1619         101 :             if (iFeat > 0)
    1620             :             {
    1621          66 :                 auto psArray = out_array->children[iArrowField];
    1622          66 :                 auto panOffsets = static_cast<int32_t *>(
    1623          66 :                     const_cast<void *>(psArray->buffers[1]));
    1624          66 :                 const uint32_t nCurLength =
    1625          66 :                     static_cast<uint32_t>(panOffsets[iFeat]);
    1626          66 :                 if (nWKBSize <= nMemLimit && nWKBSize > nMemLimit - nCurLength)
    1627             :                 {
    1628           0 :                     goto after_loop;
    1629             :                 }
    1630             :             }
    1631             : 
    1632             :             GByte *outPtr =
    1633         101 :                 sHelper.GetPtrForStringOrBinary(iArrowField, iFeat, nWKBSize);
    1634         101 :             if (outPtr == nullptr)
    1635             :             {
    1636           0 :                 errorErrno = ENOMEM;
    1637           0 :                 goto error;
    1638             :             }
    1639         101 :             poOGRGeometry->exportToWkb(wkbNDR, outPtr, wkbVariantIso);
    1640             :         }
    1641             : 
    1642         107 :         abSetFields.clear();
    1643         107 :         abSetFields.resize(sHelper.m_nFieldCount);
    1644             : 
    1645         107 :         if (properties != nullptr)
    1646             :         {
    1647         105 :             const auto data = properties->data();
    1648         105 :             const auto size = properties->size();
    1649             : 
    1650         105 :             uoffset_t offset = 0;
    1651             :             // size must be at least large enough to contain
    1652             :             // a single column index and smallest value type
    1653         105 :             if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t)))
    1654             :             {
    1655           0 :                 CPLErrorInvalidSize("property value");
    1656           0 :                 goto error;
    1657             :             }
    1658             : 
    1659         512 :             while (offset + 1 < size)
    1660             :             {
    1661         407 :                 if (offset + sizeof(uint16_t) > size)
    1662             :                 {
    1663           0 :                     CPLErrorInvalidSize("property value");
    1664           0 :                     goto error;
    1665             :                 }
    1666             :                 uint16_t i;
    1667         407 :                 memcpy(&i, data + offset, sizeof(i));
    1668         407 :                 CPL_LSBPTR16(&i);
    1669         407 :                 offset += sizeof(uint16_t);
    1670             :                 // TODO: use columns from feature if defined
    1671         407 :                 const auto columns = m_poHeader->columns();
    1672         407 :                 if (columns == nullptr)
    1673             :                 {
    1674           0 :                     CPLErrorInvalidPointer("columns");
    1675           0 :                     goto error;
    1676             :                 }
    1677         407 :                 if (i >= columns->size())
    1678             :                 {
    1679           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1680             :                              "Column index %hu out of range", i);
    1681           0 :                     goto error;
    1682             :                 }
    1683             : 
    1684         407 :                 abSetFields[i] = true;
    1685         407 :                 const auto column = columns->Get(i);
    1686         407 :                 const auto type = column->type();
    1687         407 :                 const int iArrowField = sHelper.m_mapOGRFieldToArrowField[i];
    1688         407 :                 const bool isIgnored = iArrowField < 0;
    1689         407 :                 auto psArray =
    1690         407 :                     isIgnored ? nullptr : out_array->children[iArrowField];
    1691             : 
    1692         407 :                 switch (type)
    1693             :                 {
    1694          36 :                     case ColumnType::Bool:
    1695          36 :                         if (offset + sizeof(unsigned char) > size)
    1696             :                         {
    1697           0 :                             CPLErrorInvalidSize("bool value");
    1698           0 :                             goto error;
    1699             :                         }
    1700          36 :                         if (!isIgnored)
    1701             :                         {
    1702          36 :                             if (*(data + offset))
    1703             :                             {
    1704          20 :                                 sHelper.SetBoolOn(psArray, iFeat);
    1705             :                             }
    1706             :                         }
    1707          36 :                         offset += sizeof(unsigned char);
    1708          36 :                         break;
    1709             : 
    1710           0 :                     case ColumnType::Byte:
    1711           0 :                         if (offset + sizeof(signed char) > size)
    1712             :                         {
    1713           0 :                             CPLErrorInvalidSize("byte value");
    1714           0 :                             goto error;
    1715             :                         }
    1716           0 :                         if (!isIgnored)
    1717             :                         {
    1718           0 :                             sHelper.SetInt8(psArray, iFeat,
    1719           0 :                                             *reinterpret_cast<const int8_t *>(
    1720           0 :                                                 data + offset));
    1721             :                         }
    1722           0 :                         offset += sizeof(signed char);
    1723           0 :                         break;
    1724             : 
    1725           0 :                     case ColumnType::UByte:
    1726           0 :                         if (offset + sizeof(unsigned char) > size)
    1727             :                         {
    1728           0 :                             CPLErrorInvalidSize("ubyte value");
    1729           0 :                             goto error;
    1730             :                         }
    1731           0 :                         if (!isIgnored)
    1732             :                         {
    1733           0 :                             sHelper.SetUInt8(psArray, iFeat,
    1734           0 :                                              *reinterpret_cast<const uint8_t *>(
    1735           0 :                                                  data + offset));
    1736             :                         }
    1737           0 :                         offset += sizeof(unsigned char);
    1738           0 :                         break;
    1739             : 
    1740          36 :                     case ColumnType::Short:
    1741          36 :                         if (offset + sizeof(int16_t) > size)
    1742             :                         {
    1743           0 :                             CPLErrorInvalidSize("short value");
    1744           0 :                             goto error;
    1745             :                         }
    1746          36 :                         if (!isIgnored)
    1747             :                         {
    1748             :                             short s;
    1749          32 :                             memcpy(&s, data + offset, sizeof(int16_t));
    1750          32 :                             CPL_LSBPTR16(&s);
    1751          32 :                             sHelper.SetInt16(psArray, iFeat, s);
    1752             :                         }
    1753          36 :                         offset += sizeof(int16_t);
    1754          36 :                         break;
    1755             : 
    1756           0 :                     case ColumnType::UShort:
    1757           0 :                         if (offset + sizeof(uint16_t) > size)
    1758             :                         {
    1759           0 :                             CPLErrorInvalidSize("ushort value");
    1760           0 :                             goto error;
    1761             :                         }
    1762           0 :                         if (!isIgnored)
    1763             :                         {
    1764             :                             uint16_t s;
    1765           0 :                             memcpy(&s, data + offset, sizeof(uint16_t));
    1766           0 :                             CPL_LSBPTR16(&s);
    1767           0 :                             sHelper.SetInt32(psArray, iFeat, s);
    1768             :                         }
    1769           0 :                         offset += sizeof(uint16_t);
    1770           0 :                         break;
    1771             : 
    1772          20 :                     case ColumnType::Int:
    1773          20 :                         if (offset + sizeof(int32_t) > size)
    1774             :                         {
    1775           0 :                             CPLErrorInvalidSize("int32 value");
    1776           0 :                             goto error;
    1777             :                         }
    1778          20 :                         if (!isIgnored)
    1779             :                         {
    1780             :                             int32_t nVal;
    1781          20 :                             memcpy(&nVal, data + offset, sizeof(int32_t));
    1782          20 :                             CPL_LSBPTR32(&nVal);
    1783          20 :                             sHelper.SetInt32(psArray, iFeat, nVal);
    1784             :                         }
    1785          20 :                         offset += sizeof(int32_t);
    1786          20 :                         break;
    1787             : 
    1788           0 :                     case ColumnType::UInt:
    1789           0 :                         if (offset + sizeof(uint32_t) > size)
    1790             :                         {
    1791           0 :                             CPLErrorInvalidSize("uint value");
    1792           0 :                             goto error;
    1793             :                         }
    1794           0 :                         if (!isIgnored)
    1795             :                         {
    1796             :                             uint32_t v;
    1797           0 :                             memcpy(&v, data + offset, sizeof(int32_t));
    1798           0 :                             CPL_LSBPTR32(&v);
    1799           0 :                             sHelper.SetInt64(psArray, iFeat, v);
    1800             :                         }
    1801           0 :                         offset += sizeof(int32_t);
    1802           0 :                         break;
    1803             : 
    1804          80 :                     case ColumnType::Long:
    1805          80 :                         if (offset + sizeof(int64_t) > size)
    1806             :                         {
    1807           0 :                             CPLErrorInvalidSize("int64 value");
    1808           0 :                             goto error;
    1809             :                         }
    1810          80 :                         if (!isIgnored)
    1811             :                         {
    1812             :                             int64_t v;
    1813          80 :                             memcpy(&v, data + offset, sizeof(int64_t));
    1814          80 :                             CPL_LSBPTR64(&v);
    1815          80 :                             sHelper.SetInt64(psArray, iFeat, v);
    1816             :                         }
    1817          80 :                         offset += sizeof(int64_t);
    1818          80 :                         break;
    1819             : 
    1820           0 :                     case ColumnType::ULong:
    1821           0 :                         if (offset + sizeof(uint64_t) > size)
    1822             :                         {
    1823           0 :                             CPLErrorInvalidSize("uint64 value");
    1824           0 :                             goto error;
    1825             :                         }
    1826           0 :                         if (!isIgnored)
    1827             :                         {
    1828             :                             uint64_t v;
    1829           0 :                             memcpy(&v, data + offset, sizeof(v));
    1830           0 :                             CPL_LSBPTR64(&v);
    1831           0 :                             sHelper.SetDouble(psArray, iFeat,
    1832             :                                               static_cast<double>(v));
    1833             :                         }
    1834           0 :                         offset += sizeof(int64_t);
    1835           0 :                         break;
    1836             : 
    1837          20 :                     case ColumnType::Float:
    1838          20 :                         if (offset + sizeof(float) > size)
    1839             :                         {
    1840           0 :                             CPLErrorInvalidSize("float value");
    1841           0 :                             goto error;
    1842             :                         }
    1843          20 :                         if (!isIgnored)
    1844             :                         {
    1845             :                             float f;
    1846          20 :                             memcpy(&f, data + offset, sizeof(float));
    1847          20 :                             CPL_LSBPTR32(&f);
    1848          20 :                             sHelper.SetFloat(psArray, iFeat, f);
    1849             :                         }
    1850          20 :                         offset += sizeof(float);
    1851          20 :                         break;
    1852             : 
    1853          80 :                     case ColumnType::Double:
    1854          80 :                         if (offset + sizeof(double) > size)
    1855             :                         {
    1856           0 :                             CPLErrorInvalidSize("double value");
    1857           0 :                             goto error;
    1858             :                         }
    1859          80 :                         if (!isIgnored)
    1860             :                         {
    1861             :                             double v;
    1862          80 :                             memcpy(&v, data + offset, sizeof(double));
    1863          80 :                             CPL_LSBPTR64(&v);
    1864          80 :                             sHelper.SetDouble(psArray, iFeat, v);
    1865             :                         }
    1866          80 :                         offset += sizeof(double);
    1867          80 :                         break;
    1868             : 
    1869          23 :                     case ColumnType::DateTime:
    1870             :                     {
    1871          23 :                         if (!bDateTimeAsString)
    1872             :                         {
    1873          20 :                             if (offset + sizeof(uint32_t) > size)
    1874             :                             {
    1875           0 :                                 CPLErrorInvalidSize("datetime length ");
    1876           0 :                                 goto error;
    1877             :                             }
    1878             :                             uint32_t len;
    1879          20 :                             memcpy(&len, data + offset, sizeof(int32_t));
    1880          20 :                             CPL_LSBPTR32(&len);
    1881          20 :                             offset += sizeof(uint32_t);
    1882          20 :                             if (len > size - offset || len > 32)
    1883             :                             {
    1884           0 :                                 CPLErrorInvalidSize("datetime value");
    1885           0 :                                 goto error;
    1886             :                             }
    1887          20 :                             if (!isIgnored)
    1888             :                             {
    1889             :                                 OGRField ogrField;
    1890          20 :                                 if (ParseDateTime(
    1891             :                                         std::string_view(
    1892             :                                             reinterpret_cast<const char *>(
    1893          20 :                                                 data + offset),
    1894             :                                             len),
    1895             :                                         &ogrField))
    1896             :                                 {
    1897          20 :                                     sHelper.SetDateTime(
    1898             :                                         psArray, iFeat, brokenDown,
    1899          20 :                                         sHelper.m_anTZFlags[i], ogrField);
    1900             :                                 }
    1901             :                                 else
    1902             :                                 {
    1903             :                                     char str[32 + 1];
    1904           0 :                                     memcpy(str, data + offset, len);
    1905           0 :                                     str[len] = '\0';
    1906           0 :                                     if (OGRParseDate(str, &ogrField, 0))
    1907             :                                     {
    1908           0 :                                         sHelper.SetDateTime(
    1909             :                                             psArray, iFeat, brokenDown,
    1910           0 :                                             sHelper.m_anTZFlags[i], ogrField);
    1911             :                                     }
    1912             :                                 }
    1913             :                             }
    1914          20 :                             offset += len;
    1915          20 :                             break;
    1916             :                         }
    1917             :                         else
    1918             :                         {
    1919             :                             [[fallthrough]];
    1920             :                         }
    1921             :                     }
    1922             : 
    1923             :                     case ColumnType::String:
    1924             :                     case ColumnType::Json:
    1925             :                     case ColumnType::Binary:
    1926             :                     {
    1927         115 :                         if (offset + sizeof(uint32_t) > size)
    1928             :                         {
    1929           0 :                             CPLErrorInvalidSize("string length");
    1930           0 :                             goto error;
    1931             :                         }
    1932             :                         uint32_t len;
    1933         115 :                         memcpy(&len, data + offset, sizeof(int32_t));
    1934         115 :                         CPL_LSBPTR32(&len);
    1935         115 :                         offset += sizeof(uint32_t);
    1936         115 :                         if (len > size - offset)
    1937             :                         {
    1938           0 :                             CPLErrorInvalidSize("string value");
    1939           0 :                             goto error;
    1940             :                         }
    1941         115 :                         if (!isIgnored)
    1942             :                         {
    1943         114 :                             if (iFeat > 0)
    1944             :                             {
    1945          61 :                                 auto panOffsets = static_cast<int32_t *>(
    1946          61 :                                     const_cast<void *>(psArray->buffers[1]));
    1947          61 :                                 const uint32_t nCurLength =
    1948          61 :                                     static_cast<uint32_t>(panOffsets[iFeat]);
    1949          61 :                                 if (len <= nMemLimit &&
    1950          61 :                                     len > nMemLimit - nCurLength)
    1951             :                                 {
    1952           0 :                                     goto after_loop;
    1953             :                                 }
    1954             :                             }
    1955             : 
    1956         114 :                             GByte *outPtr = sHelper.GetPtrForStringOrBinary(
    1957             :                                 iArrowField, iFeat, len);
    1958         114 :                             if (outPtr == nullptr)
    1959             :                             {
    1960           0 :                                 errorErrno = ENOMEM;
    1961           0 :                                 goto error;
    1962             :                             }
    1963         114 :                             memcpy(outPtr, data + offset, len);
    1964             :                         }
    1965         115 :                         offset += len;
    1966         115 :                         break;
    1967             :                     }
    1968             :                 }
    1969             :             }
    1970             :         }
    1971             : 
    1972             :         // Mark null fields
    1973         627 :         for (int i = 0; i < sHelper.m_nFieldCount; i++)
    1974             :         {
    1975         520 :             if (!abSetFields[i] && sHelper.m_abNullableFields[i])
    1976             :             {
    1977         113 :                 const int iArrowField = sHelper.m_mapOGRFieldToArrowField[i];
    1978         113 :                 if (iArrowField >= 0)
    1979             :                 {
    1980         113 :                     sHelper.SetNull(iArrowField, iFeat);
    1981             :                 }
    1982             :             }
    1983             :         }
    1984             : 
    1985         107 :         iFeat++;
    1986             : 
    1987         113 :     end_of_loop:
    1988             : 
    1989         113 :         if (VSIFEofL(m_poFp) || VSIFErrorL(m_poFp))
    1990             :         {
    1991           0 :             CPLDebug("FlatGeobuf", "GetNextFeature: iteration end due to EOF");
    1992           0 :             break;
    1993             :         }
    1994             : 
    1995         113 :         m_featuresPos++;
    1996         113 :         bEOFOrError = false;
    1997             :     }
    1998          11 : after_loop:
    1999          40 :     if (bEOFOrError)
    2000          29 :         m_bEOF = true;
    2001             : 
    2002          40 :     sHelper.Shrink(iFeat);
    2003             : 
    2004          40 :     if (out_array->length != 0 && m_poAttrQuery)
    2005             :     {
    2006             :         struct ArrowSchema schema;
    2007          21 :         stream->get_schema(stream, &schema);
    2008          21 :         CPLAssert(schema.release != nullptr);
    2009          21 :         CPLAssert(schema.n_children == out_array->n_children);
    2010             :         // Spatial filter already evaluated
    2011          21 :         auto poFilterGeomBackup = m_poFilterGeom;
    2012          21 :         m_poFilterGeom = nullptr;
    2013          21 :         CPLStringList aosOptions;
    2014          21 :         if (!m_poFilterGeom)
    2015             :         {
    2016             :             aosOptions.SetNameValue("BASE_SEQUENTIAL_FID",
    2017          21 :                                     CPLSPrintf(CPL_FRMT_GIB, nFeatureIdxStart));
    2018             :         }
    2019          21 :         PostFilterArrowArray(&schema, out_array, aosOptions.List());
    2020          21 :         schema.release(&schema);
    2021          21 :         m_poFilterGeom = poFilterGeomBackup;
    2022             :     }
    2023             : 
    2024          40 :     if (out_array->length == 0)
    2025             :     {
    2026          10 :         if (out_array->release)
    2027          10 :             out_array->release(out_array);
    2028          10 :         memset(out_array, 0, sizeof(*out_array));
    2029             : 
    2030          10 :         if (m_poAttrQuery || m_poFilterGeom)
    2031             :         {
    2032          10 :             goto begin;
    2033             :         }
    2034             :     }
    2035             : 
    2036          30 :     return 0;
    2037             : 
    2038           0 : error:
    2039           0 :     sHelper.ClearArray();
    2040           0 :     return errorErrno;
    2041             : }
    2042             : 
    2043       65722 : OGRErr OGRFlatGeobufLayer::CreateField(const OGRFieldDefn *poField,
    2044             :                                        int /* bApproxOK */)
    2045             : {
    2046             :     // CPLDebugOnly("FlatGeobuf", "CreateField %s %s", poField->GetNameRef(),
    2047             :     // poField->GetFieldTypeName(poField->GetType()));
    2048       65722 :     if (!TestCapability(OLCCreateField))
    2049             :     {
    2050           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2051             :                  "Unable to create new fields after first feature written.");
    2052           0 :         return OGRERR_FAILURE;
    2053             :     }
    2054             : 
    2055       65722 :     if (m_poFeatureDefn->GetFieldCount() > std::numeric_limits<uint16_t>::max())
    2056             :     {
    2057           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2058             :                  "Cannot create features with more than 65536 columns");
    2059           1 :         return OGRERR_FAILURE;
    2060             :     }
    2061             : 
    2062       65721 :     m_poFeatureDefn->AddFieldDefn(poField);
    2063             : 
    2064       65721 :     return OGRERR_NONE;
    2065             : }
    2066             : 
    2067         271 : OGRErr OGRFlatGeobufLayer::ICreateFeature(OGRFeature *poNewFeature)
    2068             : {
    2069         271 :     if (!m_create)
    2070             :     {
    2071           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2072             :                  "CreateFeature() not supported on read-only layer");
    2073           1 :         return OGRERR_FAILURE;
    2074             :     }
    2075             : 
    2076         270 :     const auto fieldCount = m_poFeatureDefn->GetFieldCount();
    2077             : 
    2078         270 :     std::vector<uint8_t> &properties = m_writeProperties;
    2079         270 :     properties.clear();
    2080         270 :     properties.reserve(1024 * 4);
    2081         540 :     FlatBufferBuilder fbb;
    2082         270 :     fbb.TrackMinAlign(8);
    2083             : 
    2084       66379 :     for (int i = 0; i < fieldCount; i++)
    2085             :     {
    2086       66109 :         const auto fieldDef = m_poFeatureDefn->GetFieldDefn(i);
    2087       66109 :         if (!poNewFeature->IsFieldSetAndNotNull(i))
    2088         107 :             continue;
    2089             : 
    2090       66002 :         uint16_t column_index_le = static_cast<uint16_t>(i);
    2091       66002 :         CPL_LSBPTR16(&column_index_le);
    2092             : 
    2093             :         // CPLDebugOnly("FlatGeobuf", "DEBUG ICreateFeature: column_index_le:
    2094             :         // %hu", column_index_le);
    2095             : 
    2096             :         std::copy(reinterpret_cast<const uint8_t *>(&column_index_le),
    2097       66002 :                   reinterpret_cast<const uint8_t *>(&column_index_le + 1),
    2098       66002 :                   std::back_inserter(properties));
    2099             : 
    2100       66002 :         const auto fieldType = fieldDef->GetType();
    2101       66002 :         const auto fieldSubType = fieldDef->GetSubType();
    2102       66002 :         const auto field = poNewFeature->GetRawFieldRef(i);
    2103       66002 :         switch (fieldType)
    2104             :         {
    2105       65608 :             case OGRFieldType::OFTInteger:
    2106             :             {
    2107       65608 :                 int nVal = field->Integer;
    2108       65608 :                 if (fieldSubType == OFSTBoolean)
    2109             :                 {
    2110           6 :                     GByte byVal = static_cast<GByte>(nVal);
    2111             :                     std::copy(reinterpret_cast<const uint8_t *>(&byVal),
    2112           6 :                               reinterpret_cast<const uint8_t *>(&byVal + 1),
    2113           6 :                               std::back_inserter(properties));
    2114             :                 }
    2115       65602 :                 else if (fieldSubType == OFSTInt16)
    2116             :                 {
    2117           6 :                     short sVal = static_cast<short>(nVal);
    2118           6 :                     CPL_LSBPTR16(&sVal);
    2119             :                     std::copy(reinterpret_cast<const uint8_t *>(&sVal),
    2120           6 :                               reinterpret_cast<const uint8_t *>(&sVal + 1),
    2121           6 :                               std::back_inserter(properties));
    2122             :                 }
    2123             :                 else
    2124             :                 {
    2125       65596 :                     CPL_LSBPTR32(&nVal);
    2126             :                     std::copy(reinterpret_cast<const uint8_t *>(&nVal),
    2127       65596 :                               reinterpret_cast<const uint8_t *>(&nVal + 1),
    2128       65596 :                               std::back_inserter(properties));
    2129             :                 }
    2130       65608 :                 break;
    2131             :             }
    2132          70 :             case OGRFieldType::OFTInteger64:
    2133             :             {
    2134          70 :                 GIntBig nVal = field->Integer64;
    2135          70 :                 CPL_LSBPTR64(&nVal);
    2136             :                 std::copy(reinterpret_cast<const uint8_t *>(&nVal),
    2137          70 :                           reinterpret_cast<const uint8_t *>(&nVal + 1),
    2138          70 :                           std::back_inserter(properties));
    2139          70 :                 break;
    2140             :             }
    2141         100 :             case OGRFieldType::OFTReal:
    2142             :             {
    2143         100 :                 double dfVal = field->Real;
    2144         100 :                 if (fieldSubType == OFSTFloat32)
    2145             :                 {
    2146           4 :                     float fVal = static_cast<float>(dfVal);
    2147           4 :                     CPL_LSBPTR32(&fVal);
    2148             :                     std::copy(reinterpret_cast<const uint8_t *>(&fVal),
    2149           4 :                               reinterpret_cast<const uint8_t *>(&fVal + 1),
    2150           4 :                               std::back_inserter(properties));
    2151             :                 }
    2152             :                 else
    2153             :                 {
    2154          96 :                     CPL_LSBPTR64(&dfVal);
    2155             :                     std::copy(reinterpret_cast<const uint8_t *>(&dfVal),
    2156          96 :                               reinterpret_cast<const uint8_t *>(&dfVal + 1),
    2157          96 :                               std::back_inserter(properties));
    2158             :                 }
    2159         100 :                 break;
    2160             :             }
    2161         103 :             case OGRFieldType::OFTDate:
    2162             :             case OGRFieldType::OFTTime:
    2163             :             case OGRFieldType::OFTDateTime:
    2164             :             {
    2165             :                 char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    2166             :                 const size_t len =
    2167         103 :                     OGRGetISO8601DateTime(field, false, szBuffer);
    2168         103 :                 uint32_t l_le = static_cast<uint32_t>(len);
    2169         103 :                 CPL_LSBPTR32(&l_le);
    2170             :                 std::copy(reinterpret_cast<const uint8_t *>(&l_le),
    2171         103 :                           reinterpret_cast<const uint8_t *>(&l_le + 1),
    2172         103 :                           std::back_inserter(properties));
    2173             :                 std::copy(szBuffer, szBuffer + len,
    2174         103 :                           std::back_inserter(properties));
    2175         103 :                 break;
    2176             :             }
    2177         116 :             case OGRFieldType::OFTString:
    2178             :             {
    2179         116 :                 const size_t len = strlen(field->String);
    2180         232 :                 if (len >= feature_max_buffer_size ||
    2181         116 :                     properties.size() > feature_max_buffer_size - len)
    2182             :                 {
    2183           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2184             :                              "ICreateFeature: String too long");
    2185           0 :                     return OGRERR_FAILURE;
    2186             :                 }
    2187         116 :                 if (!CPLIsUTF8(field->String, static_cast<int>(len)))
    2188             :                 {
    2189           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2190             :                              "ICreateFeature: String '%s' is not a valid UTF-8 "
    2191             :                              "string",
    2192             :                              field->String);
    2193           0 :                     return OGRERR_FAILURE;
    2194             :                 }
    2195             : 
    2196             :                 // Valid cast since feature_max_buffer_size is 2 GB
    2197         116 :                 uint32_t l_le = static_cast<uint32_t>(len);
    2198         116 :                 CPL_LSBPTR32(&l_le);
    2199             :                 std::copy(reinterpret_cast<const uint8_t *>(&l_le),
    2200         116 :                           reinterpret_cast<const uint8_t *>(&l_le + 1),
    2201         116 :                           std::back_inserter(properties));
    2202             :                 try
    2203             :                 {
    2204             :                     // to avoid coverity scan warning: "To avoid a quadratic
    2205             :                     // time penalty when using reserve(), always increase the
    2206             :                     // capacity
    2207             :                     /// by a multiple of its current value"
    2208         117 :                     if (properties.size() + len > properties.capacity() &&
    2209           1 :                         properties.size() <
    2210           1 :                             std::numeric_limits<size_t>::max() / 2)
    2211             :                     {
    2212           1 :                         properties.reserve(std::max(2 * properties.size(),
    2213           2 :                                                     properties.size() + len));
    2214             :                     }
    2215             :                 }
    2216           0 :                 catch (const std::bad_alloc &)
    2217             :                 {
    2218           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
    2219             :                              "ICreateFeature: String too long");
    2220           0 :                     return OGRERR_FAILURE;
    2221             :                 }
    2222         116 :                 std::copy(field->String, field->String + len,
    2223         116 :                           std::back_inserter(properties));
    2224         116 :                 break;
    2225             :             }
    2226             : 
    2227           5 :             case OGRFieldType::OFTBinary:
    2228             :             {
    2229           5 :                 const size_t len = field->Binary.nCount;
    2230          10 :                 if (len >= feature_max_buffer_size ||
    2231           5 :                     properties.size() > feature_max_buffer_size - len)
    2232             :                 {
    2233           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2234             :                              "ICreateFeature: Binary too long");
    2235           0 :                     return OGRERR_FAILURE;
    2236             :                 }
    2237           5 :                 uint32_t l_le = static_cast<uint32_t>(len);
    2238           5 :                 CPL_LSBPTR32(&l_le);
    2239             :                 std::copy(reinterpret_cast<const uint8_t *>(&l_le),
    2240           5 :                           reinterpret_cast<const uint8_t *>(&l_le + 1),
    2241           5 :                           std::back_inserter(properties));
    2242             :                 try
    2243             :                 {
    2244             :                     // to avoid coverity scan warning: "To avoid a quadratic
    2245             :                     // time penalty when using reserve(), always increase the
    2246             :                     // capacity
    2247             :                     /// by a multiple of its current value"
    2248           5 :                     if (properties.size() + len > properties.capacity() &&
    2249           0 :                         properties.size() <
    2250           0 :                             std::numeric_limits<size_t>::max() / 2)
    2251             :                     {
    2252           0 :                         properties.reserve(std::max(2 * properties.size(),
    2253           0 :                                                     properties.size() + len));
    2254             :                     }
    2255             :                 }
    2256           0 :                 catch (const std::bad_alloc &)
    2257             :                 {
    2258           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory,
    2259             :                              "ICreateFeature: Binary too long");
    2260           0 :                     return OGRERR_FAILURE;
    2261             :                 }
    2262           5 :                 std::copy(field->Binary.paData, field->Binary.paData + len,
    2263           5 :                           std::back_inserter(properties));
    2264           5 :                 break;
    2265             :             }
    2266             : 
    2267           0 :             default:
    2268           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2269             :                          "ICreateFeature: Missing implementation for "
    2270             :                          "OGRFieldType %d",
    2271             :                          fieldType);
    2272           0 :                 return OGRERR_FAILURE;
    2273             :         }
    2274             :     }
    2275             : 
    2276             :     // CPLDebugOnly("FlatGeobuf", "DEBUG ICreateFeature: properties.size():
    2277             :     // %lu", static_cast<long unsigned int>(properties.size()));
    2278             : 
    2279         270 :     const auto ogrGeometry = poNewFeature->GetGeometryRef();
    2280             : #ifdef DEBUG
    2281             :     // char *wkt;
    2282             :     // ogrGeometry->exportToWkt(&wkt);
    2283             :     // CPLDebugOnly("FlatGeobuf", "poNewFeature as wkt: %s", wkt);
    2284             : #endif
    2285         466 :     if (m_bCreateSpatialIndexAtClose &&
    2286         196 :         (ogrGeometry == nullptr || ogrGeometry->IsEmpty()))
    2287             :     {
    2288          35 :         CPLError(
    2289             :             CE_Failure, CPLE_AppDefined,
    2290             :             "ICreateFeature: NULL geometry not supported with spatial index");
    2291          35 :         return OGRERR_FAILURE;
    2292             :     }
    2293         456 :     if (ogrGeometry != nullptr && m_geometryType != GeometryType::Unknown &&
    2294         221 :         ogrGeometry->getGeometryType() != m_eGType)
    2295             :     {
    2296          30 :         CPLError(CE_Failure, CPLE_AppDefined,
    2297             :                  "ICreateFeature: Mismatched geometry type. "
    2298             :                  "Feature geometry type is %s, "
    2299             :                  "expected layer geometry type is %s",
    2300          15 :                  OGRGeometryTypeToName(ogrGeometry->getGeometryType()),
    2301             :                  OGRGeometryTypeToName(m_eGType));
    2302          15 :         return OGRERR_FAILURE;
    2303             :     }
    2304             : 
    2305             :     try
    2306             :     {
    2307             :         // FlatBuffer serialization will crash/assert if the vectors go
    2308             :         // beyond FLATBUFFERS_MAX_BUFFER_SIZE. We cannot easily anticipate
    2309             :         // the size of the FlatBuffer, but WKB might be a good approximation.
    2310             :         // Takes an extra security margin of 10%
    2311         220 :         flatbuffers::Offset<FlatGeobuf::Geometry> geometryOffset = 0;
    2312         220 :         if (ogrGeometry && !ogrGeometry->IsEmpty())
    2313             :         {
    2314         214 :             const auto nWKBSize = ogrGeometry->WkbSize();
    2315         214 :             if (nWKBSize > feature_max_buffer_size - nWKBSize / 10)
    2316             :             {
    2317           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2318             :                          "ICreateFeature: Too big geometry");
    2319           0 :                 return OGRERR_FAILURE;
    2320             :             }
    2321         214 :             GeometryWriter writer{fbb, ogrGeometry, m_geometryType, m_hasZ,
    2322         428 :                                   m_hasM};
    2323         214 :             geometryOffset = writer.write(0);
    2324             :         }
    2325         220 :         const auto pProperties = properties.empty() ? nullptr : &properties;
    2326         220 :         if (properties.size() > feature_max_buffer_size - geometryOffset.o)
    2327             :         {
    2328           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    2329             :                      "ICreateFeature: Too big feature");
    2330           0 :             return OGRERR_FAILURE;
    2331             :         }
    2332             :         // TODO: write columns if mixed schema in collection
    2333             :         const auto feature =
    2334         220 :             CreateFeatureDirect(fbb, geometryOffset, pProperties);
    2335         220 :         fbb.FinishSizePrefixed(feature);
    2336             : 
    2337         220 :         OGREnvelope psEnvelope;
    2338         220 :         if (ogrGeometry != nullptr)
    2339             :         {
    2340         215 :             ogrGeometry->getEnvelope(&psEnvelope);
    2341         215 :             if (m_sExtent.IsInit())
    2342          69 :                 m_sExtent.Merge(psEnvelope);
    2343             :             else
    2344         146 :                 m_sExtent = psEnvelope;
    2345             :         }
    2346             : 
    2347         220 :         if (m_featuresCount == 0)
    2348             :         {
    2349         148 :             if (m_poFpWrite == nullptr)
    2350             :             {
    2351           0 :                 CPLErrorInvalidPointer("output file handler");
    2352           0 :                 return OGRERR_FAILURE;
    2353             :             }
    2354         148 :             if (!SupportsSeekWhileWriting(m_osFilename))
    2355             :             {
    2356           2 :                 writeHeader(m_poFpWrite, 0, nullptr);
    2357             :             }
    2358             :             else
    2359             :             {
    2360             :                 std::vector<double> dummyExtent(
    2361         146 :                     4, std::numeric_limits<double>::quiet_NaN());
    2362         146 :                 const uint64_t dummyFeatureCount =
    2363             :                     0xDEADBEEF;  // write non-zero value, otherwise the reserved
    2364             :                                  // size is not OK
    2365         146 :                 writeHeader(m_poFpWrite, dummyFeatureCount,
    2366             :                             &dummyExtent);  // we will update it later
    2367         146 :                 m_offsetAfterHeader = m_writeOffset;
    2368             :             }
    2369         148 :             CPLDebugOnly("FlatGeobuf", "Writing first feature at offset: %lu",
    2370             :                          static_cast<long unsigned int>(m_writeOffset));
    2371             :         }
    2372             : 
    2373         220 :         m_maxFeatureSize =
    2374         220 :             std::max(m_maxFeatureSize, static_cast<uint32_t>(fbb.GetSize()));
    2375             :         size_t c =
    2376         220 :             VSIFWriteL(fbb.GetBufferPointer(), 1, fbb.GetSize(), m_poFpWrite);
    2377         220 :         if (c == 0)
    2378           0 :             return CPLErrorIO("writing feature");
    2379         220 :         if (m_bCreateSpatialIndexAtClose)
    2380             :         {
    2381             :             FeatureItem item;
    2382         181 :             item.size = static_cast<uint32_t>(fbb.GetSize());
    2383         181 :             item.offset = m_writeOffset;
    2384         181 :             item.nodeItem = {psEnvelope.MinX, psEnvelope.MinY, psEnvelope.MaxX,
    2385         181 :                              psEnvelope.MaxY, 0};
    2386         181 :             m_featureItems.emplace_back(std::move(item));
    2387             :         }
    2388         220 :         m_writeOffset += c;
    2389             : 
    2390         220 :         m_featuresCount++;
    2391             : 
    2392         220 :         return OGRERR_NONE;
    2393             :     }
    2394           0 :     catch (const std::bad_alloc &)
    2395             :     {
    2396           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2397             :                  "ICreateFeature: Memory allocation failure");
    2398           0 :         return OGRERR_FAILURE;
    2399             :     }
    2400             : }
    2401             : 
    2402          14 : OGRErr OGRFlatGeobufLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    2403             :                                       bool bForce)
    2404             : {
    2405          14 :     if (m_sExtent.IsInit())
    2406             :     {
    2407          12 :         *psExtent = m_sExtent;
    2408          12 :         return OGRERR_NONE;
    2409             :     }
    2410           2 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    2411             : }
    2412             : 
    2413       66170 : int OGRFlatGeobufLayer::TestCapability(const char *pszCap) const
    2414             : {
    2415       66170 :     if (EQUAL(pszCap, OLCCreateField))
    2416       65740 :         return m_create;
    2417         430 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2418          18 :         return m_create;
    2419         412 :     else if (EQUAL(pszCap, OLCRandomRead))
    2420           4 :         return m_poHeader != nullptr && m_poHeader->index_node_size() > 0;
    2421         408 :     else if (EQUAL(pszCap, OLCIgnoreFields))
    2422           1 :         return true;
    2423         407 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    2424         161 :         return true;
    2425         246 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    2426         180 :         return true;
    2427          66 :     else if (EQUAL(pszCap, OLCZGeometries))
    2428           8 :         return true;
    2429          58 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    2430           6 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
    2431           6 :                m_featuresCount > 0;
    2432          55 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    2433           5 :         return m_sExtent.IsInit();
    2434          50 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2435           0 :         return m_poHeader != nullptr && m_poHeader->index_node_size() > 0;
    2436          50 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2437          30 :         return true;
    2438          20 :     else if (EQUAL(pszCap, OLCFastGetArrowStream))
    2439          13 :         return true;
    2440             :     else
    2441           7 :         return false;
    2442             : }
    2443             : 
    2444         385 : void OGRFlatGeobufLayer::ResetReading()
    2445             : {
    2446         385 :     CPLDebugOnly("FlatGeobuf", "ResetReading");
    2447         385 :     m_offset = m_offsetFeatures;
    2448         385 :     m_bEOF = false;
    2449         385 :     m_featuresPos = 0;
    2450         385 :     m_foundItems.clear();
    2451         385 :     m_featuresCount = m_poHeader ? m_poHeader->features_count() : 0;
    2452         385 :     m_queriedSpatialIndex = false;
    2453         385 :     m_ignoreSpatialFilter = false;
    2454         385 :     m_ignoreAttributeFilter = false;
    2455         385 :     return;
    2456             : }
    2457             : 
    2458         337 : std::string OGRFlatGeobufLayer::GetTempFilePath(const CPLString &fileName,
    2459             :                                                 CSLConstList papszOptions)
    2460             : {
    2461         674 :     const CPLString osDirname(CPLGetPathSafe(fileName.c_str()));
    2462         674 :     const CPLString osBasename(CPLGetBasenameSafe(fileName.c_str()));
    2463         337 :     const char *pszTempDir = CSLFetchNameValue(papszOptions, "TEMPORARY_DIR");
    2464             :     std::string osTempFile =
    2465             :         pszTempDir ? CPLFormFilenameSafe(pszTempDir, osBasename, nullptr)
    2466         651 :         : (STARTS_WITH(fileName, "/vsi") && !STARTS_WITH(fileName, "/vsimem/"))
    2467         337 :             ? CPLGenerateTempFilenameSafe(osBasename)
    2468         674 :             : CPLFormFilenameSafe(osDirname, osBasename, nullptr);
    2469         337 :     osTempFile += "_temp.fgb";
    2470         674 :     return osTempFile;
    2471             : }
    2472             : 
    2473         177 : VSILFILE *OGRFlatGeobufLayer::CreateOutputFile(const CPLString &osFilename,
    2474             :                                                CSLConstList papszOptions,
    2475             :                                                bool isTemp)
    2476             : {
    2477         354 :     std::string osTempFile;
    2478             :     VSILFILE *poFpWrite;
    2479             :     int savedErrno;
    2480         177 :     if (isTemp)
    2481             :     {
    2482         160 :         CPLDebug("FlatGeobuf", "Spatial index requested will write to temp "
    2483             :                                "file and do second pass on close");
    2484         160 :         osTempFile = GetTempFilePath(osFilename, papszOptions);
    2485         160 :         poFpWrite = VSIFOpenL(osTempFile.c_str(), "w+b");
    2486         160 :         savedErrno = errno;
    2487             :         // Unlink it now to avoid stale temporary file if killing the process
    2488             :         // (only works on Unix)
    2489         160 :         VSIUnlink(osTempFile.c_str());
    2490             :     }
    2491             :     else
    2492             :     {
    2493          17 :         CPLDebug("FlatGeobuf",
    2494             :                  "No spatial index will write directly to output");
    2495          17 :         if (!SupportsSeekWhileWriting(osFilename))
    2496           1 :             poFpWrite = VSIFOpenL(osFilename, "wb");
    2497             :         else
    2498          16 :             poFpWrite = VSIFOpenL(osFilename, "w+b");
    2499          17 :         savedErrno = errno;
    2500             :     }
    2501         177 :     if (poFpWrite == nullptr)
    2502             :     {
    2503           2 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s:\n%s",
    2504             :                  osFilename.c_str(), VSIStrerror(savedErrno));
    2505           2 :         return nullptr;
    2506             :     }
    2507         175 :     return poFpWrite;
    2508             : }
    2509             : 
    2510         177 : OGRFlatGeobufLayer *OGRFlatGeobufLayer::Create(
    2511             :     GDALDataset *poDS, const char *pszLayerName, const char *pszFilename,
    2512             :     const OGRSpatialReference *poSpatialRef, OGRwkbGeometryType eGType,
    2513             :     bool bCreateSpatialIndexAtClose, CSLConstList papszOptions)
    2514             : {
    2515         354 :     std::string osTempFile = GetTempFilePath(pszFilename, papszOptions);
    2516             :     VSILFILE *poFpWrite =
    2517         177 :         CreateOutputFile(pszFilename, papszOptions, bCreateSpatialIndexAtClose);
    2518         177 :     if (poFpWrite == nullptr)
    2519           2 :         return nullptr;
    2520             :     OGRFlatGeobufLayer *layer = new OGRFlatGeobufLayer(
    2521             :         poDS, pszLayerName, pszFilename, poSpatialRef, eGType,
    2522         175 :         bCreateSpatialIndexAtClose, poFpWrite, osTempFile, papszOptions);
    2523         175 :     return layer;
    2524             : }
    2525             : 
    2526         153 : OGRFlatGeobufLayer *OGRFlatGeobufLayer::Open(const Header *poHeader,
    2527             :                                              GByte *headerBuf,
    2528             :                                              const char *pszFilename,
    2529             :                                              VSILFILE *poFp, uint64_t offset)
    2530             : {
    2531             :     OGRFlatGeobufLayer *layer =
    2532         153 :         new OGRFlatGeobufLayer(poHeader, headerBuf, pszFilename, poFp, offset);
    2533         153 :     return layer;
    2534             : }
    2535             : 
    2536         153 : OGRFlatGeobufLayer *OGRFlatGeobufLayer::Open(const char *pszFilename,
    2537             :                                              VSILFILE *fp, bool bVerifyBuffers)
    2538             : {
    2539         153 :     uint64_t offset = sizeof(magicbytes);
    2540         153 :     CPLDebugOnly("FlatGeobuf", "Start at offset: %lu",
    2541             :                  static_cast<long unsigned int>(offset));
    2542         153 :     if (VSIFSeekL(fp, offset, SEEK_SET) == -1)
    2543             :     {
    2544           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to get seek in file");
    2545           0 :         return nullptr;
    2546             :     }
    2547             :     uint32_t headerSize;
    2548         153 :     if (VSIFReadL(&headerSize, 4, 1, fp) != 1)
    2549             :     {
    2550           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read header size");
    2551           0 :         return nullptr;
    2552             :     }
    2553         153 :     CPL_LSBPTR32(&headerSize);
    2554         153 :     CPLDebugOnly("FlatGeobuf", "headerSize: %d", headerSize);
    2555         153 :     if (headerSize > header_max_buffer_size)
    2556             :     {
    2557           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2558             :                  "Header size too large (> 10 MB)");
    2559           0 :         return nullptr;
    2560             :     }
    2561             :     std::unique_ptr<GByte, VSIFreeReleaser> buf(
    2562         306 :         static_cast<GByte *>(VSIMalloc(headerSize)));
    2563         153 :     if (buf == nullptr)
    2564             :     {
    2565           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2566             :                  "Failed to allocate memory for header");
    2567           0 :         return nullptr;
    2568             :     }
    2569         153 :     if (VSIFReadL(buf.get(), 1, headerSize, fp) != headerSize)
    2570             :     {
    2571           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read header");
    2572           0 :         return nullptr;
    2573             :     }
    2574         153 :     if (bVerifyBuffers)
    2575             :     {
    2576         152 :         Verifier v(buf.get(), headerSize, 64U, 1000000U, false);
    2577         152 :         const auto ok = VerifyHeaderBuffer(v);
    2578         152 :         if (!ok)
    2579             :         {
    2580           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2581             :                      "Header failed consistency verification");
    2582           0 :             return nullptr;
    2583             :         }
    2584             :     }
    2585         153 :     const auto header = GetHeader(buf.get());
    2586         153 :     offset += 4 + headerSize;
    2587         153 :     CPLDebugOnly("FlatGeobuf", "Add header size + length prefix to offset (%d)",
    2588             :                  4 + headerSize);
    2589             : 
    2590         153 :     const auto featuresCount = header->features_count();
    2591             : 
    2592         153 :     if (featuresCount >
    2593         459 :         std::min(static_cast<uint64_t>(std::numeric_limits<size_t>::max() / 8),
    2594         153 :                  static_cast<uint64_t>(100) * 1000 * 1000 * 1000))
    2595             :     {
    2596           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too many features");
    2597           0 :         return nullptr;
    2598             :     }
    2599             : 
    2600         153 :     const auto index_node_size = header->index_node_size();
    2601         153 :     if (index_node_size > 0)
    2602             :     {
    2603             :         try
    2604             :         {
    2605         122 :             const auto treeSize = PackedRTree::size(featuresCount);
    2606         122 :             CPLDebugOnly("FlatGeobuf", "Tree start at offset (%lu)",
    2607             :                          static_cast<long unsigned int>(offset));
    2608         122 :             offset += treeSize;
    2609         122 :             CPLDebugOnly("FlatGeobuf", "Add tree size to offset (%lu)",
    2610             :                          static_cast<long unsigned int>(treeSize));
    2611             :         }
    2612           0 :         catch (const std::exception &e)
    2613             :         {
    2614           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2615           0 :                      "Failed to calculate tree size: %s", e.what());
    2616           0 :             return nullptr;
    2617             :         }
    2618             :     }
    2619             : 
    2620         153 :     CPLDebugOnly("FlatGeobuf", "Features start at offset (%lu)",
    2621             :                  static_cast<long unsigned int>(offset));
    2622             : 
    2623         153 :     CPLDebugOnly("FlatGeobuf", "Opening OGRFlatGeobufLayer");
    2624         153 :     auto poLayer = OGRFlatGeobufLayer::Open(header, buf.release(), pszFilename,
    2625             :                                             fp, offset);
    2626         153 :     poLayer->VerifyBuffers(bVerifyBuffers);
    2627             : 
    2628         153 :     return poLayer;
    2629             : }
    2630             : 
    2631             : OGRFlatGeobufBaseLayerInterface::~OGRFlatGeobufBaseLayerInterface() = default;

Generated by: LCOV version 1.14