Coverage Report

Created: 2026-02-14 09:00

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
985k
    : pszName(nullptr), pszFormatString(CPLStrdup("")), eType(DDFString),
33
985k
      eBinaryFormat(NotBinary), bIsVariable(TRUE),
34
985k
      chFormatDelimiter(DDF_UNIT_TERMINATOR), nFormatWidth(0), nMaxBufChars(0),
35
985k
      pachBuffer(nullptr)
36
985k
{
37
985k
}
38
39
/************************************************************************/
40
/*                          ~DDFSubfieldDefn()                          */
41
/************************************************************************/
42
43
DDFSubfieldDefn::~DDFSubfieldDefn()
44
45
985k
{
46
985k
    CPLFree(pszName);
47
985k
    CPLFree(pszFormatString);
48
985k
    CPLFree(pachBuffer);
49
985k
}
50
51
/************************************************************************/
52
/*                              SetName()                               */
53
/************************************************************************/
54
55
void DDFSubfieldDefn::SetName(const char *pszNewName)
56
57
985k
{
58
985k
    int i;
59
60
985k
    CPLFree(pszName);
61
62
985k
    pszName = CPLStrdup(pszNewName);
63
64
986k
    for (i = static_cast<int>(strlen(pszName)) - 1; i > 0 && pszName[i] == ' ';
65
985k
         i--)
66
1.14k
        pszName[i] = '\0';
67
985k
}
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
952k
{
83
952k
    CPLFree(pszFormatString);
84
952k
    pszFormatString = CPLStrdup(pszFormat);
85
86
    /* -------------------------------------------------------------------- */
87
    /*      These values will likely be used.                               */
88
    /* -------------------------------------------------------------------- */
89
952k
    if (pszFormatString[1] == '(')
90
899k
    {
91
899k
        nFormatWidth = atoi(pszFormatString + 2);
92
899k
        if (nFormatWidth < 0)
93
199
        {
94
199
            CPLError(CE_Failure, CPLE_AppDefined, "Format width %s is invalid.",
95
199
                     pszFormatString + 2);
96
199
            return FALSE;
97
199
        }
98
899k
        bIsVariable = nFormatWidth == 0;
99
899k
    }
100
53.1k
    else
101
53.1k
        bIsVariable = TRUE;
102
103
    /* -------------------------------------------------------------------- */
104
    /*      Interpret the format string.                                    */
105
    /* -------------------------------------------------------------------- */
106
952k
    switch (pszFormatString[0])
107
952k
    {
108
396k
        case 'A':
109
400k
        case 'C':  // It isn't clear to me how this is different than 'A'
110
400k
            eType = DDFString;
111
400k
            break;
112
113
63.6k
        case 'R':
114
63.6k
            eType = DDFFloat;
115
63.6k
            break;
116
117
433k
        case 'I':
118
459k
        case 'S':
119
459k
            eType = DDFInt;
120
459k
            break;
121
122
10.2k
        case 'B':
123
27.5k
        case 'b':
124
            // Is the width expressed in bits? (is it a bitstring)
125
27.5k
            bIsVariable = FALSE;
126
27.5k
            if (pszFormatString[1] == '\0')
127
79
                return FALSE;
128
129
27.4k
            if (pszFormatString[1] == '(')
130
10.0k
            {
131
10.0k
                nFormatWidth = atoi(pszFormatString + 2);
132
10.0k
                if (nFormatWidth < 0 || nFormatWidth % 8 != 0)
133
165
                {
134
165
                    CPLError(CE_Failure, CPLE_AppDefined,
135
165
                             "Format width %s is invalid.",
136
165
                             pszFormatString + 2);
137
165
                    return FALSE;
138
165
                }
139
140
9.91k
                nFormatWidth = nFormatWidth / 8;
141
9.91k
                eBinaryFormat = SInt;  // good default, works for SDTS.
142
143
9.91k
                if (nFormatWidth < 5)
144
9.67k
                    eType = DDFInt;
145
235
                else
146
235
                    eType = DDFBinaryString;
147
9.91k
            }
148
149
            // or do we have a binary type indicator? (is it binary)
150
17.3k
            else
151
17.3k
            {
152
17.3k
                if (pszFormatString[1] < '0' || pszFormatString[1] > '5')
153
725
                {
154
725
                    CPLError(CE_Failure, CPLE_AppDefined,
155
725
                             "Binary format = %c is invalid.",
156
725
                             pszFormatString[1]);
157
725
                    return FALSE;
158
725
                }
159
16.6k
                eBinaryFormat = (DDFBinaryFormat)(pszFormatString[1] - '0');
160
16.6k
                nFormatWidth = atoi(pszFormatString + 2);
161
16.6k
                if (nFormatWidth < 0)
162
610
                {
163
610
                    CPLError(CE_Failure, CPLE_AppDefined,
164
610
                             "Format width %s is invalid.",
165
610
                             pszFormatString + 2);
166
610
                    return FALSE;
167
610
                }
168
169
16.0k
                if (eBinaryFormat == SInt || eBinaryFormat == UInt)
170
13.9k
                    eType = DDFInt;
171
2.07k
                else
172
2.07k
                    eType = DDFFloat;
173
16.0k
            }
174
25.9k
            break;
175
176
25.9k
        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
573
            CPLError(CE_Failure, CPLE_AppDefined,
180
573
                     "Format type of `%c' not supported.\n",
181
573
                     pszFormatString[0]);
182
183
573
            return FALSE;
184
185
1.02k
        default:
186
1.02k
            CPLError(CE_Failure, CPLE_AppDefined,
187
1.02k
                     "Format type of `%c' not recognised.\n",
188
1.02k
                     pszFormatString[0]);
189
190
1.02k
            return FALSE;
191
952k
    }
192
193
949k
    return TRUE;
194
952k
}
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.49M
{
251
3.49M
    if (!bIsVariable)
252
3.04M
    {
253
3.04M
        if (nFormatWidth > nMaxBytes)
254
43.3k
        {
255
43.3k
            CPLError(CE_Warning, CPLE_AppDefined,
256
43.3k
                     "Only %d bytes available for subfield %s with\n"
257
43.3k
                     "format string %s ... returning shortened data.",
258
43.3k
                     nMaxBytes, pszName, pszFormatString);
259
260
43.3k
            if (pnConsumedBytes != nullptr)
261
24.5k
                *pnConsumedBytes = nMaxBytes;
262
263
43.3k
            return nMaxBytes;
264
43.3k
        }
265
3.00M
        else
266
3.00M
        {
267
3.00M
            if (pnConsumedBytes != nullptr)
268
2.80M
                *pnConsumedBytes = nFormatWidth;
269
270
3.00M
            return nFormatWidth;
271
3.00M
        }
272
3.04M
    }
273
442k
    else
274
442k
    {
275
442k
        int nLength = 0;
276
442k
        int bAsciiField = TRUE;
277
442k
        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
442k
        if (nMaxBytes > 1 &&
301
436k
            (pachSourceData[nMaxBytes - 2] == chFormatDelimiter ||
302
428k
             pachSourceData[nMaxBytes - 2] == DDF_FIELD_TERMINATOR) &&
303
35.1k
            pachSourceData[nMaxBytes - 1] == 0x00)
304
6.43k
            bAsciiField = FALSE;
305
306
        //        if( !bAsciiField )
307
        //            CPLDebug( "ISO8211", "Non-ASCII field detected." );
308
309
12.2M
        while (nLength < nMaxBytes)
310
12.0M
        {
311
12.0M
            if (bAsciiField)
312
11.9M
            {
313
11.9M
                if (pachSourceData[nLength] == chFormatDelimiter ||
314
11.8M
                    pachSourceData[nLength] == DDF_FIELD_TERMINATOR)
315
222k
                    break;
316
11.9M
            }
317
82.9k
            else
318
82.9k
            {
319
82.9k
                if (nLength > 0 &&
320
76.5k
                    (pachSourceData[nLength - 1] == chFormatDelimiter ||
321
71.6k
                     pachSourceData[nLength - 1] == DDF_FIELD_TERMINATOR) &&
322
10.7k
                    pachSourceData[nLength] == 0)
323
6.43k
                {
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
6.43k
                    if (nLength + 1 < nMaxBytes &&
328
3.08k
                        pachSourceData[nLength + 1] == DDF_FIELD_TERMINATOR)
329
918
                        extraConsumedBytes++;
330
6.43k
                    break;
331
6.43k
                }
332
82.9k
            }
333
334
11.7M
            nLength++;
335
11.7M
        }
336
337
442k
        if (pnConsumedBytes != nullptr)
338
351k
        {
339
351k
            if (nMaxBytes == 0)
340
2.92k
                *pnConsumedBytes = nLength + extraConsumedBytes;
341
348k
            else
342
348k
                *pnConsumedBytes = nLength + extraConsumedBytes + 1;
343
351k
        }
344
345
442k
        return nLength;
346
442k
    }
347
3.49M
}
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
482k
{
392
482k
    int nLength = GetDataLength(pachSourceData, nMaxBytes, pnConsumedBytes);
393
394
    /* -------------------------------------------------------------------- */
395
    /*      Do we need to grow the buffer.                                  */
396
    /* -------------------------------------------------------------------- */
397
482k
    if (nMaxBufChars < nLength + 1)
398
233k
    {
399
233k
        CPLFree(pachBuffer);
400
401
233k
        nMaxBufChars = nLength + 1;
402
233k
        pachBuffer = (char *)CPLMalloc(nMaxBufChars);
403
233k
    }
404
405
    /* -------------------------------------------------------------------- */
406
    /*      Copy the data to the buffer.  We use memcpy() so that it        */
407
    /*      will work for binary data.                                      */
408
    /* -------------------------------------------------------------------- */
409
482k
    memcpy(pachBuffer, pachSourceData, nLength);
410
482k
    pachBuffer[nLength] = '\0';
411
412
482k
    return pachBuffer;
413
482k
}
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.48k
{
447
1.48k
    switch (pszFormatString[0])
448
1.48k
    {
449
347
        case 'A':
450
669
        case 'I':
451
1.20k
        case 'R':
452
1.36k
        case 'S':
453
1.37k
        case 'C':
454
1.37k
            return CPLAtof(
455
1.37k
                ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
456
457
17
        case 'B':
458
113
        case 'b':
459
113
        {
460
113
            unsigned char abyData[8];
461
113
            void *pabyData = abyData;
462
463
113
            if (nFormatWidth > nMaxBytes)
464
2
            {
465
2
                CPLError(CE_Warning, CPLE_AppDefined,
466
2
                         "Attempt to extract float subfield %s with format %s\n"
467
2
                         "failed as only %d bytes available.  Using zero.",
468
2
                         pszName, pszFormatString, nMaxBytes);
469
2
                return 0;
470
2
            }
471
111
            if (nFormatWidth > static_cast<int>(sizeof(abyData)))
472
2
            {
473
2
                CPLError(CE_Failure, CPLE_AppDefined,
474
2
                         "Format width %d too large", nFormatWidth);
475
2
                return 0;
476
2
            }
477
478
109
            if (pnConsumedBytes != nullptr)
479
109
                *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
109
#ifdef CPL_LSB
485
109
            if (pszFormatString[0] == 'B')
486
#else
487
            if (pszFormatString[0] == 'b')
488
#endif
489
16
            {
490
49
                for (int i = 0; i < nFormatWidth; i++)
491
33
                    abyData[nFormatWidth - i - 1] = pachSourceData[i];
492
16
            }
493
93
            else
494
93
            {
495
93
                memcpy(abyData, pachSourceData, nFormatWidth);
496
93
            }
497
498
            // Interpret the bytes of data.
499
109
            switch (eBinaryFormat)
500
109
            {
501
40
                case UInt:
502
40
                    if (nFormatWidth == 1)
503
5
                        return abyData[0];
504
35
                    else if (nFormatWidth == 2)
505
3
                        return *((GUInt16 *)pabyData);
506
32
                    else if (nFormatWidth == 4)
507
27
                        return *((GUInt32 *)pabyData);
508
5
                    else
509
5
                    {
510
                        // CPLAssert( false );
511
5
                        return 0.0;
512
5
                    }
513
514
45
                case SInt:
515
45
                    if (nFormatWidth == 1)
516
2
                        return *((signed char *)abyData);
517
43
                    else if (nFormatWidth == 2)
518
7
                        return *((GInt16 *)pabyData);
519
36
                    else if (nFormatWidth == 4)
520
4
                        return *((GInt32 *)pabyData);
521
32
                    else
522
32
                    {
523
                        // CPLAssert( false );
524
32
                        return 0.0;
525
32
                    }
526
527
3
                case FloatReal:
528
3
                    if (nFormatWidth == 4)
529
1
                        return *((float *)pabyData);
530
2
                    else if (nFormatWidth == 8)
531
0
                        return *((double *)pabyData);
532
2
                    else
533
2
                    {
534
                        // CPLAssert( false );
535
2
                        return 0.0;
536
2
                    }
537
538
15
                case NotBinary:
539
18
                case FPReal:
540
21
                case FloatComplex:
541
                    // CPLAssert( false );
542
21
                    return 0.0;
543
109
            }
544
0
            break;
545
            // end of 'b'/'B' case.
546
109
        }
547
548
0
        default:
549
            // CPLAssert( false );
550
0
            return 0.0;
551
1.48k
    }
552
553
    // CPLAssert( false );
554
0
    return 0.0;
555
1.48k
}
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
374k
{
588
374k
    switch (pszFormatString[0])
589
374k
    {
590
10.6k
        case 'A':
591
117k
        case 'I':
592
126k
        case 'R':
593
129k
        case 'S':
594
167k
        case 'C':
595
167k
            return atoi(
596
167k
                ExtractStringData(pachSourceData, nMaxBytes, pnConsumedBytes));
597
598
1.42k
        case 'B':
599
206k
        case 'b':
600
206k
        {
601
206k
            unsigned char abyData[8];
602
206k
            void *pabyData = abyData;
603
604
206k
            if (nFormatWidth > nMaxBytes ||
605
206k
                nFormatWidth >= (int)sizeof(abyData))
606
728
            {
607
728
                CPLError(
608
728
                    CE_Warning, CPLE_AppDefined,
609
728
                    "Attempt to extract int subfield %s with format %s\n"
610
728
                    "failed as only %d bytes available.  Using zero.",
611
728
                    pszName, pszFormatString,
612
728
                    std::min(nMaxBytes, static_cast<int>(sizeof(abyData))));
613
728
                return 0;
614
728
            }
615
616
206k
            if (pnConsumedBytes != nullptr)
617
206k
                *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
206k
#ifdef CPL_LSB
623
206k
            if (pszFormatString[0] == 'B')
624
#else
625
            if (pszFormatString[0] == 'b')
626
#endif
627
1.20k
            {
628
3.56k
                for (int i = 0; i < nFormatWidth; i++)
629
2.35k
                    abyData[nFormatWidth - i - 1] = pachSourceData[i];
630
1.20k
            }
631
204k
            else
632
204k
            {
633
204k
                memcpy(abyData, pachSourceData, nFormatWidth);
634
204k
            }
635
636
            // Interpret the bytes of data.
637
206k
            switch (eBinaryFormat)
638
206k
            {
639
36.2k
                case UInt:
640
36.2k
                    if (nFormatWidth == 4)
641
32.5k
                        return (int)*((GUInt32 *)pabyData);
642
3.64k
                    else if (nFormatWidth == 1)
643
1.41k
                        return abyData[0];
644
2.22k
                    else if (nFormatWidth == 2)
645
517
                        return *((GUInt16 *)pabyData);
646
1.70k
                    else
647
1.70k
                    {
648
                        // CPLAssert( false );
649
1.70k
                        return 0;
650
1.70k
                    }
651
652
163k
                case SInt:
653
163k
                    if (nFormatWidth == 4)
654
23.2k
                        return *((GInt32 *)pabyData);
655
140k
                    else if (nFormatWidth == 1)
656
18.3k
                        return *((signed char *)abyData);
657
122k
                    else if (nFormatWidth == 2)
658
1.09k
                        return *((GInt16 *)pabyData);
659
121k
                    else
660
121k
                    {
661
                        // CPLAssert( false );
662
121k
                        return 0;
663
121k
                    }
664
665
5.15k
                case FloatReal:
666
5.15k
                    if (nFormatWidth == 4)
667
4.63k
                        return (int)*((float *)pabyData);
668
519
                    else if (nFormatWidth == 8)
669
0
                        return (int)*((double *)pabyData);
670
519
                    else
671
519
                    {
672
                        // CPLAssert( false );
673
519
                        return 0;
674
519
                    }
675
676
271
                case NotBinary:
677
483
                case FPReal:
678
780
                case FloatComplex:
679
                    // CPLAssert( false );
680
780
                    return 0;
681
206k
            }
682
0
            break;
683
            // end of 'b'/'B' case.
684
206k
        }
685
686
0
        default:
687
            // CPLAssert( false );
688
0
            return 0;
689
374k
    }
690
691
    // CPLAssert( false );
692
0
    return 0;
693
374k
}
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
}