/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #ifndef GDCMELEMENT_H #define GDCMELEMENT_H #include "gdcmTypes.h" #include "gdcmVR.h" #include "gdcmTag.h" #include "gdcmVM.h" #include "gdcmByteValue.h" #include "gdcmDataElement.h" #include "gdcmSwapper.h" #include #include #include #include #include #include namespace gdcm_ns { // Forward declaration /** * \brief EncodingImplementation * * \note TODO */ template class EncodingImplementation; /** * \brief A class which is used to produce compile errors for an * invalid combination of template parameters. * * Invalid combinations have specialized declarations with no * definition. */ template class ElementDisableCombinations {}; template <> class ElementDisableCombinations {}; template <> class ElementDisableCombinations {}; // Make it impossible to compile these other cases template class ElementDisableCombinations; template class ElementDisableCombinations; /** * \brief Element class * * \note TODO */ template class Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: typename VRToType::Type Internal[VMToLength::Length]; typedef typename VRToType::Type Type; static VR GetVR() { return (VR::VRType)TVR; } static VM GetVM() { return (VM::VMType)TVM; } unsigned long GetLength() const { return VMToLength::Length; } // Implementation of Print is common to all Mode (ASCII/Binary) // TODO: Can we print a \ when in ASCII...well I don't think so // it would mean we used a bad VM then, right? void Print(std::ostream &_os) const { _os << Internal[0]; // VM is at least guarantee to be one for(int i=1; i::Length; ++i) _os << "," << Internal[i]; } const typename VRToType::Type *GetValues() const { return Internal; } const typename VRToType::Type &GetValue(unsigned int idx = 0) const { assert( idx < VMToLength::Length ); return Internal[idx]; } typename VRToType::Type &GetValue(unsigned int idx = 0) { assert( idx < VMToLength::Length ); return Internal[idx]; } typename VRToType::Type operator[] (unsigned int idx) const { return GetValue(idx); } void SetValue(typename VRToType::Type v, unsigned int idx = 0) { assert( idx < VMToLength::Length ); Internal[idx] = v; } void SetFromDataElement(DataElement const &de) { const ByteValue *bv = de.GetByteValue(); if( !bv ) return; #ifdef GDCM_WORDS_BIGENDIAN if( de.GetVR() == VR::UN /*|| de.GetVR() == VR::INVALID*/ ) #else if( de.GetVR() == VR::UN || de.GetVR() == VR::INVALID ) #endif { Set(de.GetValue()); } else { SetNoSwap(de.GetValue()); } } DataElement GetAsDataElement() const { DataElement ret; std::ostringstream os; EncodingImplementation::Mode>::Write(Internal, GetLength(),os); ret.SetVR( (VR::VRType)TVR ); assert( ret.GetVR() != VR::SQ ); if( (VR::VRType)VRToEncoding::Mode == VR::VRASCII ) { if( GetVR() != VR::UI ) { if( os.str().size() % 2 ) { os << " "; } } } VL::Type osStrSize = (VL::Type)os.str().size(); ret.SetByteValue( os.str().c_str(), osStrSize ); return ret; } void Read(std::istream &_is) { return EncodingImplementation::Mode>::Read(Internal, GetLength(),_is); } void Write(std::ostream &_os) const { return EncodingImplementation::Mode>::Write(Internal, GetLength(),_os); } // FIXME: remove this function // this is only used in gdcm::SplitMosaicFilter / to pass value of a CSAElement void Set(Value const &v) { const ByteValue *bv = dynamic_cast(&v); if( bv ) { //memcpy(Internal, bv->GetPointer(), bv->GetLength()); std::stringstream ss; std::string s = std::string( bv->GetPointer(), bv->GetLength() ); ss.str( s ); EncodingImplementation::Mode>::Read(Internal, GetLength(),ss); } } protected: void SetNoSwap(Value const &v) { const ByteValue *bv = dynamic_cast(&v); assert( bv ); // That would be bad... //memcpy(Internal, bv->GetPointer(), bv->GetLength()); std::stringstream ss; std::string s = std::string( bv->GetPointer(), bv->GetLength() ); ss.str( s ); EncodingImplementation::Mode>::ReadNoSwap(Internal, GetLength(),ss); } }; struct ignore_char { ignore_char(char c): m_char(c) {} char m_char; }; ignore_char const backslash('\\'); inline std::istream& operator>> (std::istream& in, ignore_char const& ic) { if (!in.eof()) in.clear(in.rdstate() & ~std::ios_base::failbit); if (in.get() != ic.m_char) in.setstate(std::ios_base::failbit); return in; } // Implementation to perform formatted read and write template<> class EncodingImplementation { public: template // FIXME this should be VRToType::Type static inline void ReadComputeLength(T* data, unsigned int &length, std::istream &_is) { assert( data ); //assert( length ); // != 0 length = 0; assert( _is ); #if 0 char sep; while( _is >> data[length++] ) { // Get the separator in between the values assert( _is ); _is.get(sep); assert( sep == '\\' || sep == ' ' ); // FIXME: Bad use of assert if( sep == ' ' ) length--; // FIXME } #else while( _is >> std::ws >> data[length++] >> std::ws >> backslash ) { } #endif } template // FIXME this should be VRToType::Type static inline void Read(T* data, unsigned long length, std::istream &_is) { assert( data ); assert( length ); // != 0 assert( _is ); // FIXME BUG: what if >> operation fails ? // gdcmData/MR00010001.dcm / SpacingBetweenSlices _is >> std::ws >> data[0]; char sep; //std::cout << "GetLength: " << af->GetLength() << std::endl; for(unsigned long i=1; i> std::ws >> sep; //_is.get(sep); //assert( sep == '\\' ); // FIXME: Bad use of assert _is >> std::ws >> data[i]; } } template static inline void ReadNoSwap(T* data, unsigned long length, std::istream &_is) { Read(data,length,_is); } template static inline void Write(const T* data, unsigned long length, std::ostream &_os) { assert( data ); assert( length ); assert( _os ); _os << data[0]; for(unsigned long i=1; i std::string to_string ( Float data ) { std::stringstream in; // in.imbue(std::locale::classic()); // This is not required AFAIK int const digits = static_cast< int >( - std::log( std::numeric_limits::epsilon() ) / static_cast< Float >( std::log( 10.0 ) ) ); if ( in << std::dec << std::setprecision(/*2+*/digits) << data ) { return ( in.str() ); } else { throw "Impossible Conversion"; // should not happen ... } } #else // http://stackoverflow.com/questions/32631178/writing-ieee-754-1985-double-as-ascii-on-a-limited-16-bytes-string static inline void clean(char *mant) { char *ix = mant + strlen(mant) - 1; while(('0' == *ix) && (ix > mant)) { *ix-- = '\0'; } if ('.' == *ix) { *ix = '\0'; } } static int add1(char *buf, int n) { if (n < 0) return 1; if (buf[n] == '9') { buf[n] = '0'; return add1(buf, n-1); } else { buf[n] = (char)(buf[n] + 1); } return 0; } static int doround(char *buf, unsigned int n) { char c; if (n >= strlen(buf)) return 0; c = buf[n]; buf[n] = 0; if ((c >= '5') && (c <= '9')) return add1(buf, n-1); return 0; } static int roundat(char *buf, unsigned int i, int iexp) { if (doround(buf, i) != 0) { iexp += 1; switch(iexp) { case -2: strcpy(buf, ".01"); break; case -1: strcpy(buf, ".1"); break; case 0: strcpy(buf, "1."); break; case 1: strcpy(buf, "10"); break; case 2: strcpy(buf, "100"); break; default: sprintf(buf, "1e%d", iexp); } return 1; } return 0; } template < typename Float > static void x16printf(char *buf, int size, Float f) { char line[40]; char *mant = line + 1; int iexp, lexp, i; char exp[6]; if (f < 0) { f = -f; size -= 1; *buf++ = '-'; } sprintf(line, "%1.16e", f); if (line[0] == '-') { f = -f; size -= 1; *buf++ = '-'; sprintf(line, "%1.16e", f); } *mant = line[0]; i = (int)strcspn(mant, "eE"); mant[i] = '\0'; iexp = (int)strtol(mant + i + 1, nullptr, 10); lexp = sprintf(exp, "e%d", iexp); if ((iexp >= size) || (iexp < -3)) { i = roundat(mant, size - 1 -lexp, iexp); if(i == 1) { strcpy(buf, mant); return; } buf[0] = mant[0]; buf[1] = '.'; strncpy(buf + i + 2, mant + 1, size - 2 - lexp); buf[size-lexp] = 0; clean(buf); strcat(buf, exp); } else if (iexp >= size - 2) { roundat(mant, iexp + 1, iexp); strcpy(buf, mant); } else if (iexp >= 0) { i = roundat(mant, size - 1, iexp); if (i == 1) { strcpy(buf, mant); return; } strncpy(buf, mant, iexp + 1); buf[iexp + 1] = '.'; strncpy(buf + iexp + 2, mant + iexp + 1, size - iexp - 1); buf[size] = 0; clean(buf); } else { int j; i = roundat(mant, size + 1 + iexp, iexp); if (i == 1) { strcpy(buf, mant); return; } buf[0] = '.'; for(j=0; j< -1 - iexp; j++) { buf[j+1] = '0'; } if ((i == 1) && (iexp != -1)) { buf[-iexp] = '1'; buf++; } strncpy(buf - iexp, mant, size + 1 + iexp); buf[size] = 0; clean(buf); } } #endif template<> inline void EncodingImplementation::Write(const double* data, unsigned long length, std::ostream &_os) { assert( data ); assert( length ); assert( _os ); #ifdef VRDS16ILLEGAL _os << to_string(data[0]); #else char buf[16+1]; x16printf(buf, 16, data[0]); _os << buf; #endif for(unsigned long i=1; i class EncodingImplementation { public: template // FIXME this should be VRToType::Type static inline void ReadComputeLength(T* data, unsigned int &length, std::istream &_is) { const unsigned int type_size = sizeof(T); assert( data ); // Can we read from pointer ? //assert( length ); length /= type_size; assert( _is ); // Is stream valid ? _is.read( reinterpret_cast(data+0), type_size); for(unsigned long i=1; i(data+i), type_size ); } } template static inline void ReadNoSwap(T* data, unsigned long length, std::istream &_is) { const unsigned int type_size = sizeof(T); assert( data ); // Can we read from pointer ? assert( length ); assert( _is ); // Is stream valid ? _is.read( reinterpret_cast(data+0), type_size); for(unsigned long i=1; i(data+i), type_size ); } //ByteSwap::SwapRangeFromSwapCodeIntoSystem(data, // _is.GetSwapCode(), length); //SwapperNoOp::SwapArray(data,length); } template static inline void Read(T* data, unsigned long length, std::istream &_is) { const unsigned int type_size = sizeof(T); assert( data ); // Can we read from pointer ? assert( length ); assert( _is ); // Is stream valid ? _is.read( reinterpret_cast(data+0), type_size); for(unsigned long i=1; i(data+i), type_size ); } //ByteSwap::SwapRangeFromSwapCodeIntoSystem(data, // _is.GetSwapCode(), length); SwapperNoOp::SwapArray(data,length); } template static inline void Write(const T* data, unsigned long length, std::ostream &_os) { const unsigned int type_size = sizeof(T); assert( data ); // Can we write into pointer ? assert( length ); assert( _os ); // Is stream valid ? //ByteSwap::SwapRangeFromSwapCodeIntoSystem((T*)data, // _os.GetSwapCode(), length); T swappedData = SwapperNoOp::Swap(data[0]); _os.write( reinterpret_cast(&swappedData), type_size); for(unsigned long i=1; i(&swappedData), type_size ); } //ByteSwap::SwapRangeFromSwapCodeIntoSystem((T*)data, // _os.GetSwapCode(), length); } }; // For particular case for ASCII string // WARNING: This template explicitly instantiates a particular // EncodingImplementation THEREFORE it is required to be declared after the // EncodingImplementation is needs (doh!) #if 0 template class Element { public: Element(const char array[]) { unsigned int i = 0; const char sep = '\\'; std::string sarray = array; std::string::size_type pos1 = 0; std::string::size_type pos2 = sarray.find(sep, pos1+1); while(pos2 != std::string::npos) { Internal[i++] = sarray.substr(pos1, pos2-pos1); pos1 = pos2+1; pos2 = sarray.find(sep, pos1+1); } Internal[i] = sarray.substr(pos1, pos2-pos1); // Shouldn't we do the contrary, since we know how many separators // (and default behavior is to discard anything after the VM declared assert( GetLength()-1 == i ); } unsigned long GetLength() const { return VMToLength::Length; } // Implementation of Print is common to all Mode (ASCII/Binary) void Print(std::ostream &_os) const { _os << Internal[0]; // VM is at least guarantee to be one for(int i=1; i::Length; ++i) _os << "," << Internal[i]; } void Read(std::istream &_is) { EncodingImplementation::Read(Internal, GetLength(),_is); } void Write(std::ostream &_os) const { EncodingImplementation::Write(Internal, GetLength(),_os); } private: typename String Internal[VMToLength::Length]; }; template< int TVM> class Element : public StringElement { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; }; #endif // Implementation for the undefined length (dynamically allocated array) template class Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: // This the way to prevent default initialization explicit Element() { Internal=nullptr; Length=0; Save = false; } ~Element() { if( Save ) { delete[] Internal; } Internal = nullptr; } static VR GetVR() { return (VR::VRType)TVR; } static VM GetVM() { return VM::VM1_n; } // Length manipulation // SetLength should really be protected anyway...all operation // should go through SetArray unsigned long GetLength() const { return Length; } typedef typename VRToType::Type Type; void SetLength(unsigned long len) { const unsigned int size = sizeof(Type); if( len ) { if( len > Length ) { // perform realloc assert( (len / size) * size == len ); Type *internal = new Type[len / size]; assert( Save == false ); Save = true; // ???? if( Internal ) { memcpy(internal, Internal, len); delete[] Internal; } Internal = internal; } } Length = len / size; } // If save is set to zero user should not delete the pointer //void SetArray(const typename VRToType::Type *array, int len, bool save = false) void SetArray(const Type *array, unsigned long len, bool save = false) { if( save ) { SetLength(len); // realloc memcpy(Internal, array, len/*/sizeof(Type)*/); assert( Save == false ); } else { // TODO rewrite this stupid code: assert( Length == 0 ); assert( Internal == nullptr ); assert( Save == false ); Length = len / sizeof(Type); //assert( (len / sizeof(Type)) * sizeof(Type) == len ); // MR00010001.dcm is a tough kid: 0019,105a is supposed to be VR::FL, VM::VM3 but // length is 14 bytes instead of 12 bytes. Simply consider value is total garbage. if( (len / sizeof(Type)) * sizeof(Type) != len ) { Internal = nullptr; Length = 0; } else Internal = const_cast(array); } Save = save; } void SetValue(typename VRToType::Type v, unsigned int idx = 0) { assert( idx < Length ); Internal[idx] = v; } const typename VRToType::Type &GetValue(unsigned int idx = 0) const { assert( idx < Length ); return Internal[idx]; } typename VRToType::Type &GetValue(unsigned int idx = 0) { //assert( idx < Length ); return Internal[idx]; } typename VRToType::Type operator[] (unsigned int idx) const { return GetValue(idx); } void Set(Value const &v) { const ByteValue *bv = dynamic_cast(&v); assert( bv ); // That would be bad... if( (VR::VRType)(VRToEncoding::Mode) == VR::VRBINARY ) { const Type* array = (const Type*)bv->GetVoidPointer(); if( array ) { assert( array ); // That would be bad... assert( Internal == nullptr ); SetArray(array, bv->GetLength() ); } } else { std::stringstream ss; std::string s = std::string( bv->GetPointer(), bv->GetLength() ); ss.str( s ); EncodingImplementation::Mode>::Read(Internal, GetLength(),ss); } } void SetFromDataElement(DataElement const &de) { const ByteValue *bv = de.GetByteValue(); if( !bv ) return; #ifdef GDCM_WORDS_BIGENDIAN if( de.GetVR() == VR::UN /*|| de.GetVR() == VR::INVALID*/ ) #else if( de.GetVR() == VR::UN || de.GetVR() == VR::INVALID ) #endif { Set(de.GetValue()); } else { SetNoSwap(de.GetValue()); } } // Need to be placed after definition of EncodingImplementation void WriteASCII(std::ostream &os) const { return EncodingImplementation::Write(Internal, GetLength(), os); } // Implementation of Print is common to all Mode (ASCII/Binary) void Print(std::ostream &_os) const { assert( Length ); assert( Internal ); _os << Internal[0]; // VM is at least guarantee to be one const unsigned long length = GetLength() < 25 ? GetLength() : 25; for(unsigned long i=1; i::Mode>::Read(Internal, GetLength(),_is); } //void ReadComputeLength(std::istream &_is) { // if( !Internal ) return; // EncodingImplementation::Mode>::ReadComputeLength(Internal, // Length,_is); // } void Write(std::ostream &_os) const { EncodingImplementation::Mode>::Write(Internal, GetLength(),_os); } DataElement GetAsDataElement() const { DataElement ret; ret.SetVR( (VR::VRType)TVR ); assert( ret.GetVR() != VR::SQ ); if( Internal ) { std::ostringstream os; EncodingImplementation::Mode>::Write(Internal, GetLength(),os); if( (VR::VRType)VRToEncoding::Mode == VR::VRASCII ) { if( GetVR() != VR::UI ) { if( os.str().size() % 2 ) { os << " "; } } } VL::Type osStrSize = (VL::Type)os.str().size(); ret.SetByteValue( os.str().c_str(), osStrSize ); } return ret; } Element(const Element&_val) { if( this != &_val) { *this = _val; } } Element &operator=(const Element &_val) { Length = 0; // SYITF Internal = 0; SetArray(_val.Internal, _val.Length, true); return *this; } protected: void SetNoSwap(Value const &v) { const ByteValue *bv = dynamic_cast(&v); assert( bv ); // That would be bad... if( (VR::VRType)(VRToEncoding::Mode) == VR::VRBINARY ) { const Type* array = (const Type*)bv->GetPointer(); if( array ) { assert( array ); // That would be bad... assert( Internal == nullptr ); SetArray(array, bv->GetLength() ); } } else { std::stringstream ss; std::string s = std::string( bv->GetPointer(), bv->GetLength() ); ss.str( s ); EncodingImplementation::Mode>::ReadNoSwap(Internal, GetLength(),ss); } } private: typename VRToType::Type *Internal; unsigned long Length; // unsigned int ?? bool Save; }; //template //class Element : public Element {}; // Partial specialization for derivatives of 1-n : 2-n, 3-n ... template class Element : public Element { public: typedef Element Parent; void SetLength(int len) { if( len != 1 && len != 2 ) return; Parent::SetLength(len); } }; template class Element : public Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: typedef Element Parent; void SetLength(int len) { if( len <= 1 ) return; Parent::SetLength(len); } }; template class Element : public Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: typedef Element Parent; void SetLength(int len) { if( len % 2 ) return; Parent::SetLength(len); } }; template class Element : public Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: typedef Element Parent; void SetLength(int len) { if( len <= 2 ) return; Parent::SetLength(len); } }; template class Element : public Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: typedef Element Parent; void SetLength(int len) { if( len % 3 ) return; Parent::SetLength(len); } }; template class Element : public Element { public: typedef Element Parent; void SetLength(int len) { if( len != 3 && len != 4 ) return; Parent::SetLength(len); } }; //template struct VRToLength; //template <> struct VRToLength //{ enum { Length = VM::VM1 }; } //template<> //class Element : public Element::Length > // only 0010 1010 AS 1 Patient's Age template<> class Element { enum { ElementDisableCombinationsCheck = sizeof ( ElementDisableCombinations ) }; public: char Internal[VMToLength::Length * sizeof( VRToType::Type )]; void Print(std::ostream &_os) const { _os << Internal; } unsigned long GetLength() const { return VMToLength::Length; } }; template <> class Element : public Element {}; // Same for OW: template <> class Element : public Element {}; } // namespace gdcm_ns #endif //GDCMELEMENT_H