EVOLUTION-MANAGER
Edit File: ddffielddefn.cpp
/****************************************************************************** * * Project: ISO 8211 Access * Purpose: Implements the DDFFieldDefn class. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "iso8211.h" #include <cctype> #include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_string.h" CPL_CVSID("$Id: ddffielddefn.cpp 36552 2016-11-29 06:01:45Z goatbar $"); #define CPLE_DiscardedFormat 1301 /************************************************************************/ /* DDFFieldDefn() */ /************************************************************************/ DDFFieldDefn::DDFFieldDefn() : poModule(NULL), pszTag(NULL), _fieldName(NULL), _arrayDescr(NULL), _formatControls(NULL), bRepeatingSubfields(FALSE), nFixedWidth(0), _data_struct_code(dsc_elementary), _data_type_code(dtc_char_string), nSubfieldCount(0), papoSubfields(NULL) {} /************************************************************************/ /* ~DDFFieldDefn() */ /************************************************************************/ DDFFieldDefn::~DDFFieldDefn() { CPLFree( pszTag ); CPLFree( _fieldName ); CPLFree( _arrayDescr ); CPLFree( _formatControls ); for( int i = 0; i < nSubfieldCount; i++ ) delete papoSubfields[i]; CPLFree( papoSubfields ); } /************************************************************************/ /* AddSubfield() */ /************************************************************************/ void DDFFieldDefn::AddSubfield( const char *pszName, const char *pszFormat ) { DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn; poSFDefn->SetName( pszName ); poSFDefn->SetFormat( pszFormat ); AddSubfield( poSFDefn ); } /************************************************************************/ /* AddSubfield() */ /************************************************************************/ void DDFFieldDefn::AddSubfield( DDFSubfieldDefn *poNewSFDefn, int bDontAddToFormat ) { nSubfieldCount++; papoSubfields = static_cast<DDFSubfieldDefn **>( CPLRealloc( papoSubfields, sizeof(void*) * nSubfieldCount )); papoSubfields[nSubfieldCount-1] = poNewSFDefn; if( bDontAddToFormat ) return; /* -------------------------------------------------------------------- */ /* Add this format to the format list. We don't bother */ /* aggregating formats here. */ /* -------------------------------------------------------------------- */ if( _formatControls == NULL || strlen(_formatControls) == 0 ) { CPLFree( _formatControls ); _formatControls = CPLStrdup( "()" ); } const int nOldLen = static_cast<int>(strlen(_formatControls)); char *pszNewFormatControls = static_cast<char *>( CPLMalloc(nOldLen+3+strlen(poNewSFDefn->GetFormat()))); strcpy( pszNewFormatControls, _formatControls ); pszNewFormatControls[nOldLen-1] = '\0'; if( pszNewFormatControls[nOldLen-2] != '(' ) strcat( pszNewFormatControls, "," ); strcat( pszNewFormatControls, poNewSFDefn->GetFormat() ); strcat( pszNewFormatControls, ")" ); CPLFree( _formatControls ); _formatControls = pszNewFormatControls; /* -------------------------------------------------------------------- */ /* Add the subfield name to the list. */ /* -------------------------------------------------------------------- */ if( _arrayDescr == NULL ) _arrayDescr = CPLStrdup(""); _arrayDescr = (char *) CPLRealloc(_arrayDescr, strlen(_arrayDescr)+strlen(poNewSFDefn->GetName())+2); if( strlen(_arrayDescr) > 0 && (_arrayDescr[0] != '*' || strlen(_arrayDescr) > 1) ) strcat( _arrayDescr, "!" ); strcat( _arrayDescr, poNewSFDefn->GetName() ); } /************************************************************************/ /* Create() */ /* */ /* Initialize a new field defn from application input, instead */ /* of from an existing file. */ /************************************************************************/ int DDFFieldDefn::Create( const char *pszTagIn, const char *pszFieldName, const char *pszDescription, DDF_data_struct_code eDataStructCode, DDF_data_type_code eDataTypeCode, const char *pszFormat ) { CPLAssert( pszTag == NULL ); poModule = NULL; pszTag = CPLStrdup( pszTagIn ); _fieldName = CPLStrdup( pszFieldName ); _arrayDescr = CPLStrdup( pszDescription ); _data_struct_code = eDataStructCode; _data_type_code = eDataTypeCode; if( pszFormat != NULL ) _formatControls = CPLStrdup( pszFormat ); else _formatControls = CPLStrdup( "" ); if( pszDescription != NULL && *pszDescription == '*' ) bRepeatingSubfields = TRUE; return TRUE; } /************************************************************************/ /* SetFormatControls() */ /************************************************************************/ void DDFFieldDefn::SetFormatControls( const char* pszVal ) { CPLFree(_formatControls); _formatControls = CPLStrdup(pszVal ? pszVal : ""); } /************************************************************************/ /* GenerateDDREntry() */ /************************************************************************/ int DDFFieldDefn::GenerateDDREntry( DDFModule * poModuleIn, char **ppachData, int *pnLength ) { const int iFDOffset = poModuleIn->GetFieldControlLength(); CPLAssert(iFDOffset >= 6 && iFDOffset <= 9); *pnLength = static_cast<int>(iFDOffset + strlen(_fieldName) + 1 + strlen(_arrayDescr) + 1 + strlen(_formatControls) + 1); if( strlen(_arrayDescr) == 0 ) *pnLength -= 1; if( strlen(_formatControls) == 0 ) *pnLength -= 1; if( ppachData == NULL ) return TRUE; *ppachData = static_cast<char *>(CPLMalloc(*pnLength + 1)); if( _data_struct_code == dsc_elementary ) (*ppachData)[0] = '0'; else if( _data_struct_code == dsc_vector ) (*ppachData)[0] = '1'; else if( _data_struct_code == dsc_array ) (*ppachData)[0] = '2'; else if( _data_struct_code == dsc_concatenated ) (*ppachData)[0] = '3'; if( _data_type_code == dtc_char_string ) (*ppachData)[1] = '0'; else if( _data_type_code == dtc_implicit_point ) (*ppachData)[1] = '1'; else if( _data_type_code == dtc_explicit_point ) (*ppachData)[1] = '2'; else if( _data_type_code == dtc_explicit_point_scaled ) (*ppachData)[1] = '3'; else if( _data_type_code == dtc_char_bit_string ) (*ppachData)[1] = '4'; else if( _data_type_code == dtc_bit_string ) (*ppachData)[1] = '5'; else if( _data_type_code == dtc_mixed_data_type ) (*ppachData)[1] = '6'; (*ppachData)[2] = '0'; (*ppachData)[3] = '0'; (*ppachData)[4] = ';'; (*ppachData)[5] = '&'; if( iFDOffset > 6 ) (*ppachData)[6] = ' '; if( iFDOffset > 7 ) (*ppachData)[7] = ' '; if( iFDOffset > 8 ) (*ppachData)[8] = ' '; snprintf( *ppachData + iFDOffset, *pnLength+1 - iFDOffset, "%s", _fieldName ); if( strlen(_arrayDescr) > 0 ) snprintf( *ppachData + strlen(*ppachData), *pnLength+1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR, _arrayDescr ); if( strlen(_formatControls) > 0 ) snprintf( *ppachData + strlen(*ppachData), *pnLength+1 - strlen(*ppachData), "%c%s", DDF_UNIT_TERMINATOR, _formatControls ); snprintf( *ppachData + strlen(*ppachData), *pnLength+1 - strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR ); return TRUE; } /************************************************************************/ /* Initialize() */ /* */ /* Initialize the field definition from the information in the */ /* DDR record. This is called by DDFModule::Open(). */ /************************************************************************/ int DDFFieldDefn::Initialize( DDFModule * poModuleIn, const char * pszTagIn, int nFieldEntrySize, const char * pachFieldArea ) { int iFDOffset = poModuleIn->GetFieldControlLength(); poModule = poModuleIn; pszTag = CPLStrdup( pszTagIn ); /* -------------------------------------------------------------------- */ /* Set the data struct and type codes. */ /* -------------------------------------------------------------------- */ switch( pachFieldArea[0] ) { case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ case '0': _data_struct_code = dsc_elementary; break; case '1': _data_struct_code = dsc_vector; break; case '2': _data_struct_code = dsc_array; break; case '3': _data_struct_code = dsc_concatenated; break; default: CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized data_struct_code value %c.\n" "Field %s initialization incorrect.", pachFieldArea[0], pszTag ); _data_struct_code = dsc_elementary; } switch( pachFieldArea[1] ) { case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ case '0': _data_type_code = dtc_char_string; break; case '1': _data_type_code = dtc_implicit_point; break; case '2': _data_type_code = dtc_explicit_point; break; case '3': _data_type_code = dtc_explicit_point_scaled; break; case '4': _data_type_code = dtc_char_bit_string; break; case '5': _data_type_code = dtc_bit_string; break; case '6': _data_type_code = dtc_mixed_data_type; break; default: CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized data_type_code value %c.\n" "Field %s initialization incorrect.", pachFieldArea[1], pszTag ); _data_type_code = dtc_char_string; } /* -------------------------------------------------------------------- */ /* Capture the field name, description (sub field names), and */ /* format statements. */ /* -------------------------------------------------------------------- */ int nCharsConsumed = 0; _fieldName = DDFFetchVariable( pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed ); iFDOffset += nCharsConsumed; _arrayDescr = DDFFetchVariable( pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed ); iFDOffset += nCharsConsumed; _formatControls = DDFFetchVariable( pachFieldArea + iFDOffset, nFieldEntrySize - iFDOffset, DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, &nCharsConsumed ); /* -------------------------------------------------------------------- */ /* Parse the subfield info. */ /* -------------------------------------------------------------------- */ if( _data_struct_code != dsc_elementary ) { if( !BuildSubfields() ) return FALSE; if( !ApplyFormats() ) return FALSE; } return TRUE; } /************************************************************************/ /* Dump() */ /************************************************************************/ /** * Write out field definition info to debugging file. * * A variety of information about this field definition, and all its * subfields is written to the give debugging file handle. * * @param fp The standard IO file handle to write to. i.e. stderr */ void DDFFieldDefn::Dump( FILE * fp ) { const char *pszValue = ""; fprintf( fp, " DDFFieldDefn:\n" ); fprintf( fp, " Tag = `%s'\n", pszTag ); fprintf( fp, " _fieldName = `%s'\n", _fieldName ); fprintf( fp, " _arrayDescr = `%s'\n", _arrayDescr ); fprintf( fp, " _formatControls = `%s'\n", _formatControls ); switch( _data_struct_code ) { case dsc_elementary: pszValue = "elementary"; break; case dsc_vector: pszValue = "vector"; break; case dsc_array: pszValue = "array"; break; case dsc_concatenated: pszValue = "concatenated"; break; default: CPLAssert( false ); pszValue = "(unknown)"; } fprintf( fp, " _data_struct_code = %s\n", pszValue ); switch( _data_type_code ) { case dtc_char_string: pszValue = "char_string"; break; case dtc_implicit_point: pszValue = "implicit_point"; break; case dtc_explicit_point: pszValue = "explicit_point"; break; case dtc_explicit_point_scaled: pszValue = "explicit_point_scaled"; break; case dtc_char_bit_string: pszValue = "char_bit_string"; break; case dtc_bit_string: pszValue = "bit_string"; break; case dtc_mixed_data_type: pszValue = "mixed_data_type"; break; default: CPLAssert( false ); pszValue = "(unknown)"; break; } fprintf( fp, " _data_type_code = %s\n", pszValue ); for( int i = 0; i < nSubfieldCount; i++ ) papoSubfields[i]->Dump( fp ); } /************************************************************************/ /* BuildSubfields() */ /* */ /* Based on the _arrayDescr build a set of subfields. */ /************************************************************************/ int DDFFieldDefn::BuildSubfields() { const char *pszSublist = _arrayDescr; /* -------------------------------------------------------------------- */ /* It is valid to define a field with _arrayDesc */ /* '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'. */ /* This basically indicates that there are 3 (YCOO,XCOO) */ /* structures named STPT, CTPT and ENPT. But we can't handle */ /* such a case gracefully here, so we just ignore the */ /* "structure names" and treat such a thing as a repeating */ /* YCOO/XCOO array. This occurs with the AR2D field of some */ /* AML S-57 files for instance. */ /* */ /* We accomplish this by ignoring everything before the last */ /* '*' in the subfield list. */ /* -------------------------------------------------------------------- */ if( strrchr(pszSublist, '*') != NULL ) pszSublist = strrchr(pszSublist, '*'); /* -------------------------------------------------------------------- */ /* Strip off the repeating marker, when it occurs, but mark our */ /* field as repeating. */ /* -------------------------------------------------------------------- */ if( pszSublist[0] == '*' ) { bRepeatingSubfields = TRUE; pszSublist++; } /* -------------------------------------------------------------------- */ /* split list of fields . */ /* -------------------------------------------------------------------- */ char **papszSubfieldNames = CSLTokenizeStringComplex( pszSublist, "!", FALSE, FALSE ); /* -------------------------------------------------------------------- */ /* minimally initialize the subfields. More will be done later. */ /* -------------------------------------------------------------------- */ const int nSFCount = CSLCount( papszSubfieldNames ); for( int iSF = 0; iSF < nSFCount; iSF++ ) { DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn; poSFDefn->SetName( papszSubfieldNames[iSF] ); AddSubfield( poSFDefn, TRUE ); } CSLDestroy( papszSubfieldNames ); return TRUE; } /************************************************************************/ /* ExtractSubstring() */ /* */ /* Extract a substring terminated by a comma (or end of */ /* string). Commas in brackets are ignored as terminated with */ /* bracket nesting understood gracefully. If the returned */ /* string would being and end with a bracket then strip off the */ /* brackets. */ /* */ /* Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D". */ /* Give a string like "3A,2C" return "3A". */ /************************************************************************/ char *DDFFieldDefn::ExtractSubstring( const char * pszSrc ) { int nBracket = 0; int i = 0; // Used after for. for( ; pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); i++ ) { if( pszSrc[i] == '(' ) nBracket++; else if( pszSrc[i] == ')' ) nBracket--; } char *pszReturn = NULL; if( pszSrc[0] == '(' ) { pszReturn = CPLStrdup( pszSrc + 1 ); pszReturn[i-2] = '\0'; } else { pszReturn = CPLStrdup( pszSrc ); pszReturn[i] = '\0'; } return pszReturn; } /************************************************************************/ /* ExpandFormat() */ /************************************************************************/ char *DDFFieldDefn::ExpandFormat( const char * pszSrc ) { size_t nDestMax = 32; char *pszDest = static_cast<char *>(CPLMalloc(nDestMax + 1)); int nRepeat = 0; size_t iSrc = 0; size_t iDst = 0; pszDest[0] = '\0'; while( pszSrc[iSrc] != '\0' ) { // This is presumably an extra level of brackets around some // binary stuff related to rescanning which we don't care to do // (see 6.4.3.3 of the standard. We just strip off the extra // layer of brackets. if( (iSrc == 0 || pszSrc[iSrc-1] == ',') && pszSrc[iSrc] == '(' ) { char *pszContents = ExtractSubstring( pszSrc+iSrc ); char *pszExpandedContents = ExpandFormat( pszContents ); if( strlen(pszExpandedContents) + strlen(pszDest) + 1 > nDestMax ) { nDestMax = 2 * (strlen(pszExpandedContents) + strlen(pszDest)); pszDest = static_cast<char *>(CPLRealloc(pszDest,nDestMax + 1)); } strcat( pszDest, pszExpandedContents ); iDst = strlen(pszDest); iSrc = iSrc + strlen(pszContents) + 2; CPLFree( pszContents ); CPLFree( pszExpandedContents ); } // This is a repeated subclause. else if( (iSrc == 0 || pszSrc[iSrc-1] == ',') && isdigit(pszSrc[iSrc]) ) { nRepeat = atoi(pszSrc+iSrc); // Skip over repeat count. const char *pszNext = pszSrc + iSrc; // Used after for. for( ; isdigit(*pszNext); pszNext++ ) iSrc++; char *pszContents = ExtractSubstring( pszNext ); char *pszExpandedContents = ExpandFormat( pszContents ); for( int i = 0; i < nRepeat; i++ ) { if( strlen(pszExpandedContents) + strlen(pszDest) + 1 + 1 > nDestMax ) { nDestMax = 2 * (strlen(pszExpandedContents) + strlen(pszDest) + 1); pszDest = static_cast<char *>(CPLRealloc(pszDest,nDestMax + 1)); } strcat( pszDest, pszExpandedContents ); if( i < nRepeat-1 ) strcat( pszDest, "," ); } iDst = strlen(pszDest); if( pszNext[0] == '(' ) iSrc = iSrc + strlen(pszContents) + 2; else iSrc = iSrc + strlen(pszContents); CPLFree( pszContents ); CPLFree( pszExpandedContents ); } else { if( iDst+1 >= nDestMax ) { nDestMax = 2 * iDst; pszDest = static_cast<char *>(CPLRealloc(pszDest, nDestMax)); } pszDest[iDst++] = pszSrc[iSrc++]; pszDest[iDst] = '\0'; } } return pszDest; } /************************************************************************/ /* ApplyFormats() */ /* */ /* This method parses the format string partially, and then */ /* applies a subfield format string to each subfield object. */ /* It in turn does final parsing of the subfield formats. */ /************************************************************************/ int DDFFieldDefn::ApplyFormats() { /* -------------------------------------------------------------------- */ /* Verify that the format string is contained within brackets. */ /* -------------------------------------------------------------------- */ if( strlen(_formatControls) < 2 || _formatControls[0] != '(' || _formatControls[strlen(_formatControls)-1] != ')' ) { CPLError( CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), "Format controls for `%s' field missing brackets:%s", pszTag, _formatControls ); return FALSE; } /* -------------------------------------------------------------------- */ /* Duplicate the string, and strip off the brackets. */ /* -------------------------------------------------------------------- */ char *pszFormatList = ExpandFormat( _formatControls ); /* -------------------------------------------------------------------- */ /* Tokenize based on commas. */ /* -------------------------------------------------------------------- */ char **papszFormatItems = CSLTokenizeStringComplex(pszFormatList, ",", FALSE, FALSE ); CPLFree( pszFormatList ); /* -------------------------------------------------------------------- */ /* Apply the format items to subfields. */ /* -------------------------------------------------------------------- */ int iFormatItem = 0; // Used after for. for( ; papszFormatItems[iFormatItem] != NULL; iFormatItem++ ) { const char *pszPastPrefix = papszFormatItems[iFormatItem]; while( *pszPastPrefix >= '0' && *pszPastPrefix <= '9' ) pszPastPrefix++; /////////////////////////////////////////////////////////////// // Did we get too many formats for the subfields created // by names? This may be legal by the 8211 specification, but // isn't encountered in any formats we care about so we just // blow. if( iFormatItem >= nSubfieldCount ) { CPLError( CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), "Got more formats than subfields for field `%s'.", pszTag ); break; } if( !papoSubfields[iFormatItem]->SetFormat(pszPastPrefix) ) { CSLDestroy( papszFormatItems ); return FALSE; } } /* -------------------------------------------------------------------- */ /* Verify that we got enough formats, cleanup and return. */ /* -------------------------------------------------------------------- */ CSLDestroy( papszFormatItems ); if( iFormatItem < nSubfieldCount ) { CPLError( CE_Warning, static_cast<CPLErrorNum>(CPLE_DiscardedFormat), "Got less formats than subfields for field `%s'.", pszTag ); return FALSE; } /* -------------------------------------------------------------------- */ /* If all the fields are fixed width, then we are fixed width */ /* too. This is important for repeating fields. */ /* -------------------------------------------------------------------- */ nFixedWidth = 0; for( int i = 0; i < nSubfieldCount; i++ ) { if( papoSubfields[i]->GetWidth() == 0 ) { nFixedWidth = 0; break; } else { nFixedWidth += papoSubfields[i]->GetWidth(); } } return TRUE; } /************************************************************************/ /* FindSubfieldDefn() */ /************************************************************************/ /** * Find a subfield definition by its mnemonic tag. * * @param pszMnemonic The name of the field. * * @return The subfield pointer, or NULL if there isn't any such subfield. */ DDFSubfieldDefn *DDFFieldDefn::FindSubfieldDefn( const char * pszMnemonic ) { for( int i = 0; i < nSubfieldCount; i++ ) { if( EQUAL(papoSubfields[i]->GetName(),pszMnemonic) ) return papoSubfields[i]; } return NULL; } /************************************************************************/ /* GetSubfield() */ /* */ /* Fetch a subfield by its index. */ /************************************************************************/ /** * Fetch a subfield by index. * * @param i The index subfield index. (Between 0 and GetSubfieldCount()-1) * * @return The subfield pointer, or NULL if the index is out of range. */ DDFSubfieldDefn *DDFFieldDefn::GetSubfield( int i ) { if( i < 0 || i >= nSubfieldCount ) { CPLAssert( false ); return NULL; } return papoSubfields[i]; } /************************************************************************/ /* GetDefaultValue() */ /************************************************************************/ /** * Return default data for field instance. */ char *DDFFieldDefn::GetDefaultValue( int *pnSize ) { /* -------------------------------------------------------------------- */ /* Loop once collecting the sum of the subfield lengths. */ /* -------------------------------------------------------------------- */ int nTotalSize = 0; for( int iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ ) { int nSubfieldSize = 0; if( !papoSubfields[iSubfield]->GetDefaultValue( NULL, 0, &nSubfieldSize ) ) return NULL; nTotalSize += nSubfieldSize; } /* -------------------------------------------------------------------- */ /* Allocate buffer. */ /* -------------------------------------------------------------------- */ char *pachData = static_cast<char *>(CPLMalloc( nTotalSize )); if( pnSize != NULL ) *pnSize = nTotalSize; /* -------------------------------------------------------------------- */ /* Loop again, collecting actual default values. */ /* -------------------------------------------------------------------- */ int nOffset = 0; for( int iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ ) { int nSubfieldSize; if( !papoSubfields[iSubfield]->GetDefaultValue( pachData + nOffset, nTotalSize - nOffset, &nSubfieldSize ) ) { CPLAssert( false ); return NULL; } nOffset += nSubfieldSize; } CPLAssert( nOffset == nTotalSize ); return pachData; }