Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/iso8211/ddfsubfielddefn.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  ISO 8211 Access
4
 * Purpose:  Implements the DDFSubfieldDefn class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "iso8211.h"
16
17
#include <cstdio>
18
#include <cstdlib>
19
#include <cstring>
20
21
#include <algorithm>
22
23
#include "cpl_conv.h"
24
#include "cpl_error.h"
25
#include "cpl_string.h"
26
27
/************************************************************************/
28
/*                          DDFSubfieldDefn()                           */
29
/************************************************************************/
30
31
1.54M
DDFSubfieldDefn::DDFSubfieldDefn() = default;
32
33
/************************************************************************/
34
/*                          ~DDFSubfieldDefn()                          */
35
/************************************************************************/
36
37
1.54M
DDFSubfieldDefn::~DDFSubfieldDefn() = default;
38
39
/************************************************************************/
40
/*                              SetName()                               */
41
/************************************************************************/
42
43
void DDFSubfieldDefn::SetName(const char *pszNewName)
44
45
1.54M
{
46
1.54M
    osName = pszNewName;
47
1.54M
    while (!osName.empty() && osName.back() == ' ')
48
4.89k
        osName.pop_back();
49
1.54M
}
50
51
/************************************************************************/
52
/*                             SetFormat()                              */
53
/*                                                                      */
54
/*      While interpreting the format string we don't support:          */
55
/*                                                                      */
56
/*       o Passing an explicit terminator for variable length field.    */
57
/*       o 'X' for unused data ... this should really be filtered       */
58
/*         out by DDFFieldDefn::ApplyFormats(), but isn't.              */
59
/*       o 'B' bitstrings that aren't a multiple of eight.              */
60
/************************************************************************/
61
62
int DDFSubfieldDefn::SetFormat(const char *pszFormat)
63
64
1.47M
{
65
1.47M
    osFormatString = pszFormat;
66
67
    /* -------------------------------------------------------------------- */
68
    /*      These values will likely be used.                               */
69
    /* -------------------------------------------------------------------- */
70
1.47M
    if (osFormatString.size() >= 2 && osFormatString[1] == '(')
71
766k
    {
72
766k
        nFormatWidth = atoi(osFormatString.c_str() + 2);
73
766k
        if (nFormatWidth < 0)
74
986
        {
75
986
            CPLError(CE_Failure, CPLE_AppDefined, "Format width %s is invalid.",
76
986
                     osFormatString.c_str() + 2);
77
986
            return FALSE;
78
986
        }
79
765k
        bIsVariable = nFormatWidth == 0;
80
765k
    }
81
711k
    else
82
711k
        bIsVariable = TRUE;
83
84
    /* -------------------------------------------------------------------- */
85
    /*      Interpret the format string.                                    */
86
    /* -------------------------------------------------------------------- */
87
1.47M
    switch (osFormatString[0])
88
1.47M
    {
89
442k
        case 'A':
90
446k
        case 'C':  // It isn't clear to me how this is different than 'A'
91
446k
            eType = DDFString;
92
446k
            break;
93
94
93.3k
        case 'R':
95
93.3k
            eType = DDFFloat;
96
93.3k
            break;
97
98
409k
        case 'I':
99
426k
        case 'S':
100
426k
            eType = DDFInt;
101
426k
            break;
102
103
3.89k
        case 'B':
104
507k
        case 'b':
105
            // Is the width expressed in bits? (is it a bitstring)
106
507k
            bIsVariable = FALSE;
107
507k
            if (osFormatString[1] == '\0')
108
454
                return FALSE;
109
110
506k
            if (osFormatString[1] == '(')
111
6.08k
            {
112
6.08k
                nFormatWidth = atoi(osFormatString.c_str() + 2);
113
6.08k
                if (nFormatWidth < 0 || nFormatWidth % 8 != 0)
114
541
                {
115
541
                    CPLError(CE_Failure, CPLE_AppDefined,
116
541
                             "Format width %s is invalid.",
117
541
                             osFormatString.c_str() + 2);
118
541
                    return FALSE;
119
541
                }
120
121
5.54k
                nFormatWidth = nFormatWidth / 8;
122
5.54k
                eBinaryFormat = SInt;  // good default, works for SDTS.
123
124
5.54k
                if (nFormatWidth < 5)
125
4.73k
                    eType = DDFInt;
126
816
                else
127
816
                    eType = DDFBinaryString;
128
5.54k
            }
129
130
            // or do we have a binary type indicator? (is it binary)
131
500k
            else
132
500k
            {
133
500k
                if (osFormatString[1] < '0' || osFormatString[1] > '5')
134
1.66k
                {
135
1.66k
                    CPLError(CE_Failure, CPLE_AppDefined,
136
1.66k
                             "Binary format = %c is invalid.",
137
1.66k
                             osFormatString[1]);
138
1.66k
                    return FALSE;
139
1.66k
                }
140
499k
                eBinaryFormat =
141
499k
                    static_cast<DDFBinaryFormat>(osFormatString[1] - '0');
142
499k
                nFormatWidth = atoi(osFormatString.c_str() + 2);
143
499k
                if (nFormatWidth < 0)
144
737
                {
145
737
                    CPLError(CE_Failure, CPLE_AppDefined,
146
737
                             "Format width %s is invalid.",
147
737
                             osFormatString.c_str() + 2);
148
737
                    return FALSE;
149
737
                }
150
151
498k
                if (eBinaryFormat == SInt || eBinaryFormat == UInt)
152
467k
                    eType = DDFInt;
153
30.8k
                else
154
30.8k
                    eType = DDFFloat;
155
498k
            }
156
503k
            break;
157
158
503k
        case 'X':
159
            // 'X' is extra space, and should not be directly assigned to a
160
            // subfield ... I have not encountered it in use yet though.
161
915
            CPLError(CE_Failure, CPLE_AppDefined,
162
915
                     "Format type of `%c' not supported.\n", osFormatString[0]);
163
164
915
            return FALSE;
165
166
2.90k
        default:
167
2.90k
            CPLError(CE_Failure, CPLE_AppDefined,
168
2.90k
                     "Format type of `%c' not recognised.\n",
169
2.90k
                     osFormatString[0]);
170
171
2.90k
            return FALSE;
172
1.47M
    }
173
174
1.47M
    return TRUE;
175
1.47M
}
176
177
/************************************************************************/
178
/*                                Dump()                                */
179
/************************************************************************/
180
181
/**
182
 * Write out subfield definition info to debugging file.
183
 *
184
 * A variety of information about this field definition is written to the
185
 * give debugging file handle.
186
 *
187
 * @param fp The standard IO file handle to write to.  i.e. stderr
188
 */
189
190
void DDFSubfieldDefn::Dump(FILE *fp, int nNestingLevel) const
191
192
0
{
193
0
    std::string osIndent;
194
0
    for (int i = 0; i < nNestingLevel; ++i)
195
0
        osIndent += "  ";
196
197
0
#define Print(...)                                                             \
198
0
    do                                                                         \
199
0
    {                                                                          \
200
0
        fprintf(fp, "%s", osIndent.c_str());                                   \
201
0
        fprintf(fp, __VA_ARGS__);                                              \
202
0
    } while (0)
203
204
0
    Print("DDFSubfieldDefn:\n");
205
0
    Print("    Label = `%s'\n", osName.c_str());
206
0
    Print("    FormatString = `%s'\n", osFormatString.c_str());
207
0
}
208
209
/************************************************************************/
210
/*                           GetDataLength()                            */
211
/*                                                                      */
212
/*      This method will scan for the end of a variable field.          */
213
/************************************************************************/
214
215
/**
216
 * Scan for the end of variable length data.  Given a pointer to the data
217
 * for this subfield (from within a DDFRecord) this method will return the
218
 * number of bytes which are data for this subfield.  The number of bytes
219
 * consumed as part of this field can also be fetched.  This number may
220
 * be one longer than the length if there is a terminator character
221
 * used.<p>
222
 *
223
 * This method is mainly for internal use, or for applications which
224
 * want the raw binary data to interpret themselves.  Otherwise use one
225
 * of ExtractStringData(), ExtractIntData() or ExtractFloatData().
226
 *
227
 * @param pachSourceData The pointer to the raw data for this field.  This
228
 * may have come from DDFRecord::GetData(), taking into account skip factors
229
 * over previous subfields data.
230
 * @param nMaxBytes The maximum number of bytes that are accessible after
231
 * pachSourceData.
232
 * @param pnConsumedBytes Pointer to an integer into which the number of
233
 * bytes consumed by this field should be written.  May be NULL to ignore.
234
 *
235
 * @return The number of bytes at pachSourceData which are actual data for
236
 * this record (not including unit, or field terminator).
237
 */
238
239
int DDFSubfieldDefn::GetDataLength(const char *pachSourceData, int nMaxBytes,
240
                                   int *pnConsumedBytes) const
241
242
7.93M
{
243
7.93M
    if (!bIsVariable)
244
5.59M
    {
245
5.59M
        if (nFormatWidth > nMaxBytes)
246
3.74k
        {
247
3.74k
            CPLError(CE_Warning, CPLE_AppDefined,
248
3.74k
                     "Only %d bytes available for subfield %s with\n"
249
3.74k
                     "format string %s ... returning shortened data.",
250
3.74k
                     nMaxBytes, osName.c_str(), osFormatString.c_str());
251
252
3.74k
            if (pnConsumedBytes != nullptr)
253
2.41k
                *pnConsumedBytes = nMaxBytes;
254
255
3.74k
            return nMaxBytes;
256
3.74k
        }
257
5.59M
        else
258
5.59M
        {
259
5.59M
            if (pnConsumedBytes != nullptr)
260
5.54M
                *pnConsumedBytes = nFormatWidth;
261
262
5.59M
            return nFormatWidth;
263
5.59M
        }
264
5.59M
    }
265
2.33M
    else
266
2.33M
    {
267
2.33M
        int nLength = 0;
268
2.33M
        int bAsciiField = TRUE;
269
2.33M
        int extraConsumedBytes = 0;
270
271
        /* We only check for the field terminator because of some buggy
272
         * datasets with missing format terminators.  However, we have found
273
         * the field terminator and unit terminators are legal characters
274
         * within the fields of some extended datasets (such as JP34NC94.000).
275
         * So we don't check for the field terminator and unit terminators as
276
         * a single byte if the field appears to be multi-byte which we
277
         * establish by checking for the buffer ending with 0x1e 0x00 (a
278
         * two byte field terminator).
279
         *
280
         * In the case of S57, the subfield ATVL of the NATF field can be
281
         * encoded in lexical level 2 (see S57 specification, Edition 3.1,
282
         * paragraph 2.4 and 2.5). In that case the Unit Terminator and Field
283
         * Terminator are followed by the NULL character.
284
         * A better fix would be to read the NALL tag in the DSSI to check
285
         * that the lexical level is 2, instead of relying on the value of
286
         * the first byte as we are doing - but that is not information
287
         * that is available at the libiso8211 level (bug #1526)
288
         */
289
290
        // If the whole field ends with 0x1e 0x00 then we assume this
291
        // field is a double byte character set.
292
2.33M
        if (nMaxBytes > 1 &&
293
2.30M
            (pachSourceData[nMaxBytes - 2] == chFormatDelimiter ||
294
2.03M
             pachSourceData[nMaxBytes - 2] == DDF_FIELD_TERMINATOR) &&
295
296k
            pachSourceData[nMaxBytes - 1] == 0x00)
296
3.45k
            bAsciiField = FALSE;
297
298
        //        if( !bAsciiField )
299
        //            CPLDebug( "ISO8211", "Non-ASCII field detected." );
300
301
27.2M
        while (nLength < nMaxBytes)
302
26.9M
        {
303
26.9M
            if (bAsciiField)
304
26.9M
            {
305
26.9M
                if (pachSourceData[nLength] == chFormatDelimiter ||
306
25.0M
                    pachSourceData[nLength] == DDF_FIELD_TERMINATOR)
307
2.08M
                    break;
308
26.9M
            }
309
82.1k
            else
310
82.1k
            {
311
82.1k
                if (nLength > 0 &&
312
78.6k
                    (pachSourceData[nLength - 1] == chFormatDelimiter ||
313
75.9k
                     pachSourceData[nLength - 1] == DDF_FIELD_TERMINATOR) &&
314
7.04k
                    pachSourceData[nLength] == 0)
315
3.45k
                {
316
                    // Suck up the field terminator if one follows
317
                    // or else it will be interpreted as a new subfield.
318
                    // This is a pretty ugly counter-intuitive hack!
319
3.45k
                    if (nLength + 1 < nMaxBytes &&
320
2.21k
                        pachSourceData[nLength + 1] == DDF_FIELD_TERMINATOR)
321
489
                        extraConsumedBytes++;
322
3.45k
                    break;
323
3.45k
                }
324
82.1k
            }
325
326
24.8M
            nLength++;
327
24.8M
        }
328
329
2.33M
        if (pnConsumedBytes != nullptr)
330
2.07M
        {
331
2.07M
            if (nMaxBytes == 0)
332
1.90k
                *pnConsumedBytes = nLength + extraConsumedBytes;
333
2.07M
            else
334
2.07M
                *pnConsumedBytes = nLength + extraConsumedBytes + 1;
335
2.07M
        }
336
337
2.33M
        return nLength;
338
2.33M
    }
339
7.93M
}
340
341
/************************************************************************/
342
/*                         ExtractStringData()                          */
343
/************************************************************************/
344
345
/**
346
 * Extract a zero terminated string containing the data for this subfield.
347
 * Given a pointer to the data
348
 * for this subfield (from within a DDFRecord) this method will return the
349
 * data for this subfield.  The number of bytes
350
 * consumed as part of this field can also be fetched.  This number may
351
 * be one longer than the string length if there is a terminator character
352
 * used.<p>
353
 *
354
 * This function will return the raw binary data of a subfield for
355
 * types other than DDFString, including data past zero chars.  This is
356
 * the standard way of extracting DDFBinaryString subfields for instance.<p>
357
 *
358
 * CAUTION: this method is not thread safe as it updates mutable member
359
 * variables.
360
 *
361
 * @param pachSourceData The pointer to the raw data for this field.  This
362
 * may have come from DDFRecord::GetData(), taking into account skip factors
363
 * over previous subfields data.
364
 * @param nMaxBytes The maximum number of bytes that are accessible after
365
 * pachSourceData.
366
 * @param pnConsumedBytes Pointer to an integer into which the number of
367
 * bytes consumed by this field should be written.  May be NULL to ignore.
368
 * This is used as a skip factor to increment pachSourceData to point to the
369
 * next subfields data.
370
 *
371
 * @return A pointer to a buffer containing the data for this field.  The
372
 * returned pointer is to an internal buffer which is invalidated on the
373
 * next ExtractStringData() call on this DDFSubfieldDefn().  It should not
374
 * be freed by the application.
375
 *
376
 * @see ExtractIntData(), ExtractFloatData()
377
 */
378
379
const char *DDFSubfieldDefn::ExtractStringData(const char *pachSourceData,
380
                                               int nMaxBytes,
381
                                               int *pnConsumedBytes) const
382
383
438k
{
384
438k
    const int nLength =
385
438k
        GetDataLength(pachSourceData, nMaxBytes, pnConsumedBytes);
386
387
438k
    osBuffer.assign(pachSourceData, pachSourceData + nLength);
388
389
438k
    return osBuffer.c_str();
390
438k
}
391
392
/************************************************************************/
393
/*                          ExtractFloatData()                          */
394
/************************************************************************/
395
396
/**
397
 * Extract a subfield value as a float.  Given a pointer to the data
398
 * for this subfield (from within a DDFRecord) this method will return the
399
 * floating point data for this subfield.  The number of bytes
400
 * consumed as part of this field can also be fetched.  This method may be
401
 * called for any type of subfield, and will return zero if the subfield is
402
 * not numeric.
403
 *
404
 * @param pachSourceData The pointer to the raw data for this field.  This
405
 * may have come from DDFRecord::GetData(), taking into account skip factors
406
 * over previous subfields data.
407
 * @param nMaxBytes The maximum number of bytes that are accessible after
408
 * pachSourceData.
409
 * @param pnConsumedBytes Pointer to an integer into which the number of
410
 * bytes consumed by this field should be written.  May be NULL to ignore.
411
 * This is used as a skip factor to increment pachSourceData to point to the
412
 * next subfields data.
413
 *
414
 * @return The subfield's numeric value (or zero if it isn't numeric).
415
 *
416
 * @see ExtractIntData(), ExtractStringData()
417
 */
418
419
double DDFSubfieldDefn::ExtractFloatData(const char *pachSourceData,
420
                                         int nMaxBytes,
421
                                         int *pnConsumedBytes) const
422
423
58.4k
{
424
58.4k
    switch (osFormatString[0])
425
58.4k
    {
426
357
        case 'A':
427
1.37k
        case 'I':
428
35.6k
        case 'R':
429
35.9k
        case 'S':
430
35.9k
        case 'C':
431
35.9k
            return CPLAtof(
432
35.9k
                ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
433
434
4
        case 'B':
435
22.5k
        case 'b':
436
22.5k
        {
437
22.5k
            unsigned char abyData[8];
438
22.5k
            void *pabyData = abyData;
439
440
22.5k
            if (nFormatWidth > nMaxBytes)
441
2
            {
442
2
                CPLError(CE_Warning, CPLE_AppDefined,
443
2
                         "Attempt to extract float subfield %s with format %s\n"
444
2
                         "failed as only %d bytes available.  Using zero.",
445
2
                         osName.c_str(), osFormatString.c_str(), nMaxBytes);
446
2
                return 0;
447
2
            }
448
22.5k
            if (nFormatWidth > static_cast<int>(sizeof(abyData)))
449
0
            {
450
0
                CPLError(CE_Failure, CPLE_AppDefined,
451
0
                         "Format width %d too large", nFormatWidth);
452
0
                return 0;
453
0
            }
454
455
22.5k
            if (pnConsumedBytes != nullptr)
456
22.5k
                *pnConsumedBytes = nFormatWidth;
457
458
            // Byte swap the data if it isn't in machine native format.
459
            // In any event we copy it into our buffer to ensure it is
460
            // word aligned.
461
22.5k
#ifdef CPL_LSB
462
22.5k
            if (osFormatString[0] == 'B')
463
#else
464
            if (osFormatString[0] == 'b')
465
#endif
466
4
            {
467
10
                for (int i = 0; i < nFormatWidth; i++)
468
6
                    abyData[nFormatWidth - i - 1] = pachSourceData[i];
469
4
            }
470
22.5k
            else
471
22.5k
            {
472
22.5k
                memcpy(abyData, pachSourceData, nFormatWidth);
473
22.5k
            }
474
475
            // Interpret the bytes of data.
476
22.5k
            switch (eBinaryFormat)
477
22.5k
            {
478
0
                case UInt:
479
0
                    if (nFormatWidth == 1)
480
0
                        return abyData[0];
481
0
                    else if (nFormatWidth == 2)
482
0
                        return *(static_cast<GUInt16 *>(pabyData));
483
0
                    else if (nFormatWidth == 4)
484
0
                        return *(static_cast<GUInt32 *>(pabyData));
485
0
                    else
486
0
                    {
487
                        // CPLAssert( false );
488
0
                        return 0.0;
489
0
                    }
490
491
21
                case SInt:
492
21
                    if (nFormatWidth == 1)
493
2
                        return *(static_cast<signed char *>(pabyData));
494
19
                    else if (nFormatWidth == 2)
495
0
                        return *(static_cast<GInt16 *>(pabyData));
496
19
                    else if (nFormatWidth == 4)
497
1
                        return *(static_cast<GInt32 *>(pabyData));
498
18
                    else
499
18
                    {
500
                        // CPLAssert( false );
501
18
                        return 0.0;
502
18
                    }
503
504
22.5k
                case FloatReal:
505
22.5k
                    if (nFormatWidth == 4)
506
0
                        return static_cast<double>(
507
0
                            *(static_cast<float *>(pabyData)));
508
22.5k
                    else if (nFormatWidth == 8)
509
22.5k
                        return *(static_cast<double *>(pabyData));
510
0
                    else
511
0
                    {
512
                        // CPLAssert( false );
513
0
                        return 0.0;
514
0
                    }
515
516
0
                case NotBinary:
517
1
                case FPReal:
518
1
                case FloatComplex:
519
                    // CPLAssert( false );
520
1
                    return 0.0;
521
22.5k
            }
522
0
            break;
523
            // end of 'b'/'B' case.
524
22.5k
        }
525
526
0
        default:
527
            // CPLAssert( false );
528
0
            return 0.0;
529
58.4k
    }
530
531
    // CPLAssert( false );
532
0
    return 0.0;
533
58.4k
}
534
535
/************************************************************************/
536
/*                           ExtractIntData()                           */
537
/************************************************************************/
538
539
/**
540
 * Extract a subfield value as an integer.  Given a pointer to the data
541
 * for this subfield (from within a DDFRecord) this method will return the
542
 * int data for this subfield.  The number of bytes
543
 * consumed as part of this field can also be fetched.  This method may be
544
 * called for any type of subfield, and will return zero if the subfield is
545
 * not numeric.
546
 *
547
 * @param pachSourceData The pointer to the raw data for this field.  This
548
 * may have come from DDFRecord::GetData(), taking into account skip factors
549
 * over previous subfields data.
550
 * @param nMaxBytes The maximum number of bytes that are accessible after
551
 * pachSourceData.
552
 * @param pnConsumedBytes Pointer to an integer into which the number of
553
 * bytes consumed by this field should be written.  May be NULL to ignore.
554
 * This is used as a skip factor to increment pachSourceData to point to the
555
 * next subfields data.
556
 *
557
 * @return The subfield's numeric value (or zero if it isn't numeric).
558
 *
559
 * @see ExtractFloatData(), ExtractStringData()
560
 */
561
562
int DDFSubfieldDefn::ExtractIntData(const char *pachSourceData, int nMaxBytes,
563
                                    int *pnConsumedBytes) const
564
565
607k
{
566
607k
    switch (osFormatString[0])
567
607k
    {
568
6.61k
        case 'A':
569
93.2k
        case 'I':
570
93.4k
        case 'R':
571
94.2k
        case 'S':
572
94.2k
        case 'C':
573
94.2k
            return atoi(
574
94.2k
                ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
575
576
15.7k
        case 'B':
577
513k
        case 'b':
578
513k
        {
579
513k
            unsigned char abyData[8];
580
513k
            void *pabyData = abyData;
581
582
513k
            if (nFormatWidth > nMaxBytes ||
583
507k
                nFormatWidth >= static_cast<int>(sizeof(abyData)))
584
5.28k
            {
585
5.28k
                CPLError(
586
5.28k
                    CE_Warning, CPLE_AppDefined,
587
5.28k
                    "Attempt to extract int subfield %s with format %s\n"
588
5.28k
                    "failed as only %d bytes available.  Using zero.",
589
5.28k
                    osName.c_str(), osFormatString.c_str(),
590
5.28k
                    std::min(nMaxBytes, static_cast<int>(sizeof(abyData))));
591
5.28k
                return 0;
592
5.28k
            }
593
594
507k
            if (pnConsumedBytes != nullptr)
595
507k
                *pnConsumedBytes = nFormatWidth;
596
597
            // Byte swap the data if it isn't in machine native format.
598
            // In any event we copy it into our buffer to ensure it is
599
            // word aligned.
600
507k
#ifdef CPL_LSB
601
507k
            if (osFormatString[0] == 'B')
602
#else
603
            if (osFormatString[0] == 'b')
604
#endif
605
10.5k
            {
606
50.7k
                for (int i = 0; i < nFormatWidth; i++)
607
40.1k
                    abyData[nFormatWidth - i - 1] = pachSourceData[i];
608
10.5k
            }
609
497k
            else
610
497k
            {
611
497k
                memcpy(abyData, pachSourceData, nFormatWidth);
612
497k
            }
613
614
            // Interpret the bytes of data.
615
507k
            switch (eBinaryFormat)
616
507k
            {
617
445k
                case UInt:
618
445k
                    if (nFormatWidth == 4)
619
146k
                        return static_cast<int>(
620
146k
                            *(static_cast<GUInt32 *>(pabyData)));
621
299k
                    else if (nFormatWidth == 1)
622
116k
                        return abyData[0];
623
182k
                    else if (nFormatWidth == 2)
624
182k
                        return *(static_cast<GUInt16 *>(pabyData));
625
685
                    else
626
685
                    {
627
                        // CPLAssert( false );
628
685
                        return 0;
629
685
                    }
630
631
51.2k
                case SInt:
632
51.2k
                    if (nFormatWidth == 4)
633
29.2k
                        return *(static_cast<GInt32 *>(pabyData));
634
21.9k
                    else if (nFormatWidth == 1)
635
20.6k
                        return *(static_cast<signed char *>(pabyData));
636
1.30k
                    else if (nFormatWidth == 2)
637
88
                        return *(static_cast<GInt16 *>(pabyData));
638
1.21k
                    else
639
1.21k
                    {
640
                        // CPLAssert( false );
641
1.21k
                        return 0;
642
1.21k
                    }
643
644
9.50k
                case FloatReal:
645
9.50k
                    if (nFormatWidth == 4)
646
9.42k
                        return static_cast<int>(
647
9.42k
                            *(static_cast<float *>(pabyData)));
648
85
                    else if (nFormatWidth == 8)
649
0
                        return static_cast<int>(
650
0
                            *(static_cast<double *>(pabyData)));
651
85
                    else
652
85
                    {
653
                        // CPLAssert( false );
654
85
                        return 0;
655
85
                    }
656
657
255
                case NotBinary:
658
766
                case FPReal:
659
1.27k
                case FloatComplex:
660
                    // CPLAssert( false );
661
1.27k
                    return 0;
662
507k
            }
663
0
            break;
664
            // end of 'b'/'B' case.
665
507k
        }
666
667
0
        default:
668
            // CPLAssert( false );
669
0
            return 0;
670
607k
    }
671
672
    // CPLAssert( false );
673
0
    return 0;
674
607k
}
675
676
/************************************************************************/
677
/*                              DumpData()                              */
678
/*                                                                      */
679
/*      Dump the instance data for this subfield from a data            */
680
/*      record.  This fits into the output dump stream of a DDFField.   */
681
/************************************************************************/
682
683
/**
684
 * Dump subfield value to debugging file.
685
 *
686
 * @param pachData Pointer to data for this subfield.
687
 * @param nMaxBytes Maximum number of bytes available in pachData.
688
 * @param fp File to write report to.
689
 */
690
691
void DDFSubfieldDefn::DumpData(const char *pachData, int nMaxBytes,
692
                               FILE *fp) const
693
694
0
{
695
0
    if (nMaxBytes < 0)
696
0
    {
697
0
        fprintf(fp, "      Subfield `%s' = {invalid length}\n", osName.c_str());
698
0
        return;
699
0
    }
700
0
    if (eType == DDFFloat)
701
0
        fprintf(fp, "      Subfield `%s' = %f\n", osName.c_str(),
702
0
                ExtractFloatData(pachData, nMaxBytes, nullptr));
703
0
    else if (eType == DDFInt)
704
0
        fprintf(fp, "      Subfield `%s' = %d\n", osName.c_str(),
705
0
                ExtractIntData(pachData, nMaxBytes, nullptr));
706
0
    else if (eType == DDFBinaryString)
707
0
    {
708
0
        int nBytes = 0;
709
0
        const GByte *pabyBString = reinterpret_cast<const GByte *>(
710
0
            ExtractStringData(pachData, nMaxBytes, &nBytes));
711
712
0
        fprintf(fp, "      Subfield `%s' = 0x", osName.c_str());
713
0
        for (int i = 0; i < std::min(nBytes, 24); i++)
714
0
            fprintf(fp, "%02X", pabyBString[i]);
715
716
0
        if (nBytes > 24)
717
0
            fprintf(fp, "%s", "...");
718
719
0
        fprintf(fp, "\n");
720
0
    }
721
0
    else
722
0
        fprintf(fp, "      Subfield `%s' = `%s'\n", osName.c_str(),
723
0
                ExtractStringData(pachData, nMaxBytes, nullptr));
724
0
}
725
726
/************************************************************************/
727
/*                          GetDefaultValue()                           */
728
/************************************************************************/
729
730
/**
731
 * Get default data.
732
 *
733
 * Returns the default subfield data contents for this subfield definition.
734
 * For variable length numbers this will normally be "0<unit-terminator>".
735
 * For variable length strings it will be "<unit-terminator>".  For fixed
736
 * length numbers it is zero filled.  For fixed length strings it is space
737
 * filled.  For binary numbers it is binary zero filled.
738
 *
739
 * @param pachData the buffer into which the returned default will be placed.
740
 * May be NULL if just querying default size.
741
 * @param nBytesAvailable the size of pachData in bytes.
742
 * @param pnBytesUsed will receive the size of the subfield default data in
743
 * bytes.
744
 *
745
 * @return TRUE on success or FALSE on failure or if the passed buffer is too
746
 * small to hold the default.
747
 */
748
749
int DDFSubfieldDefn::GetDefaultValue(char *pachData, int nBytesAvailable,
750
                                     int *pnBytesUsed) const
751
752
4.16k
{
753
4.16k
    int nDefaultSize;
754
755
4.16k
    if (!bIsVariable)
756
4.16k
        nDefaultSize = nFormatWidth;
757
0
    else
758
0
        nDefaultSize = 1;
759
760
4.16k
    if (pnBytesUsed != nullptr)
761
4.16k
        *pnBytesUsed = nDefaultSize;
762
763
4.16k
    if (pachData == nullptr)
764
2.08k
        return TRUE;
765
766
2.08k
    if (nBytesAvailable < nDefaultSize)
767
0
        return FALSE;
768
769
2.08k
    if (bIsVariable)
770
0
    {
771
0
        pachData[0] = DDF_UNIT_TERMINATOR;
772
0
    }
773
2.08k
    else
774
2.08k
    {
775
2.08k
        char chFillChar;
776
2.08k
        if (GetBinaryFormat() == NotBinary)
777
0
        {
778
0
            if (GetType() == DDFInt || GetType() == DDFFloat)
779
0
                chFillChar = '0'; /* ASCII zero intended */
780
0
            else
781
0
                chFillChar = ' ';
782
0
        }
783
2.08k
        else
784
2.08k
            chFillChar = 0;
785
2.08k
        memset(pachData, chFillChar, nDefaultSize);
786
2.08k
    }
787
788
2.08k
    return TRUE;
789
2.08k
}
790
791
/************************************************************************/
792
/*                         FormatStringValue()                          */
793
/************************************************************************/
794
795
/**
796
 * Format string subfield value.
797
 *
798
 * Returns a buffer with the passed in string value reformatted in a way
799
 * suitable for storage in a DDFField for this subfield.
800
 */
801
802
int DDFSubfieldDefn::FormatStringValue(char *pachData, int nBytesAvailable,
803
                                       int *pnBytesUsed, const char *pszValue,
804
                                       int nValueLength) const
805
806
0
{
807
0
    int nSize;
808
809
0
    if (nValueLength == -1)
810
0
        nValueLength = static_cast<int>(strlen(pszValue));
811
812
0
    if (bIsVariable)
813
0
    {
814
0
        nSize = nValueLength + 1;
815
0
    }
816
0
    else
817
0
    {
818
0
        nSize = nFormatWidth;
819
0
    }
820
821
0
    if (pnBytesUsed != nullptr)
822
0
        *pnBytesUsed = nSize;
823
824
0
    if (pachData == nullptr)
825
0
        return TRUE;
826
827
0
    if (nBytesAvailable < nSize)
828
0
        return FALSE;
829
830
0
    if (bIsVariable)
831
0
    {
832
0
        strncpy(pachData, pszValue, nSize - 1);
833
0
        pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
834
0
    }
835
0
    else
836
0
    {
837
0
        if (GetBinaryFormat() == NotBinary)
838
0
        {
839
0
            memset(pachData, ' ', nSize);
840
            // cppcheck-suppress redundantCopy
841
0
            memcpy(pachData, pszValue, std::min(nValueLength, nSize));
842
0
        }
843
0
        else
844
0
        {
845
0
            memset(pachData, 0, nSize);
846
            // cppcheck-suppress redundantCopy
847
0
            memcpy(pachData, pszValue, std::min(nValueLength, nSize));
848
0
        }
849
0
    }
850
851
0
    return TRUE;
852
0
}
853
854
/************************************************************************/
855
/*                           FormatIntValue()                           */
856
/************************************************************************/
857
858
/**
859
 * Format int subfield value.
860
 *
861
 * Returns a buffer with the passed in int value reformatted in a way
862
 * suitable for storage in a DDFField for this subfield.
863
 */
864
865
int DDFSubfieldDefn::FormatIntValue(char *pachData, int nBytesAvailable,
866
                                    int *pnBytesUsed, int nNewValue) const
867
868
9.23k
{
869
9.23k
    int nSize;
870
9.23k
    char szWork[30];
871
872
9.23k
    snprintf(szWork, sizeof(szWork), "%d", nNewValue);
873
874
9.23k
    if (bIsVariable)
875
0
    {
876
0
        nSize = static_cast<int>(strlen(szWork)) + 1;
877
0
    }
878
9.23k
    else
879
9.23k
    {
880
9.23k
        nSize = nFormatWidth;
881
882
9.23k
        if (GetBinaryFormat() == NotBinary &&
883
0
            static_cast<int>(strlen(szWork)) > nSize)
884
0
            return FALSE;
885
9.23k
    }
886
887
9.23k
    if (pnBytesUsed != nullptr)
888
4.63k
        *pnBytesUsed = nSize;
889
890
9.23k
    if (pachData == nullptr)
891
4.63k
        return TRUE;
892
893
4.59k
    if (nBytesAvailable < nSize)
894
0
        return FALSE;
895
896
4.59k
    if (bIsVariable)
897
0
    {
898
0
        memcpy(pachData, szWork, nSize - 1);
899
0
        pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
900
0
    }
901
4.59k
    else
902
4.59k
    {
903
4.59k
        GUInt32 nMask = 0xff;
904
905
4.59k
        switch (GetBinaryFormat())
906
4.59k
        {
907
0
            case NotBinary:
908
0
            {
909
0
                constexpr char chFillChar = '0'; /* ASCII zero intended */
910
0
                const int nZeroFillCount =
911
0
                    nSize - static_cast<int>(strlen(szWork));
912
0
                for (int i = 0; i < nZeroFillCount; ++i)
913
0
                    pachData[i] = chFillChar;
914
0
                memcpy(pachData + nZeroFillCount, szWork, strlen(szWork));
915
0
                break;
916
0
            }
917
918
4.59k
            case UInt:
919
4.59k
            case SInt:
920
14.1k
                for (int i = 0; i < nFormatWidth; i++)
921
9.58k
                {
922
9.58k
                    int iOut;
923
924
                    // big endian required?
925
9.58k
                    if (osFormatString[0] == 'B')
926
0
                        iOut = nFormatWidth - i - 1;
927
9.58k
                    else
928
9.58k
                        iOut = i;
929
930
9.58k
                    pachData[iOut] =
931
9.58k
                        static_cast<char>((nNewValue & nMask) >> (i * 8));
932
9.58k
                    nMask <<= 8;
933
9.58k
                }
934
4.59k
                break;
935
936
0
            case FloatReal:
937
0
                CPLAssert(false);
938
0
                break;
939
940
0
            default:
941
0
                CPLAssert(false);
942
0
                break;
943
4.59k
        }
944
4.59k
    }
945
946
4.59k
    return TRUE;
947
4.59k
}
948
949
/************************************************************************/
950
/*                          FormatFloatValue()                          */
951
/************************************************************************/
952
953
/**
954
 * Format float subfield value.
955
 *
956
 * Returns a buffer with the passed in float value reformatted in a way
957
 * suitable for storage in a DDFField for this subfield.
958
 */
959
960
int DDFSubfieldDefn::FormatFloatValue(char *pachData, int nBytesAvailable,
961
                                      int *pnBytesUsed, double dfNewValue) const
962
963
0
{
964
0
    int nSize;
965
0
    char szWork[120];
966
967
0
    CPLsnprintf(szWork, sizeof(szWork), "%.16g", dfNewValue);
968
969
0
    if (bIsVariable)
970
0
    {
971
0
        nSize = static_cast<int>(strlen(szWork)) + 1;
972
0
    }
973
0
    else
974
0
    {
975
0
        nSize = nFormatWidth;
976
977
0
        if (GetBinaryFormat() == NotBinary &&
978
0
            static_cast<int>(strlen(szWork)) > nSize)
979
0
            return FALSE;
980
0
    }
981
982
0
    if (pnBytesUsed != nullptr)
983
0
        *pnBytesUsed = nSize;
984
985
0
    if (pachData == nullptr)
986
0
        return TRUE;
987
988
0
    if (nBytesAvailable < nSize)
989
0
        return FALSE;
990
991
0
    if (bIsVariable)
992
0
    {
993
0
        memcpy(pachData, szWork, nSize - 1);
994
0
        pachData[nSize - 1] = DDF_UNIT_TERMINATOR;
995
0
    }
996
0
    else
997
0
    {
998
0
        if (GetBinaryFormat() == NotBinary)
999
0
        {
1000
0
            constexpr char chFillChar = '0'; /* ASCII zero intended */
1001
0
            const int nZeroFillCount = nSize - static_cast<int>(strlen(szWork));
1002
0
            for (int i = 0; i < nZeroFillCount; ++i)
1003
0
                pachData[i] = chFillChar;
1004
0
            memcpy(pachData + nZeroFillCount, szWork, strlen(szWork));
1005
0
        }
1006
0
        else if (GetBinaryFormat() == FloatReal)
1007
0
        {
1008
0
            if (nFormatWidth == 8)
1009
0
            {
1010
0
                if (osFormatString[0] == 'B')
1011
0
                {
1012
0
                    CPL_MSBPTR64(&dfNewValue);
1013
0
                }
1014
0
                else
1015
0
                {
1016
0
                    CPL_LSBPTR64(&dfNewValue);
1017
0
                }
1018
0
                memcpy(pachData, &dfNewValue, sizeof(dfNewValue));
1019
0
            }
1020
0
            else
1021
0
            {
1022
0
                CPLAssert(nFormatWidth == 4);
1023
0
                float f = static_cast<float>(dfNewValue);
1024
0
                if (osFormatString[0] == 'B')
1025
0
                {
1026
0
                    CPL_MSBPTR32(&f);
1027
0
                }
1028
0
                else
1029
0
                {
1030
0
                    CPL_LSBPTR32(&f);
1031
0
                }
1032
0
                memcpy(pachData, &f, sizeof(f));
1033
0
            }
1034
0
        }
1035
0
        else
1036
0
        {
1037
0
            CPLAssert(false);
1038
            /* implement me */
1039
0
        }
1040
0
    }
1041
1042
0
    return TRUE;
1043
0
}