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

Generated by: LCOV version 1.14