LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddffield.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 97 147 66.0 %
Date: 2026-05-29 23:25:07 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISO 8211 Access
       4             :  * Purpose:  Implements the DDFField class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2026, Even Rouault
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "iso8211.h"
      16             : 
      17             : #include <cstdio>
      18             : #include <cstdlib>
      19             : 
      20             : #include <algorithm>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_enumerate.h"
      24             : 
      25             : // Note, we implement no constructor for this class to make instantiation
      26             : // cheaper.  It is required that the Initialize() be called before anything
      27             : // else.
      28             : 
      29             : /************************************************************************/
      30             : /*                             Initialize()                             */
      31             : /************************************************************************/
      32             : 
      33      292437 : bool DDFField::Initialize(const DDFFieldDefn *poDefnIn, const char *pachDataIn,
      34             :                           int nDataSizeIn, bool bInitializeParts)
      35             : 
      36             : {
      37      292437 :     pachData = pachDataIn;
      38      292437 :     nDataSize = nDataSizeIn;
      39      292437 :     poDefn = poDefnIn;
      40             : 
      41      292437 :     return bInitializeParts ? InitializeParts() : true;
      42             : }
      43             : 
      44             : /************************************************************************/
      45             : /*                          InitializeParts()                           */
      46             : /************************************************************************/
      47             : 
      48      271491 : bool DDFField::InitializeParts()
      49             : {
      50      271491 :     const bool bCreateParts = apoFieldParts.empty();
      51      271491 :     const size_t nDefnPartsCount = poDefn->GetParts().size();
      52      271491 :     CPLAssert(bCreateParts || apoFieldParts.size() == nDefnPartsCount);
      53             : 
      54      271491 :     int iOffset = 0;
      55       33468 :     for (const auto &[iPart, poFieldDefnPart] :
      56      304959 :          cpl::enumerate(poDefn->GetParts()))
      57             :     {
      58       16734 :         if (iOffset > nDataSize)
      59             :         {
      60           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      61             :                      "Not enough bytes for part %d of field %s",
      62           0 :                      static_cast<int>(iPart), poDefn->GetName());
      63           0 :             return false;
      64             :         }
      65       16734 :         const int iOffsetBefore = iOffset;
      66       16734 :         if (nDataSize > 0)
      67             :         {
      68       16734 :             if (iPart + 1 < nDefnPartsCount)
      69             :             {
      70      102470 :                 for (const auto &poThisSFDefn : poFieldDefnPart->GetSubfields())
      71             :                 {
      72       94103 :                     int nBytesConsumed = 0;
      73       94103 :                     poThisSFDefn->GetDataLength(pachData + iOffset,
      74       94103 :                                                 nDataSize - iOffset,
      75             :                                                 &nBytesConsumed);
      76             : 
      77       94103 :                     iOffset += nBytesConsumed;
      78             :                 }
      79             :             }
      80             :             else
      81             :             {
      82        8367 :                 iOffset = nDataSize;
      83        8367 :                 if (pachData[nDataSize - 1] == DDF_FIELD_TERMINATOR)
      84        8367 :                     --iOffset;
      85             :             }
      86             :         }
      87             : 
      88       16734 :         if (bCreateParts)
      89             :         {
      90        7560 :             auto poFieldPart = std::make_unique<DDFField>();
      91        7560 :             poFieldPart->Initialize(poFieldDefnPart.get(),
      92        3780 :                                     pachData + iOffsetBefore,
      93             :                                     iOffset - iOffsetBefore, false);
      94        3780 :             apoFieldParts.push_back(std::move(poFieldPart));
      95             :         }
      96             :         else
      97             :         {
      98       25908 :             apoFieldParts[iPart]->Initialize(poFieldDefnPart.get(),
      99       12954 :                                              pachData + iOffsetBefore,
     100             :                                              iOffset - iOffsetBefore, false);
     101             :         }
     102             :     }
     103             : 
     104      271491 :     return true;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                                Dump()                                */
     109             : /************************************************************************/
     110             : 
     111             : /**
     112             :  * Write out field contents to debugging file.
     113             :  *
     114             :  * A variety of information about this field, and all its
     115             :  * subfields is written to the given debugging file handle.  Note that
     116             :  * field definition information (ala DDFFieldDefn) isn't written.
     117             :  *
     118             :  * @param fp The standard IO file handle to write to.  i.e. stderr
     119             :  */
     120             : 
     121           0 : void DDFField::Dump(FILE *fp, int nNestingLevel) const
     122             : 
     123             : {
     124           0 :     std::string osIndent;
     125           0 :     for (int i = 0; i < nNestingLevel; ++i)
     126           0 :         osIndent += "  ";
     127             : 
     128             : #define Print(...)                                                             \
     129             :     do                                                                         \
     130             :     {                                                                          \
     131             :         fprintf(fp, "%s", osIndent.c_str());                                   \
     132             :         fprintf(fp, __VA_ARGS__);                                              \
     133             :     } while (0)
     134             : 
     135           0 :     int nMaxRepeat = 8;
     136             : 
     137           0 :     const char *pszDDF_MAXDUMP = getenv("DDF_MAXDUMP");
     138           0 :     if (pszDDF_MAXDUMP != nullptr)
     139           0 :         nMaxRepeat = atoi(pszDDF_MAXDUMP);
     140             : 
     141           0 :     Print("DDFField:\n");
     142           0 :     Print("    Tag = `%s'\n", poDefn->GetName());
     143           0 :     Print("    DataSize = %d\n", nDataSize);
     144             : 
     145           0 :     if (!apoFieldParts.empty())
     146             :     {
     147           0 :         for (const auto &poPart : apoFieldParts)
     148             :         {
     149           0 :             poPart->Dump(fp, nNestingLevel + 1);
     150             :         }
     151           0 :         return;
     152             :     }
     153             : 
     154           0 :     Print("    Data = `");
     155           0 :     for (int i = 0; i < std::min(nDataSize, 40); i++)
     156             :     {
     157           0 :         if (pachData[i] < 32 || pachData[i] > 126)
     158           0 :             fprintf(fp, "\\%02X",
     159           0 :                     reinterpret_cast<const unsigned char *>(pachData)[i]);
     160             :         else
     161           0 :             fprintf(fp, "%c", pachData[i]);
     162             :     }
     163             : 
     164           0 :     if (nDataSize > 40)
     165           0 :         fprintf(fp, "...");
     166           0 :     fprintf(fp, "'\n");
     167             : 
     168             :     /* -------------------------------------------------------------------- */
     169             :     /*      dump the data of the subfields.                                 */
     170             :     /* -------------------------------------------------------------------- */
     171           0 :     int iOffset = 0;
     172           0 :     const int nRepeatCount = GetRepeatCount();
     173           0 :     for (int nLoopCount = 0; nLoopCount < nRepeatCount; nLoopCount++)
     174             :     {
     175           0 :         if (nLoopCount > nMaxRepeat)
     176             :         {
     177           0 :             Print("     ...\n");
     178           0 :             break;
     179             :         }
     180             : 
     181           0 :         for (const auto &poThisSFDefn : poDefn->GetSubfields())
     182             :         {
     183           0 :             poThisSFDefn->DumpData(pachData + iOffset, nDataSize - iOffset, fp);
     184             : 
     185           0 :             int nBytesConsumed = 0;
     186           0 :             poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
     187             :                                         &nBytesConsumed);
     188             : 
     189           0 :             iOffset += nBytesConsumed;
     190             :         }
     191             :     }
     192             : }
     193             : 
     194             : /************************************************************************/
     195             : /*                          GetSubfieldData()                           */
     196             : /************************************************************************/
     197             : 
     198             : /**
     199             :  * Fetch raw data pointer for a particular subfield of this field.
     200             :  *
     201             :  * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the
     202             :  * DDFFieldDefn corresponding with this field.  This is normally done
     203             :  * once before reading any records.  This method involves a series of
     204             :  * calls to DDFSubfield::GetDataLength() in order to track through the
     205             :  * DDFField data to that belonging to the requested subfield.  This can
     206             :  * be relatively expensive.<p>
     207             :  *
     208             :  * @param poSFDefn The definition of the subfield for which the raw
     209             :  * data pointer is desired.
     210             :  * @param pnMaxBytes The maximum number of bytes that can be accessed from
     211             :  * the returned data pointer is placed in this int, unless it is NULL.
     212             :  * @param iSubfieldIndex The instance of this subfield to fetch.  Use zero
     213             :  * (the default) for the first instance.
     214             :  *
     215             :  * @return A pointer into the DDFField's data that belongs to the subfield.
     216             :  * This returned pointer is invalidated by the next record read
     217             :  * (DDFRecord::ReadRecord()) and the returned pointer should not be freed
     218             :  * by the application.
     219             :  */
     220             : 
     221     1002180 : const char *DDFField::GetSubfieldData(const DDFSubfieldDefn *poSFDefn,
     222             :                                       int *pnMaxBytes, int iSubfieldIndex) const
     223             : 
     224             : {
     225     1002180 :     if (poSFDefn == nullptr)
     226           0 :         return nullptr;
     227             : 
     228     1002180 :     int iOffset = 0;
     229     1002180 :     if (iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0)
     230             :     {
     231      389371 :         iOffset = poDefn->GetFixedWidth() * iSubfieldIndex;
     232      389371 :         iSubfieldIndex = 0;
     233             :     }
     234             : 
     235     1203230 :     while (iSubfieldIndex >= 0)
     236             :     {
     237     2774340 :         for (const auto &poThisSFDefn : poDefn->GetSubfields())
     238             :         {
     239     2573300 :             if (nDataSize <= iOffset)
     240             :             {
     241           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     242             :                          "Invalid data size for subfield %s of %s",
     243           0 :                          poThisSFDefn->GetName(), poDefn->GetName());
     244     1002180 :                 return nullptr;
     245             :             }
     246             : 
     247     2573300 :             if (poThisSFDefn.get() == poSFDefn && iSubfieldIndex == 0)
     248             :             {
     249     1002180 :                 if (pnMaxBytes != nullptr)
     250     1002180 :                     *pnMaxBytes = nDataSize - iOffset;
     251             : 
     252     1002180 :                 return pachData + iOffset;
     253             :             }
     254             : 
     255     1571110 :             int nBytesConsumed = 0;
     256     1571110 :             poThisSFDefn->GetDataLength(pachData + iOffset, nDataSize - iOffset,
     257             :                                         &nBytesConsumed);
     258     1571110 :             iOffset += nBytesConsumed;
     259             :         }
     260             : 
     261      201043 :         iSubfieldIndex--;
     262             :     }
     263             : 
     264             :     // We didn't find our target subfield or instance!
     265           0 :     return nullptr;
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                           GetRepeatCount()                           */
     270             : /************************************************************************/
     271             : 
     272             : /**
     273             :  * How many times do the subfields of this record repeat?  This
     274             :  * will always be one for non-repeating fields.
     275             :  *
     276             :  * @return The number of times that the subfields of this record occur
     277             :  * in this record.  This will be one for non-repeating fields.
     278             :  *
     279             :  * @see <a href="example.html">8211view example program</a>
     280             :  * for a demonstration of handling repeated fields properly.
     281             :  */
     282             : 
     283      125715 : int DDFField::GetRepeatCount() const
     284             : 
     285             : {
     286      125715 :     if (!apoFieldParts.empty())
     287         982 :         return 0;
     288             : 
     289      124733 :     if (!poDefn->IsRepeating())
     290       10150 :         return 1;
     291             : 
     292             :     /* -------------------------------------------------------------------- */
     293             :     /*      The occurrence count depends on how many copies of this         */
     294             :     /*      field's list of subfields can fit into the data space.          */
     295             :     /* -------------------------------------------------------------------- */
     296      114583 :     if (poDefn->GetFixedWidth())
     297             :     {
     298       95443 :         return nDataSize / poDefn->GetFixedWidth();
     299             :     }
     300             : 
     301             :     /* -------------------------------------------------------------------- */
     302             :     /*      Note that it may be legal to have repeating variable width      */
     303             :     /*      subfields, but I don't have any samples, so I ignore it for     */
     304             :     /*      now.                                                            */
     305             :     /*                                                                      */
     306             :     /*      The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating    */
     307             :     /*      variable length field, but the count is one, so it isn't        */
     308             :     /*      much value for testing.                                         */
     309             :     /* -------------------------------------------------------------------- */
     310       19140 :     int iOffset = 0;
     311       19140 :     int iRepeatCount = 1;
     312             : 
     313             :     while (true)
     314             :     {
     315       58165 :         const int iOffsetBefore = iOffset;
     316      193252 :         for (const auto &poThisSFDefn : poDefn->GetSubfields())
     317             :         {
     318      136458 :             int nBytesConsumed = 0;
     319      136458 :             if (poThisSFDefn->GetWidth() > nDataSize - iOffset)
     320        1371 :                 nBytesConsumed = poThisSFDefn->GetWidth();
     321             :             else
     322      135087 :                 poThisSFDefn->GetDataLength(
     323      135087 :                     pachData + iOffset, nDataSize - iOffset, &nBytesConsumed);
     324             : 
     325      136458 :             iOffset += nBytesConsumed;
     326      136458 :             if (iOffset > nDataSize)
     327        1371 :                 return iRepeatCount - 1;
     328             :         }
     329       56794 :         if (iOffset == iOffsetBefore)
     330             :         {
     331             :             // Should probably emit error
     332           0 :             return iRepeatCount - 1;
     333             :         }
     334             : 
     335       56794 :         if (iOffset > nDataSize - 2)
     336       17769 :             return iRepeatCount;
     337             : 
     338       39025 :         iRepeatCount++;
     339       39025 :     }
     340             : }
     341             : 
     342             : /************************************************************************/
     343             : /*                          GetInstanceData()                           */
     344             : /************************************************************************/
     345             : 
     346             : /**
     347             :  * Get field instance data and size.
     348             :  *
     349             :  * The returned data pointer and size values are suitable for use with
     350             :  * DDFRecord::SetFieldRaw().
     351             :  *
     352             :  * @param nInstance a value from 0 to GetRepeatCount()-1.
     353             :  * @param pnInstanceSize a location to put the size (in bytes) of the
     354             :  * field instance data returned.  This size will include the unit terminator
     355             :  * (if any), but not the field terminator.  This size pointer may be NULL
     356             :  * if not needed.
     357             :  *
     358             :  * @return the data pointer, or NULL on error.
     359             :  */
     360             : 
     361        8927 : const char *DDFField::GetInstanceData(int nInstance, int *pnInstanceSize)
     362             : 
     363             : {
     364        8927 :     const int nRepeatCount = GetRepeatCount();
     365        8927 :     if (!apoFieldParts.empty() && nInstance == 0)
     366             :     {
     367         491 :         const char *pachWrkData = GetData();
     368         491 :         if (pnInstanceSize != nullptr)
     369         491 :             *pnInstanceSize = GetDataSize();
     370         491 :         return pachWrkData;
     371             :     }
     372             : 
     373        8436 :     if (nInstance < 0 || nInstance >= nRepeatCount)
     374           0 :         return nullptr;
     375             : 
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*      Special case for fields without subfields (like "0001").  We    */
     378             :     /*      don't currently handle repeating simple fields.                 */
     379             :     /* -------------------------------------------------------------------- */
     380        8436 :     if (poDefn->GetSubfieldCount() == 0)
     381             :     {
     382           0 :         const char *pachWrkData = GetData();
     383           0 :         if (pnInstanceSize != nullptr)
     384           0 :             *pnInstanceSize = GetDataSize();
     385           0 :         return pachWrkData;
     386             :     }
     387             : 
     388             :     /* -------------------------------------------------------------------- */
     389             :     /*      Get a pointer to the start of the existing data for this        */
     390             :     /*      iteration of the field.                                         */
     391             :     /* -------------------------------------------------------------------- */
     392        8436 :     int nBytesRemaining1 = 0;
     393        8436 :     int nBytesRemaining2 = 0;
     394             :     const DDFSubfieldDefn *poFirstSubfield =
     395        8436 :         poDefn->GetSubfields().front().get();
     396             : 
     397             :     const char *pachWrkData =
     398        8436 :         GetSubfieldData(poFirstSubfield, &nBytesRemaining1, nInstance);
     399        8436 :     if (pachWrkData == nullptr)
     400           0 :         return nullptr;
     401             : 
     402             :     /* -------------------------------------------------------------------- */
     403             :     /*      Figure out the size of the entire field instance, including     */
     404             :     /*      unit terminators, but not any trailing field terminator.        */
     405             :     /* -------------------------------------------------------------------- */
     406        8436 :     if (pnInstanceSize != nullptr)
     407             :     {
     408             :         const DDFSubfieldDefn *poLastSubfield =
     409         124 :             poDefn->GetSubfields().back().get();
     410             : 
     411             :         const char *pachLastData =
     412         124 :             GetSubfieldData(poLastSubfield, &nBytesRemaining2, nInstance);
     413         124 :         if (pachLastData == nullptr)
     414           0 :             return nullptr;
     415             : 
     416         124 :         int nLastSubfieldWidth = 0;
     417         124 :         poLastSubfield->GetDataLength(pachLastData, nBytesRemaining2,
     418             :                                       &nLastSubfieldWidth);
     419             : 
     420         124 :         *pnInstanceSize =
     421         124 :             nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth);
     422             :     }
     423             : 
     424        8436 :     return pachWrkData;
     425             : }

Generated by: LCOV version 1.14