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

Generated by: LCOV version 1.14