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