Coverage Report

Created: 2026-09-28 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcvrds.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 DcmDecimalString
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
25
#include "dcmtk/dcmdata/dcvrds.h"
26
#include "dcmtk/dcmdata/dcjson.h"
27
#include "dcmtk/ofstd/ofstring.h"
28
#include "dcmtk/ofstd/ofstd.h"
29
30
31
8.28k
#define MAX_DS_LENGTH 16
32
33
34
// ********************************
35
36
37
DcmDecimalString::DcmDecimalString(const DcmTag &tag,
38
                                   const Uint32 len)
39
8.28k
  : DcmByteString(tag, len)
40
8.28k
{
41
8.28k
    setMaxLength(MAX_DS_LENGTH);
42
8.28k
    setNonSignificantChars(" \\");
43
8.28k
}
44
45
46
DcmDecimalString::DcmDecimalString(const DcmDecimalString &old)
47
0
  : DcmByteString(old)
48
0
{
49
0
}
50
51
52
DcmDecimalString::~DcmDecimalString()
53
8.28k
{
54
8.28k
}
55
56
57
DcmDecimalString &DcmDecimalString::operator=(const DcmDecimalString &obj)
58
0
{
59
0
    DcmByteString::operator=(obj);
60
0
    return *this;
61
0
}
62
63
64
OFCondition DcmDecimalString::copyFrom(const DcmObject& rhs)
65
0
{
66
0
  if (this != &rhs)
67
0
  {
68
0
    if (rhs.ident() != ident()) return EC_IllegalCall;
69
0
    *this = OFstatic_cast(const DcmDecimalString &, rhs);
70
0
  }
71
0
  return EC_Normal;
72
0
}
73
74
75
// ********************************
76
77
78
DcmEVR DcmDecimalString::ident() const
79
135
{
80
135
    return EVR_DS;
81
135
}
82
83
84
OFCondition DcmDecimalString::checkValue(const OFString &vm,
85
                                         const OFBool /*oldFormat*/)
86
0
{
87
0
    OFString strVal;
88
    /* get "raw value" without any modifications (if possible) */
89
0
    OFCondition l_error = getStringValue(strVal);
90
0
    if (l_error.good())
91
0
        l_error = DcmDecimalString::checkStringValue(strVal, vm);
92
0
    return l_error;
93
0
}
94
95
96
// ********************************
97
98
99
OFCondition DcmDecimalString::getFloat64(Float64 &doubleVal,
100
                                         const unsigned long pos)
101
0
{
102
    /* get decimal string value */
103
0
    OFString str;
104
0
    OFCondition l_error = getOFString(str, pos, OFTrue /*normalize*/);
105
0
    if (l_error.good())
106
0
    {
107
0
        OFBool success = OFFalse;
108
        /* convert string to float value */
109
0
        doubleVal = OFStandard::atof(str.c_str(), &success);
110
0
        if (!success)
111
0
            l_error = EC_CorruptedData;
112
0
    }
113
0
    return l_error;
114
0
}
115
116
117
// ********************************
118
119
120
OFCondition DcmDecimalString::getFloat64Vector(OFVector<Float64> &doubleVals)
121
0
{
122
    /* get stored value */
123
0
    char *strVal = NULL;
124
0
    Uint32 strLen = 0;
125
0
    OFCondition l_error = getString(strVal, strLen);
126
    /* clear result variable */
127
0
    doubleVals.clear();
128
0
    if (l_error.good() && (strVal != NULL))
129
0
    {
130
        /* determine number of stored values */
131
0
        const unsigned long vm = getVM();
132
0
        if (vm > 0)
133
0
        {
134
0
            Float64 doubleVal;
135
0
            OFString doubleStr;
136
0
            char *p = strVal;
137
0
            OFBool success = OFFalse;
138
            /* avoid memory re-allocations by specifying the expected size */
139
0
            doubleVals.reserve(vm);
140
            /* iterate over the string value and search for delimiters */
141
0
            for (Uint32 i = 0; i <= strLen; i++)
142
0
            {
143
0
                if ((i == strLen) || (*p == '\\'))
144
0
                {
145
                    /* extract single value and convert it to floating point */
146
0
                    doubleStr.assign(strVal, p - strVal);
147
0
                    doubleVal = OFStandard::atof(doubleStr.c_str(), &success);
148
0
                    if (success)
149
0
                    {
150
                        /* store floating point value in result variable */
151
0
                        doubleVals.push_back(doubleVal);
152
0
                        strVal = p + 1;
153
0
                    }
154
0
                    else
155
0
                    {
156
0
                        l_error = EC_CorruptedData;
157
0
                        break;
158
0
                    }
159
0
                }
160
0
                ++p;
161
0
            }
162
0
        }
163
0
    }
164
0
    return l_error;
165
0
}
166
167
168
169
// ********************************
170
171
172
OFCondition DcmDecimalString::getOFString(OFString &stringVal,
173
                                          const unsigned long pos,
174
                                          OFBool normalize)
175
15
{
176
    /* call inherited method */
177
15
    OFCondition l_error = DcmByteString::getOFString(stringVal, pos);
178
    /* normalize string if required */
179
15
    if (l_error.good() && normalize)
180
15
        normalizeString(stringVal, !MULTIPART, DELETE_LEADING, DELETE_TRAILING);
181
15
    return l_error;
182
15
}
183
184
185
// ********************************
186
187
188
OFCondition DcmDecimalString::putFloat64(const Float64 val,
189
                                         const unsigned long pos)
190
0
{
191
0
    return putFloat64Prec(val, pos, 6);
192
0
}
193
194
195
OFCondition DcmDecimalString::putFloat64Prec(const Float64 val,
196
                                             const unsigned long pos,
197
                                             const Uint8 prec,
198
                                             const OFBool cutTrailZeroes)
199
0
{
200
0
    if (prec > 100)
201
0
        return EC_IllegalParameter;
202
203
    // Reserve enough bytes for conversion (considering high precision values that
204
    // might be cut off later, i.e. 16 for integer part, 1 for dot, 14 max precision, 1 for NUL, i.e.
205
0
    char buf[16 + 1 + 14 +1];
206
0
    int written = OFStandard::snprintf(buf, 32, "%.*f", prec, val);
207
0
    if (written > 31 /* NUL not counting in */)
208
0
    {
209
0
        return EC_IllegalParameter;
210
0
    }
211
212
0
    OFString str(buf);
213
0
    if (cutTrailZeroes)
214
0
    {
215
0
        size_t dot = str.find_last_of('.');
216
0
        if (dot != OFString_npos)
217
0
        {
218
0
            size_t last_valid = str.find_last_not_of('0');
219
0
            if (last_valid != str.length() -1)
220
0
            {
221
0
                if (str[last_valid] == '.')
222
0
                    str = str.substr(0, last_valid);
223
0
                else
224
0
                    str = str.substr(0, last_valid+1);
225
0
            }
226
0
        }
227
0
    }
228
0
    if (str.length() > 16)
229
0
    {
230
0
        return EC_IllegalParameter;
231
0
    }
232
233
0
    return putOFStringAtPos(str.c_str(), pos);
234
0
}
235
236
237
// ********************************
238
239
240
OFCondition DcmDecimalString::writeXML(STD_NAMESPACE ostream &out,
241
                                       const size_t flags)
242
0
{
243
0
    if (flags & DCMTypes::XF_useNativeModel)
244
0
    {
245
        /* for the Native DICOM Model output, we do not need any specific DS handling */
246
0
        return DcmElement::writeXML(out, flags);
247
0
    } else {
248
        /* XML start tag: <element tag="gggg,eeee" vr="XX" ...> */
249
0
        writeXMLStartTag(out, flags);
250
        /* write element value (if loaded) */
251
0
        if (valueLoaded())
252
0
        {
253
            /* get string data (without normalization) */
254
0
            char *value = NULL;
255
0
            Uint32 length = 0;
256
0
            getString(value, length);
257
0
            if ((value != NULL) && (length > 0))
258
0
            {
259
                /* explicitly convert to OFString because of possible NULL bytes */
260
0
                OFString stringVal(value, length);
261
0
                const OFBool convertNonASCII = (flags & DCMTypes::XF_convertNonASCII) > 0;
262
                /* check whether conversion to XML markup string is required */
263
0
                if (OFStandard::checkForMarkupConversion(stringVal, convertNonASCII))
264
0
                    OFStandard::convertToMarkupStream(out, stringVal, convertNonASCII);
265
0
                else
266
0
                    out << value;
267
0
            }
268
0
        }
269
        /* XML end tag: </element> */
270
0
        writeXMLEndTag(out, flags);
271
        /* always report success */
272
0
        return EC_Normal;
273
0
    }
274
0
}
275
276
277
// ********************************
278
279
280
OFCondition DcmDecimalString::writeJson(STD_NAMESPACE ostream &out,
281
                                        DcmJsonFormat &format)
282
0
{
283
0
    OFCondition status = EC_Normal;
284
285
    /* always write JSON Opener */
286
0
    writeJsonOpener(out, format);
287
0
    OFBool isValid;
288
289
0
    if (!isEmpty())
290
0
    {
291
        /* write element value */
292
0
        if (format.asBulkDataURI(getTag(), getLength()))
293
0
        {
294
            /* adjust byte order to little endian */
295
0
            Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian));
296
0
            status = format.writeBulkData(out, getTag(), getLengthField(), byteValues);
297
0
        }
298
0
        else
299
0
        {
300
0
            const unsigned long vm = getVM();
301
0
            if (vm > 0)
302
0
            {
303
0
                OFString value;
304
0
                OFString vmstring = "1";
305
0
                status = getOFString(value, 0L);
306
0
                if (status.bad())
307
0
                    return status;
308
0
                format.printValuePrefix(out);
309
310
0
                isValid = checkStringValue(value, vmstring).good();
311
0
                switch (format.getJsonNumStringPolicy())
312
0
                {
313
0
                  case DcmJsonFormat::NSP_auto:
314
0
                    if (isValid)
315
0
                        DcmJsonFormat::printNumberDecimal(out, value);
316
0
                    else
317
0
                    {
318
0
                        DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string");
319
0
                        DcmJsonFormat::printValueString(out, value);
320
0
                    }
321
0
                    break;
322
0
                  case DcmJsonFormat::NSP_always_number:
323
0
                    if (isValid)
324
0
                        DcmJsonFormat::printNumberDecimal(out, value);
325
0
                    else
326
0
                    {
327
0
                        DCMDATA_WARN("encountered illegal DS value '" << value << "', aborting conversion to JSON");
328
0
                        return EC_CannotWriteStringAsJSONNumber;
329
0
                    }
330
0
                    break;
331
0
                  case DcmJsonFormat::NSP_always_string:
332
0
                    if (!isValid)
333
0
                    {
334
0
                        DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string");
335
0
                    }
336
0
                    DcmJsonFormat::printValueString(out, value);
337
0
                    break;
338
0
                }
339
340
0
                for (unsigned long valNo = 1; valNo < vm; ++valNo)
341
0
                {
342
0
                    status = getOFString(value, valNo);
343
0
                    if (status.bad())
344
0
                        return status;
345
0
                    format.printNextArrayElementPrefix(out);
346
347
0
                    isValid = checkStringValue(value, vmstring).good();
348
0
                    switch (format.getJsonNumStringPolicy())
349
0
                    {
350
0
                      case DcmJsonFormat::NSP_auto:
351
0
                        if (isValid)
352
0
                            DcmJsonFormat::printNumberDecimal(out, value);
353
0
                        else
354
0
                        {
355
0
                            DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string");
356
0
                            DcmJsonFormat::printValueString(out, value);
357
0
                        }
358
0
                        break;
359
0
                      case DcmJsonFormat::NSP_always_number:
360
0
                        if (isValid)
361
0
                            DcmJsonFormat::printNumberDecimal(out, value);
362
0
                        else
363
0
                        {
364
0
                            DCMDATA_WARN("encountered illegal DS value '" << value << "', aborting conversion to JSON");
365
0
                            return EC_CannotWriteStringAsJSONNumber;
366
0
                        }
367
0
                        break;
368
0
                      case DcmJsonFormat::NSP_always_string:
369
0
                        if (!isValid)
370
0
                        {
371
0
                            DCMDATA_WARN("encountered illegal DS value '" << value << "', converting to JSON string");
372
0
                        }
373
0
                        DcmJsonFormat::printValueString(out, value);
374
0
                        break;
375
0
                    }
376
0
                }
377
0
                format.printValueSuffix(out);
378
0
            }
379
0
        }
380
0
    }
381
382
    /* write JSON Closer  */
383
0
    writeJsonCloser(out, format);
384
0
    return status;
385
0
}
386
387
388
// ********************************
389
390
391
OFCondition DcmDecimalString::checkStringValue(const OFString &value,
392
                                               const OFString &vm)
393
0
{
394
0
    return DcmByteString::checkStringValue(value, vm, "ds", 6, MAX_DS_LENGTH);
395
0
}