Coverage Report

Created: 2025-11-15 08:43

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