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