LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/arrow - ogrfeatherdrivercore.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 90 99 90.9 %
Date: 2025-10-22 21:46:17 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Feather Translator
       4             :  * Purpose:  Implements OGRFeatherDriver.
       5             :  * Author:   Even Rouault, <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_frmts.h"
      14             : 
      15             : #ifdef PLUGIN_FILENAME
      16             : #include "gdalplugindriverproxy.h"
      17             : #endif
      18             : 
      19             : #include "ogrsf_frmts.h"
      20             : #include "gdal_priv.h"
      21             : 
      22             : #include "ogrfeatherdrivercore.h"
      23             : 
      24             : /************************************************************************/
      25             : /*              OGRFeatherDriverIsArrowIPCStreamBasic()                 */
      26             : /************************************************************************/
      27             : 
      28       51499 : static int OGRFeatherDriverIsArrowIPCStreamBasic(GDALOpenInfo *poOpenInfo)
      29             : {
      30             :     // WARNING: if making changes in that method, reflect them in
      31             :     // IsArrowIPCStream() in ogrfeatherdriver.cpp
      32             : 
      33       51499 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "ARROW_IPC_STREAM:"))
      34           3 :         return true;
      35             : 
      36       51496 :     constexpr int CONTINUATION_SIZE = 4;  // 0xFFFFFFFF
      37       51496 :     constexpr int METADATA_SIZE_SIZE = 4;
      38             : 
      39             :     // See
      40             :     // https://arrow.apache.org/docs/format/Columnar.html#encapsulated-message-format
      41       51496 :     if (poOpenInfo->fpL != nullptr &&
      42        1985 :         poOpenInfo->nHeaderBytes >= CONTINUATION_SIZE + METADATA_SIZE_SIZE &&
      43        1856 :         memcmp(poOpenInfo->pabyHeader, "\xFF\xFF\xFF\xFF", CONTINUATION_SIZE) ==
      44             :             0)
      45             :     {
      46          13 :         const char *pszExt = poOpenInfo->osExtension.c_str();
      47          13 :         if (EQUAL(pszExt, "arrows") || EQUAL(pszExt, "ipc"))
      48           3 :             return true;
      49             : 
      50          10 :         const uint32_t nMetadataSize =
      51          10 :             CPL_LSBUINT32PTR(poOpenInfo->pabyHeader + CONTINUATION_SIZE);
      52          10 :         if (strcmp(poOpenInfo->pszFilename, "/vsistdin/") == 0)
      53             :         {
      54             :             // Padding after metadata and before body is not necessarily present
      55             :             // but the body must be at least 4 bytes
      56           1 :             constexpr int PADDING_MAX_SIZE = 4;
      57             : 
      58             :             // /vsistdin/ cannot seek back beyond first MB
      59           1 :             if (nMetadataSize >
      60             :                 1024 * 1024 -
      61             :                     (CONTINUATION_SIZE + METADATA_SIZE_SIZE + PADDING_MAX_SIZE))
      62             :             {
      63           1 :                 if (poOpenInfo->IsSingleAllowedDriver("ARROW"))
      64           1 :                     return true;
      65           0 :                 return GDAL_IDENTIFY_UNKNOWN;
      66             :             }
      67           0 :             const int nSizeToRead = CONTINUATION_SIZE + METADATA_SIZE_SIZE +
      68           0 :                                     nMetadataSize + PADDING_MAX_SIZE;
      69           0 :             if (!poOpenInfo->TryToIngest(nSizeToRead))
      70             :             {
      71           0 :                 return false;
      72             :             }
      73             : 
      74           0 :             if (poOpenInfo->IsSingleAllowedDriver("ARROW"))
      75           0 :                 return true;
      76           0 :             return GDAL_IDENTIFY_UNKNOWN;
      77             :         }
      78             : 
      79           9 :         VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
      80           9 :         const auto nFileSize = VSIFTellL(poOpenInfo->fpL);
      81           9 :         VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
      82           9 :         if (nMetadataSize >
      83           9 :             nFileSize - (CONTINUATION_SIZE + METADATA_SIZE_SIZE))
      84           4 :             return false;
      85             : 
      86           5 :         if (poOpenInfo->IsSingleAllowedDriver("ARROW"))
      87           0 :             return true;
      88           5 :         return GDAL_IDENTIFY_UNKNOWN;
      89             :     }
      90       51483 :     return false;
      91             : }
      92             : 
      93             : /************************************************************************/
      94             : /*                    OGRFeatherDriverIsArrowFileFormat()               */
      95             : /************************************************************************/
      96             : 
      97             : template <size_t N> constexpr int constexpr_length(const char (&)[N])
      98             : {
      99             :     return static_cast<int>(N - 1);
     100             : }
     101             : 
     102       52023 : bool OGRFeatherDriverIsArrowFileFormat(GDALOpenInfo *poOpenInfo)
     103             : {
     104             :     // See https://arrow.apache.org/docs/format/Columnar.html#ipc-file-format
     105       52023 :     bool bRet = false;
     106       52023 :     constexpr const char SIGNATURE[] = "ARROW1";
     107       52023 :     constexpr int SIGNATURE_SIZE = constexpr_length(SIGNATURE);
     108             :     static_assert(SIGNATURE_SIZE == 6, "SIGNATURE_SIZE == 6");
     109       52023 :     constexpr int SIGNATURE_PLUS_PADDING = SIGNATURE_SIZE + 2;
     110       52023 :     constexpr int FOOTERSIZE_SIZE = 4;
     111       52023 :     if (poOpenInfo->fpL != nullptr &&
     112        2512 :         poOpenInfo->nHeaderBytes >=
     113        2353 :             SIGNATURE_PLUS_PADDING + FOOTERSIZE_SIZE + SIGNATURE_SIZE &&
     114        2353 :         memcmp(poOpenInfo->pabyHeader, SIGNATURE, SIGNATURE_SIZE) == 0)
     115             :     {
     116        1072 :         VSIFSeekL(poOpenInfo->fpL, 0, SEEK_END);
     117        1072 :         const auto nFileSize = VSIFTellL(poOpenInfo->fpL);
     118        1072 :         VSIFSeekL(poOpenInfo->fpL,
     119             :                   nFileSize - (FOOTERSIZE_SIZE + SIGNATURE_SIZE), SEEK_SET);
     120        1072 :         uint32_t nFooterSize = 0;
     121             :         static_assert(sizeof(nFooterSize) == FOOTERSIZE_SIZE,
     122             :                       "sizeof(nFooterSize) == FOOTERSIZE_SIZE");
     123        1072 :         VSIFReadL(&nFooterSize, 1, sizeof(nFooterSize), poOpenInfo->fpL);
     124        1072 :         CPL_LSBPTR32(&nFooterSize);
     125        1072 :         unsigned char abyTrailingBytes[SIGNATURE_SIZE] = {0};
     126        1072 :         VSIFReadL(&abyTrailingBytes[0], 1, SIGNATURE_SIZE, poOpenInfo->fpL);
     127        2144 :         bRet = memcmp(abyTrailingBytes, SIGNATURE, SIGNATURE_SIZE) == 0 &&
     128        1072 :                nFooterSize < nFileSize;
     129        1072 :         VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
     130             :     }
     131       52023 :     return bRet;
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /*                             Identify()                               */
     136             : /************************************************************************/
     137             : 
     138       51500 : int OGRFeatherDriverIdentify(GDALOpenInfo *poOpenInfo)
     139             : {
     140       51500 :     if (STARTS_WITH(poOpenInfo->pszFilename, "gdalvsi://"))
     141             :     {
     142           1 :         GDALOpenInfo oOpenInfo(poOpenInfo->pszFilename + strlen("gdalvsi://"),
     143           2 :                                poOpenInfo->nOpenFlags);
     144           1 :         return OGRFeatherDriverIdentify(&oOpenInfo);
     145             :     }
     146             : 
     147       51499 :     int ret = OGRFeatherDriverIsArrowIPCStreamBasic(poOpenInfo);
     148       51499 :     if (ret == GDAL_IDENTIFY_TRUE || ret == GDAL_IDENTIFY_UNKNOWN)
     149          12 :         return ret;
     150       51487 :     return OGRFeatherDriverIsArrowFileFormat(poOpenInfo);
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                OGRFeatherDriverSetCommonMetadata()                   */
     155             : /************************************************************************/
     156             : 
     157        1769 : void OGRFeatherDriverSetCommonMetadata(GDALDriver *poDriver)
     158             : {
     159        1769 :     poDriver->SetDescription(DRIVER_NAME);
     160        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     161        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     162        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     163        1769 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     164        1769 :                               "(Geo)Arrow IPC File Format / Stream");
     165        1769 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "arrow feather arrows ipc");
     166        1769 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     167        1769 :                               "drivers/vector/feather.html");
     168        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     169        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
     170        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     171        1769 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     172        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_REOPEN_AFTER_WRITE_REQUIRED, "YES");
     173        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_CAN_READ_AFTER_DELETE, "YES");
     174             : 
     175        1769 :     poDriver->SetMetadataItem(
     176             :         GDAL_DMD_CREATIONFIELDDATATYPES,
     177             :         "Integer Integer64 Real String Date Time DateTime "
     178        1769 :         "Binary IntegerList Integer64List RealList StringList");
     179        1769 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
     180        1769 :                               "Boolean Int16 Float32 JSON UUID");
     181        1769 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
     182             :                               "WidthPrecision Nullable "
     183        1769 :                               "Comment AlternativeName Domain");
     184             : 
     185        1769 :     poDriver->pfnIdentify = OGRFeatherDriverIdentify;
     186        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     187        1769 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
     188        1769 : }
     189             : 
     190             : /************************************************************************/
     191             : /*                  DeclareDeferredOGRArrowPlugin()                     */
     192             : /************************************************************************/
     193             : 
     194             : #ifdef PLUGIN_FILENAME
     195        2038 : void DeclareDeferredOGRArrowPlugin()
     196             : {
     197        2038 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
     198             :     {
     199         283 :         return;
     200             :     }
     201        1755 :     auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     202             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     203             :     poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     204             :                               PLUGIN_INSTALLATION_MESSAGE);
     205             : #endif
     206        1755 :     OGRFeatherDriverSetCommonMetadata(poDriver);
     207        1755 :     GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     208             : }
     209             : #endif

Generated by: LCOV version 1.14