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

Generated by: LCOV version 1.14