LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddfrecord.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 514 666 77.2 %
Date: 2026-06-19 21:24:00 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISO 8211 Access
       4             :  * Purpose:  Implements the DDFRecord class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "iso8211.h"
      16             : 
      17             : #include <cstddef>
      18             : #include <cstdio>
      19             : #include <cstring>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_vsi.h"
      24             : 
      25             : constexpr int nLeaderSize = 24;
      26             : 
      27             : /************************************************************************/
      28             : /*                             DDFRecord()                              */
      29             : /************************************************************************/
      30             : 
      31       31953 : DDFRecord::DDFRecord(DDFModule *poModuleIn)
      32       31953 :     : poModule(poModuleIn), _sizeFieldTag(poModuleIn->GetSizeFieldTag())
      33             : {
      34       31953 : }
      35             : 
      36             : /************************************************************************/
      37             : /*                             ~DDFRecord()                             */
      38             : /************************************************************************/
      39             : 
      40       31953 : DDFRecord::~DDFRecord()
      41             : 
      42             : {
      43       31953 :     Clear();
      44       31953 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                             GetFields()                              */
      48             : /************************************************************************/
      49             : 
      50             : /**
      51             :  * Return all fields of the specified name.
      52             :  */
      53             : 
      54             : std::vector<const DDFField *>
      55       24392 : DDFRecord::GetFields(const char *pszFieldName) const
      56             : {
      57       24392 :     std::vector<const DDFField *> res;
      58      100034 :     for (auto &field : apoFields)
      59             :     {
      60       75642 :         if (strcmp(field->GetFieldDefn()->GetName(), pszFieldName) == 0)
      61       10636 :             res.push_back(field.get());
      62             :     }
      63       24392 :     return res;
      64             : }
      65             : 
      66             : /************************************************************************/
      67             : /*                             GetFields()                              */
      68             : /************************************************************************/
      69             : 
      70             : /**
      71             :  * Return all fields of the specified name.
      72             :  */
      73             : 
      74        1052 : std::vector<DDFField *> DDFRecord::GetFields(const char *pszFieldName)
      75             : {
      76        1052 :     std::vector<DDFField *> res;
      77        4629 :     for (auto &field : apoFields)
      78             :     {
      79        3577 :         if (strcmp(field->GetFieldDefn()->GetName(), pszFieldName) == 0)
      80         338 :             res.push_back(field.get());
      81             :     }
      82        1052 :     return res;
      83             : }
      84             : 
      85             : /************************************************************************/
      86             : /*                                Dump()                                */
      87             : /************************************************************************/
      88             : 
      89             : /**
      90             :  * Write out record contents to debugging file.
      91             :  *
      92             :  * A variety of information about this record, and all its fields and
      93             :  * subfields is written to the given debugging file handle.  Note that
      94             :  * field definition information (ala DDFFieldDefn) isn't written.
      95             :  *
      96             :  * @param fp The standard IO file handle to write to.  i.e. stderr
      97             :  */
      98             : 
      99           0 : void DDFRecord::Dump(FILE *fp, int nNestingLevel) const
     100             : 
     101             : {
     102           0 :     std::string osIndent;
     103           0 :     for (int i = 0; i < nNestingLevel; ++i)
     104           0 :         osIndent += "  ";
     105             : 
     106             : #define Print(...)                                                             \
     107             :     do                                                                         \
     108             :     {                                                                          \
     109             :         fprintf(fp, "%s", osIndent.c_str());                                   \
     110             :         fprintf(fp, __VA_ARGS__);                                              \
     111             :     } while (0)
     112             : 
     113           0 :     Print("DDFRecord:\n");
     114           0 :     Print("    bReuseHeader = %d\n", bReuseHeader);
     115           0 :     Print("    nDataSize = %d\n", GetDataSize());
     116           0 :     Print("    _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n",
     117             :           _sizeFieldLength, _sizeFieldPos, _sizeFieldTag);
     118             : 
     119           0 :     for (const auto &poField : apoFields)
     120             :     {
     121           0 :         poField->Dump(fp, nNestingLevel + 1);
     122             :     }
     123           0 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                                Read()                                */
     127             : /*                                                                      */
     128             : /*      Read a record of data from the file, and parse the header to    */
     129             : /*      build a field list for the record (or reuse the existing one    */
     130             : /*      if reusing headers).  It is expected that the file pointer      */
     131             : /*      will be positioned at the beginning of a data record.  It is    */
     132             : /*      the DDFModule's responsibility to do so.                        */
     133             : /*                                                                      */
     134             : /*      This method should only be called by the DDFModule class.       */
     135             : /************************************************************************/
     136             : 
     137       31861 : int DDFRecord::Read()
     138             : 
     139             : {
     140             :     /* -------------------------------------------------------------------- */
     141             :     /*      Redefine the record on the basis of the header if needed.       */
     142             :     /*      As a side effect this will read the data for the record as well.*/
     143             :     /* -------------------------------------------------------------------- */
     144       31861 :     if (!bReuseHeader)
     145             :     {
     146       31861 :         return ReadHeader();
     147             :     }
     148           0 :     if (nFieldOffset < 0)
     149           0 :         return FALSE;
     150             : 
     151             :     /* -------------------------------------------------------------------- */
     152             :     /*      Otherwise we read just the data and carefully overlay it on     */
     153             :     /*      the previous records data without disturbing the rest of the    */
     154             :     /*      record.                                                         */
     155             :     /* -------------------------------------------------------------------- */
     156             :     size_t nReadBytes;
     157             : 
     158           0 :     CPLAssert(nFieldOffset <= static_cast<int>(osData.size()));
     159           0 :     nReadBytes = VSIFReadL(osData.data() + nFieldOffset, 1,
     160           0 :                            osData.size() - nFieldOffset, poModule->GetFP());
     161           0 :     if (nReadBytes != osData.size() - nFieldOffset && nReadBytes == 0 &&
     162           0 :         VSIFEofL(poModule->GetFP()))
     163             :     {
     164           0 :         return FALSE;
     165             :     }
     166           0 :     else if (nReadBytes != osData.size() - nFieldOffset)
     167             :     {
     168           0 :         CPLError(CE_Failure, CPLE_FileIO, "Data record is short on DDF file.");
     169             : 
     170           0 :         return FALSE;
     171             :     }
     172             : 
     173             :     // notdef: eventually we may have to do something at this point to
     174             :     // notify the DDFField's that their data values have changed.
     175             : 
     176           0 :     return TRUE;
     177             : }
     178             : 
     179             : /************************************************************************/
     180             : /*                               Write()                                */
     181             : /************************************************************************/
     182             : 
     183             : /**
     184             :  * Write record out to module.
     185             :  *
     186             :  * This method writes the current record to the module to which it is
     187             :  * attached.  Normally this would be at the end of the file, and only used
     188             :  * for modules newly created with DDFModule::Create().  Rewriting existing
     189             :  * records is not supported at this time.  Calling Write() multiple times
     190             :  * on a DDFRecord will result it multiple copies being written at the end of
     191             :  * the module.
     192             :  *
     193             :  * @return TRUE on success or FALSE on failure.
     194             :  */
     195             : 
     196        1355 : int DDFRecord::Write()
     197             : 
     198             : {
     199        1355 :     ResetDirectory();
     200             : 
     201             :     /* -------------------------------------------------------------------- */
     202             :     /*      Prepare leader.                                                 */
     203             :     /* -------------------------------------------------------------------- */
     204             :     char szLeader[nLeaderSize + 1];
     205             : 
     206        1355 :     memset(szLeader, ' ', nLeaderSize);
     207             : 
     208        1355 :     snprintf(szLeader + 0, sizeof(szLeader) - 0, "%05d",
     209        1355 :              static_cast<int>(osData.size() + nLeaderSize));
     210        1355 :     szLeader[5] = ' ';
     211        1355 :     szLeader[6] = 'D';
     212             : 
     213        1355 :     snprintf(szLeader + 12, sizeof(szLeader) - 12, "%05d",
     214        1355 :              static_cast<int>(nFieldOffset + nLeaderSize));
     215        1355 :     szLeader[17] = ' ';
     216             : 
     217        1355 :     szLeader[20] = static_cast<char>('0' + _sizeFieldLength);
     218        1355 :     szLeader[21] = static_cast<char>('0' + _sizeFieldPos);
     219        1355 :     szLeader[22] = '0';
     220        1355 :     szLeader[23] = static_cast<char>('0' + _sizeFieldTag);
     221             : 
     222             :     /* notdef: lots of stuff missing */
     223             : 
     224             :     /* -------------------------------------------------------------------- */
     225             :     /*      Write the leader.                                               */
     226             :     /* -------------------------------------------------------------------- */
     227        1355 :     int bRet = VSIFWriteL(szLeader, nLeaderSize, 1, poModule->GetFP()) > 0;
     228             : 
     229             :     /* -------------------------------------------------------------------- */
     230             :     /*      Write the remainder of the record.                              */
     231             :     /* -------------------------------------------------------------------- */
     232        1355 :     bRet &= VSIFWriteL(osData.data(), osData.size(), 1, poModule->GetFP()) > 0;
     233             : 
     234        1355 :     return bRet ? TRUE : FALSE;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                               Clear()                                */
     239             : /*                                                                      */
     240             : /*      Clear any information associated with the last header in        */
     241             : /*      preparation for reading a new header.                           */
     242             : /************************************************************************/
     243             : 
     244       63814 : void DDFRecord::Clear()
     245             : 
     246             : {
     247       63814 :     apoFields.clear();
     248       63814 :     osData.clear();
     249       63814 :     bReuseHeader = FALSE;
     250       63814 : }
     251             : 
     252             : /************************************************************************/
     253             : /*                             ReadHeader()                             */
     254             : /*                                                                      */
     255             : /*      This perform the header reading and parsing job for the         */
     256             : /*      Read() method.  It reads the header, and builds a field         */
     257             : /*      list.                                                           */
     258             : /************************************************************************/
     259             : 
     260       31861 : int DDFRecord::ReadHeader()
     261             : 
     262             : {
     263             :     /* -------------------------------------------------------------------- */
     264             :     /*      Clear any existing information.                                 */
     265             :     /* -------------------------------------------------------------------- */
     266       31861 :     Clear();
     267             : 
     268             :     /* -------------------------------------------------------------------- */
     269             :     /*      Read the 24 byte leader.                                        */
     270             :     /* -------------------------------------------------------------------- */
     271             :     char achLeader[nLeaderSize];
     272             :     int nReadBytes;
     273             : 
     274       31861 :     nReadBytes = static_cast<int>(
     275       31861 :         VSIFReadL(achLeader, 1, nLeaderSize, poModule->GetFP()));
     276       31861 :     if (nReadBytes == 0 && VSIFEofL(poModule->GetFP()))
     277             :     {
     278         563 :         nFieldOffset = -1;
     279         563 :         return FALSE;
     280             :     }
     281             :     // The ASRP and USRP specifications mentions that 0x5E / ^ character can be
     282             :     // used as a padding byte so that the file size is a multiple of 8192.
     283       31298 :     else if (achLeader[0] == '^')
     284             :     {
     285           8 :         nFieldOffset = -1;
     286           8 :         return FALSE;
     287             :     }
     288       31290 :     else if (nReadBytes != static_cast<int>(nLeaderSize))
     289             :     {
     290           0 :         CPLError(CE_Failure, CPLE_FileIO, "Leader is short on DDF file.");
     291           0 :         nFieldOffset = -1;
     292           0 :         return FALSE;
     293             :     }
     294             : 
     295             :     /* -------------------------------------------------------------------- */
     296             :     /*      Extract information from leader.                                */
     297             :     /* -------------------------------------------------------------------- */
     298             :     int _recLength, _fieldAreaStart;
     299             :     char _leaderIden;
     300             : 
     301       31290 :     _recLength = DDFScanInt(achLeader + 0, 5);
     302       31290 :     _leaderIden = achLeader[6];
     303       31290 :     _fieldAreaStart = DDFScanInt(achLeader + 12, 5);
     304             : 
     305       31290 :     _sizeFieldLength = achLeader[20] - '0';
     306       31290 :     _sizeFieldPos = achLeader[21] - '0';
     307       31290 :     _sizeFieldTag = achLeader[23] - '0';
     308             : 
     309       31290 :     if (_sizeFieldLength <= 0 || _sizeFieldLength > 9 || _sizeFieldPos <= 0 ||
     310       31290 :         _sizeFieldPos > 9 || _sizeFieldTag <= 0 || _sizeFieldTag > 9)
     311             :     {
     312           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     313             :                  "ISO8211 record leader appears to be corrupt.");
     314           0 :         nFieldOffset = -1;
     315           0 :         return FALSE;
     316             :     }
     317             : 
     318       31290 :     if (_leaderIden == 'R')
     319           0 :         bReuseHeader = TRUE;
     320             : 
     321       31290 :     nFieldOffset = _fieldAreaStart - nLeaderSize;
     322             : 
     323             :     /* -------------------------------------------------------------------- */
     324             :     /*      Is there anything seemly screwy about this record?              */
     325             :     /* -------------------------------------------------------------------- */
     326       31290 :     if (((_recLength <= 24 || _recLength > 100000000) && (_recLength != 0)) ||
     327       31290 :         _fieldAreaStart < 24 || _fieldAreaStart > 100000)
     328             :     {
     329           0 :         CPLError(
     330             :             CE_Failure, CPLE_FileIO,
     331             :             "Data record appears to be corrupt on DDF file.\n"
     332             :             " -- ensure that the files were uncompressed without modifying\n"
     333             :             "carriage return/linefeeds (by default WINZIP does this).");
     334           0 :         nFieldOffset = -1;
     335           0 :         return FALSE;
     336             :     }
     337             : 
     338             :     /* ==================================================================== */
     339             :     /*      Handle the normal case with the record length available.        */
     340             :     /* ==================================================================== */
     341       31290 :     if (_recLength != 0)
     342             :     {
     343             :         /* --------------------------------------------------------------------
     344             :          */
     345             :         /*      Read the remainder of the record. */
     346             :         /* --------------------------------------------------------------------
     347             :          */
     348       31289 :         int nDataSize = _recLength - nLeaderSize;
     349       31289 :         osData.resize(nDataSize);
     350             : 
     351       62578 :         if (VSIFReadL(osData.data(), 1, osData.size(), poModule->GetFP()) !=
     352       31289 :             osData.size())
     353             :         {
     354           0 :             CPLError(CE_Failure, CPLE_FileIO,
     355             :                      "Data record is short on DDF file.");
     356           0 :             nFieldOffset = -1;
     357           0 :             return FALSE;
     358             :         }
     359             : 
     360             :         /* --------------------------------------------------------------------
     361             :          */
     362             :         /*      If we don't find a field terminator at the end of the record */
     363             :         /*      we will read extra bytes till we get to it. */
     364             :         /* --------------------------------------------------------------------
     365             :          */
     366       31291 :         while (!osData.empty() && osData.back() != DDF_FIELD_TERMINATOR &&
     367           1 :                (osData.size() < 2 ||
     368           1 :                 osData[osData.size() - 2] != DDF_FIELD_TERMINATOR))
     369             :         {
     370           0 :             osData.resize(osData.size() + 1);
     371             : 
     372           0 :             if (VSIFReadL(&(osData.back()), 1, 1, poModule->GetFP()) != 1)
     373             :             {
     374           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     375             :                          "Data record is short on DDF file.");
     376           0 :                 nFieldOffset = -1;
     377           0 :                 return FALSE;
     378             :             }
     379             :             static bool bFirstTime = true;
     380           0 :             if (bFirstTime)
     381             :             {
     382           0 :                 bFirstTime = false;
     383           0 :                 CPLDebug("ISO8211",
     384             :                          "Didn't find field terminator, read one more byte.");
     385             :             }
     386             :         }
     387             : 
     388       31289 :         if (nFieldOffset >= static_cast<int>(osData.size()))
     389             :         {
     390           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     391             :                      "nFieldOffset < static_cast<int>(osData.size())");
     392           0 :             nFieldOffset = -1;
     393           0 :             return FALSE;
     394             :         }
     395             : 
     396             :         /* --------------------------------------------------------------------
     397             :          */
     398             :         /*      Loop over the directory entries, making a pass counting them. */
     399             :         /* --------------------------------------------------------------------
     400             :          */
     401             :         int i;
     402             :         int nFieldEntryWidth;
     403             : 
     404       31289 :         nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
     405       31289 :         if (nFieldEntryWidth <= 0)
     406             :         {
     407           0 :             CPLError(CE_Failure, CPLE_FileIO, "Invalid entry width = %d",
     408             :                      nFieldEntryWidth);
     409           0 :             nFieldOffset = -1;
     410           0 :             return FALSE;
     411             :         }
     412             : 
     413       31289 :         int nFieldCount = 0;
     414      151907 :         for (i = 0; i + nFieldEntryWidth <= static_cast<int>(osData.size());
     415      120618 :              i += nFieldEntryWidth)
     416             :         {
     417      151868 :             if (osData[i] == DDF_FIELD_TERMINATOR)
     418       31250 :                 break;
     419             : 
     420      120618 :             nFieldCount++;
     421             :         }
     422             : 
     423             :         /* --------------------------------------------------------------------
     424             :          */
     425             :         /*      Allocate, and read field definitions. */
     426             :         /* --------------------------------------------------------------------
     427             :          */
     428       31289 :         apoFields.resize(nFieldCount);
     429             : 
     430       31289 :         int nLastFieldPos = 0;
     431       31289 :         int nLastFieldLength = 0;
     432      151906 :         for (i = 0; i < nFieldCount; i++)
     433             :         {
     434             :             char szTag[128];
     435      120618 :             int nEntryOffset = i * nFieldEntryWidth;
     436             :             int nFieldLength, nFieldPos;
     437             : 
     438             :             /* --------------------------------------------------------------------
     439             :              */
     440             :             /*      Read the position information and tag. */
     441             :             /* --------------------------------------------------------------------
     442             :              */
     443      120618 :             strncpy(szTag, osData.c_str() + nEntryOffset, _sizeFieldTag);
     444      120618 :             szTag[_sizeFieldTag] = '\0';
     445             : 
     446      120618 :             nEntryOffset += _sizeFieldTag;
     447             :             nFieldLength =
     448      120618 :                 DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
     449             : 
     450      120618 :             nEntryOffset += _sizeFieldLength;
     451             :             nFieldPos =
     452      120618 :                 DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldPos);
     453             : 
     454             :             /* --------------------------------------------------------------------
     455             :              */
     456             :             /*      Find the corresponding field in the module directory. */
     457             :             /* --------------------------------------------------------------------
     458             :              */
     459      120618 :             DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
     460             : 
     461      120618 :             if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
     462             :             {
     463           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     464             :                          "Undefined field `%s' encountered in data record.",
     465             :                          szTag);
     466           1 :                 return FALSE;
     467             :             }
     468             : 
     469      241236 :             if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
     470      120618 :                 static_cast<int>(osData.size()) -
     471      120618 :                         (_fieldAreaStart + nFieldPos - nLeaderSize) <
     472             :                     nFieldLength)
     473             :             {
     474           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     475             :                          "Not enough byte to initialize field `%s'.", szTag);
     476           0 :                 nFieldOffset = -1;
     477           0 :                 return FALSE;
     478             :             }
     479             : 
     480             :             // This check is not strictly needed for reading scenarios, but
     481             :             // in update scenarios (such as S57/S101), it is essential to avoid
     482             :             // issues when resizing fields.
     483      120618 :             if (nFieldPos < nLastFieldPos + nLastFieldLength)
     484             :             {
     485           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     486             :                          "Field `%s' overlapping with previous one.", szTag);
     487           1 :                 nFieldOffset = -1;
     488           1 :                 return FALSE;
     489             :             }
     490             : 
     491             :             /* --------------------------------------------------------------------
     492             :              */
     493             :             /*      Assign info the DDFField. */
     494             :             /* --------------------------------------------------------------------
     495             :              */
     496      120617 :             apoFields[i] = std::make_unique<DDFField>();
     497      241234 :             if (!apoFields[i]->Initialize(poFieldDefn,
     498      120617 :                                           osData.c_str() + _fieldAreaStart +
     499      120617 :                                               nFieldPos - nLeaderSize,
     500             :                                           nFieldLength, true))
     501             :             {
     502             :                 // Error message emitted by Initialize()
     503           0 :                 nFieldOffset = -1;
     504           0 :                 return FALSE;
     505             :             }
     506             : 
     507      120617 :             nLastFieldPos = nFieldPos;
     508      120617 :             nLastFieldLength = nFieldLength;
     509             :         }
     510             : 
     511       31288 :         return TRUE;
     512             :     }
     513             :     /* ==================================================================== */
     514             :     /*      Handle the exceptional case where the record length is          */
     515             :     /*      zero.  In this case we have to read all the data based on       */
     516             :     /*      the size of data items as per ISO8211 spec Annex C, 1.5.1.      */
     517             :     /*                                                                      */
     518             :     /*      See Bugzilla bug 181 and test with file US4CN21M.000.           */
     519             :     /* ==================================================================== */
     520             :     else
     521             :     {
     522           1 :         CPLDebug("ISO8211",
     523             :                  "Record with zero length, use variant (C.1.5.1) logic.");
     524             : 
     525             :         /* ----------------------------------------------------------------- */
     526             :         /*   _recLength == 0, handle the large record.                       */
     527             :         /*                                                                   */
     528             :         /*   Read the remainder of the record.                               */
     529             :         /* ----------------------------------------------------------------- */
     530           1 :         osData.clear();
     531             : 
     532             :         /* ----------------------------------------------------------------- */
     533             :         /*   Loop over the directory entries, making a pass counting them.   */
     534             :         /* ----------------------------------------------------------------- */
     535           1 :         int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag;
     536           1 :         int nFieldCount = 0;
     537           1 :         int i = 0;
     538             : 
     539           1 :         if (nFieldEntryWidth == 0)
     540             :         {
     541           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     542             :                      "Invalid record buffer size : %d.", nFieldEntryWidth);
     543           0 :             nFieldOffset = -1;
     544           0 :             return FALSE;
     545             :         }
     546             : 
     547           2 :         std::string osEntry;
     548           1 :         osEntry.resize(nFieldEntryWidth);
     549             : 
     550             :         // while we're not at the end, store this entry,
     551             :         // and keep on reading...
     552           2 :         do
     553             :         {
     554             :             // read an Entry:
     555           3 :             if (nFieldEntryWidth !=
     556           3 :                 static_cast<int>(VSIFReadL(osEntry.data(), 1, nFieldEntryWidth,
     557           3 :                                            poModule->GetFP())))
     558             :             {
     559           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     560             :                          "Data record is short on DDF file.");
     561           0 :                 nFieldOffset = -1;
     562           0 :                 return FALSE;
     563             :             }
     564             : 
     565           3 :             osData.append(osEntry.c_str(), nFieldEntryWidth);
     566             : 
     567           3 :             if (DDF_FIELD_TERMINATOR != osEntry[0])
     568             :             {
     569           2 :                 nFieldCount++;
     570           2 :                 if (nFieldCount == 1000)
     571             :                 {
     572           0 :                     CPLError(CE_Failure, CPLE_FileIO,
     573             :                              "Too many fields in DDF file.");
     574           0 :                     nFieldOffset = -1;
     575           0 :                     return FALSE;
     576             :                 }
     577             :             }
     578           3 :         } while (DDF_FIELD_TERMINATOR != osEntry[0]);
     579             : 
     580             :         // --------------------------------------------------------------------
     581             :         // Now, rewind a little.  Only the TERMINATOR should have been read
     582             :         // --------------------------------------------------------------------
     583           1 :         int rewindSize = nFieldEntryWidth - 1;
     584           1 :         VSILFILE *fp = poModule->GetFP();
     585           1 :         vsi_l_offset pos = VSIFTellL(fp) - rewindSize;
     586           1 :         if (VSIFSeekL(fp, pos, SEEK_SET) < 0)
     587           0 :             return FALSE;
     588           1 :         osData.resize(osData.size() - rewindSize);
     589             : 
     590             :         // --------------------------------------------------------------------
     591             :         // Okay, now let's populate the heck out of osData...
     592             :         // --------------------------------------------------------------------
     593           3 :         for (i = 0; i < nFieldCount; i++)
     594             :         {
     595           2 :             int nEntryOffset = (i * nFieldEntryWidth) + _sizeFieldTag;
     596             :             int nFieldLength =
     597           2 :                 DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
     598           2 :             if (nFieldLength < 0)
     599             :             {
     600           0 :                 nFieldOffset = -1;
     601           0 :                 return FALSE;
     602             :             }
     603             : 
     604           2 :             osEntry.resize(nFieldLength);
     605             : 
     606             :             // read an Entry:
     607           2 :             if (nFieldLength !=
     608           2 :                 static_cast<int>(VSIFReadL(osEntry.data(), 1, nFieldLength,
     609           2 :                                            poModule->GetFP())))
     610             :             {
     611           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     612             :                          "Data record is short on DDF file.");
     613           0 :                 nFieldOffset = -1;
     614           0 :                 return FALSE;
     615             :             }
     616             : 
     617             :             // move this temp buffer into more permanent storage:
     618           2 :             osData.append(osEntry.data(), nFieldLength);
     619             :         }
     620             : 
     621           1 :         if (nFieldOffset >= static_cast<int>(osData.size()))
     622             :         {
     623           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     624             :                      "nFieldOffset < static_cast<int>(osData.size())");
     625           0 :             nFieldOffset = -1;
     626           0 :             return FALSE;
     627             :         }
     628             : 
     629             :         /* ----------------------------------------------------------------- */
     630             :         /*     Allocate, and read field definitions.                         */
     631             :         /* ----------------------------------------------------------------- */
     632           1 :         apoFields.resize(nFieldCount);
     633             : 
     634           1 :         int nLastFieldPos = 0;
     635           1 :         int nLastFieldLength = 0;
     636           3 :         for (i = 0; i < nFieldCount; i++)
     637             :         {
     638             :             char szTag[128];
     639           2 :             int nEntryOffset = i * nFieldEntryWidth;
     640             :             int nFieldLength, nFieldPos;
     641             : 
     642             :             /* ------------------------------------------------------------- */
     643             :             /* Read the position information and tag.                        */
     644             :             /* ------------------------------------------------------------- */
     645           2 :             strncpy(szTag, osData.c_str() + nEntryOffset, _sizeFieldTag);
     646           2 :             szTag[_sizeFieldTag] = '\0';
     647             : 
     648           2 :             nEntryOffset += _sizeFieldTag;
     649             :             nFieldLength =
     650           2 :                 DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldLength);
     651             : 
     652           2 :             nEntryOffset += _sizeFieldLength;
     653             :             nFieldPos =
     654           2 :                 DDFScanInt(osData.c_str() + nEntryOffset, _sizeFieldPos);
     655             : 
     656             :             /* ------------------------------------------------------------- */
     657             :             /* Find the corresponding field in the module directory.         */
     658             :             /* ------------------------------------------------------------- */
     659           2 :             DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn(szTag);
     660             : 
     661           2 :             if (poFieldDefn == nullptr || nFieldLength < 0 || nFieldPos < 0)
     662             :             {
     663           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     664             :                          "Undefined field `%s' encountered in data record.",
     665             :                          szTag);
     666           0 :                 nFieldOffset = -1;
     667           0 :                 return FALSE;
     668             :             }
     669             : 
     670           4 :             if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 ||
     671           2 :                 static_cast<int>(osData.size()) -
     672           2 :                         (_fieldAreaStart + nFieldPos - nLeaderSize) <
     673             :                     nFieldLength)
     674             :             {
     675           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     676             :                          "Not enough byte to initialize field `%s'.", szTag);
     677           0 :                 nFieldOffset = -1;
     678           0 :                 return FALSE;
     679             :             }
     680             : 
     681             :             // This check is not strictly needed for reading scenarios, but
     682             :             // in update scenarios (such as S57/S101), it is essential to avoid
     683             :             // issues when resizing fields.
     684           2 :             if (nFieldPos < nLastFieldPos + nLastFieldLength)
     685             :             {
     686           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     687             :                          "Field `%s' overlapping with previous one.", szTag);
     688           0 :                 nFieldOffset = -1;
     689           0 :                 return FALSE;
     690             :             }
     691             : 
     692             :             /* ------------------------------------------------------------- */
     693             :             /* Assign info the DDFField.                                     */
     694             :             /* ------------------------------------------------------------- */
     695             : 
     696           2 :             apoFields[i] = std::make_unique<DDFField>();
     697           4 :             if (!apoFields[i]->Initialize(poFieldDefn,
     698           2 :                                           osData.c_str() + _fieldAreaStart +
     699           2 :                                               nFieldPos - nLeaderSize,
     700             :                                           nFieldLength, true))
     701             :             {
     702             :                 // Error message emitted by Initialize()
     703           0 :                 nFieldOffset = -1;
     704           0 :                 return FALSE;
     705             :             }
     706             : 
     707           2 :             nLastFieldPos = nFieldPos;
     708           2 :             nLastFieldLength = nFieldLength;
     709             :         }
     710             : 
     711           1 :         return TRUE;
     712             :     }
     713             : }
     714             : 
     715             : /************************************************************************/
     716             : /*                             FindField()                              */
     717             : /************************************************************************/
     718             : 
     719             : /**
     720             :  * Find the named field within this record.
     721             :  *
     722             :  * @param pszName The name of the field to fetch.  The comparison is
     723             :  * case insensitive.
     724             :  * @param iFieldIndex The instance of this field to fetch. Use zero (the
     725             :  * default) for the first instance.
     726             :  *
     727             :  * @return Pointer to the requested DDFField. This pointer is to an
     728             :  * internal object, and should not be freed.  It remains valid until
     729             :  * the next record read.
     730             :  */
     731             : 
     732      590429 : const DDFField *DDFRecord::FindField(const char *pszName, int iFieldIndex) const
     733             : 
     734             : {
     735     1693180 :     for (const auto &poField : apoFields)
     736             :     {
     737     1673000 :         const DDFFieldDefn *poFieldDefn = poField->GetFieldDefn();
     738     1673000 :         if (poFieldDefn && EQUAL(poFieldDefn->GetName(), pszName))
     739             :         {
     740      571267 :             if (iFieldIndex == 0)
     741      570256 :                 return poField.get();
     742             :             else
     743        1011 :                 iFieldIndex--;
     744             :         }
     745             :     }
     746             : 
     747       20173 :     return nullptr;
     748             : }
     749             : 
     750             : /************************************************************************/
     751             : /*                              GetField()                              */
     752             : /************************************************************************/
     753             : 
     754             : /**
     755             :  * Fetch field object based on index.
     756             :  *
     757             :  * @param i The index of the field to fetch.  Between 0 and GetFieldCount()-1.
     758             :  *
     759             :  * @return A DDFField pointer, or NULL if the index is out of range.
     760             :  */
     761             : 
     762      224313 : const DDFField *DDFRecord::GetField(int i) const
     763             : 
     764             : {
     765      224313 :     if (i < 0 || static_cast<size_t>(i) >= apoFields.size())
     766           0 :         return nullptr;
     767             :     else
     768      224313 :         return apoFields[i].get();
     769             : }
     770             : 
     771             : /************************************************************************/
     772             : /*                          FindSubfieldDefn()                          */
     773             : /************************************************************************/
     774             : 
     775             : /* static */ std::tuple<const DDFField *, const DDFSubfieldDefn *>
     776      553929 : DDFRecord::FindSubfieldDefn(const DDFField *poField, const char *pszSubfield,
     777             :                             bool bEmitError)
     778             : {
     779      553929 :     if (poField->GetParts().empty())
     780             :     {
     781             :         /* -------------------------------------------------------------------- */
     782             :         /*      Get the subfield definition                                     */
     783             :         /* -------------------------------------------------------------------- */
     784             :         const DDFSubfieldDefn *poSFDefn =
     785      534259 :             poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
     786      534259 :         if (poSFDefn == nullptr)
     787             :         {
     788          88 :             if (bEmitError)
     789             :             {
     790           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     791             :                          "Cannot find subfield %s of %s", pszSubfield,
     792             :                          poField->GetFieldDefn()->GetName());
     793             :             }
     794          88 :             return {nullptr, nullptr};
     795             :         }
     796             : 
     797      534171 :         return {nullptr, poSFDefn};
     798             :     }
     799             :     else
     800             :     {
     801       21470 :         for (auto &poPart : poField->GetParts())
     802             :         {
     803             :             const DDFSubfieldDefn *poSFDefn =
     804       21470 :                 poPart->GetFieldDefn()->FindSubfieldDefn(pszSubfield);
     805       21470 :             if (poSFDefn)
     806       39340 :                 return {poPart.get(), poSFDefn};
     807             :         }
     808           0 :         if (bEmitError)
     809             :         {
     810           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     811             :                      "Cannot find subfield %s of %s", pszSubfield,
     812             :                      poField->GetFieldDefn()->GetName());
     813             :         }
     814           0 :         return {nullptr, nullptr};
     815             :     }
     816             : }
     817             : 
     818             : /************************************************************************/
     819             : /*                          FindSubfieldDefn()                          */
     820             : /************************************************************************/
     821             : 
     822             : std::tuple<const DDFField *, const DDFField *, const DDFSubfieldDefn *>
     823       27762 : DDFRecord::FindSubfieldDefn(const char *pszField, int iFieldIndex,
     824             :                             const char *pszSubfield, bool bEmitError) const
     825             : {
     826             :     /* -------------------------------------------------------------------- */
     827             :     /*      Fetch the field. If this fails, return zero.                    */
     828             :     /* -------------------------------------------------------------------- */
     829       27762 :     const DDFField *poField = FindField(pszField, iFieldIndex);
     830       27762 :     if (poField == nullptr)
     831             :     {
     832           0 :         if (bEmitError)
     833             :         {
     834           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     835             :                      "Cannot find field index %d of %s", iFieldIndex, pszField);
     836             :         }
     837           0 :         return {nullptr, nullptr, nullptr};
     838             :     }
     839             : 
     840       27762 :     auto [poPartField, poSFDefn] =
     841       27762 :         FindSubfieldDefn(poField, pszSubfield, bEmitError);
     842       55524 :     return {poSFDefn ? poField : nullptr, poPartField, poSFDefn};
     843             : }
     844             : 
     845             : /************************************************************************/
     846             : /*                           GetIntSubfield()                           */
     847             : /************************************************************************/
     848             : 
     849             : /**
     850             :  * Fetch value of a subfield as an integer. This is a convenience
     851             :  * function for fetching a subfield of a field within this record.
     852             :  *
     853             :  * @param poField The field containing the subfield.
     854             :  * @param iSubfieldIndex The instance of this subfield within the record.
     855             :  * Use zero for the first instance.
     856             :  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
     857             :  * succeeds, or FALSE if it fails. Use NULL if you don't want to check
     858             :  * success.
     859             :  * @return The value of the subfield, or zero if it failed for some reason.
     860             :  */
     861             : 
     862      486112 : int DDFRecord::GetIntSubfield(const DDFField *poField, const char *pszSubfield,
     863             :                               int iSubfieldIndex, int *pnSuccess) const
     864             : 
     865             : {
     866      486112 :     int nDummyErr = FALSE;
     867             : 
     868      486112 :     if (pnSuccess == nullptr)
     869      397197 :         pnSuccess = &nDummyErr;
     870             : 
     871      486112 :     *pnSuccess = FALSE;
     872             : 
     873             :     /* -------------------------------------------------------------------- */
     874             :     /*      Get the subfield definition                                     */
     875             :     /* -------------------------------------------------------------------- */
     876      486112 :     auto [poPartField, poSFDefn] =
     877      486112 :         FindSubfieldDefn(poField, pszSubfield, false);
     878      486112 :     if (poSFDefn == nullptr)
     879          34 :         return 0;
     880      486078 :     if (poPartField)
     881        8476 :         poField = poPartField;
     882             : 
     883             :     /* -------------------------------------------------------------------- */
     884             :     /*      Get a pointer to the data.                                      */
     885             :     /* -------------------------------------------------------------------- */
     886             :     int nBytesRemaining;
     887             : 
     888             :     const char *l_pachData =
     889      486078 :         poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
     890      486078 :     if (l_pachData == nullptr)
     891           0 :         return 0;
     892             : 
     893             :     /* -------------------------------------------------------------------- */
     894             :     /*      Return the extracted value.                                     */
     895             :     /*                                                                      */
     896             :     /*      Assume an error has occurred if no bytes are consumed.           */
     897             :     /* -------------------------------------------------------------------- */
     898      486078 :     int nConsumedBytes = 0;
     899             :     int nResult =
     900      486078 :         poSFDefn->ExtractIntData(l_pachData, nBytesRemaining, &nConsumedBytes);
     901             : 
     902      486078 :     if (nConsumedBytes > 0)
     903      486078 :         *pnSuccess = TRUE;
     904             : 
     905      486078 :     return nResult;
     906             : }
     907             : 
     908             : /************************************************************************/
     909             : /*                           GetIntSubfield()                           */
     910             : /************************************************************************/
     911             : 
     912             : /**
     913             :  * Fetch value of a subfield as an integer. This is a convenience
     914             :  * function for fetching a subfield of a field within this record.
     915             :  *
     916             :  * @param pszField The name of the field containing the subfield.
     917             :  * @param iFieldIndex The instance of this field within the record. Use
     918             :  * zero for the first instance of this field.
     919             :  * @param pszSubfield The name of the subfield within the selected field.
     920             :  * @param iSubfieldIndex The instance of this subfield within the record.
     921             :  * Use zero for the first instance.
     922             :  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
     923             :  * succeeds, or FALSE if it fails. Use NULL if you don't want to check
     924             :  * success.
     925             :  * @return The value of the subfield, or zero if it failed for some reason.
     926             :  */
     927             : 
     928      380182 : int DDFRecord::GetIntSubfield(const char *pszField, int iFieldIndex,
     929             :                               const char *pszSubfield, int iSubfieldIndex,
     930             :                               int *pnSuccess) const
     931             : 
     932             : {
     933      380182 :     const DDFField *poField = FindField(pszField, iFieldIndex);
     934      380182 :     if (poField == nullptr)
     935             :     {
     936          33 :         if (pnSuccess)
     937           0 :             *pnSuccess = FALSE;
     938          33 :         return 0;
     939             :     }
     940             : 
     941      380149 :     return GetIntSubfield(poField, pszSubfield, iSubfieldIndex, pnSuccess);
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /*                          GetFloatSubfield()                          */
     946             : /************************************************************************/
     947             : 
     948             : /**
     949             :  * Fetch value of a subfield as a float (double). This is a convenience
     950             :  * function for fetching a subfield of a field within this record.
     951             :  *
     952             :  * @param pszField The name of the field containing the subfield.
     953             :  * @param iFieldIndex The instance of this field within the record. Use
     954             :  * zero for the first instance of this field.
     955             :  * @param pszSubfield The name of the subfield within the selected field.
     956             :  * @param iSubfieldIndex The instance of this subfield within the record.
     957             :  * Use zero for the first instance.
     958             :  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
     959             :  * succeeds, or FALSE if it fails. Use NULL if you don't want to check
     960             :  * success.
     961             :  * @return The value of the subfield, or zero if it failed for some reason.
     962             :  */
     963             : 
     964        1917 : double DDFRecord::GetFloatSubfield(const char *pszField, int iFieldIndex,
     965             :                                    const char *pszSubfield, int iSubfieldIndex,
     966             :                                    int *pnSuccess) const
     967             : 
     968             : {
     969        1917 :     int nDummyErr = FALSE;
     970             : 
     971        1917 :     if (pnSuccess == nullptr)
     972          84 :         pnSuccess = &nDummyErr;
     973             : 
     974        1917 :     *pnSuccess = FALSE;
     975             : 
     976             :     /* -------------------------------------------------------------------- */
     977             :     /*      Fetch the field. If this fails, return zero.                    */
     978             :     /* -------------------------------------------------------------------- */
     979        1917 :     auto [poField, poPartField, poSFDefn] =
     980        1917 :         FindSubfieldDefn(pszField, iFieldIndex, pszSubfield, false);
     981        1917 :     if (poSFDefn == nullptr)
     982           2 :         return 0;
     983        1915 :     if (poPartField)
     984           0 :         poField = poPartField;
     985             : 
     986             :     /* -------------------------------------------------------------------- */
     987             :     /*      Get a pointer to the data.                                      */
     988             :     /* -------------------------------------------------------------------- */
     989             :     int nBytesRemaining;
     990             : 
     991             :     const char *l_pachData =
     992        1915 :         poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
     993        1915 :     if (l_pachData == nullptr)
     994           0 :         return 0;
     995             : 
     996             :     /* -------------------------------------------------------------------- */
     997             :     /*      Return the extracted value.                                     */
     998             :     /* -------------------------------------------------------------------- */
     999        1915 :     int nConsumedBytes = 0;
    1000        1915 :     double dfResult = poSFDefn->ExtractFloatData(l_pachData, nBytesRemaining,
    1001             :                                                  &nConsumedBytes);
    1002             : 
    1003        1915 :     if (nConsumedBytes > 0)
    1004        1915 :         *pnSuccess = TRUE;
    1005             : 
    1006        1915 :     return dfResult;
    1007             : }
    1008             : 
    1009             : /************************************************************************/
    1010             : /*                         GetStringSubfield()                          */
    1011             : /************************************************************************/
    1012             : 
    1013             : /**
    1014             :  * Fetch value of a subfield as a string. This is a convenience
    1015             :  * function for fetching a subfield of a field within this record.
    1016             :  *
    1017             :  * @param poField The field containing the subfield.
    1018             :  * @param pszSubfield The name of the subfield within the selected field.
    1019             :  * @param iSubfieldIndex The instance of this subfield within the record.
    1020             :  * Use zero for the first instance.
    1021             :  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
    1022             :  * succeeds, or FALSE if it fails. Use NULL if you don't want to check
    1023             :  * success.
    1024             :  * @return The value of the subfield, or NULL if it failed for some reason.
    1025             :  * The returned pointer is to internal data and should not be modified or
    1026             :  * freed by the application.
    1027             :  */
    1028             : 
    1029       40055 : const char *DDFRecord::GetStringSubfield(const DDFField *poField,
    1030             :                                          const char *pszSubfield,
    1031             :                                          int iSubfieldIndex,
    1032             :                                          int *pnSuccess) const
    1033             : 
    1034             : {
    1035       40055 :     int nDummyErr = FALSE;
    1036             : 
    1037       40055 :     if (pnSuccess == nullptr)
    1038       35054 :         pnSuccess = &nDummyErr;
    1039             : 
    1040       40055 :     *pnSuccess = FALSE;
    1041             : 
    1042             :     /* -------------------------------------------------------------------- */
    1043             :     /*      Fetch the field. If this fails, return zero.                    */
    1044             :     /* -------------------------------------------------------------------- */
    1045       40055 :     auto [poPartField, poSFDefn] =
    1046       40055 :         FindSubfieldDefn(poField, pszSubfield, false);
    1047       40055 :     if (poSFDefn == nullptr)
    1048          52 :         return nullptr;
    1049       40003 :     if (poPartField)
    1050        6912 :         poField = poPartField;
    1051             : 
    1052             :     /* -------------------------------------------------------------------- */
    1053             :     /*      Get a pointer to the data.                                      */
    1054             :     /* -------------------------------------------------------------------- */
    1055             :     int nBytesRemaining;
    1056             : 
    1057             :     const char *l_pachData =
    1058       40003 :         poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex);
    1059       40003 :     if (l_pachData == nullptr)
    1060           0 :         return nullptr;
    1061             : 
    1062             :     /* -------------------------------------------------------------------- */
    1063             :     /*      Return the extracted value.                                     */
    1064             :     /* -------------------------------------------------------------------- */
    1065       40003 :     *pnSuccess = TRUE;
    1066             : 
    1067       40003 :     return poSFDefn->ExtractStringData(l_pachData, nBytesRemaining, nullptr);
    1068             : }
    1069             : 
    1070             : /************************************************************************/
    1071             : /*                         GetStringSubfield()                          */
    1072             : /************************************************************************/
    1073             : 
    1074             : /**
    1075             :  * Fetch value of a subfield as a string. This is a convenience
    1076             :  * function for fetching a subfield of a field within this record.
    1077             :  *
    1078             :  * @param pszField The name of the field containing the subfield.
    1079             :  * @param iFieldIndex The instance of this field within the record. Use
    1080             :  * zero for the first instance of this field.
    1081             :  * @param pszSubfield The name of the subfield within the selected field.
    1082             :  * @param iSubfieldIndex The instance of this subfield within the record.
    1083             :  * Use zero for the first instance.
    1084             :  * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch
    1085             :  * succeeds, or FALSE if it fails. Use NULL if you don't want to check
    1086             :  * success.
    1087             :  * @return The value of the subfield, or NULL if it failed for some reason.
    1088             :  * The returned pointer is to internal data and should not be modified or
    1089             :  * freed by the application.
    1090             :  */
    1091             : 
    1092       23544 : const char *DDFRecord::GetStringSubfield(const char *pszField, int iFieldIndex,
    1093             :                                          const char *pszSubfield,
    1094             :                                          int iSubfieldIndex,
    1095             :                                          int *pnSuccess) const
    1096             : 
    1097             : {
    1098       23544 :     const DDFField *poField = FindField(pszField, iFieldIndex);
    1099       23544 :     if (poField == nullptr)
    1100             :     {
    1101           0 :         if (pnSuccess)
    1102           0 :             *pnSuccess = FALSE;
    1103           0 :         return nullptr;
    1104             :     }
    1105             : 
    1106       23544 :     return GetStringSubfield(poField, pszSubfield, iSubfieldIndex, pnSuccess);
    1107             : }
    1108             : 
    1109             : /************************************************************************/
    1110             : /*                               Clone()                                */
    1111             : /************************************************************************/
    1112             : 
    1113             : /**
    1114             :  * Make a copy of a record.
    1115             :  *
    1116             :  * This method is used to make a copy of a record that will become (mostly)
    1117             :  * the properly of application.
    1118             :  *
    1119             :  * @return A new copy of the DDFRecord. Its lifetime must not extend the one
    1120             :  * of the DDFModule of the original record, unless TransferTo() is called.
    1121             :  */
    1122             : 
    1123       29865 : std::unique_ptr<DDFRecord> DDFRecord::Clone() const
    1124             : 
    1125             : {
    1126       29865 :     auto poNR = std::make_unique<DDFRecord>(poModule);
    1127             : 
    1128       29865 :     poNR->bReuseHeader = false;
    1129       29865 :     poNR->nFieldOffset = nFieldOffset;
    1130             : 
    1131       29865 :     poNR->osData = osData;
    1132             : 
    1133       29865 :     poNR->apoFields.resize(apoFields.size());
    1134      145589 :     for (size_t i = 0; i < apoFields.size(); i++)
    1135             :     {
    1136             :         int nOffset;
    1137             : 
    1138      115724 :         nOffset = static_cast<int>(apoFields[i]->GetData() - osData.c_str());
    1139      115724 :         poNR->apoFields[i] = std::make_unique<DDFField>();
    1140      347172 :         poNR->apoFields[i]->Initialize(apoFields[i]->GetFieldDefn(),
    1141      115724 :                                        poNR->osData.c_str() + nOffset,
    1142      115724 :                                        apoFields[i]->GetDataSize(), true);
    1143             :     }
    1144             : 
    1145       29865 :     return poNR;
    1146             : }
    1147             : 
    1148             : /************************************************************************/
    1149             : /*                             TransferTo()                             */
    1150             : /************************************************************************/
    1151             : 
    1152             : /**
    1153             :  * Transfer this record to another module.
    1154             :  *
    1155             :  * All DDFFieldDefn
    1156             :  * references are transcribed onto the new module based on field names.
    1157             :  * If any fields don't have a similarly named field on the target module
    1158             :  * the operation will fail.  No validation of field types and properties
    1159             :  * is done, but this operation is intended only to be used between
    1160             :  * modules with matching definitions of all affected fields.
    1161             :  *
    1162             :  * @param poTargetModule the module to which the record should be transferred
    1163             :  *
    1164             :  * @return true on success
    1165             :  */
    1166             : 
    1167          68 : bool DDFRecord::TransferTo(DDFModule *poTargetModule)
    1168             : 
    1169             : {
    1170             :     /* -------------------------------------------------------------------- */
    1171             :     /*      Verify that all fields have a corresponding field definition    */
    1172             :     /*      on the target module.                                           */
    1173             :     /* -------------------------------------------------------------------- */
    1174         300 :     for (const auto &poField : apoFields)
    1175             :     {
    1176         233 :         const DDFFieldDefn *poDefn = poField->GetFieldDefn();
    1177             : 
    1178         233 :         if (poTargetModule->FindFieldDefn(poDefn->GetName()) == nullptr)
    1179             :         {
    1180           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1181             :                      "Cannot find field definition %s in target module",
    1182             :                      poDefn->GetName());
    1183           1 :             return false;
    1184             :         }
    1185             : 
    1186             :         //TODO? check equality between source and target field definitions
    1187             :     }
    1188             : 
    1189             :     /* -------------------------------------------------------------------- */
    1190             :     /*      Update all internal information to reference other module.      */
    1191             :     /* -------------------------------------------------------------------- */
    1192         298 :     for (auto &poField : apoFields)
    1193             :     {
    1194             :         DDFFieldDefn *poDefn =
    1195         231 :             poTargetModule->FindFieldDefn(poField->GetFieldDefn()->GetName());
    1196             : 
    1197         231 :         poField->Initialize(poDefn, poField->GetData(), poField->GetDataSize(),
    1198             :                             true);
    1199             :     }
    1200             : 
    1201          67 :     poModule = poTargetModule;
    1202             : 
    1203          67 :     return true;
    1204             : }
    1205             : 
    1206             : /************************************************************************/
    1207             : /*                            DeleteField()                             */
    1208             : /************************************************************************/
    1209             : 
    1210             : /**
    1211             :  * Delete a field instance from a record.
    1212             :  *
    1213             :  * Remove a field from this record, cleaning up the data
    1214             :  * portion and repacking the fields list.  We don't try to
    1215             :  * reallocate the data area of the record to be smaller.
    1216             :  *
    1217             :  * NOTE: This method doesn't actually remove the header
    1218             :  * information for this field from the record tag list yet.
    1219             :  * This should be added if the resulting record is even to be
    1220             :  * written back to disk!
    1221             :  *
    1222             :  * @param poTarget the field instance on this record to delete.
    1223             :  *
    1224             :  * @return TRUE on success, or FALSE on failure.  Failure can occur if
    1225             :  * poTarget isn't really a field on this record.
    1226             :  */
    1227             : 
    1228          51 : int DDFRecord::DeleteField(DDFField *poTarget)
    1229             : 
    1230             : {
    1231             :     int iTarget;
    1232             : 
    1233             :     /* -------------------------------------------------------------------- */
    1234             :     /*      Find which field we are to delete.                              */
    1235             :     /* -------------------------------------------------------------------- */
    1236         165 :     for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
    1237             :     {
    1238         165 :         if (apoFields[iTarget].get() == poTarget)
    1239          51 :             break;
    1240             :     }
    1241             : 
    1242          51 :     if (iTarget == GetFieldCount())
    1243           0 :         return FALSE;
    1244             : 
    1245             :     /* -------------------------------------------------------------------- */
    1246             :     /*      Change the target fields data size to zero. This takes care     */
    1247             :     /*      of repacking the data array, and updating all the following     */
    1248             :     /*      field data pointers.                                            */
    1249             :     /* -------------------------------------------------------------------- */
    1250          51 :     ResizeField(poTarget, 0);
    1251             : 
    1252             :     /* -------------------------------------------------------------------- */
    1253             :     /*      remove the target field, moving down all the other fields       */
    1254             :     /*      one step in the field list.                                     */
    1255             :     /* -------------------------------------------------------------------- */
    1256          51 :     apoFields.erase(apoFields.begin() + iTarget);
    1257             : 
    1258         219 :     for (auto &poField : apoFields)
    1259         168 :         poField->InitializeParts();
    1260             : 
    1261          51 :     return TRUE;
    1262             : }
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                            ResizeField()                             */
    1266             : /************************************************************************/
    1267             : 
    1268             : /**
    1269             :  * Alter field data size within record.
    1270             :  *
    1271             :  * This method will rearrange a DDFRecord altering the amount of space
    1272             :  * reserved for one of the existing fields.  All following fields will
    1273             :  * be shifted accordingly. This includes updating the DDFField infos,
    1274             :  * and actually moving stuff within the data array after reallocating
    1275             :  * to the desired size.
    1276             :  *
    1277             :  * @param poField the field to alter.
    1278             :  * @param nNewDataSize the number of data bytes to be reserved for the field.
    1279             :  *
    1280             :  * @return TRUE on success or FALSE on failure.
    1281             :  */
    1282             : 
    1283       11360 : int DDFRecord::ResizeField(DDFField *poField, int nNewDataSize)
    1284             : 
    1285             : {
    1286             :     /* -------------------------------------------------------------------- */
    1287             :     /*      Find which field we are to resize.                              */
    1288             :     /* -------------------------------------------------------------------- */
    1289             :     int iTarget;
    1290       30661 :     for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
    1291             :     {
    1292       30661 :         if (apoFields[iTarget].get() == poField)
    1293       11360 :             break;
    1294             :     }
    1295             : 
    1296       11360 :     if (iTarget == GetFieldCount())
    1297           0 :         return FALSE;
    1298             : 
    1299             :     /* -------------------------------------------------------------------- */
    1300             :     /*      How much data needs to be shifted up or down after this field?  */
    1301             :     /* -------------------------------------------------------------------- */
    1302       11360 :     const int nOldDataSize = poField->GetDataSize();
    1303             :     const int nBytesToMove =
    1304       11360 :         static_cast<int>(osData.size()) -
    1305       11360 :         static_cast<int>(poField->GetData() + nOldDataSize - osData.data());
    1306             : 
    1307             :     /* -------------------------------------------------------------------- */
    1308             :     /*      Store field offsets                                             */
    1309             :     /* -------------------------------------------------------------------- */
    1310       11360 :     std::vector<int> anOffsets;
    1311       42148 :     for (auto &poIterField : apoFields)
    1312             :     {
    1313       30788 :         anOffsets.push_back(
    1314       30788 :             static_cast<int>(poIterField->GetData() - osData.data()));
    1315             :     }
    1316             : 
    1317             :     /* -------------------------------------------------------------------- */
    1318             :     /*      Reallocate the data buffer accordingly.                         */
    1319             :     /* -------------------------------------------------------------------- */
    1320             :     // Don't realloc things smaller ... we will cut off some data.
    1321       11360 :     const int nBytesToAdd = nNewDataSize - poField->GetDataSize();
    1322       11360 :     if (nBytesToAdd > 0)
    1323             :     {
    1324       11183 :         osData.resize(osData.size() + nBytesToAdd);
    1325             : 
    1326             :         /* ---------------------------------------------------------------- */
    1327             :         /*      Update the target fields info.                              */
    1328             :         /* ---------------------------------------------------------------- */
    1329       11183 :         poField->Initialize(poField->GetFieldDefn(),
    1330       11183 :                             osData.c_str() + anOffsets[iTarget], nNewDataSize,
    1331             :                             false);
    1332             :     }
    1333             : 
    1334             :     /* -------------------------------------------------------------------- */
    1335             :     /*      Shift the data beyond this field up or down as needed.          */
    1336             :     /* -------------------------------------------------------------------- */
    1337       11360 :     if (nBytesToMove > 0)
    1338          78 :         memmove(const_cast<char *>(poField->GetData()) + nNewDataSize,
    1339          78 :                 poField->GetData() + nOldDataSize, nBytesToMove);
    1340             : 
    1341             :     /* -------------------------------------------------------------------- */
    1342             :     /*      Shift all following fields down, and update their data          */
    1343             :     /*      locations.                                                      */
    1344             :     /* -------------------------------------------------------------------- */
    1345       11360 :     if (nBytesToAdd < 0)
    1346             :     {
    1347          69 :         osData.resize(osData.size() - (-nBytesToAdd));
    1348             : 
    1349             :         /* ---------------------------------------------------------------- */
    1350             :         /*      Update the target fields info.                              */
    1351             :         /* ---------------------------------------------------------------- */
    1352          69 :         poField->Initialize(poField->GetFieldDefn(),
    1353          69 :                             osData.c_str() + anOffsets[iTarget], nNewDataSize,
    1354             :                             false);
    1355             :     }
    1356             : 
    1357             :     /* -------------------------------------------------------------------- */
    1358             :     /*      Update fields up to the resized one to point into newly         */
    1359             :     /*      allocated buffer.                                               */
    1360             :     /* -------------------------------------------------------------------- */
    1361       30661 :     for (int i = 0; i < iTarget; i++)
    1362             :     {
    1363       19301 :         auto &poIterField = apoFields[i];
    1364       38602 :         poIterField->Initialize(poIterField->GetFieldDefn(),
    1365       19301 :                                 osData.c_str() + anOffsets[i],
    1366             :                                 poIterField->GetDataSize(), false);
    1367             :     }
    1368             : 
    1369             :     /* -------------------------------------------------------------------- */
    1370             :     /*      Shift all following fields down, and update their data          */
    1371             :     /*      locations.                                                      */
    1372             :     /* -------------------------------------------------------------------- */
    1373       11487 :     for (int i = iTarget + 1; i < GetFieldCount(); i++)
    1374             :     {
    1375         381 :         apoFields[i]->Initialize(apoFields[i]->GetFieldDefn(),
    1376         127 :                                  osData.c_str() + anOffsets[i] + nBytesToAdd,
    1377         127 :                                  apoFields[i]->GetDataSize(), false);
    1378             :     }
    1379             : 
    1380       11360 :     return TRUE;
    1381             : }
    1382             : 
    1383             : /************************************************************************/
    1384             : /*                              AddField()                              */
    1385             : /************************************************************************/
    1386             : 
    1387             : /**
    1388             :  * Add a new field to record.
    1389             :  *
    1390             :  * Add a new zero sized field to the record.  The new field is always
    1391             :  * added at the end of the record.
    1392             :  *
    1393             :  * NOTE: This method doesn't currently update the header information for
    1394             :  * the record to include the field information for this field, so the
    1395             :  * resulting record image isn't suitable for writing to disk.  However,
    1396             :  * everything else about the record state should be updated properly to
    1397             :  * reflect the new field.
    1398             :  *
    1399             :  * @param poDefn the definition of the field to be added.
    1400             :  *
    1401             :  * @return the field object on success, or NULL on failure.
    1402             :  */
    1403             : 
    1404        4269 : DDFField *DDFRecord::AddField(const DDFFieldDefn *poDefn)
    1405             : 
    1406             : {
    1407             :     /* -------------------------------------------------------------------- */
    1408             :     /*      Reallocate the fields array larger by one, and initialize       */
    1409             :     /*      the new field.                                                  */
    1410             :     /* -------------------------------------------------------------------- */
    1411        4269 :     apoFields.resize(apoFields.size() + 1);
    1412        4269 :     apoFields.back() = std::make_unique<DDFField>();
    1413             : 
    1414             :     /* -------------------------------------------------------------------- */
    1415             :     /*      Initialize the new field properly.                              */
    1416             :     /* -------------------------------------------------------------------- */
    1417        4269 :     if (apoFields.size() == 1)
    1418             :     {
    1419        1355 :         apoFields[0]->Initialize(poDefn, GetData(), 0, false);
    1420             :     }
    1421             :     else
    1422             :     {
    1423        2914 :         apoFields.back()->Initialize(
    1424             :             poDefn,
    1425        2914 :             apoFields[GetFieldCount() - 2]->GetData() +
    1426        2914 :                 apoFields[GetFieldCount() - 2]->GetDataSize(),
    1427             :             0, false);
    1428             :     }
    1429             : 
    1430             :     /* -------------------------------------------------------------------- */
    1431             :     /*      Initialize field.                                               */
    1432             :     /* -------------------------------------------------------------------- */
    1433        4269 :     CreateDefaultFieldInstance(apoFields[GetFieldCount() - 1].get(), 0);
    1434             : 
    1435        4269 :     return apoFields.back().get();
    1436             : }
    1437             : 
    1438             : /************************************************************************/
    1439             : /*                            SetFieldRaw()                             */
    1440             : /************************************************************************/
    1441             : 
    1442             : /**
    1443             :  * Set the raw contents of a field instance.
    1444             :  *
    1445             :  * @param poField the field to set data within.
    1446             :  * @param iIndexWithinField The instance of this field to replace.  Must
    1447             :  * be a value between 0 and GetRepeatCount().  If GetRepeatCount() is used, a
    1448             :  * new instance of the field is appended.
    1449             :  * @param pachRawData the raw data to replace this field instance with.
    1450             :  * @param nRawDataSize the number of bytes pointed to by pachRawData.
    1451             :  *
    1452             :  * @return TRUE on success or FALSE on failure.
    1453             :  */
    1454             : 
    1455        7037 : int DDFRecord::SetFieldRaw(DDFField *poField, int iIndexWithinField,
    1456             :                            const char *pachRawData, int nRawDataSize)
    1457             : 
    1458             : {
    1459             :     int iTarget, nRepeatCount;
    1460             : 
    1461             :     /* -------------------------------------------------------------------- */
    1462             :     /*      Find which field we are to update.                              */
    1463             :     /* -------------------------------------------------------------------- */
    1464       19653 :     for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
    1465             :     {
    1466       19653 :         if (apoFields[iTarget].get() == poField)
    1467        7037 :             break;
    1468             :     }
    1469             : 
    1470        7037 :     if (iTarget == GetFieldCount())
    1471           0 :         return FALSE;
    1472             : 
    1473        7037 :     nRepeatCount = poField->GetRepeatCount();
    1474             : 
    1475        7037 :     if (iIndexWithinField < 0 || iIndexWithinField > nRepeatCount)
    1476           0 :         return FALSE;
    1477             : 
    1478             :     /* -------------------------------------------------------------------- */
    1479             :     /*      Are we adding an instance? This is easier and different         */
    1480             :     /*      than replacing an existing instance.                            */
    1481             :     /* -------------------------------------------------------------------- */
    1482        9938 :     if ((iIndexWithinField == nRepeatCount ||
    1483        9019 :          !poField->GetFieldDefn()->IsRepeating()) &&
    1484        1982 :         !(nRepeatCount == 0 && poField->GetDataSize() > 0))
    1485             :     {
    1486        6423 :         if (!poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0)
    1487           0 :             return FALSE;
    1488             : 
    1489        6423 :         int nOldSize = poField->GetDataSize();
    1490        6423 :         if (nOldSize == 0)
    1491        4269 :             nOldSize++;  // for added DDF_FIELD_TERMINATOR.
    1492             : 
    1493        6423 :         if (!ResizeField(poField, nOldSize + nRawDataSize))
    1494           0 :             return FALSE;
    1495             : 
    1496        6423 :         char *pachFieldData = const_cast<char *>(poField->GetData());
    1497        6423 :         memcpy(pachFieldData + nOldSize - 1, pachRawData, nRawDataSize);
    1498        6423 :         pachFieldData[nOldSize + nRawDataSize - 1] = DDF_FIELD_TERMINATOR;
    1499             : 
    1500       24922 :         for (auto &poIterField : apoFields)
    1501       18499 :             poIterField->InitializeParts();
    1502             : 
    1503        6423 :         return TRUE;
    1504             :     }
    1505             : 
    1506             :     /* -------------------------------------------------------------------- */
    1507             :     /*      Get a pointer to the start of the existing data for this        */
    1508             :     /*      iteration of the field.                                         */
    1509             :     /* -------------------------------------------------------------------- */
    1510         614 :     const char *pachWrkData = nullptr;
    1511         614 :     int nInstanceSize = 0;
    1512             : 
    1513             :     // We special case this to avoid a lot of warnings when initializing
    1514             :     // the field the first time.
    1515         614 :     if (poField->GetDataSize() == 0)
    1516             :     {
    1517           0 :         pachWrkData = poField->GetData();
    1518             :     }
    1519             :     else
    1520             :     {
    1521             :         pachWrkData =
    1522         614 :             poField->GetInstanceData(iIndexWithinField, &nInstanceSize);
    1523             :     }
    1524             : 
    1525             :     /* -------------------------------------------------------------------- */
    1526             :     /*      Create new image of this whole field.                           */
    1527             :     /* -------------------------------------------------------------------- */
    1528         614 :     int nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize;
    1529             : 
    1530         614 :     std::string osNewImage;
    1531         614 :     osNewImage.resize(nNewFieldSize);
    1532             : 
    1533         614 :     int nPreBytes = static_cast<int>(pachWrkData - poField->GetData());
    1534         614 :     int nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize;
    1535             : 
    1536         614 :     memcpy(osNewImage.data(), poField->GetData(), nPreBytes);
    1537        1842 :     memcpy(osNewImage.data() + nPreBytes + nRawDataSize,
    1538         614 :            poField->GetData() + nPreBytes + nInstanceSize, nPostBytes);
    1539         614 :     memcpy(osNewImage.data() + nPreBytes, pachRawData, nRawDataSize);
    1540             : 
    1541             :     /* -------------------------------------------------------------------- */
    1542             :     /*      Resize the field to the desired new size.                       */
    1543             :     /* -------------------------------------------------------------------- */
    1544         614 :     ResizeField(poField, nNewFieldSize);
    1545             : 
    1546         614 :     memcpy(const_cast<char *>(poField->GetData()), osNewImage.data(),
    1547             :            nNewFieldSize);
    1548             : 
    1549        1769 :     for (auto &poIterField : apoFields)
    1550        1155 :         poIterField->InitializeParts();
    1551             : 
    1552         614 :     return TRUE;
    1553             : }
    1554             : 
    1555             : /************************************************************************/
    1556             : /*                            SetFieldRaw()                             */
    1557             : /************************************************************************/
    1558             : 
    1559             : /**
    1560             :  * Set the raw contents of a field (all instances in case it is a repeated one)
    1561             :  *
    1562             :  * A DDF_FIELD_TERMINATOR will be automatically added at the end of the raw data
    1563             :  * if not already present.
    1564             :  *
    1565             :  * @param poField the field to set data within.
    1566             :  * @param pachRawData the raw data to replace this field with.
    1567             :  * @param nRawDataSize the number of bytes pointed to by pachRawData.
    1568             :  *
    1569             :  * @return TRUE on success or FALSE on failure.
    1570             :  */
    1571             : 
    1572         116 : int DDFRecord::SetFieldRaw(DDFField *poField, const char *pachRawData,
    1573             :                            int nRawDataSize)
    1574             : 
    1575             : {
    1576         116 :     const bool bAddFieldTerminator =
    1577         232 :         (nRawDataSize == 0 ||
    1578         116 :          pachRawData[nRawDataSize - 1] != DDF_FIELD_TERMINATOR);
    1579             : 
    1580             :     /* -------------------------------------------------------------------- */
    1581             :     /*      Resize the field to the desired new size.                       */
    1582             :     /* -------------------------------------------------------------------- */
    1583         116 :     if (!ResizeField(poField, nRawDataSize + (bAddFieldTerminator ? 1 : 0)))
    1584           0 :         return FALSE;
    1585             : 
    1586         116 :     memcpy(const_cast<char *>(poField->GetData()), pachRawData, nRawDataSize);
    1587         116 :     if (bAddFieldTerminator)
    1588           0 :         const_cast<char *>(poField->GetData())[nRawDataSize] =
    1589             :             DDF_FIELD_TERMINATOR;
    1590             : 
    1591         507 :     for (auto &poIterField : apoFields)
    1592         391 :         poIterField->InitializeParts();
    1593             : 
    1594         116 :     return TRUE;
    1595             : }
    1596             : 
    1597             : /************************************************************************/
    1598             : /*                           UpdateFieldRaw()                           */
    1599             : /************************************************************************/
    1600             : 
    1601        4156 : int DDFRecord::UpdateFieldRaw(DDFField *poField, DDFField *poPartField,
    1602             :                               int iIndexWithinField, int nStartOffset,
    1603             :                               int nOldSize, const char *pachRawData,
    1604             :                               int nRawDataSize)
    1605             : 
    1606             : {
    1607             :     int iTarget;
    1608             : 
    1609             :     /* -------------------------------------------------------------------- */
    1610             :     /*      Find which field we are to update.                              */
    1611             :     /* -------------------------------------------------------------------- */
    1612       10524 :     for (iTarget = 0; iTarget < GetFieldCount(); iTarget++)
    1613             :     {
    1614       10524 :         if (apoFields[iTarget].get() == poField)
    1615        4156 :             break;
    1616             :     }
    1617             : 
    1618        4156 :     if (iTarget == GetFieldCount())
    1619           0 :         return FALSE;
    1620             : 
    1621        4156 :     auto poLowLevelField = poPartField ? poPartField : poField;
    1622        4156 :     const int nRepeatCount = poLowLevelField->GetRepeatCount();
    1623             : 
    1624        4156 :     if (iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount)
    1625           0 :         return FALSE;
    1626             : 
    1627             :     /* -------------------------------------------------------------------- */
    1628             :     /*      Figure out how much pre and post data there is.                 */
    1629             :     /* -------------------------------------------------------------------- */
    1630             :     char *const pachWrkData =
    1631        4156 :         const_cast<char *>(poLowLevelField->GetInstanceData(iIndexWithinField));
    1632             :     const int nPreBytes =
    1633        4156 :         static_cast<int>(pachWrkData - poField->GetData() + nStartOffset);
    1634        4156 :     const int nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize;
    1635             : 
    1636             :     /* -------------------------------------------------------------------- */
    1637             :     /*      If we aren't changing the size, just copy over the existing     */
    1638             :     /*      data.                                                           */
    1639             :     /* -------------------------------------------------------------------- */
    1640        4156 :     if (nOldSize == nRawDataSize)
    1641             :     {
    1642           0 :         memcpy(pachWrkData + nStartOffset, pachRawData, nRawDataSize);
    1643           0 :         return TRUE;
    1644             :     }
    1645             : 
    1646             :     /* -------------------------------------------------------------------- */
    1647             :     /*      If we are shrinking, move in the new data, and shuffle down     */
    1648             :     /*      the old before resizing.                                        */
    1649             :     /* -------------------------------------------------------------------- */
    1650        4156 :     char *pabyFieldData = const_cast<char *>(poField->GetData());
    1651        4156 :     if (nRawDataSize < nOldSize)
    1652             :     {
    1653           0 :         memcpy(pabyFieldData + nPreBytes, pachRawData, nRawDataSize);
    1654           0 :         memmove(pabyFieldData + nPreBytes + nRawDataSize,
    1655           0 :                 pabyFieldData + nPreBytes + nOldSize, nPostBytes);
    1656             :     }
    1657             : 
    1658             :     /* -------------------------------------------------------------------- */
    1659             :     /*      Resize the whole buffer.                                        */
    1660             :     /* -------------------------------------------------------------------- */
    1661        4156 :     if (!ResizeField(poField, poField->GetDataSize() - nOldSize + nRawDataSize))
    1662           0 :         return FALSE;
    1663             : 
    1664             :     /* -------------------------------------------------------------------- */
    1665             :     /*      If we growing the buffer, shuffle up the post data, and         */
    1666             :     /*      move in our new values.                                         */
    1667             :     /* -------------------------------------------------------------------- */
    1668        4156 :     pabyFieldData = const_cast<char *>(poField->GetData());
    1669        4156 :     if (nRawDataSize >= nOldSize)
    1670             :     {
    1671        4156 :         memmove(pabyFieldData + nPreBytes + nRawDataSize,
    1672        4156 :                 pabyFieldData + nPreBytes + nOldSize, nPostBytes);
    1673        4156 :         memcpy(pabyFieldData + nPreBytes, pachRawData, nRawDataSize);
    1674             :     }
    1675             : 
    1676       14680 :     for (auto &poIterField : apoFields)
    1677       10524 :         poIterField->InitializeParts();
    1678             : 
    1679        4156 :     return TRUE;
    1680             : }
    1681             : 
    1682             : /************************************************************************/
    1683             : /*                           ResetDirectory()                           */
    1684             : /*                                                                      */
    1685             : /*      Re-prepares the directory information for the record.           */
    1686             : /************************************************************************/
    1687             : 
    1688        1355 : void DDFRecord::ResetDirectory()
    1689             : 
    1690             : {
    1691             :     /* -------------------------------------------------------------------- */
    1692             :     /*      Eventually we should try to optimize the size of offset and     */
    1693             :     /*      field length.                                                   */
    1694             :     /* -------------------------------------------------------------------- */
    1695             : 
    1696             :     /* -------------------------------------------------------------------- */
    1697             :     /*      Compute how large the directory needs to be.                    */
    1698             :     /* -------------------------------------------------------------------- */
    1699             :     int nEntrySize, nDirSize;
    1700             : 
    1701        1355 :     nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag;
    1702        1355 :     nDirSize = nEntrySize * GetFieldCount() + 1;
    1703             : 
    1704             :     /* -------------------------------------------------------------------- */
    1705             :     /*      If the directory size is different than what is currently       */
    1706             :     /*      reserved for it, we must resize.                                */
    1707             :     /* -------------------------------------------------------------------- */
    1708        1355 :     if (nDirSize != nFieldOffset)
    1709             :     {
    1710             :         const int nNewDataSize =
    1711        1355 :             static_cast<int>(osData.size()) - nFieldOffset + nDirSize;
    1712        2710 :         std::string osNewData;
    1713        1355 :         osNewData.resize(nNewDataSize);
    1714        1355 :         memcpy(osNewData.data() + nDirSize, osData.c_str() + nFieldOffset,
    1715        1355 :                nNewDataSize - nDirSize);
    1716             : 
    1717        2710 :         std::vector<int> anOffsets;
    1718        5589 :         for (auto &poField : apoFields)
    1719             :         {
    1720        4234 :             anOffsets.push_back(static_cast<int>(
    1721        4234 :                 poField->GetData() - osData.c_str() - nFieldOffset + nDirSize));
    1722             :         }
    1723             : 
    1724        1355 :         osData = std::move(osNewData);
    1725        1355 :         nFieldOffset = nDirSize;
    1726             : 
    1727        5589 :         for (size_t i = 0; i < apoFields.size(); ++i)
    1728             :         {
    1729        4234 :             auto &poField = apoFields[i];
    1730        8468 :             poField->Initialize(poField->GetFieldDefn(),
    1731        4234 :                                 osData.c_str() + anOffsets[i],
    1732             :                                 poField->GetDataSize(), true);
    1733             :         }
    1734             :     }
    1735             : 
    1736             :     /* -------------------------------------------------------------------- */
    1737             :     /*      Now set each directory entry.                                   */
    1738             :     /* -------------------------------------------------------------------- */
    1739        1355 :     int iField = 0;
    1740        5589 :     for (auto &poField : apoFields)
    1741             :     {
    1742        4234 :         const DDFFieldDefn *poDefn = poField->GetFieldDefn();
    1743             :         char szFormat[128];
    1744             : 
    1745        4234 :         snprintf(szFormat, sizeof(szFormat), "%%%ds%%0%dd%%0%dd", _sizeFieldTag,
    1746             :                  _sizeFieldLength, _sizeFieldPos);
    1747             : 
    1748        4234 :         snprintf(osData.data() + nEntrySize * iField, nEntrySize + 1, szFormat,
    1749             :                  poDefn->GetName(), poField->GetDataSize(),
    1750        4234 :                  poField->GetData() - osData.data() - nFieldOffset);
    1751        4234 :         ++iField;
    1752             :     }
    1753             : 
    1754        1355 :     osData[nEntrySize * GetFieldCount()] = DDF_FIELD_TERMINATOR;
    1755        1355 : }
    1756             : 
    1757             : /************************************************************************/
    1758             : /*                     CreateDefaultFieldInstance()                     */
    1759             : /************************************************************************/
    1760             : 
    1761             : /**
    1762             :  * Initialize default instance.
    1763             :  *
    1764             :  * This method is normally only used internally by the AddField() method
    1765             :  * to initialize the new field instance with default subfield values.  It
    1766             :  * installs default data for one instance of the field in the record
    1767             :  * using the DDFFieldDefn::GetDefaultValue() method and
    1768             :  * DDFRecord::SetFieldRaw().
    1769             :  *
    1770             :  * @param poField the field within the record to be assign a default
    1771             :  * instance.
    1772             :  * @param iIndexWithinField the instance to set (may not have been tested with
    1773             :  * values other than 0).
    1774             :  *
    1775             :  * @return TRUE on success or FALSE on failure.
    1776             :  */
    1777             : 
    1778        6423 : int DDFRecord::CreateDefaultFieldInstance(DDFField *poField,
    1779             :                                           int iIndexWithinField)
    1780             : 
    1781             : {
    1782        6423 :     int nRawSize = 0;
    1783        6423 :     char *pachRawData = poField->GetFieldDefn()->GetDefaultValue(&nRawSize);
    1784        6423 :     if (pachRawData == nullptr)
    1785         224 :         return FALSE;
    1786             : 
    1787             :     const int nSuccess =
    1788        6199 :         SetFieldRaw(poField, iIndexWithinField, pachRawData, nRawSize);
    1789             : 
    1790        6199 :     CPLFree(pachRawData);
    1791             : 
    1792        6199 :     return nSuccess;
    1793             : }
    1794             : 
    1795             : /************************************************************************/
    1796             : /*                   GetSubfieldDataForSetSubfield()                    */
    1797             : /************************************************************************/
    1798             : 
    1799       25845 : char *DDFRecord::GetSubfieldDataForSetSubfield(DDFField *poField,
    1800             :                                                DDFField *poPartField,
    1801             :                                                const DDFSubfieldDefn *poSFDefn,
    1802             :                                                int iSubfieldIndex,
    1803             :                                                int &nMaxBytes)
    1804             : {
    1805       25845 :     nMaxBytes = 0;
    1806             : 
    1807       25845 :     char *pachSubfieldData = nullptr;
    1808             : 
    1809       25845 :     const auto poLowLevelField = poPartField ? poPartField : poField;
    1810       26675 :     if (poPartField && poLowLevelField->GetFieldDefn()->IsRepeating() &&
    1811         830 :         iSubfieldIndex == poLowLevelField->GetRepeatCount())
    1812             :     {
    1813         491 :         if (poPartField != poField->GetParts().back().get())
    1814             :         {
    1815           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1816             :                      "Can only append new field values to last part of field");
    1817           0 :             return nullptr;
    1818             :         }
    1819             : 
    1820         491 :         int nRawSize = 0;
    1821             :         char *pachRawData =
    1822         491 :             poLowLevelField->GetFieldDefn()->GetDefaultValue(&nRawSize);
    1823         491 :         if (pachRawData == nullptr)
    1824           0 :             return nullptr;
    1825             : 
    1826         491 :         std::string osNewData(poField->GetData(), poField->GetDataSize());
    1827         491 :         if (!osNewData.empty() && osNewData.back() == DDF_FIELD_TERMINATOR)
    1828         491 :             osNewData.pop_back();
    1829         491 :         osNewData.append(pachRawData, nRawSize);
    1830         491 :         osNewData += static_cast<char>(DDF_FIELD_TERMINATOR);
    1831         491 :         CPLFree(pachRawData);
    1832             : 
    1833         491 :         if (!SetFieldRaw(poField, 0, osNewData.c_str(),
    1834         491 :                          static_cast<int>(osNewData.size())))
    1835           0 :             return nullptr;
    1836             : 
    1837         491 :         pachSubfieldData = const_cast<char *>(poLowLevelField->GetSubfieldData(
    1838             :             poSFDefn, &nMaxBytes, iSubfieldIndex));
    1839         491 :         if (pachSubfieldData == nullptr)
    1840           0 :             return nullptr;
    1841             :     }
    1842             :     else
    1843             :     {
    1844       25354 :         pachSubfieldData = const_cast<char *>(poLowLevelField->GetSubfieldData(
    1845             :             poSFDefn, &nMaxBytes, iSubfieldIndex));
    1846       25354 :         if (pachSubfieldData == nullptr)
    1847           0 :             return nullptr;
    1848             : 
    1849             :         /* -------------------------------------------------------------------- */
    1850             :         /*      Add new instance if we have run out of data.                    */
    1851             :         /* -------------------------------------------------------------------- */
    1852       25354 :         if (poPartField == nullptr &&
    1853       21563 :             (nMaxBytes == 0 ||
    1854       21563 :              (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR)))
    1855             :         {
    1856        2154 :             CreateDefaultFieldInstance(poField, iSubfieldIndex);
    1857             : 
    1858             :             // Refetch.
    1859             :             pachSubfieldData = const_cast<char *>(
    1860        2154 :                 poField->GetSubfieldData(poSFDefn, &nMaxBytes, iSubfieldIndex));
    1861        2154 :             if (pachSubfieldData == nullptr)
    1862           0 :                 return nullptr;
    1863             :         }
    1864             :     }
    1865             : 
    1866       25845 :     return pachSubfieldData;
    1867             : }
    1868             : 
    1869             : /************************************************************************/
    1870             : /*                         SetStringSubfield()                          */
    1871             : /************************************************************************/
    1872             : 
    1873             : /**
    1874             :  * Set a string subfield in record.
    1875             :  *
    1876             :  * The value of a given subfield is replaced with a new string value
    1877             :  * formatted appropriately.
    1878             :  *
    1879             :  * @param pszField the field name to operate on.
    1880             :  * @param iFieldIndex the field index to operate on (zero based).
    1881             :  * @param pszSubfield the subfield name to operate on.
    1882             :  * @param iSubfieldIndex the subfield index to operate on (zero based).
    1883             :  * @param pszValue the new string to place in the subfield. This may be
    1884             :  * arbitrary binary bytes if nValueLength is specified.
    1885             :  * @param nValueLength the number of valid bytes in pszValue, may be -1 to
    1886             :  * internally fetch with strlen().
    1887             :  *
    1888             :  * @return TRUE if successful, and FALSE if not.
    1889             :  */
    1890             : 
    1891        5253 : int DDFRecord::SetStringSubfield(const char *pszField, int iFieldIndex,
    1892             :                                  const char *pszSubfield, int iSubfieldIndex,
    1893             :                                  const char *pszValue, int nValueLength)
    1894             : 
    1895             : {
    1896             :     /* -------------------------------------------------------------------- */
    1897             :     /*      Get the subfield definition                                     */
    1898             :     /* -------------------------------------------------------------------- */
    1899        5253 :     auto [poField, poPartField, poSFDefn] =
    1900        5253 :         FindSubfieldDefn(pszField, iFieldIndex, pszSubfield);
    1901        5253 :     if (!poSFDefn)
    1902             :     {
    1903           0 :         return FALSE;
    1904             :     }
    1905             : 
    1906             :     /* -------------------------------------------------------------------- */
    1907             :     /*      How long will the formatted value be?                           */
    1908             :     /* -------------------------------------------------------------------- */
    1909             :     int nFormattedLen;
    1910             : 
    1911        5253 :     if (!poSFDefn->FormatStringValue(nullptr, 0, &nFormattedLen, pszValue,
    1912             :                                      nValueLength))
    1913           0 :         return FALSE;
    1914             : 
    1915             :     /* -------------------------------------------------------------------- */
    1916             :     /*      Get a pointer to the data.                                      */
    1917             :     /* -------------------------------------------------------------------- */
    1918        5253 :     int nMaxBytes = 0;
    1919        5253 :     char *pachSubfieldData = GetSubfieldDataForSetSubfield(
    1920             :         poField, poPartField, poSFDefn, iSubfieldIndex, nMaxBytes);
    1921        5253 :     if (!pachSubfieldData)
    1922           0 :         return FALSE;
    1923             : 
    1924             :     /* -------------------------------------------------------------------- */
    1925             :     /*      If the new length matches the existing length, just overlay     */
    1926             :     /*      and return.                                                     */
    1927             :     /* -------------------------------------------------------------------- */
    1928             :     int nExistingLength;
    1929             : 
    1930        5253 :     poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
    1931             : 
    1932        5253 :     if (nExistingLength == nFormattedLen)
    1933             :     {
    1934        1097 :         return poSFDefn->FormatStringValue(pachSubfieldData, nFormattedLen,
    1935        1097 :                                            nullptr, pszValue, nValueLength);
    1936             :     }
    1937             : 
    1938             :     /* -------------------------------------------------------------------- */
    1939             :     /*      We will need to resize the raw data.                            */
    1940             :     /* -------------------------------------------------------------------- */
    1941        4156 :     const auto poLowLevelField = poPartField ? poPartField : poField;
    1942             :     const char *pachFieldInstData =
    1943        4156 :         poLowLevelField->GetInstanceData(iSubfieldIndex);
    1944             : 
    1945        4156 :     const int nStartOffset =
    1946        4156 :         static_cast<int>(pachSubfieldData - pachFieldInstData);
    1947             : 
    1948        8312 :     std::string osNewData;
    1949        4156 :     osNewData.resize(nFormattedLen);
    1950        4156 :     poSFDefn->FormatStringValue(osNewData.data(), nFormattedLen, nullptr,
    1951             :                                 pszValue, nValueLength);
    1952             : 
    1953        4156 :     return UpdateFieldRaw(poField, poPartField, iSubfieldIndex, nStartOffset,
    1954        8312 :                           nExistingLength, osNewData.data(), nFormattedLen);
    1955             : }
    1956             : 
    1957             : /************************************************************************/
    1958             : /*                           SetIntSubfield()                           */
    1959             : /************************************************************************/
    1960             : 
    1961             : /**
    1962             :  * Set an integer subfield in record.
    1963             :  *
    1964             :  * The value of a given subfield is replaced with a new integer value
    1965             :  * formatted appropriately.
    1966             :  *
    1967             :  * @param pszField the field name to operate on.
    1968             :  * @param iFieldIndex the field index to operate on (zero based).
    1969             :  * @param pszSubfield the subfield name to operate on.
    1970             :  * @param iSubfieldIndex the subfield index to operate on (zero based).
    1971             :  * @param nNewValue the new value to place in the subfield.
    1972             :  *
    1973             :  * @return TRUE if successful, and FALSE if not.
    1974             :  */
    1975             : 
    1976       19972 : int DDFRecord::SetIntSubfield(const char *pszField, int iFieldIndex,
    1977             :                               const char *pszSubfield, int iSubfieldIndex,
    1978             :                               int nNewValue)
    1979             : 
    1980             : {
    1981             :     /* -------------------------------------------------------------------- */
    1982             :     /*      Get the subfield definition                                     */
    1983             :     /* -------------------------------------------------------------------- */
    1984       19972 :     auto [poField, poPartField, poSFDefn] =
    1985       19972 :         FindSubfieldDefn(pszField, iFieldIndex, pszSubfield);
    1986       19972 :     if (!poSFDefn)
    1987             :     {
    1988           0 :         return FALSE;
    1989             :     }
    1990             : 
    1991             :     /* -------------------------------------------------------------------- */
    1992             :     /*      How long will the formatted value be?                           */
    1993             :     /* -------------------------------------------------------------------- */
    1994             :     int nFormattedLen;
    1995             : 
    1996       19972 :     if (!poSFDefn->FormatIntValue(nullptr, 0, &nFormattedLen, nNewValue))
    1997           0 :         return FALSE;
    1998             : 
    1999             :     /* -------------------------------------------------------------------- */
    2000             :     /*      Get a pointer to the data.                                      */
    2001             :     /* -------------------------------------------------------------------- */
    2002       19972 :     int nMaxBytes = 0;
    2003       19972 :     char *pachSubfieldData = GetSubfieldDataForSetSubfield(
    2004             :         poField, poPartField, poSFDefn, iSubfieldIndex, nMaxBytes);
    2005       19972 :     if (!pachSubfieldData)
    2006           0 :         return FALSE;
    2007             : 
    2008             :     /* -------------------------------------------------------------------- */
    2009             :     /*      If the new length matches the existing length, just overlay     */
    2010             :     /*      and return.                                                     */
    2011             :     /* -------------------------------------------------------------------- */
    2012             :     int nExistingLength;
    2013             : 
    2014       19972 :     poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
    2015             : 
    2016       19972 :     if (nExistingLength == nFormattedLen)
    2017             :     {
    2018       19972 :         return poSFDefn->FormatIntValue(pachSubfieldData, nFormattedLen,
    2019       19972 :                                         nullptr, nNewValue);
    2020             :     }
    2021             : 
    2022             :     /* -------------------------------------------------------------------- */
    2023             :     /*      We will need to resize the raw data.                            */
    2024             :     /* -------------------------------------------------------------------- */
    2025           0 :     const auto poLowLevelField = poPartField ? poPartField : poField;
    2026             :     const char *pachFieldInstData =
    2027           0 :         poLowLevelField->GetInstanceData(iSubfieldIndex);
    2028             : 
    2029           0 :     const int nStartOffset =
    2030           0 :         static_cast<int>(pachSubfieldData - pachFieldInstData);
    2031             : 
    2032           0 :     std::string osNewData;
    2033           0 :     osNewData.resize(nFormattedLen);
    2034           0 :     poSFDefn->FormatIntValue(osNewData.data(), nFormattedLen, nullptr,
    2035             :                              nNewValue);
    2036             : 
    2037           0 :     return UpdateFieldRaw(poField, poPartField, iSubfieldIndex, nStartOffset,
    2038           0 :                           nExistingLength, osNewData.data(), nFormattedLen);
    2039             : }
    2040             : 
    2041             : /************************************************************************/
    2042             : /*                          SetFloatSubfield()                          */
    2043             : /************************************************************************/
    2044             : 
    2045             : /**
    2046             :  * Set a float subfield in record.
    2047             :  *
    2048             :  * The value of a given subfield is replaced with a new float value
    2049             :  * formatted appropriately.
    2050             :  *
    2051             :  * @param pszField the field name to operate on.
    2052             :  * @param iFieldIndex the field index to operate on (zero based).
    2053             :  * @param pszSubfield the subfield name to operate on.
    2054             :  * @param iSubfieldIndex the subfield index to operate on (zero based).
    2055             :  * @param dfNewValue the new value to place in the subfield.
    2056             :  *
    2057             :  * @return TRUE if successful, and FALSE if not.
    2058             :  */
    2059             : 
    2060         620 : int DDFRecord::SetFloatSubfield(const char *pszField, int iFieldIndex,
    2061             :                                 const char *pszSubfield, int iSubfieldIndex,
    2062             :                                 double dfNewValue)
    2063             : 
    2064             : {
    2065             :     /* -------------------------------------------------------------------- */
    2066             :     /*      Get the subfield definition                                     */
    2067             :     /* -------------------------------------------------------------------- */
    2068         620 :     auto [poField, poPartField, poSFDefn] =
    2069         620 :         FindSubfieldDefn(pszField, iFieldIndex, pszSubfield);
    2070         620 :     if (!poSFDefn)
    2071             :     {
    2072           0 :         return FALSE;
    2073             :     }
    2074             : 
    2075             :     /* -------------------------------------------------------------------- */
    2076             :     /*      How long will the formatted value be?                           */
    2077             :     /* -------------------------------------------------------------------- */
    2078             :     int nFormattedLen;
    2079             : 
    2080         620 :     if (!poSFDefn->FormatFloatValue(nullptr, 0, &nFormattedLen, dfNewValue))
    2081           0 :         return FALSE;
    2082             : 
    2083             :     /* -------------------------------------------------------------------- */
    2084             :     /*      Get a pointer to the data.                                      */
    2085             :     /* -------------------------------------------------------------------- */
    2086         620 :     int nMaxBytes = 0;
    2087         620 :     char *pachSubfieldData = GetSubfieldDataForSetSubfield(
    2088             :         poField, poPartField, poSFDefn, iSubfieldIndex, nMaxBytes);
    2089         620 :     if (!pachSubfieldData)
    2090           0 :         return FALSE;
    2091             : 
    2092             :     /* -------------------------------------------------------------------- */
    2093             :     /*      If the new length matches the existing length, just overlay     */
    2094             :     /*      and return.                                                     */
    2095             :     /* -------------------------------------------------------------------- */
    2096             :     int nExistingLength;
    2097             : 
    2098         620 :     poSFDefn->GetDataLength(pachSubfieldData, nMaxBytes, &nExistingLength);
    2099             : 
    2100         620 :     if (nExistingLength == nFormattedLen)
    2101             :     {
    2102         620 :         return poSFDefn->FormatFloatValue(pachSubfieldData, nFormattedLen,
    2103         620 :                                           nullptr, dfNewValue);
    2104             :     }
    2105             : 
    2106             :     /* -------------------------------------------------------------------- */
    2107             :     /*      We will need to resize the raw data.                            */
    2108             :     /* -------------------------------------------------------------------- */
    2109           0 :     const auto poLowLevelField = poPartField ? poPartField : poField;
    2110             :     const char *pachFieldInstData =
    2111           0 :         poLowLevelField->GetInstanceData(iSubfieldIndex);
    2112             : 
    2113           0 :     const int nStartOffset =
    2114           0 :         static_cast<int>(pachSubfieldData - pachFieldInstData);
    2115             : 
    2116           0 :     std::string osNewData;
    2117           0 :     osNewData.resize(nFormattedLen);
    2118           0 :     poSFDefn->FormatFloatValue(osNewData.data(), nFormattedLen, nullptr,
    2119             :                                dfNewValue);
    2120             : 
    2121           0 :     return UpdateFieldRaw(poField, poPartField, iSubfieldIndex, nStartOffset,
    2122           0 :                           nExistingLength, osNewData.data(), nFormattedLen);
    2123             : }

Generated by: LCOV version 1.14