Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcvrfd.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1994-2025, OFFIS e.V.
4
 *  All rights reserved.  See COPYRIGHT file for details.
5
 *
6
 *  This software and supporting documentation were developed by
7
 *
8
 *    OFFIS e.V.
9
 *    R&D Division Health
10
 *    Escherweg 2
11
 *    D-26121 Oldenburg, Germany
12
 *
13
 *
14
 *  Module:  dcmdata
15
 *
16
 *  Author:  Gerd Ehlers, Andreas Barth
17
 *
18
 *  Purpose: Implementation of class DcmFloatingPointDouble
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
#include "dcmtk/dcmdata/dcvrfd.h"
25
#include "dcmtk/ofstd/ofstream.h"
26
#include "dcmtk/ofstd/ofstd.h"
27
#include "dcmtk/ofstd/ofmath.h"
28
#include "dcmtk/dcmdata/dcjson.h"
29
30
// ********************************
31
32
DcmFloatingPointDouble::DcmFloatingPointDouble(const DcmTag &tag)
33
0
  : DcmElement(tag, 0)
34
0
{
35
0
}
36
37
38
DcmFloatingPointDouble::DcmFloatingPointDouble(const DcmTag &tag,
39
                                               const Uint32 len)
40
0
  : DcmElement(tag, len)
41
0
{
42
0
}
43
44
45
DcmFloatingPointDouble::DcmFloatingPointDouble(const DcmFloatingPointDouble &old)
46
0
  : DcmElement(old)
47
0
{
48
0
}
49
50
51
DcmFloatingPointDouble::~DcmFloatingPointDouble()
52
0
{
53
0
}
54
55
56
DcmFloatingPointDouble &DcmFloatingPointDouble::operator=(const DcmFloatingPointDouble &obj)
57
0
{
58
0
    DcmElement::operator=(obj);
59
0
    return *this;
60
0
}
61
62
63
int DcmFloatingPointDouble::compare(const DcmElement& rhs) const
64
0
{
65
0
    int result = DcmElement::compare(rhs);
66
0
    if (result != 0)
67
0
    {
68
0
        return result;
69
0
    }
70
71
    /* cast away constness (dcmdata is not const correct...) */
72
0
    DcmFloatingPointDouble* myThis = NULL;
73
0
    DcmFloatingPointDouble* myRhs = NULL;
74
0
    myThis = OFconst_cast(DcmFloatingPointDouble*, this);
75
0
    myRhs = OFstatic_cast(DcmFloatingPointDouble*, OFconst_cast(DcmElement*, &rhs));
76
77
    /* compare number of values */
78
0
    unsigned long thisNumValues = myThis->getNumberOfValues();
79
0
    unsigned long rhsNumValues = myRhs->getNumberOfValues();
80
0
    if (thisNumValues < rhsNumValues)
81
0
    {
82
0
        return -1;
83
0
    }
84
0
    else if (thisNumValues > rhsNumValues)
85
0
    {
86
0
        return 1;
87
0
    }
88
89
    /* iterate over all components and test equality */
90
0
    for (unsigned long count = 0; count < thisNumValues; count++)
91
0
    {
92
0
        Float64 val = 0;
93
0
        if (myThis->getFloat64(val, count).good())
94
0
        {
95
0
            Float64 rhsVal = 0;
96
0
            if (myRhs->getFloat64(rhsVal, count).good())
97
0
            {
98
0
                if (val > rhsVal)
99
0
                {
100
0
                    return 1;
101
0
                }
102
0
                else if (val < rhsVal)
103
0
                {
104
0
                    return -1;
105
0
                }
106
0
            }
107
0
        }
108
0
    }
109
110
    /* all values as well as VM equal: objects are equal */
111
0
    return 0;
112
0
}
113
114
115
OFCondition DcmFloatingPointDouble::copyFrom(const DcmObject& rhs)
116
0
{
117
0
    if (this != &rhs)
118
0
    {
119
0
        if (rhs.ident() != ident()) return EC_IllegalCall;
120
0
        *this = OFstatic_cast(const DcmFloatingPointDouble &, rhs);
121
0
    }
122
0
    return EC_Normal;
123
0
}
124
125
126
// ********************************
127
128
129
DcmEVR DcmFloatingPointDouble::ident() const
130
0
{
131
0
    return EVR_FD;
132
0
}
133
134
135
OFCondition DcmFloatingPointDouble::checkValue(const OFString &vm,
136
                                               const OFBool /*oldFormat*/)
137
0
{
138
    /* check VM only, further checks on the floating point values could be added later */
139
0
    return DcmElement::checkVM(getVM(), vm);
140
0
}
141
142
143
unsigned long DcmFloatingPointDouble::getVM()
144
0
{
145
0
    return getNumberOfValues();
146
0
}
147
148
149
unsigned long DcmFloatingPointDouble::getNumberOfValues()
150
0
{
151
0
    return OFstatic_cast(unsigned long, getLengthField() / sizeof(Float64));
152
0
}
153
154
155
// ********************************
156
157
158
void DcmFloatingPointDouble::print(STD_NAMESPACE ostream &out,
159
                                   const size_t flags,
160
                                   const int level,
161
                                   const char * /*pixelFileName*/,
162
                                   size_t * /*pixelCounter*/)
163
0
{
164
0
    if (valueLoaded())
165
0
    {
166
        /* get double data */
167
0
        Float64 *doubleVals;
168
0
        errorFlag = getFloat64Array(doubleVals);
169
0
        if (doubleVals != NULL)
170
0
        {
171
            /* do not use getVM() because derived classes might always return 1 */
172
0
            const unsigned long count = getNumberOfValues();
173
            /* double-check length field for valid value */
174
0
            if (count > 0)
175
0
            {
176
0
                const unsigned long maxLength = (flags & DCMTypes::PF_shortenLongTagValues) ?
177
0
                    DCM_OptPrintLineLength : OFstatic_cast(unsigned long, -1) /*unlimited*/;
178
0
                unsigned long printedLength = 0;
179
0
                unsigned long newLength = 0;
180
0
                char buffer[64];
181
                /* print line start with tag and VR */
182
0
                printInfoLineStart(out, flags, level);
183
                /* print multiple values */
184
0
                for (unsigned int i = 0; i < count; i++, doubleVals++)
185
0
                {
186
                    /* check whether first value is printed (omit delimiter) */
187
0
                    if (i == 0)
188
0
                        OFStandard::ftoa(buffer, sizeof(buffer), *doubleVals, 0, 0, -2 /* print enough digits to permit lossless conversion back to FD */);
189
0
                    else
190
0
                    {
191
0
                        buffer[0] = '\\';
192
0
                        OFStandard::ftoa(buffer + 1, sizeof(buffer) - 1, *doubleVals, 0, 0, -2 /* print enough digits to permit lossless conversion back to FD */);
193
0
                    }
194
                    /* check whether current value sticks to the length limit */
195
0
                    newLength = printedLength + OFstatic_cast(unsigned long, strlen(buffer));
196
0
                    if ((newLength <= maxLength) && ((i + 1 == count) || (newLength + 3 <= maxLength)))
197
0
                    {
198
0
                        out << buffer;
199
0
                        printedLength = newLength;
200
0
                    } else {
201
                        /* check whether output has been truncated */
202
0
                        if (i + 1 < count)
203
0
                        {
204
0
                            out << "...";
205
0
                            printedLength += 3;
206
0
                        }
207
0
                        break;
208
0
                    }
209
0
                }
210
                /* print line end with length, VM and tag name */
211
0
                printInfoLineEnd(out, flags, printedLength);
212
0
            } else {
213
                /* count can be zero if we have an invalid element with less than eight bytes length */
214
0
                printInfoLine(out, flags, level, "(invalid value)");
215
0
            }
216
0
        } else
217
0
            printInfoLine(out, flags, level, "(no value available)" );
218
0
    } else
219
0
        printInfoLine(out, flags, level, "(not loaded)" );
220
0
}
221
222
223
// ********************************
224
225
226
OFCondition DcmFloatingPointDouble::getFloat64(Float64 &doubleVal,
227
                                               const unsigned long pos)
228
0
{
229
    /* get double data */
230
0
    Float64 *doubleValues = NULL;
231
0
    errorFlag = getFloat64Array(doubleValues);
232
    /* check data before returning */
233
0
    if (errorFlag.good())
234
0
    {
235
0
        if (doubleValues == NULL)
236
0
            errorFlag = EC_IllegalCall;
237
        /* do not use getVM() because derived classes might always return 1 */
238
0
        else if (pos >= getNumberOfValues())
239
0
            errorFlag = EC_IllegalParameter;
240
0
        else
241
0
            doubleVal = doubleValues[pos];
242
0
    }
243
    /* clear value in case of error */
244
0
    if (errorFlag.bad())
245
0
        doubleVal = 0;
246
0
    return errorFlag;
247
0
}
248
249
250
OFCondition DcmFloatingPointDouble::getFloat64Array(Float64 *&doubleVals)
251
0
{
252
0
    doubleVals = OFstatic_cast(Float64 *, getValue());
253
0
    return errorFlag;
254
0
}
255
256
257
// ********************************
258
259
260
OFCondition DcmFloatingPointDouble::getOFString(OFString &stringVal,
261
                                                const unsigned long pos,
262
                                                OFBool /*normalize*/)
263
0
{
264
0
    Float64 doubleVal;
265
    /* get the specified numeric value */
266
0
    errorFlag = getFloat64(doubleVal, pos);
267
0
    if (errorFlag.good())
268
0
    {
269
        /* ... and convert it to a character string */
270
0
        char buffer[64];
271
0
        OFStandard::ftoa(buffer, sizeof(buffer), doubleVal, 0, 0, -2 /* print enough digits to permit lossless conversion back to FD */);
272
        /* assign result */
273
0
        stringVal = buffer;
274
0
    }
275
0
    return errorFlag;
276
0
}
277
278
279
// ********************************
280
281
282
OFCondition DcmFloatingPointDouble::putFloat64(const Float64 doubleVal,
283
                                               const unsigned long pos)
284
0
{
285
0
    Float64 val = doubleVal;
286
0
    errorFlag = changeValue(&val, OFstatic_cast(Uint32, sizeof(Float64) * pos), OFstatic_cast(Uint32, sizeof(Float64)));
287
0
    return errorFlag;
288
0
}
289
290
291
OFCondition DcmFloatingPointDouble::putFloat64Array(const Float64 *doubleVals,
292
                                                    const unsigned long numDoubles)
293
0
{
294
0
    errorFlag = EC_Normal;
295
0
    if (numDoubles > 0)
296
0
    {
297
        /* check for valid data */
298
0
        if (doubleVals != NULL)
299
0
            errorFlag = putValue(doubleVals, OFstatic_cast(Uint32, sizeof(Float64) * OFstatic_cast(size_t, numDoubles)));
300
0
        else
301
0
            errorFlag = EC_CorruptedData;
302
0
    } else
303
0
        putValue(NULL, 0);
304
305
0
    return errorFlag;
306
0
}
307
308
309
// ********************************
310
311
312
OFCondition DcmFloatingPointDouble::putString(const char *stringVal)
313
0
{
314
    /* determine length of the string value */
315
0
    const size_t stringLen = (stringVal != NULL) ? strlen(stringVal) : 0;
316
    /* call the real function */
317
0
    return putString(stringVal, OFstatic_cast(Uint32, stringLen));
318
0
}
319
320
321
OFCondition DcmFloatingPointDouble::putString(const char *stringVal,
322
                                              const Uint32 stringLen)
323
0
{
324
0
    errorFlag = EC_Normal;
325
    /* determine VM of the string */
326
0
    const unsigned long vm = DcmElement::determineVM(stringVal, stringLen);
327
0
    if (vm > 0)
328
0
    {
329
0
        Float64 *field = new Float64[vm];
330
0
        OFBool success = OFFalse;
331
0
        OFString value;
332
0
        size_t pos = 0;
333
        /* retrieve double data from character string */
334
0
        for (unsigned long i = 0; (i < vm) && errorFlag.good(); i++)
335
0
        {
336
            /* get specified value from multi-valued string */
337
0
            pos = DcmElement::getValueFromString(stringVal, pos, stringLen, value);
338
0
            if (!value.empty())
339
0
            {
340
0
                field[i] = OFStandard::atof(value.c_str(), &success);
341
0
                if (!success)
342
0
                    errorFlag = EC_CorruptedData;
343
0
            } else
344
0
                errorFlag = EC_CorruptedData;
345
0
        }
346
        /* set binary data as the element value */
347
0
        if (errorFlag == EC_Normal)
348
0
            errorFlag = putFloat64Array(field, vm);
349
        /* delete temporary buffer */
350
0
        delete[] field;
351
0
    } else
352
0
        putValue(NULL, 0);
353
0
    return errorFlag;
354
0
}
355
356
357
// ********************************
358
359
360
OFCondition DcmFloatingPointDouble::verify(const OFBool autocorrect)
361
0
{
362
    /* check for valid value length */
363
0
    if (getLengthField() % (sizeof(Float64)) != 0)
364
0
    {
365
0
        errorFlag = EC_CorruptedData;
366
0
        if (autocorrect)
367
0
        {
368
            /* strip to valid length */
369
0
            setLengthField(getLengthField() - (getLengthField() % OFstatic_cast(Uint32, sizeof(Float64))));
370
0
        }
371
0
    } else
372
0
        errorFlag = EC_Normal;
373
0
    return errorFlag;
374
0
}
375
376
377
OFBool DcmFloatingPointDouble::matches(const DcmElement& candidate,
378
                                       const OFBool enableWildCardMatching) const
379
0
{
380
0
  OFstatic_cast(void,enableWildCardMatching);
381
0
  if (ident() == candidate.ident())
382
0
  {
383
    // some const casts to call the getter functions, I do not modify the values, I promise!
384
0
    DcmFloatingPointDouble& key = OFconst_cast(DcmFloatingPointDouble&,*this);
385
0
    DcmElement& can = OFconst_cast(DcmElement&,candidate);
386
0
    Float64 a, b;
387
0
    for( unsigned long ui = 0; ui < key.getVM(); ++ui )
388
0
      for( unsigned long uj = 0; uj < can.getVM(); ++uj )
389
0
        if( key.getFloat64( a, ui ).good() && can.getFloat64( b, uj ).good() && a == b )
390
0
          return OFTrue;
391
0
    return key.getVM() == 0;
392
0
  }
393
0
  return OFFalse;
394
0
}
395
396
// ********************************
397
398
OFCondition DcmFloatingPointDouble::writeJson(STD_NAMESPACE ostream &out,
399
                                              DcmJsonFormat &format)
400
0
{
401
0
    OFCondition status = EC_Normal;
402
403
    /* always write JSON Opener */
404
0
    writeJsonOpener(out, format);
405
406
    /* write element value (if non-empty) */
407
0
    if (!isEmpty())
408
0
    {
409
0
        const unsigned long vm = getVM();
410
411
0
        if (! format.getJsonExtensionEnabled())
412
0
        {
413
          // check if any values is 'inf' or 'nan', and return an error in this case
414
          // since the JSON extension that would allow us to write these is not enabled
415
0
          Float64 f = 0.0;
416
0
          for (unsigned long valNo = 1; valNo < vm; ++valNo)
417
0
          {
418
0
            status = getFloat64(f, valNo);
419
0
            if (status.bad()) return status;
420
0
            if ((OFMath::isinf)(f) || (OFMath::isnan)(f)) return EC_CannotWriteJsonNumber;
421
0
          }
422
0
        }
423
424
0
        if (format.asBulkDataURI(getTag(), getLength()))
425
0
        {
426
            /* adjust byte order to little endian */
427
0
            Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian));
428
0
            status = format.writeBulkData(out, getTag(), getLengthField(), byteValues);
429
0
        }
430
0
        else
431
0
        {
432
0
            OFString value;
433
0
            status = getOFString(value, 0L);
434
0
            if (status.bad()) return status;
435
0
            format.printValuePrefix(out);
436
0
            DcmJsonFormat::printNumberDecimal(out, value);
437
0
            for (unsigned long valNo = 1; valNo < vm; ++valNo)
438
0
            {
439
0
                status = getOFString(value, valNo);
440
0
                if (status.bad()) return status;
441
0
                format.printNextArrayElementPrefix(out);
442
0
                DcmJsonFormat::printNumberDecimal(out, value);
443
0
            }
444
0
            format.printValueSuffix(out);
445
0
        }
446
0
    }
447
    /* write JSON Closer  */
448
0
    writeJsonCloser(out, format);
449
0
    return status;
450
0
}