LCOV - code coverage report
Current view: top level - frmts/iso8211 - ddffielddefn.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 339 497 68.2 %
Date: 2026-05-29 23:25:07 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISO 8211 Access
       4             :  * Purpose:  Implements the DDFFieldDefn class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2026, Even Rouault
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "iso8211.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <cctype>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : 
      24             : #include "cpl_conv.h"
      25             : #include "cpl_error.h"
      26             : #include "cpl_string.h"
      27             : 
      28             : #define CPLE_DiscardedFormat 1301
      29             : 
      30             : /************************************************************************/
      31             : /*                            DDFFieldDefn()                            */
      32             : /************************************************************************/
      33             : 
      34             : DDFFieldDefn::DDFFieldDefn() = default;
      35             : 
      36             : /************************************************************************/
      37             : /*                           ~DDFFieldDefn()                            */
      38             : /************************************************************************/
      39             : 
      40             : DDFFieldDefn::~DDFFieldDefn() = default;
      41             : 
      42             : /************************************************************************/
      43             : /*                            AddSubfield()                             */
      44             : /************************************************************************/
      45             : 
      46        1691 : void DDFFieldDefn::AddSubfield(const char *pszName, const char *pszFormat)
      47             : 
      48             : {
      49        1691 :     auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
      50             : 
      51        1691 :     poSFDefn->SetName(pszName);
      52        1691 :     poSFDefn->SetFormat(pszFormat);
      53        1691 :     AddSubfield(std::move(poSFDefn));
      54        1691 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                            AddSubfield()                             */
      58             : /************************************************************************/
      59             : 
      60       64284 : void DDFFieldDefn::AddSubfield(std::unique_ptr<DDFSubfieldDefn> poNewSFDefn,
      61             :                                bool bDontAddToFormat)
      62             : 
      63             : {
      64       64284 :     if (bDontAddToFormat)
      65             :     {
      66       62593 :         apoSubfields.push_back(std::move(poNewSFDefn));
      67       62593 :         return;
      68             :     }
      69             : 
      70             :     /* -------------------------------------------------------------------- */
      71             :     /*      Add this format to the format list.  We don't bother            */
      72             :     /*      aggregating formats here.                                       */
      73             :     /* -------------------------------------------------------------------- */
      74        1691 :     if (_formatControls.empty())
      75             :     {
      76         342 :         _formatControls = "()";
      77             :     }
      78             : 
      79        3382 :     std::string osNewFormatControls = _formatControls;
      80        1691 :     osNewFormatControls.pop_back();
      81        1691 :     if (!osNewFormatControls.empty() && osNewFormatControls.back() != '(')
      82        1349 :         osNewFormatControls += ',';
      83        1691 :     osNewFormatControls += poNewSFDefn->GetFormat();
      84        1691 :     osNewFormatControls += ')';
      85             : 
      86        1691 :     _formatControls = std::move(osNewFormatControls);
      87             : 
      88             :     /* -------------------------------------------------------------------- */
      89             :     /*      Add the subfield name to the list.                              */
      90             :     /* -------------------------------------------------------------------- */
      91        3192 :     if (!_arrayDescr.empty() &&
      92        1501 :         (_arrayDescr[0] != '*' || _arrayDescr.size() > 1))
      93        1349 :         _arrayDescr += '!';
      94        1691 :     _arrayDescr += poNewSFDefn->GetName();
      95             : 
      96        1691 :     apoSubfields.push_back(std::move(poNewSFDefn));
      97             : }
      98             : 
      99             : /************************************************************************/
     100             : /*                               Create()                               */
     101             : /*                                                                      */
     102             : /*      Initialize a new field defn from application input, instead     */
     103             : /*      of from an existing file.                                       */
     104             : /************************************************************************/
     105             : 
     106        3178 : int DDFFieldDefn::Create(const char *pszTagIn, const char *pszFieldName,
     107             :                          const char *pszDescription,
     108             :                          DDF_data_struct_code eDataStructCode,
     109             :                          DDF_data_type_code eDataTypeCode,
     110             :                          const char *pszFormat)
     111             : 
     112             : {
     113        3178 :     CPLAssert(osTag.empty());
     114        3178 :     poModule = nullptr;
     115        3178 :     osTag = pszTagIn;
     116        3178 :     _fieldName = pszFieldName;
     117        3178 :     _arrayDescr = pszDescription ? pszDescription : "";
     118             : 
     119        3178 :     _data_struct_code = eDataStructCode;
     120        3178 :     _data_type_code = eDataTypeCode;
     121             : 
     122        3178 :     _formatControls = pszFormat ? pszFormat : "";
     123             : 
     124        3178 :     bRepeatingSubfields = (pszDescription != nullptr && *pszDescription == '*');
     125             : 
     126        3178 :     if (!_formatControls.empty() && _data_struct_code != dsc_elementary)
     127             :     {
     128        2592 :         if (!BuildSubfields())
     129           0 :             return false;
     130             : 
     131        2592 :         if (apoFieldParts.empty() && !ApplyFormats())
     132           0 :             return false;
     133             :     }
     134             : 
     135        3178 :     return TRUE;
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                          GenerateDDREntry()                          */
     140             : /************************************************************************/
     141             : 
     142        9474 : int DDFFieldDefn::GenerateDDREntry(DDFModule *poModuleIn, char **ppachData,
     143             :                                    int *pnLength)
     144             : 
     145             : {
     146        9474 :     const int iFDOffset = poModuleIn->GetFieldControlLength();
     147        9474 :     CPLAssert(iFDOffset >= 6 && iFDOffset <= 9);
     148        9474 :     *pnLength = static_cast<int>(iFDOffset + _fieldName.size() + 1 +
     149        9474 :                                  _arrayDescr.size() + 1);
     150        9474 :     if (!_formatControls.empty())
     151             :     {
     152        8808 :         *pnLength += static_cast<int>(_formatControls.size() + 1);
     153             :     }
     154             : 
     155        9474 :     if (ppachData == nullptr)
     156        6316 :         return TRUE;
     157             : 
     158        3158 :     *ppachData = static_cast<char *>(CPLMalloc(*pnLength + 1));
     159        3158 :     (*ppachData)[*pnLength] = 0;
     160             : 
     161        3158 :     if (_data_struct_code == dsc_elementary)
     162         242 :         (*ppachData)[0] = '0';
     163        2916 :     else if (_data_struct_code == dsc_vector)
     164        1512 :         (*ppachData)[0] = '1';
     165        1404 :     else if (_data_struct_code == dsc_array)
     166        1100 :         (*ppachData)[0] = '2';
     167         304 :     else if (_data_struct_code == dsc_concatenated)
     168         304 :         (*ppachData)[0] = '3';
     169             : 
     170        3158 :     if (_data_type_code == dtc_char_string)
     171         224 :         (*ppachData)[1] = '0';
     172        2934 :     else if (_data_type_code == dtc_implicit_point)
     173        1288 :         (*ppachData)[1] = '1';
     174        1646 :     else if (_data_type_code == dtc_explicit_point)
     175           0 :         (*ppachData)[1] = '2';
     176        1646 :     else if (_data_type_code == dtc_explicit_point_scaled)
     177           0 :         (*ppachData)[1] = '3';
     178        1646 :     else if (_data_type_code == dtc_char_bit_string)
     179           0 :         (*ppachData)[1] = '4';
     180        1646 :     else if (_data_type_code == dtc_bit_string)
     181          57 :         (*ppachData)[1] = '5';
     182        1589 :     else if (_data_type_code == dtc_mixed_data_type)
     183        1589 :         (*ppachData)[1] = '6';
     184             : 
     185        3158 :     (*ppachData)[2] = '0';
     186        3158 :     (*ppachData)[3] = '0';
     187        3158 :     (*ppachData)[4] = ';';
     188        3158 :     (*ppachData)[5] = '&';
     189        3158 :     if (iFDOffset > 6 && _escapeSequence.size() >= 1)
     190        3158 :         (*ppachData)[6] = _escapeSequence[0];
     191        3158 :     if (iFDOffset > 7 && _escapeSequence.size() >= 2)
     192        3158 :         (*ppachData)[7] = _escapeSequence[1];
     193        3158 :     if (iFDOffset > 8 && _escapeSequence.size() >= 3)
     194        3158 :         (*ppachData)[8] = _escapeSequence[2];
     195        3158 :     snprintf(*ppachData + iFDOffset, *pnLength + 1 - iFDOffset, "%s",
     196             :              _fieldName.c_str());
     197        3158 :     snprintf(*ppachData + strlen(*ppachData),
     198        3158 :              *pnLength + 1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR,
     199             :              _arrayDescr.c_str());
     200        3158 :     if (!_formatControls.empty())
     201             :     {
     202             :         // empty for '0000' of S-57 & S-111
     203        2936 :         snprintf(*ppachData + strlen(*ppachData),
     204        2936 :                  *pnLength + 1 - strlen(*ppachData), "%c%s",
     205             :                  DDF_UNIT_TERMINATOR, _formatControls.c_str());
     206             :     }
     207        3158 :     snprintf(*ppachData + strlen(*ppachData),
     208        3158 :              *pnLength + 1 - strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR);
     209             : 
     210        3158 :     return TRUE;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                             Initialize()                             */
     215             : /*                                                                      */
     216             : /*      Initialize the field definition from the information in the     */
     217             : /*      DDR record.  This is called by DDFModule::Open().               */
     218             : /************************************************************************/
     219             : 
     220       10214 : int DDFFieldDefn::Initialize(DDFModule *poModuleIn, const char *pszTagIn,
     221             :                              int nFieldEntrySize, const char *pachFieldArea)
     222             : 
     223             : {
     224       10214 :     int iFDOffset = poModuleIn->GetFieldControlLength();
     225             : 
     226       10214 :     poModule = poModuleIn;
     227             : 
     228       10214 :     osTag = pszTagIn;
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      Set the data struct and type codes.                             */
     232             :     /* -------------------------------------------------------------------- */
     233       10214 :     switch (pachFieldArea[0])
     234             :     {
     235         770 :         case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
     236             :         case '0':
     237         770 :             _data_struct_code = dsc_elementary;
     238         770 :             break;
     239             : 
     240        5011 :         case '1':
     241        5011 :             _data_struct_code = dsc_vector;
     242        5011 :             break;
     243             : 
     244        3477 :         case '2':
     245        3477 :             _data_struct_code = dsc_array;
     246        3477 :             break;
     247             : 
     248         956 :         case '3':
     249         956 :             _data_struct_code = dsc_concatenated;
     250         956 :             break;
     251             : 
     252           0 :         default:
     253           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     254             :                      "Unrecognized data_struct_code value %c.\n"
     255             :                      "Field %s initialization incorrect.",
     256           0 :                      pachFieldArea[0], osTag.c_str());
     257           0 :             _data_struct_code = dsc_elementary;
     258             :     }
     259             : 
     260       10214 :     switch (pachFieldArea[1])
     261             :     {
     262         833 :         case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */
     263             :         case '0':
     264         833 :             _data_type_code = dtc_char_string;
     265         833 :             break;
     266             : 
     267        4340 :         case '1':
     268        4340 :             _data_type_code = dtc_implicit_point;
     269        4340 :             break;
     270             : 
     271          16 :         case '2':
     272          16 :             _data_type_code = dtc_explicit_point;
     273          16 :             break;
     274             : 
     275           0 :         case '3':
     276           0 :             _data_type_code = dtc_explicit_point_scaled;
     277           0 :             break;
     278             : 
     279           0 :         case '4':
     280           0 :             _data_type_code = dtc_char_bit_string;
     281           0 :             break;
     282             : 
     283         128 :         case '5':
     284         128 :             _data_type_code = dtc_bit_string;
     285         128 :             break;
     286             : 
     287        4897 :         case '6':
     288        4897 :             _data_type_code = dtc_mixed_data_type;
     289        4897 :             break;
     290             : 
     291           0 :         default:
     292           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     293             :                      "Unrecognized data_type_code value %c.\n"
     294             :                      "Field %s initialization incorrect.",
     295           0 :                      pachFieldArea[1], osTag.c_str());
     296           0 :             _data_type_code = dtc_char_string;
     297             :     }
     298             : 
     299       10214 :     if (nFieldEntrySize >= iFDOffset && iFDOffset > 6)
     300        9779 :         _escapeSequence.assign(pachFieldArea + 6, iFDOffset - 6);
     301             : 
     302             :     /* -------------------------------------------------------------------- */
     303             :     /*      Capture the field name, description (sub field names), and      */
     304             :     /*      format statements.                                              */
     305             :     /* -------------------------------------------------------------------- */
     306             : 
     307       10214 :     int nCharsConsumed = 0;
     308       10214 :     _fieldName = DDFFetchVariable(
     309       10214 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     310       10214 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     311       10214 :     iFDOffset += nCharsConsumed;
     312             : 
     313       10214 :     _arrayDescr = DDFFetchVariable(
     314       10214 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     315       10214 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     316       10214 :     iFDOffset += nCharsConsumed;
     317             : 
     318       10214 :     _formatControls = DDFFetchVariable(
     319       10214 :         pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset,
     320       10214 :         DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed);
     321             : 
     322             :     /* -------------------------------------------------------------------- */
     323             :     /*      Parse the subfield info.                                        */
     324             :     /* -------------------------------------------------------------------- */
     325       10214 :     if (_data_struct_code != dsc_elementary)
     326             :     {
     327        9444 :         if (!BuildSubfields())
     328           0 :             return false;
     329             : 
     330        9444 :         if (apoFieldParts.empty() && !ApplyFormats())
     331           0 :             return false;
     332             :     }
     333             : 
     334       10214 :     return TRUE;
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /*                         SetEscapeSequence()                          */
     339             : /************************************************************************/
     340             : 
     341        2724 : void DDFFieldDefn::SetEscapeSequence(const std::string &val)
     342             : {
     343        2724 :     _escapeSequence = val;
     344        2724 : }
     345             : 
     346             : /************************************************************************/
     347             : /*                                Dump()                                */
     348             : /************************************************************************/
     349             : 
     350             : /**
     351             :  * Write out field definition info to debugging file.
     352             :  *
     353             :  * A variety of information about this field definition, and all its
     354             :  * subfields is written to the give debugging file handle.
     355             :  *
     356             :  * @param fp The standard IO file handle to write to.  i.e. stderr
     357             :  */
     358             : 
     359           0 : void DDFFieldDefn::Dump(FILE *fp, int nNestingLevel) const
     360             : 
     361             : {
     362           0 :     std::string osIndent;
     363           0 :     for (int i = 0; i < nNestingLevel; ++i)
     364           0 :         osIndent += "  ";
     365             : 
     366             : #define Print(...)                                                             \
     367             :     do                                                                         \
     368             :     {                                                                          \
     369             :         fprintf(fp, "%s", osIndent.c_str());                                   \
     370             :         fprintf(fp, __VA_ARGS__);                                              \
     371             :     } while (0)
     372             : 
     373           0 :     const char *pszValue = "";
     374           0 :     CPL_IGNORE_RET_VAL(pszValue);  // Make CSA happy
     375             : 
     376           0 :     Print("DDFFieldDefn:\n");
     377           0 :     Print("    Tag = `%s'\n", osTag.c_str());
     378           0 :     Print("    _fieldName = `%s'\n", _fieldName.c_str());
     379           0 :     Print("    _arrayDescr = `%s'\n", _arrayDescr.c_str());
     380           0 :     Print("    _formatControls = `%s'\n", _formatControls.c_str());
     381             : 
     382           0 :     switch (_data_struct_code)
     383             :     {
     384           0 :         case dsc_elementary:
     385           0 :             pszValue = "elementary";
     386           0 :             break;
     387             : 
     388           0 :         case dsc_vector:
     389           0 :             pszValue = "vector";
     390           0 :             break;
     391             : 
     392           0 :         case dsc_array:
     393           0 :             pszValue = "array";
     394           0 :             break;
     395             : 
     396           0 :         case dsc_concatenated:
     397           0 :             pszValue = "concatenated";
     398           0 :             break;
     399             :     }
     400             : 
     401           0 :     Print("    _data_struct_code = %s\n", pszValue);
     402             : 
     403           0 :     switch (_data_type_code)
     404             :     {
     405           0 :         case dtc_char_string:
     406           0 :             pszValue = "char_string";
     407           0 :             break;
     408             : 
     409           0 :         case dtc_implicit_point:
     410           0 :             pszValue = "implicit_point";
     411           0 :             break;
     412             : 
     413           0 :         case dtc_explicit_point:
     414           0 :             pszValue = "explicit_point";
     415           0 :             break;
     416             : 
     417           0 :         case dtc_explicit_point_scaled:
     418           0 :             pszValue = "explicit_point_scaled";
     419           0 :             break;
     420             : 
     421           0 :         case dtc_char_bit_string:
     422           0 :             pszValue = "char_bit_string";
     423           0 :             break;
     424             : 
     425           0 :         case dtc_bit_string:
     426           0 :             pszValue = "bit_string";
     427           0 :             break;
     428             : 
     429           0 :         case dtc_mixed_data_type:
     430           0 :             pszValue = "mixed_data_type";
     431           0 :             break;
     432             :     }
     433             : 
     434           0 :     Print("    _data_type_code = %s\n", pszValue);
     435             : 
     436           0 :     for (const auto &poField : apoFieldParts)
     437           0 :         poField->Dump(fp, nNestingLevel + 1);
     438             : 
     439           0 :     for (const auto &poSubfield : apoSubfields)
     440           0 :         poSubfield->Dump(fp, nNestingLevel + 1);
     441           0 : }
     442             : 
     443             : /************************************************************************/
     444             : /*                           BuildSubfields()                           */
     445             : /*                                                                      */
     446             : /*      Based on the _arrayDescr build a set of subfields.              */
     447             : /************************************************************************/
     448             : 
     449       14556 : bool DDFFieldDefn::BuildSubfields()
     450             : 
     451             : {
     452       14556 :     const char *pszSublist = _arrayDescr.c_str();
     453             : 
     454       14556 :     if (_data_struct_code == dsc_concatenated)
     455             :     {
     456             :         // Split on two consecutive backslashes.
     457        1260 :         std::vector<std::string> aosPartDescr;
     458             :         {
     459        2520 :             std::string osCur;
     460       80210 :             for (size_t i = 0; i < _arrayDescr.size(); ++i)
     461             :             {
     462       78950 :                 const char c = _arrayDescr[i];
     463       78950 :                 if (c == '\\' && _arrayDescr[i + 1] == '\\')
     464             :                 {
     465        1260 :                     aosPartDescr.push_back(osCur);
     466        1260 :                     osCur.clear();
     467        1260 :                     ++i;
     468             :                 }
     469             :                 else
     470             :                 {
     471       77690 :                     osCur += c;
     472             :                 }
     473             :             }
     474        1260 :             aosPartDescr.push_back(std::move(osCur));
     475             :         }
     476        2520 :         if (aosPartDescr.size() > 1 && !_formatControls.empty() &&
     477        2520 :             _formatControls.front() == '(' && _formatControls.back() == ')')
     478             :         {
     479        1260 :             const char *pszFormatCur = _formatControls.c_str() + 1;
     480        3780 :             for (size_t i = 0; i < aosPartDescr.size(); ++i)
     481             :             {
     482        2520 :                 const std::string &osPartDescr = aosPartDescr[i];
     483             :                 // Check there are no repeated subfields but in the last part
     484        3780 :                 if (i < aosPartDescr.size() - 1 &&
     485        1260 :                     osPartDescr.find('*') != std::string::npos)
     486             :                 {
     487           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     488             :                              "Tag %s: repeated fields found in a part that is "
     489             :                              "not the last one: %s",
     490             :                              osTag.c_str(), _arrayDescr.c_str());
     491           0 :                     return false;
     492             :                 }
     493             : 
     494             :                 const int nSubfieldsInPart = static_cast<int>(
     495        2520 :                     std::count(osPartDescr.begin(), osPartDescr.end(), '!') +
     496        2520 :                     1);
     497        2520 :                 int nSubFieldCounter = 0;
     498        2520 :                 const char *pszFormatStart = pszFormatCur;
     499        2520 :                 bool justAfterFieldFormat = true;
     500        2520 :                 int nParenthesisLevel = 0;
     501       19116 :                 while (i < aosPartDescr.size() - 1 && *pszFormatCur != '\0')
     502             :                 {
     503       17856 :                     if (justAfterFieldFormat && *pszFormatCur >= '1' &&
     504        5811 :                         *pszFormatCur <= '9')
     505             :                     {
     506        2068 :                         char *pszNext = nullptr;
     507             :                         const int nRepeat = static_cast<int>(
     508        2068 :                             strtol(pszFormatCur, &pszNext, 10));
     509        2068 :                         if (!(*pszNext) || nRepeat <= 0 || nRepeat > 1000)
     510             :                         {
     511           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     512             :                                      "Tag %s: invalid formatControls: %s",
     513             :                                      osTag.c_str(), _formatControls.c_str());
     514           0 :                             return false;
     515             :                         }
     516        2068 :                         if (*pszNext == '(')
     517             :                         {
     518           0 :                             pszFormatCur = pszNext + 1;
     519           0 :                             int nGroupSubFieldCount = 0;
     520           0 :                             while (*pszFormatCur)
     521             :                             {
     522           0 :                                 if (*pszFormatCur == '(')
     523             :                                 {
     524             :                                     // Implementation limitation. Perhaps OK per the standard
     525           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
     526             :                                              "Tag %s: unsupported "
     527             :                                              "formatControls: %s",
     528             :                                              osTag.c_str(),
     529             :                                              _formatControls.c_str());
     530           0 :                                     return false;
     531             :                                 }
     532           0 :                                 else if (*pszFormatCur >= '1' &&
     533           0 :                                          *pszFormatCur <= '9')
     534             :                                 {
     535             :                                     const long nIncrement =
     536           0 :                                         strtol(pszFormatCur, &pszNext, 10);
     537           0 :                                     if (!(*pszNext) ||
     538             :                                         nIncrement >
     539           0 :                                             INT_MAX - nGroupSubFieldCount)
     540             :                                     {
     541           0 :                                         CPLError(CE_Failure, CPLE_AppDefined,
     542             :                                                  "Tag %s: invalid "
     543             :                                                  "formatControls: %s",
     544             :                                                  osTag.c_str(),
     545             :                                                  _formatControls.c_str());
     546           0 :                                         return false;
     547             :                                     }
     548           0 :                                     nGroupSubFieldCount +=
     549             :                                         static_cast<int>(nIncrement);
     550           0 :                                     pszFormatCur = pszNext;
     551             :                                 }
     552           0 :                                 else if (*pszFormatCur == ',')
     553             :                                 {
     554           0 :                                     nGroupSubFieldCount++;
     555           0 :                                     ++pszFormatCur;
     556             :                                 }
     557           0 :                                 else if (*pszFormatCur == ')')
     558             :                                 {
     559           0 :                                     nGroupSubFieldCount++;
     560           0 :                                     break;
     561             :                                 }
     562             :                                 else
     563             :                                 {
     564           0 :                                     ++pszFormatCur;
     565             :                                 }
     566             :                             }
     567           0 :                             if (!*pszFormatCur)
     568             :                             {
     569           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     570             :                                          "Tag %s: invalid formatControls: %s",
     571             :                                          osTag.c_str(),
     572             :                                          _formatControls.c_str());
     573           0 :                                 return false;
     574             :                             }
     575           0 :                             ++pszFormatCur;
     576           0 :                             if (nGroupSubFieldCount < 0 ||
     577           0 :                                 nGroupSubFieldCount > INT_MAX / nRepeat ||
     578             :                                 nSubFieldCounter >
     579           0 :                                     INT_MAX - nGroupSubFieldCount * nRepeat)
     580             :                             {
     581           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     582             :                                          "Tag %s: invalid "
     583             :                                          "formatControls: %s",
     584             :                                          osTag.c_str(),
     585             :                                          _formatControls.c_str());
     586           0 :                                 return false;
     587             :                             }
     588           0 :                             nSubFieldCounter += nGroupSubFieldCount * nRepeat;
     589           0 :                             if (*pszFormatCur == ')')
     590           0 :                                 break;
     591           0 :                             if (*pszFormatCur != ',')
     592             :                             {
     593           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     594             :                                          "Tag %s: invalid formatControls: %s",
     595             :                                          osTag.c_str(),
     596             :                                          _formatControls.c_str());
     597           0 :                                 return false;
     598             :                             }
     599           0 :                             ++pszFormatCur;
     600             :                         }
     601             :                         else
     602             :                         {
     603        2068 :                             nSubFieldCounter += nRepeat;
     604        2068 :                             pszFormatCur = pszNext;
     605        4946 :                             while (*pszFormatCur && *pszFormatCur != ',' &&
     606        2878 :                                    *pszFormatCur != ')')
     607             :                             {
     608        2878 :                                 ++pszFormatCur;
     609             :                             }
     610        2068 :                             if (*pszFormatCur == ')')
     611           0 :                                 break;
     612        2068 :                             if (*pszFormatCur != ',')
     613             :                             {
     614           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     615             :                                          "Tag %s: invalid formatControls: %s",
     616             :                                          osTag.c_str(),
     617             :                                          _formatControls.c_str());
     618           0 :                                 return false;
     619             :                             }
     620        2068 :                             ++pszFormatCur;
     621             :                         }
     622        2068 :                         justAfterFieldFormat = true;
     623             :                     }
     624       15788 :                     else if (*pszFormatCur == ',')
     625             :                     {
     626        3743 :                         ++nSubFieldCounter;
     627        3743 :                         ++pszFormatCur;
     628        3743 :                         justAfterFieldFormat = true;
     629             :                     }
     630       12045 :                     else if (*pszFormatCur == '(')
     631             :                     {
     632         834 :                         ++nParenthesisLevel;
     633         834 :                         ++pszFormatCur;
     634         834 :                         justAfterFieldFormat = false;
     635             :                     }
     636       11211 :                     else if (*pszFormatCur == ')')
     637             :                     {
     638         834 :                         if (--nParenthesisLevel < 0)
     639             :                         {
     640           0 :                             ++nSubFieldCounter;
     641           0 :                             break;
     642             :                         }
     643         834 :                         ++pszFormatCur;
     644         834 :                         justAfterFieldFormat = false;
     645             :                     }
     646             :                     else
     647             :                     {
     648       10377 :                         ++pszFormatCur;
     649       10377 :                         justAfterFieldFormat = false;
     650             :                     }
     651             : 
     652       17856 :                     if (nSubFieldCounter > nSubfieldsInPart)
     653             :                     {
     654           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     655             :                                  "Tag %s: mismatch between arrayDescr:%s and "
     656             :                                  "formatControls: %s",
     657             :                                  osTag.c_str(), _arrayDescr.c_str(),
     658             :                                  _formatControls.c_str());
     659           0 :                         return false;
     660             :                     }
     661       17856 :                     else if (nSubFieldCounter == nSubfieldsInPart)
     662             :                     {
     663        1260 :                         break;
     664             :                     }
     665             :                 }
     666        2520 :                 if (i < aosPartDescr.size() - 1)
     667             :                 {
     668        1260 :                     if (*pszFormatCur == 0 || pszFormatCur[-1] != ',' ||
     669             :                         nParenthesisLevel > 0)
     670             :                     {
     671           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     672             :                                  "Tag %s: invalid formatControls: %s",
     673             :                                  osTag.c_str(), _formatControls.c_str());
     674           0 :                         return false;
     675             :                     }
     676        1260 :                     if (nSubFieldCounter != nSubfieldsInPart)
     677             :                     {
     678           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     679             :                                  "Tag %s: mismatch between arrayDescr:%s and "
     680             :                                  "formatControls: %s",
     681             :                                  osTag.c_str(), _arrayDescr.c_str(),
     682             :                                  _formatControls.c_str());
     683           0 :                         return false;
     684             :                     }
     685             :                 }
     686             : 
     687        2520 :                 std::string osPartFormatControls;
     688        2520 :                 if (i < aosPartDescr.size() - 1)
     689             :                 {
     690        1260 :                     if (pszFormatCur == pszFormatStart)
     691             :                     {
     692           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     693             :                                  "Tag %s: mismatch between arrayDescr:%s and "
     694             :                                  "formatControls or invalid formatControls: %s",
     695             :                                  osTag.c_str(), _arrayDescr.c_str(),
     696             :                                  _formatControls.c_str());
     697           0 :                         return false;
     698             :                     }
     699        1260 :                     osPartFormatControls = '(';
     700             :                     osPartFormatControls.append(
     701        1260 :                         pszFormatStart, pszFormatCur - pszFormatStart - 1);
     702        1260 :                     osPartFormatControls += ')';
     703             :                 }
     704             :                 else
     705             :                 {
     706        2520 :                     if (*pszFormatStart == '(' && _formatControls.size() > 2 &&
     707        1260 :                         _formatControls[_formatControls.size() - 2] == ')')
     708             :                     {
     709             :                         // S101 2.0
     710             :                         osPartFormatControls.append(pszFormatStart,
     711        1260 :                                                     strlen(pszFormatStart) - 1);
     712             :                     }
     713             :                     else
     714             :                     {
     715             :                         // Earlier versions
     716           0 :                         osPartFormatControls = '(';
     717           0 :                         osPartFormatControls += pszFormatStart;
     718             :                     }
     719             :                 }
     720             : 
     721        2520 :                 auto poPartFieldDefn = std::make_unique<DDFFieldDefn>();
     722        2520 :                 poPartFieldDefn->poModule = poModule;
     723             :                 // poPartFieldDefn->osTag: not set on purpose
     724             :                 // poPartFieldDefn->_fieldName: not set on purpose
     725        2520 :                 poPartFieldDefn->_arrayDescr = osPartDescr;
     726        2520 :                 poPartFieldDefn->_formatControls =
     727        5040 :                     std::move(osPartFormatControls);
     728        2520 :                 poPartFieldDefn->_data_struct_code =
     729             :                     dsc_vector;  // not necessarily exact, but good enough
     730        2520 :                 poPartFieldDefn->_data_type_code = _data_type_code;
     731        2520 :                 poPartFieldDefn->bRepeatingSubfields =
     732        2520 :                     !osPartDescr.empty() && osPartDescr.front() == '*';
     733             : 
     734        5040 :                 if (!poPartFieldDefn->BuildSubfields() ||
     735        2520 :                     !poPartFieldDefn->ApplyFormats())
     736           0 :                     return false;
     737             : 
     738        2520 :                 apoFieldParts.push_back(std::move(poPartFieldDefn));
     739             :             }
     740             : 
     741        1260 :             return true;
     742             :         }
     743             :     }
     744             : 
     745             :     /* -------------------------------------------------------------------- */
     746             :     /*      It is valid to define a field with _arrayDesc                   */
     747             :     /*      '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'.        */
     748             :     /*      This basically indicates that there are 3 (YCOO,XCOO)           */
     749             :     /*      structures named STPT, CTPT and ENPT.  But we can't handle      */
     750             :     /*      such a case gracefully here, so we just ignore the              */
     751             :     /*      "structure names" and treat such a thing as a repeating         */
     752             :     /*      YCOO/XCOO array.  This occurs with the AR2D field of some       */
     753             :     /*      AML S-57 files for instance.                                    */
     754             :     /*                                                                      */
     755             :     /*      We accomplish this by ignoring everything before the last       */
     756             :     /*      '*' in the subfield list.                                       */
     757             :     /* -------------------------------------------------------------------- */
     758       13296 :     if (strrchr(pszSublist, '*') != nullptr)
     759        5711 :         pszSublist = strrchr(pszSublist, '*');
     760             : 
     761             :     /* -------------------------------------------------------------------- */
     762             :     /*      Strip off the repeating marker, when it occurs, but mark our    */
     763             :     /*      field as repeating.                                             */
     764             :     /* -------------------------------------------------------------------- */
     765       13296 :     if (pszSublist[0] == '*')
     766             :     {
     767        5711 :         bRepeatingSubfields = TRUE;
     768        5711 :         pszSublist++;
     769             :     }
     770             : 
     771             :     /* -------------------------------------------------------------------- */
     772             :     /*      split list of fields .                                          */
     773             :     /* -------------------------------------------------------------------- */
     774             :     const CPLStringList aosSubfieldNames(
     775       13296 :         CSLTokenizeStringComplex(pszSublist, "!", FALSE, FALSE));
     776             : 
     777             :     /* -------------------------------------------------------------------- */
     778             :     /*      minimally initialize the subfields.  More will be done later.   */
     779             :     /* -------------------------------------------------------------------- */
     780       75889 :     for (const char *pszSubfieldName : cpl::Iterate(aosSubfieldNames))
     781             :     {
     782       62593 :         auto poSFDefn = std::make_unique<DDFSubfieldDefn>();
     783             : 
     784       62593 :         poSFDefn->SetName(pszSubfieldName);
     785       62593 :         AddSubfield(std::move(poSFDefn), true);
     786             :     }
     787             : 
     788       13296 :     return true;
     789             : }
     790             : 
     791             : /************************************************************************/
     792             : /*                          ExtractSubstring()                          */
     793             : /*                                                                      */
     794             : /*      Extract a substring terminated by a comma (or end of            */
     795             : /*      string).  Commas in brackets are ignored as terminated with     */
     796             : /*      bracket nesting understood gracefully.  If the returned         */
     797             : /*      string would begin and end with a bracket then strip off the    */
     798             : /*      brackets.                                                       */
     799             : /*                                                                      */
     800             : /*      Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D".    */
     801             : /*      Giveh a string like "3A,2C" return "3A".                        */
     802             : /*      Giveh a string like "(3A,2C" return an empty string             */
     803             : /*      Giveh a string like "3A),2C" return an empty string             */
     804             : /************************************************************************/
     805             : 
     806       22968 : std::string DDFFieldDefn::ExtractSubstring(const char *pszSrc)
     807             : 
     808             : {
     809       22968 :     int nBracket = 0;
     810       22968 :     int i = 0;  // Used after for.
     811      211751 :     for (; pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); i++)
     812             :     {
     813      188783 :         if (pszSrc[i] == '(')
     814       16248 :             nBracket++;
     815      172535 :         else if (pszSrc[i] == ')')
     816             :         {
     817       16248 :             nBracket--;
     818       16248 :             if (nBracket < 0)
     819           0 :                 return std::string();
     820             :         }
     821             :     }
     822       22968 :     if (nBracket > 0)
     823           0 :         return std::string();
     824             : 
     825       22968 :     if (pszSrc[0] == '(')
     826             :     {
     827       13296 :         CPLAssert(i >= 2);
     828       13296 :         return std::string(pszSrc + 1, i - 2);
     829             :     }
     830             :     else
     831             :     {
     832        9672 :         return std::string(pszSrc, i);
     833             :     }
     834             : }
     835             : 
     836             : /************************************************************************/
     837             : /*                            ExpandFormat()                            */
     838             : /************************************************************************/
     839             : 
     840       36264 : std::string DDFFieldDefn::ExpandFormat(const char *pszSrc)
     841             : 
     842             : {
     843       72528 :     std::string osDest;
     844       36264 :     size_t iSrc = 0;
     845             : 
     846      187406 :     while (pszSrc[iSrc] != '\0')
     847             :     {
     848             :         // This is presumably an extra level of brackets around some
     849             :         // binary stuff related to rescanning which we don't care to do
     850             :         // (see 6.4.3.3 of the standard.  We just strip off the extra
     851             :         // layer of brackets.
     852      151142 :         if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') && pszSrc[iSrc] == '(')
     853             :         {
     854       13296 :             const std::string osContents = ExtractSubstring(pszSrc + iSrc);
     855       13296 :             if (osContents.empty())
     856             :             {
     857           0 :                 return std::string();
     858             :             }
     859             :             const std::string osExpandedContents =
     860       13296 :                 ExpandFormat(osContents.c_str());
     861       13296 :             if (osExpandedContents.empty())
     862             :             {
     863           0 :                 return std::string();
     864             :             }
     865             : 
     866       13296 :             if (osDest.size() + osExpandedContents.size() > 1024 * 1024)
     867             :             {
     868           0 :                 return std::string();
     869             :             }
     870             : 
     871       13296 :             osDest += osExpandedContents;
     872             : 
     873       26592 :             iSrc += osContents.size() + 2;
     874             :         }
     875             : 
     876             :         // This is a repeated subclause.
     877      137846 :         else if ((iSrc == 0 || pszSrc[iSrc - 1] == ',') &&
     878       47801 :                  isdigit(static_cast<unsigned char>(pszSrc[iSrc])))
     879             :         {
     880        9672 :             const int nRepeat = atoi(pszSrc + iSrc);
     881             :             // 100: arbitrary number. Higher values might cause performance
     882             :             // problems in the below loop
     883        9672 :             if (nRepeat < 0 || nRepeat > 100)
     884             :             {
     885           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     886             :                          "Too large repeat count: %d", nRepeat);
     887           0 :                 return std::string();
     888             :             }
     889             : 
     890             :             // Skip over repeat count.
     891        9672 :             const char *pszNext = pszSrc + iSrc;  // Used after for.
     892       20176 :             for (; isdigit(static_cast<unsigned char>(*pszNext)); pszNext++)
     893       10504 :                 iSrc++;
     894             : 
     895        9672 :             const std::string osContents = ExtractSubstring(pszNext);
     896        9672 :             if (osContents.empty())
     897             :             {
     898           0 :                 return std::string();
     899             :             }
     900             :             const std::string osExpandedContents =
     901        9672 :                 ExpandFormat(osContents.c_str());
     902        9672 :             if (osExpandedContents.empty())
     903             :             {
     904           0 :                 return std::string();
     905             :             }
     906             : 
     907        9672 :             const size_t nExpandedContentsLen = osExpandedContents.size();
     908        9672 :             if (osDest.size() + nExpandedContentsLen * nRepeat > 1024 * 1024)
     909             :             {
     910           0 :                 return std::string();
     911             :             }
     912             : 
     913       43808 :             for (int i = 0; i < nRepeat; i++)
     914             :             {
     915       34136 :                 if (i > 0)
     916       24464 :                     osDest += ',';
     917       34136 :                 osDest += osExpandedContents;
     918             :             }
     919             : 
     920        9672 :             if (pszNext[0] == '(')
     921           0 :                 iSrc += osContents.size() + 2;
     922             :             else
     923       19344 :                 iSrc += osContents.size();
     924             :         }
     925             :         else
     926             :         {
     927      128174 :             osDest += pszSrc[iSrc++];
     928             :         }
     929             :     }
     930             : 
     931       36264 :     return osDest;
     932             : }
     933             : 
     934             : /************************************************************************/
     935             : /*                            ApplyFormats()                            */
     936             : /*                                                                      */
     937             : /*      This method parses the format string partially, and then        */
     938             : /*      applies a subfield format string to each subfield object.       */
     939             : /*      It in turn does final parsing of the subfield formats.          */
     940             : /************************************************************************/
     941             : 
     942       13296 : int DDFFieldDefn::ApplyFormats()
     943             : 
     944             : {
     945             :     /* -------------------------------------------------------------------- */
     946             :     /*      Verify that the format string is contained within brackets.     */
     947             :     /* -------------------------------------------------------------------- */
     948       26592 :     if (_formatControls.size() < 2 || _formatControls[0] != '(' ||
     949       13296 :         _formatControls.back() != ')')
     950             :     {
     951           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     952             :                  "Format controls for `%s' field missing brackets:%s",
     953             :                  osTag.c_str(), _formatControls.c_str());
     954             : 
     955           0 :         return FALSE;
     956             :     }
     957             : 
     958             :     /* -------------------------------------------------------------------- */
     959             :     /*      Duplicate the string, and strip off the brackets.               */
     960             :     /* -------------------------------------------------------------------- */
     961             : 
     962       26592 :     const std::string osFormatList = ExpandFormat(_formatControls.c_str());
     963       13296 :     if (osFormatList.empty())
     964             :     {
     965           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     966             :                  "Invalid format controls for `%s': %s", osTag.c_str(),
     967             :                  _formatControls.c_str());
     968           0 :         return FALSE;
     969             :     }
     970             : 
     971             :     /* -------------------------------------------------------------------- */
     972             :     /*      Tokenize based on commas.                                       */
     973             :     /* -------------------------------------------------------------------- */
     974             :     const CPLStringList aosFormatItems(
     975       26592 :         CSLTokenizeStringComplex(osFormatList.c_str(), ",", FALSE, FALSE));
     976             : 
     977             :     /* -------------------------------------------------------------------- */
     978             :     /*      Apply the format items to subfields.                            */
     979             :     /* -------------------------------------------------------------------- */
     980       13296 :     int iFormatItem = 0;  // Used after for.
     981             : 
     982       75889 :     for (; iFormatItem < aosFormatItems.size(); iFormatItem++)
     983             :     {
     984       62593 :         const char *pszPastPrefix = aosFormatItems[iFormatItem];
     985       62593 :         while (*pszPastPrefix >= '0' && *pszPastPrefix <= '9')
     986           0 :             pszPastPrefix++;
     987             : 
     988             :         ///////////////////////////////////////////////////////////////
     989             :         // Did we get too many formats for the subfields created
     990             :         // by names?  This may be legal by the 8211 specification, but
     991             :         // isn't encountered in any formats we care about so we just
     992             :         // blow.
     993             : 
     994       62593 :         if (iFormatItem >= GetSubfieldCount())
     995             :         {
     996           0 :             CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
     997             :                      "Got more formats than subfields for field `%s'.",
     998             :                      osTag.c_str());
     999           0 :             break;
    1000             :         }
    1001             : 
    1002       62593 :         if (!apoSubfields[iFormatItem]->SetFormat(pszPastPrefix))
    1003             :         {
    1004           0 :             return FALSE;
    1005             :         }
    1006             :     }
    1007             : 
    1008             :     /* -------------------------------------------------------------------- */
    1009             :     /*      Verify that we got enough formats, cleanup and return.          */
    1010             :     /* -------------------------------------------------------------------- */
    1011             : 
    1012       13296 :     if (iFormatItem < GetSubfieldCount())
    1013             :     {
    1014           0 :         CPLError(CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
    1015             :                  "Got less formats than subfields for field `%s'.",
    1016             :                  osTag.c_str());
    1017           0 :         return FALSE;
    1018             :     }
    1019             : 
    1020             :     /* -------------------------------------------------------------------- */
    1021             :     /*      If all the fields are fixed width, then we are fixed width      */
    1022             :     /*      too.  This is important for repeating fields.                   */
    1023             :     /* -------------------------------------------------------------------- */
    1024       13296 :     nFixedWidth = 0;
    1025       56662 :     for (auto &poSubfield : apoSubfields)
    1026             :     {
    1027       48204 :         if (poSubfield->GetWidth() == 0)
    1028             :         {
    1029        4838 :             nFixedWidth = 0;
    1030        4838 :             break;
    1031             :         }
    1032             :         else
    1033             :         {
    1034       43366 :             if (nFixedWidth > INT_MAX - poSubfield->GetWidth())
    1035             :             {
    1036           0 :                 CPLError(CE_Warning,
    1037             :                          static_cast<CPLErrorNum>(CPLE_DiscardedFormat),
    1038             :                          "Invalid format controls for `%s': %s", osTag.c_str(),
    1039             :                          _formatControls.c_str());
    1040           0 :                 return FALSE;
    1041             :             }
    1042       43366 :             nFixedWidth += poSubfield->GetWidth();
    1043             :         }
    1044             :     }
    1045             : 
    1046       13296 :     return TRUE;
    1047             : }
    1048             : 
    1049             : /************************************************************************/
    1050             : /*                          FindSubfieldDefn()                          */
    1051             : /************************************************************************/
    1052             : 
    1053             : /**
    1054             :  * Find a subfield definition by its mnemonic tag.
    1055             :  *
    1056             :  * @param pszMnemonic The name of the field.
    1057             :  *
    1058             :  * @return The subfield pointer, or NULL if there isn't any such subfield.
    1059             :  */
    1060             : 
    1061             : const DDFSubfieldDefn *
    1062      746388 : DDFFieldDefn::FindSubfieldDefn(const char *pszMnemonic) const
    1063             : 
    1064             : {
    1065     1550900 :     for (const auto &poSubfield : apoSubfields)
    1066             :     {
    1067     1549010 :         if (EQUAL(poSubfield->GetName(), pszMnemonic))
    1068      744500 :             return poSubfield.get();
    1069             :     }
    1070             : 
    1071        1888 :     return nullptr;
    1072             : }
    1073             : 
    1074             : /************************************************************************/
    1075             : /*                          GetDefaultValue()                           */
    1076             : /************************************************************************/
    1077             : 
    1078             : /**
    1079             :  * Return default data for field instance.
    1080             :  */
    1081             : 
    1082        7277 : char *DDFFieldDefn::GetDefaultValue(int *pnSize) const
    1083             : 
    1084             : {
    1085        7277 :     if (!apoFieldParts.empty())
    1086             :     {
    1087         726 :         std::string osData;
    1088         726 :         for (auto &poPartFieldDefn : apoFieldParts)
    1089             :         {
    1090         726 :             if (poPartFieldDefn->IsRepeating())  // only last part
    1091         363 :                 break;
    1092         363 :             int nPartSize = 0;
    1093         363 :             char *pszVal = poPartFieldDefn->GetDefaultValue(&nPartSize);
    1094         363 :             if (!pszVal)
    1095           0 :                 return nullptr;
    1096         363 :             osData.append(pszVal, nPartSize);
    1097         363 :             CPLFree(pszVal);
    1098             :         }
    1099         363 :         if (pnSize)
    1100         363 :             *pnSize = static_cast<int>(osData.size());
    1101         363 :         char *pabyRet = static_cast<char *>(CPLMalloc(osData.size()));
    1102         363 :         memcpy(pabyRet, osData.data(), osData.size());
    1103         363 :         return pabyRet;
    1104             :     }
    1105             : 
    1106             :     /* -------------------------------------------------------------------- */
    1107             :     /*      Loop once collecting the sum of the subfield lengths.           */
    1108             :     /* -------------------------------------------------------------------- */
    1109        6914 :     int nTotalSize = 0;
    1110       32603 :     for (auto &poSubfield : apoSubfields)
    1111             :     {
    1112       25689 :         int nSubfieldSize = 0;
    1113             : 
    1114       25689 :         if (!poSubfield->GetDefaultValue(nullptr, 0, &nSubfieldSize))
    1115           0 :             return nullptr;
    1116       25689 :         nTotalSize += nSubfieldSize;
    1117             :     }
    1118             : 
    1119             :     /* -------------------------------------------------------------------- */
    1120             :     /*      Allocate buffer.                                                */
    1121             :     /* -------------------------------------------------------------------- */
    1122        6914 :     char *pachData = static_cast<char *>(CPLMalloc(nTotalSize));
    1123             : 
    1124        6914 :     if (pnSize != nullptr)
    1125        6914 :         *pnSize = nTotalSize;
    1126             : 
    1127             :     /* -------------------------------------------------------------------- */
    1128             :     /*      Loop again, collecting actual default values.                   */
    1129             :     /* -------------------------------------------------------------------- */
    1130        6914 :     int nOffset = 0;
    1131       32603 :     for (auto &poSubfield : apoSubfields)
    1132             :     {
    1133             :         int nSubfieldSize;
    1134             : 
    1135       25689 :         if (!poSubfield->GetDefaultValue(pachData + nOffset,
    1136             :                                          nTotalSize - nOffset, &nSubfieldSize))
    1137             :         {
    1138           0 :             CPLAssert(false);
    1139             :             return nullptr;
    1140             :         }
    1141             : 
    1142       25689 :         nOffset += nSubfieldSize;
    1143             :     }
    1144             : 
    1145        6914 :     CPLAssert(nOffset == nTotalSize);
    1146             : 
    1147        6914 :     return pachData;
    1148             : }

Generated by: LCOV version 1.14