LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/flatgeobuf - ogrflatgeobuflayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1178 1478 79.7 %
Date: 2025-01-18 12:42:00 Functions: 37 39 94.9 %

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

Generated by: LCOV version 1.14