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