1 /*=========================================================================
4 Module: $RCSfile: gdcmUtil.cxx,v $
6 Date: $Date: 2006/07/10 09:37:33 $
7 Version: $Revision: 1.185 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
20 #include "gdcmDebug.h"
23 #include <stdarg.h> // for va_list
25 // For GetCurrentDate, GetCurrentTime
27 #include <sys/types.h>
30 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
31 #include <sys/timeb.h>
36 #include <stdarg.h> //only included in implementation file
37 #include <stdio.h> //only included in implementation file
39 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
40 #include <winsock.h> // for gethostname and gethostbyname and GetTickCount...
41 // I haven't find a way to determine wether we need to under GetCurrentTime or not...
42 // I think the best solution would simply to get rid of this problematic function
43 // and use a 'less' common name...
44 #if !defined(__BORLANDC__) || (__BORLANDC__ >= 0x0560)
48 #include <unistd.h> // for gethostname
49 #include <netdb.h> // for gethostbyname
60 #include <sys/types.h>
63 #ifdef CMAKE_HAVE_SYS_IOCTL_H
64 #include <sys/ioctl.h> // For SIOCGIFCONF on Linux
66 #ifdef CMAKE_HAVE_SYS_SOCKET_H
67 #include <sys/socket.h>
69 #ifdef CMAKE_HAVE_SYS_SOCKIO_H
70 #include <sys/sockio.h> // For SIOCGIFCONF on SunOS
72 #ifdef CMAKE_HAVE_NET_IF_H
75 #ifdef CMAKE_HAVE_NETINET_IN_H
76 #include <netinet/in.h> //For IPPROTO_IP
78 #ifdef CMAKE_HAVE_NET_IF_DL_H
79 #include <net/if_dl.h>
81 #if defined(CMAKE_HAVE_NET_IF_ARP_H) && defined(__sun)
82 // This is absolutely necessary on SunOS
83 #include <net/if_arp.h>
86 // For GetCurrentThreadID()
88 #include <sys/types.h>
89 #include <linux/unistd.h>
97 //-------------------------------------------------------------------------
98 const std::string Util::GDCM_UID = "1.2.826.0.1.3680043.2.1143";
99 std::string Util::RootUID = GDCM_UID;
101 * File Meta Information Version (0002,0001) shall contain a two byte OB
102 * value consisting of a 0x00 byte, followed by 0x01 byte, and not the
103 * value 0x0001 encoded as a little endian 16 bit short value,
104 * which would be the other way around...
106 const uint16_t Util::FMIV = 0x0100;
107 uint8_t *Util::FileMetaInformationVersion = (uint8_t *)&FMIV;
108 std::string Util::GDCM_MAC_ADRESS = GetMACAddress();
110 //-------------------------------------------------------------------------
113 * \brief Provide a better 'c++' approach for sprintf
114 * For example c code is:
115 * char result[2048]; // hope 2048 is enough
116 * sprintf(result, "%04x|%04x", group , elem);
119 * std::ostringstream buf;
120 * buf << std::right << std::setw(4) << std::setfill('0') << std::hex
121 * << group << "|" << std::right << std::setw(4) << std::setfill('0')
122 * << std::hex << elem;
127 * result = gdcm::Util::Format("%04x|%04x", group , elem);
129 std::string Util::Format(const char *format, ...)
131 char buffer[2048]; // hope 2048 is enough
133 va_start(args, format);
134 vsprintf(buffer, format, args); //might be a security flaw
135 va_end(args); // Each invocation of va_start should be matched
136 // by a corresponding invocation of va_end
137 // args is then 'undefined'
143 * \brief Because not available in C++ (?)
144 * @param str string to check
145 * @param tokens std::vector to receive the tokenized substrings
146 * @param delimiters string containing the character delimitors
149 void Util::Tokenize (const std::string &str,
150 std::vector<std::string> &tokens,
151 const std::string &delimiters)
153 std::string::size_type lastPos = str.find_first_not_of(delimiters,0);
154 std::string::size_type pos = str.find_first_of (delimiters,lastPos);
155 while (std::string::npos != pos || std::string::npos != lastPos)
157 tokens.push_back(str.substr(lastPos, pos - lastPos));
158 lastPos = str.find_first_not_of(delimiters, pos);
159 pos = str.find_first_of (delimiters, lastPos);
164 * \brief Because not available in C++ (?)
165 * Counts the number of occurences of a substring within a string
166 * @param str string to check
167 * @param subStr substring to count
170 int Util::CountSubstring (const std::string &str,
171 const std::string &subStr)
173 int count = 0; // counts how many times it appears
174 std::string::size_type x = 0; // The index position in the string
178 x = str.find(subStr,x); // Find the substring
179 if (x != std::string::npos) // If present
181 count++; // increase the count
182 x += subStr.length(); // Skip this word
185 while (x != std::string::npos);// Carry on until not present
191 * \brief Checks whether a 'string' is printable or not (in order
192 * to avoid corrupting the terminal of invocation when printing)
193 * @param s string to check
195 bool Util::IsCleanString(std::string const &s)
197 for(unsigned int i=0; i<s.size(); i++)
199 if (!isprint((unsigned char)s[i]) )
208 * \brief Checks whether an 'area' is printable or not (in order
209 * to avoid corrupting the terminal of invocation when printing)
210 * @param s area to check (uint8_t is just for prototyping. feel free to cast)
211 * @param l area length to check
213 bool Util::IsCleanArea(uint8_t *s, int l)
215 for( int i=0; i<l; i++)
217 if (!isprint((unsigned char)s[i]) )
225 * \brief Weed out a string from the non-printable characters (in order
226 * to avoid corrupting the terminal of invocation when printing)
227 * @param s string to check (uint8_t is just for prototyping. feel free to cast)
229 std::string Util::CreateCleanString(std::string const &s)
233 for(unsigned int i=0; i<str.size(); i++)
235 if (!isprint((unsigned char)str[i]) )
243 if (!isprint((unsigned char)s[str.size()-1]) )
245 if (s[str.size()-1] == 0 )
247 str[str.size()-1] = ' ';
256 * \brief Weed out a string from the non-printable characters (in order
257 * to avoid corrupting the terminal of invocation when printing)
258 * @param s area to process (uint8_t is just for prototyping. feel free to cast)
259 * @param l area length to check
261 std::string Util::CreateCleanString(uint8_t *s, int l)
265 for( int i=0; i<l; i++)
267 if (!isprint((unsigned char)s[i]) )
273 str = str + (char )s[i];
280 * \brief Add a SEPARATOR to the end of the name if necessary
281 * @param pathname file/directory name to normalize
283 std::string Util::NormalizePath(std::string const &pathname)
286 const char SEPARATOR_X = '/';
287 const char SEPARATOR_WIN = '\\';
289 const std::string SEPARATOR = "\\";
291 const std::string SEPARATOR = "/";
294 std::string name = pathname;
295 int size = name.size();
297 // if ( name[size-1] != SEPARATOR_X && name[size-1] != SEPARATOR_WIN )
298 if ( name[size-1] != GDCM_FILESEPARATOR )
300 name += GDCM_FILESEPARATOR;
306 * \brief Get the (directory) path from a full path file name
307 * @param fullName file/directory name to extract Path from
309 std::string Util::GetPath(std::string const &fullName)
311 std::string res = fullName;
314 int pos1 = res.rfind("/");
315 int pos2 = res.rfind("\\");
325 int pos = res.rfind(GDCM_FILESEPARATOR);
331 * \brief Get the (last) name of a full path file name
332 * @param fullName file/directory name to extract end name from
334 std::string Util::GetName(std::string const &fullName)
336 std::string filename = fullName;
338 std::string::size_type slash_pos = filename.rfind("/");
339 std::string::size_type backslash_pos = filename.rfind("\\");
340 // At least with my gcc4.0.1, unfound char results in pos =4294967295 ...
341 //slash_pos = slash_pos > backslash_pos ? slash_pos : backslash_pos;
342 slash_pos = slash_pos < backslash_pos ? slash_pos : backslash_pos;
344 std::string::size_type slash_pos = filename.rfind(GDCM_FILESEPARATOR);
345 if (slash_pos != std::string::npos )
347 return filename.substr(slash_pos + 1);
356 * \brief Get the current date of the system in a dicom string
358 std::string Util::GetCurrentDate()
363 strftime(tmp,512,"%Y%m%d", localtime(&tloc) );
368 * \brief Get the current time of the system in a dicom string
370 std::string Util::GetCurrentTime()
375 strftime(tmp,512,"%H%M%S", localtime(&tloc) );
380 * \brief Get both the date and time at the same time to avoid problem
381 * around midnight where the two calls could be before and after midnight
383 std::string Util::GetCurrentDateTime()
389 // We need implementation specific functions to obtain millisecond precision
390 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
394 milliseconds = tb.millitm;
397 gettimeofday (&tv, NULL);
399 // Compute milliseconds from microseconds.
400 milliseconds = tv.tv_usec / 1000;
402 // Obtain the time of day, and convert it to a tm struct.
403 struct tm *ptm = localtime (&timep);
404 // Format the date and time, down to a single second.
405 strftime (tmp, sizeof (tmp), "%Y%m%d%H%M%S", ptm);
408 // Don't use Util::Format to accelerate execution of code
410 sprintf(tmpAll,"%s%03ld",tmp,milliseconds);
415 unsigned int Util::GetCurrentThreadID()
417 // FIXME the implementation is far from complete
418 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
419 return (unsigned int)GetCurrentThreadId();
423 // Doesn't work on fedora, but is in the man page...
424 //return (unsigned int)gettid();
427 return (unsigned int)thr_self();
429 //default implementation
436 unsigned int Util::GetCurrentProcessID()
438 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
439 // NOTE: There is also a _getpid()...
440 return (unsigned int)GetCurrentProcessId();
442 // get process identification, POSIX
443 return (unsigned int)getpid();
448 * \brief tells us whether the processor we are working with is BigEndian or not
450 bool Util::IsCurrentProcessorBigEndian()
452 #if defined(GDCM_WORDS_BIGENDIAN)
460 * \brief Create a /DICOM/ string:
461 * It should a of even length (no odd length ever)
462 * It can contain as many (if you are reading this from your
463 * editor the following character is backslash followed by zero
464 * that needed to be escaped with an extra backslash for doxygen) \\0
467 std::string Util::DicomString(const char *s, size_t l)
469 std::string r(s, s+l);
470 gdcmAssertMacro( !(r.size() % 2) ); // == basically 'l' is even
475 * \brief Create a /DICOM/ string:
476 * It should a of even length (no odd length ever)
477 * It can contain as many (if you are reading this from your
478 * editor the following character is backslash followed by zero
479 * that needed to be escaped with an extra backslash for doxygen) \\0
481 * This function is similar to DicomString(const char*),
482 * except it doesn't take a length.
483 * It only pad with a null character if length is odd
485 std::string Util::DicomString(const char *s)
487 size_t l = strlen(s);
492 std::string r(s, s+l);
493 gdcmAssertMacro( !(r.size() % 2) );
498 * \brief Safely check the equality of two Dicom String:
499 * - Both strings should be of even length
500 * - We allow padding of even length string by either
501 * a null character of a space
503 bool Util::DicomStringEqual(const std::string &s1, const char *s2)
505 // s2 is the string from the DICOM reference e.g. : 'MONOCHROME1'
506 std::string s1_even = s1; //Never change input parameter
507 std::string s2_even = DicomString( s2 );
508 if ( s1_even[s1_even.size()-1] == ' ' )
510 s1_even[s1_even.size()-1] = '\0'; //replace space character by null
512 return s1_even == s2_even;
516 * \brief Safely compare two Dicom String:
517 * - Both strings should be of even length
518 * - We allow padding of even length string by either
519 * a null character of a space
521 bool Util::CompareDicomString(const std::string &s1, const char *s2, int op)
523 // s2 is the string from the DICOM reference e.g. : 'MONOCHROME1'
524 std::string s1_even = s1; //Never change input parameter
525 std::string s2_even = DicomString( s2 );
526 if ( s1_even[s1_even.size()-1] == ' ' )
528 s1_even[s1_even.size()-1] = '\0'; //replace space character by null
533 return s1_even == s2_even;
534 case GDCM_DIFFERENT :
535 return s1_even != s2_even;
537 return s1_even > s2_even;
538 case GDCM_GREATEROREQUAL :
539 return s1_even >= s2_even;
541 return s1_even < s2_even;
542 case GDCM_LESSOREQUAL :
543 return s1_even <= s2_even;
545 gdcmDebugMacro(" Wrong operator : " << op);
551 typedef BOOL(WINAPI * pSnmpExtensionInit) (
552 IN DWORD dwTimeZeroReference,
553 OUT HANDLE * hPollForTrapEvent,
554 OUT AsnObjectIdentifier * supportedView);
556 typedef BOOL(WINAPI * pSnmpExtensionTrap) (
557 OUT AsnObjectIdentifier * enterprise,
558 OUT AsnInteger * genericTrap,
559 OUT AsnInteger * specificTrap,
560 OUT AsnTimeticks * timeStamp,
561 OUT RFC1157VarBindList * variableBindings);
563 typedef BOOL(WINAPI * pSnmpExtensionQuery) (
565 IN OUT RFC1157VarBindList * variableBindings,
566 OUT AsnInteger * errorStatus,
567 OUT AsnInteger * errorIndex);
569 typedef BOOL(WINAPI * pSnmpExtensionInitEx) (
570 OUT AsnObjectIdentifier * supportedView);
574 static int SGIGetMacAddress(unsigned char *addr)
576 FILE *f = popen("/etc/nvram eaddr","r");
582 if(fscanf(f,"%02x:%02x:%02x:%02x:%02x:%02x",
583 x,x+1,x+2,x+3,x+4,x+5) != 6)
588 for(unsigned int i = 0; i < 6; i++)
590 addr[i] = static_cast<unsigned char>(x[i]);
596 /// \brief gets current M.A.C adress (for internal use only)
597 int GetMacAddrSys ( unsigned char *addr );
598 int GetMacAddrSys ( unsigned char *addr )
602 if ( (WSAStartup(MAKEWORD(2, 0), &WinsockData)) != 0 )
604 std::cerr << "in Get MAC Adress (internal) : This program requires Winsock 2.x!"
609 HANDLE PollForTrapEvent;
610 AsnObjectIdentifier SupportedView;
611 UINT OID_ifEntryType[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 3 };
612 UINT OID_ifEntryNum[] = { 1, 3, 6, 1, 2, 1, 2, 1 };
613 UINT OID_ipMACEntAddr[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 6 };
614 AsnObjectIdentifier MIB_ifMACEntAddr = {
615 sizeof(OID_ipMACEntAddr) / sizeof(UINT), OID_ipMACEntAddr };
616 AsnObjectIdentifier MIB_ifEntryType = {
617 sizeof(OID_ifEntryType) / sizeof(UINT), OID_ifEntryType };
618 AsnObjectIdentifier MIB_ifEntryNum = {
619 sizeof(OID_ifEntryNum) / sizeof(UINT), OID_ifEntryNum };
620 RFC1157VarBindList varBindList;
621 RFC1157VarBind varBind[2];
622 AsnInteger errorStatus;
623 AsnInteger errorIndex;
624 AsnObjectIdentifier MIB_NULL = { 0, 0 };
629 // Load the SNMP dll and get the addresses of the functions necessary
630 HINSTANCE m_hInst = LoadLibrary("inetmib1.dll");
631 if (m_hInst < (HINSTANCE) HINSTANCE_ERROR)
635 pSnmpExtensionInit m_Init =
636 (pSnmpExtensionInit) GetProcAddress(m_hInst, "SnmpExtensionInit");
637 pSnmpExtensionQuery m_Query =
638 (pSnmpExtensionQuery) GetProcAddress(m_hInst, "SnmpExtensionQuery");
639 m_Init(GetTickCount(), &PollForTrapEvent, &SupportedView);
641 /* Initialize the variable list to be retrieved by m_Query */
642 varBindList.list = varBind;
643 varBind[0].name = MIB_NULL;
644 varBind[1].name = MIB_NULL;
646 // Copy in the OID to find the number of entries in the
648 varBindList.len = 1; // Only retrieving one item
649 SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryNum);
650 m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
652 // printf("# of adapters in this system : %i\n",
653 // varBind[0].value.asnValue.number);
656 // Copy in the OID of ifType, the type of interface
657 SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryType);
659 // Copy in the OID of ifPhysAddress, the address
660 SNMP_oidcpy(&varBind[1].name, &MIB_ifMACEntAddr);
664 // Submit the query. Responses will be loaded into varBindList.
665 // We can expect this call to succeed a # of times corresponding
666 // to the # of adapters reported to be in the system
667 ret = m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
675 // Confirm that the proper type has been returned
676 ret = SNMP_oidncmp(&varBind[0].name, &MIB_ifEntryType,
677 MIB_ifEntryType.idLength);
682 dtmp = varBind[0].value.asnValue.number;
684 // Type 6 describes ethernet interfaces
687 // Confirm that we have an address here
688 ret = SNMP_oidncmp(&varBind[1].name, &MIB_ifMACEntAddr,
689 MIB_ifMACEntAddr.idLength);
690 if ( !ret && varBind[1].value.asnValue.address.stream != NULL )
692 if ( (varBind[1].value.asnValue.address.stream[0] == 0x44)
693 && (varBind[1].value.asnValue.address.stream[1] == 0x45)
694 && (varBind[1].value.asnValue.address.stream[2] == 0x53)
695 && (varBind[1].value.asnValue.address.stream[3] == 0x54)
696 && (varBind[1].value.asnValue.address.stream[4] == 0x00) )
698 // Ignore all dial-up networking adapters
699 std::cerr << "in Get MAC Adress (internal) : Interface #"
700 << j << " is a DUN adapter\n";
703 if ( (varBind[1].value.asnValue.address.stream[0] == 0x00)
704 && (varBind[1].value.asnValue.address.stream[1] == 0x00)
705 && (varBind[1].value.asnValue.address.stream[2] == 0x00)
706 && (varBind[1].value.asnValue.address.stream[3] == 0x00)
707 && (varBind[1].value.asnValue.address.stream[4] == 0x00)
708 && (varBind[1].value.asnValue.address.stream[5] == 0x00) )
710 // Ignore NULL addresses returned by other network
712 std::cerr << "in Get MAC Adress (internal) : Interface #"
713 << j << " is a NULL address\n";
716 memcpy( addr, varBind[1].value.asnValue.address.stream, 6);
723 SNMP_FreeVarBind(&varBind[0]);
724 SNMP_FreeVarBind(&varBind[1]);
726 #endif //Win32 version
729 return SGIGetMacAddress(addr);
733 // implementation for POSIX system
734 #if defined(CMAKE_HAVE_NET_IF_ARP_H) && defined(__sun)
735 //The POSIX version is broken anyway on Solaris, plus would require full
737 struct arpreq parpreq;
738 struct sockaddr_in *psa;
739 struct hostent *phost;
740 char hostname[MAXHOSTNAMELEN];
744 if (gethostname(hostname, MAXHOSTNAMELEN) != 0 )
746 perror("in Get MAC Adress (internal) : gethostname");
749 phost = gethostbyname(hostname);
750 paddrs = phost->h_addr_list;
752 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
755 perror("in Get MAC Adress (internal) : sock");
758 memset(&parpreq, 0, sizeof(struct arpreq));
759 psa = (struct sockaddr_in *) &parpreq.arp_pa;
761 memset(psa, 0, sizeof(struct sockaddr_in));
762 psa->sin_family = AF_INET;
763 memcpy(&psa->sin_addr, *paddrs, sizeof(struct in_addr));
765 status = ioctl(sock, SIOCGARP, &parpreq);
768 perror("in Get MAC Adress (internal) : SIOCGARP");
771 memcpy(addr, parpreq.arp_ha.sa_data, 6);
775 #ifdef CMAKE_HAVE_NET_IF_H
777 struct ifreq ifr, *ifrp;
782 #if defined(AF_LINK) && (!defined(SIOCGIFHWADDR) && !defined(SIOCGENADDR))
783 struct sockaddr_dl *sdlp;
787 // BSD 4.4 defines the size of an ifreq to be
788 // max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
789 // However, under earlier systems, sa_len isn't present, so the size is
790 // just sizeof(struct ifreq)
791 // We should investigate the use of SIZEOF_ADDR_IFREQ
795 #define max(a,b) ((a) > (b) ? (a) : (b))
797 #define ifreq_size(i) max(sizeof(struct ifreq),\
798 sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
800 #define ifreq_size(i) sizeof(struct ifreq)
801 #endif // HAVE_SA_LEN
803 if ( (sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0 )
807 memset(buf, 0, sizeof(buf));
808 ifc.ifc_len = sizeof(buf);
810 if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0)
816 for (i = 0; i < n; i+= ifreq_size(*ifrp) )
818 ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
819 strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
821 if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
823 a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
826 // In theory this call should also work on Sun Solaris, but apparently
827 // SIOCGENADDR is not implemented properly thus the call
828 // ioctl(sd, SIOCGENADDR, &ifr) always returns errno=2
829 // (No such file or directory)
830 // Furthermore the DLAPI seems to require full root access
831 if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
833 a = (unsigned char *) ifr.ifr_enaddr;
836 sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
837 if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
839 a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
841 perror("in Get MAC Adress (internal) : No way to access hardware");
845 #endif // SIOCGENADDR
846 #endif // SIOCGIFHWADDR
847 if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) continue;
858 // Not implemented platforms (or no Ethernet cable !)
860 /// \todo FIXME I wish we don't pollute command line applications when no Ethernet cable !
861 //perror("in Get MAC Adress (internal) : There was a configuration problem (or no Ethernet cable !) on your plateform");
863 // But the following -> error: invalid use of 'this' in non-member function
864 //gdcmWarningMacro( "in Get MAC Adress (internal) : There was a configuration problem (or no Ethernet cable !) on your plateform");
872 * \brief Mini function to return the last digit from a number express in base 256
873 * pre condition data contain an array of 6 unsigned char
874 * post condition carry contain the last digit
876 inline int getlastdigit(unsigned char *data)
878 int extended, carry = 0;
881 extended = (carry << 8) + data[i];
882 data[i] = extended / 10;
883 carry = extended % 10;
889 * \brief Encode the mac address on a fixed length string of 15 characters.
890 * we save space this way.
892 std::string Util::GetMACAddress()
894 // This code is the result of a long internet search to find something
895 // as compact as possible (not OS independant). We only have to separate
896 // 3 OS: Win32, SunOS and 'real' POSIX
897 // http://groups-beta.google.com/group/comp.unix.solaris/msg/ad36929d783d63be
898 // http://bdn.borland.com/article/0,1410,26040,00.html
899 unsigned char addr[6];
901 int stat = GetMacAddrSys(addr);
904 // We need to convert a 6 digit number from base 256 to base 10, using integer
905 // would requires a 48bits one. To avoid this we have to reimplement the div + modulo
912 res = getlastdigit(addr);
913 sres.insert(sres.begin(), '0' + res);
914 zero = (addr[0] == 0) && (addr[1] == 0) && (addr[2] == 0)
915 && (addr[3] == 0) && (addr[4] == 0) && (addr[5] == 0);
922 gdcmStaticWarningMacro("Problem in finding the MAC Address");
928 * \brief Creates a new UID. As stipulated in the DICOM ref
929 * each time a DICOM image is created it should have
930 * a unique identifier (URI)
931 * @param root is the DICOM prefix assigned by IOS group
933 std::string Util::CreateUniqueUID(const std::string &root)
939 // gdcm UID prefix, as supplied by http://www.medicalconnections.co.uk
947 // A root was specified use it to forge our new UID:
949 //append += Util::GetMACAddress(); // to save CPU time
950 append += Util::GDCM_MAC_ADRESS;
952 append += Util::GetCurrentDateTime();
954 //Also add a mini random number just in case:
956 int r = (int) (100.0*rand()/RAND_MAX);
957 // Don't use Util::Format to accelerate the execution
958 sprintf(tmp,"%02d", r);
961 // If append is too long we need to rehash it
962 if ( (prefix + append).size() > 64 )
964 gdcmStaticErrorMacro( "Size of UID is too long." );
965 // we need a hash function to truncate this number
966 // if only md5 was cross plateform
970 return prefix + append;
973 void Util::SetRootUID(const std::string &root)
981 const std::string &Util::GetRootUID()
986 //-------------------------------------------------------------------------
988 * \brief binary_write binary_write
989 * @param os ostream to write to
990 * @param val 16 bits value to write
992 std::ostream &binary_write(std::ostream &os, const uint16_t &val)
994 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
996 swap = ( val << 8 | val >> 8 );
998 return os.write(reinterpret_cast<const char*>(&swap), 2);
1000 return os.write(reinterpret_cast<const char*>(&val), 2);
1001 #endif //GDCM_WORDS_BIGENDIAN
1005 * \brief binary_write binary_write
1006 * @param os ostream to write to
1007 * @param val 32 bits value to write
1009 std::ostream &binary_write(std::ostream &os, const uint32_t &val)
1011 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
1013 swap = ( (val<<24) | ((val<<8) & 0x00ff0000) |
1014 ((val>>8) & 0x0000ff00) | (val>>24) );
1015 return os.write(reinterpret_cast<const char*>(&swap), 4);
1017 return os.write(reinterpret_cast<const char*>(&val), 4);
1018 #endif //GDCM_WORDS_BIGENDIAN
1022 * \brief binary_write binary_write
1023 * @param os ostream to write to
1024 * @param val double (64 bits) value to write
1026 std::ostream &binary_write(std::ostream &os, const double &val)
1028 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
1031 char *beg = (char *)&swap;
1032 char *end = beg + 7;
1034 for (unsigned int i = 0; i<7; i++)
1042 return os.write(reinterpret_cast<const char*>(&swap), 8);
1044 return os.write(reinterpret_cast<const char*>(&val), 8);
1045 #endif //GDCM_WORDS_BIGENDIAN
1049 * \brief binary_write binary_write
1050 * @param os ostream to write to
1051 * @param val 8 bits characters aray to write
1053 std::ostream &binary_write(std::ostream &os, const char *val)
1055 return os.write(val, strlen(val));
1059 * \brief binary_write binary_write
1060 * @param os ostream to write to
1061 * @param val std::string value to write
1063 std::ostream &binary_write(std::ostream &os, std::string const &val)
1065 return os.write(val.c_str(), val.size());
1069 * \brief binary_write binary_write
1070 * @param os ostream to write to
1071 * @param val 8 bits 'characters' aray to write
1072 * @param len length of the 'value' to be written
1074 std::ostream &binary_write(std::ostream &os, const uint8_t *val, size_t len)
1076 // We are writting sizeof(char) thus no need to swap bytes
1077 return os.write(reinterpret_cast<const char*>(val), len);
1081 * \brief binary_write binary_write
1082 * @param os ostream to write to
1083 * @param val 16 bits words aray to write
1084 * @param len length (in bytes) of the 'value' to be written
1086 std::ostream &binary_write(std::ostream &os, const uint16_t *val, size_t len)
1088 // This is tricky since we are writting two bytes buffer.
1089 // Be carefull with little endian vs big endian.
1090 // Also this other trick is to allocate a small (efficient) buffer that store
1091 // intermediate result before writting it.
1092 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
1093 const int BUFFER_SIZE = 4096;
1094 static char buffer[BUFFER_SIZE];
1095 uint16_t *binArea16 = (uint16_t*)val; //for the const
1097 // how many BUFFER_SIZE long pieces in binArea ?
1098 int nbPieces = len/BUFFER_SIZE; //(16 bits = 2 Bytes)
1099 int remainingSize = len%BUFFER_SIZE;
1101 for (int j=0;j<nbPieces;j++)
1103 uint16_t *pbuffer = (uint16_t*)buffer; //reinitialize pbuffer
1104 for (int i = 0; i < BUFFER_SIZE/2; i++)
1106 *pbuffer = *binArea16 >> 8 | *binArea16 << 8;
1110 os.write ( buffer, BUFFER_SIZE );
1112 if ( remainingSize > 0)
1114 uint16_t *pbuffer = (uint16_t*)buffer; //reinitialize pbuffer
1115 for (int i = 0; i < remainingSize/2; i++)
1117 *pbuffer = *binArea16 >> 8 | *binArea16 << 8;
1121 os.write ( buffer, remainingSize );
1125 return os.write(reinterpret_cast<const char*>(val), len);
1129 std::string Util::ConvertToMD5 (std::string &inPszToCrypt)
1131 char *szChar = new char[ inPszToCrypt.size()+1 ];
1132 char *szHexOutput = new char[ 16 * 2 + 1 ];
1134 strcpy( szChar, inPszToCrypt.c_str() );
1135 // création du code md5
1136 nLen = strlen( szChar );
1138 uint8_t digest[ 16 ];
1139 md5_init ( &state );
1140 md5_append ( &state, (const uint8_t *)szChar, nLen);
1141 md5_finish ( &state, digest );
1142 for ( nDi = 0; nDi < 16; nDi++)
1143 sprintf( szHexOutput + nDi * 2, "%02x", digest[ nDi ] );
1144 szHexOutput[16 * 2]=0;
1146 std::string md5String=szHexOutput;
1147 delete [] szHexOutput;
1151 //-------------------------------------------------------------------------
1154 //-------------------------------------------------------------------------
1157 * \brief Return the IP adress of the machine writting the DICOM image
1159 std::string Util::GetIPAddress()
1161 // This is a rip from
1162 // http://www.codeguru.com/Cpp/I-N/internet/network/article.php/c3445/
1163 #ifndef HOST_NAME_MAX
1164 // SUSv2 guarantees that `Host names are limited to 255 bytes'.
1165 // POSIX 1003.1-2001 guarantees that `Host names (not including the
1166 // terminating NUL) are limited to HOST_NAME_MAX bytes'.
1167 #define HOST_NAME_MAX 255
1168 // In this case we should maybe check the string was not truncated.
1169 // But I don't known how to check that...
1170 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
1171 // with WinSock DLL we need to initialize the WinSock before using gethostname
1172 WORD wVersionRequested = MAKEWORD(1,0);
1174 int err = WSAStartup(wVersionRequested,&WSAData);
1177 // Tell the user that we could not find a usable
1184 #endif //HOST_NAME_MAX
1187 char szHostName[HOST_NAME_MAX+1];
1188 int r = gethostname(szHostName, HOST_NAME_MAX);
1192 // Get host adresses
1193 struct hostent *pHost = gethostbyname(szHostName);
1195 for( int i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
1197 for( int j = 0; j<pHost->h_length; j++ )
1199 if ( j > 0 ) str += ".";
1201 str += Util::Format("%u",
1202 (unsigned int)((unsigned char*)pHost->h_addr_list[i])[j]);
1204 // str now contains one local IP address
1206 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__)
1212 // If an error occur r == -1
1213 // Most of the time it will return 127.0.0.1...
1217 void Util::hfpswap(double *a, double *b)
1227 Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
1229 This software is provided 'as-is', without any express or implied
1230 warranty. In no event will the authors be held liable for any damages
1231 arising from the use of this software.
1233 Permission is granted to anyone to use this software for any purpose,
1234 including commercial applications, and to alter it and redistribute it
1235 freely, subject to the following restrictions:
1237 1. The origin of this software must not be misrepresented; you must not
1238 claim that you wrote the original software. If you use this software
1239 in a product, an acknowledgment in the product documentation would be
1240 appreciated but is not required.
1241 2. Altered source versions must be plainly marked as such, and must not be
1242 misrepresented as being the original software.
1243 3. This notice may not be removed or altered from any source distribution.
1250 /* $Id: gdcmUtil.cxx,v 1.185 2006/07/10 09:37:33 jpr Exp $ */
1253 Independent implementation of MD5 (RFC 1321).
1254 This code implements the MD5 Algorithm defined in RFC 1321, whose
1255 text is available at
1257 http://www.ietf.org/rfc/rfc1321.txt
1259 The code is derived from the text of the RFC, including the test suite
1260 (section A.5) but excluding the rest of Appendix A. It does not include
1261 any code or documentation that is identified in the RFC as being
1264 The original and principal author of md5.c is L. Peter Deutsch
1265 <ghost@aladdin.com>. Other authors are noted in the change history
1266 that follows (in reverse chronological order):
1268 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
1269 either statically or dynamically; added missing #include <string.h>
1271 2002-03-11 lpd Corrected argument list for main(), and added int return
1272 type, in test program and T value program.
1273 2002-02-21 lpd Added missing #include <stdio.h> in test program.
1274 2000-07-03 lpd Patched to eliminate warnings about "constant is
1275 unsigned in ANSI C, signed in traditional"; made test program
1277 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1278 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1279 1999-05-03 lpd Original version.
1283 //#include <string.h>
1285 #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
1286 #ifdef ARCH_IS_BIG_ENDIAN
1287 # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
1289 # define BYTE_ORDER 0
1293 #define T_MASK ((uint16_t)~0)
1294 #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
1295 #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
1296 #define T3 0x242070db
1297 #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
1298 #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
1299 #define T6 0x4787c62a
1300 #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
1301 #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
1302 #define T9 0x698098d8
1303 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
1304 #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
1305 #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
1306 #define T13 0x6b901122
1307 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
1308 #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
1309 #define T16 0x49b40821
1310 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
1311 #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
1312 #define T19 0x265e5a51
1313 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
1314 #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
1315 #define T22 0x02441453
1316 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
1317 #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
1318 #define T25 0x21e1cde6
1319 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
1320 #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
1321 #define T28 0x455a14ed
1322 #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
1323 #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
1324 #define T31 0x676f02d9
1325 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
1326 #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
1327 #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
1328 #define T35 0x6d9d6122
1329 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
1330 #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
1331 #define T38 0x4bdecfa9
1332 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
1333 #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
1334 #define T41 0x289b7ec6
1335 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
1336 #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
1337 #define T44 0x04881d05
1338 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
1339 #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
1340 #define T47 0x1fa27cf8
1341 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
1342 #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
1343 #define T50 0x432aff97
1344 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
1345 #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
1346 #define T53 0x655b59c3
1347 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
1348 #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
1349 #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
1350 #define T57 0x6fa87e4f
1351 #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
1352 #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
1353 #define T60 0x4e0811a1
1354 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
1355 #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
1356 #define T63 0x2ad7d2bb
1357 #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
1361 Util::md5_process(md5_state_t *pms, const uint8_t *data /*[64]*/)
1364 a = pms->abcd[0], b = pms->abcd[1],
1365 c = pms->abcd[2], d = pms->abcd[3];
1369 /* Define storage only for big-endian CPUs. */
1372 /* Define storage for little-endian or both types of CPUs. */
1379 * Determine dynamically whether this is a big-endian or
1380 * little-endian machine, since we can use a more efficient
1381 * algorithm on the latter.
1383 static const int w = 1;
1385 if (*((const uint8_t *)&w)) /* dynamic little-endian */
1387 #if BYTE_ORDER <= 0 /* little-endian */
1390 * On little-endian machines, we can process properly aligned
1391 * data without copying it.
1393 if (!((data - (const uint8_t *)0) & 3)) {
1394 /* data are properly aligned */
1395 X = (const uint16_t *)data;
1398 memcpy(xbuf, data, 64);
1404 else /* dynamic big-endian */
1406 #if BYTE_ORDER >= 0 /* big-endian */
1409 * On big-endian machines, we must arrange the bytes in the
1412 const uint8_t *xp = data;
1415 # if BYTE_ORDER == 0
1416 X = xbuf; /* (dynamic only) */
1418 # define xbuf /* (static only) */
1420 for (i = 0; i < 16; ++i, xp += 4)
1421 xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
1426 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
1429 /* Let [abcd k s i] denote the operation
1430 a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
1432 #define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
1433 #define SET(a, b, c, d, k, s, Ti) \
1434 t = a + F(b,c,d) + X[k] + Ti;\
1435 a = ROTATE_LEFT(t, s) + b
1436 /* Do the following 16 operations. */
1437 SET(a, b, c, d, 0, 7, T1);
1438 SET(d, a, b, c, 1, 12, T2);
1439 SET(c, d, a, b, 2, 17, T3);
1440 SET(b, c, d, a, 3, 22, T4);
1441 SET(a, b, c, d, 4, 7, T5);
1442 SET(d, a, b, c, 5, 12, T6);
1443 SET(c, d, a, b, 6, 17, T7);
1444 SET(b, c, d, a, 7, 22, T8);
1445 SET(a, b, c, d, 8, 7, T9);
1446 SET(d, a, b, c, 9, 12, T10);
1447 SET(c, d, a, b, 10, 17, T11);
1448 SET(b, c, d, a, 11, 22, T12);
1449 SET(a, b, c, d, 12, 7, T13);
1450 SET(d, a, b, c, 13, 12, T14);
1451 SET(c, d, a, b, 14, 17, T15);
1452 SET(b, c, d, a, 15, 22, T16);
1455 /* Let [abcd k s i] denote the operation
1456 a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
1458 #define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
1459 #define SET(a, b, c, d, k, s, Ti)\
1460 t = a + G(b,c,d) + X[k] + Ti; \
1461 a = ROTATE_LEFT(t, s) + b
1462 /* Do the following 16 operations. */
1463 SET(a, b, c, d, 1, 5, T17);
1464 SET(d, a, b, c, 6, 9, T18);
1465 SET(c, d, a, b, 11, 14, T19);
1466 SET(b, c, d, a, 0, 20, T20);
1467 SET(a, b, c, d, 5, 5, T21);
1468 SET(d, a, b, c, 10, 9, T22);
1469 SET(c, d, a, b, 15, 14, T23);
1470 SET(b, c, d, a, 4, 20, T24);
1471 SET(a, b, c, d, 9, 5, T25);
1472 SET(d, a, b, c, 14, 9, T26);
1473 SET(c, d, a, b, 3, 14, T27);
1474 SET(b, c, d, a, 8, 20, T28);
1475 SET(a, b, c, d, 13, 5, T29);
1476 SET(d, a, b, c, 2, 9, T30);
1477 SET(c, d, a, b, 7, 14, T31);
1478 SET(b, c, d, a, 12, 20, T32);
1481 /* Let [abcd k s t] denote the operation
1482 a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
1483 #define H(x, y, z) ((x) ^ (y) ^ (z))
1484 #define SET(a, b, c, d, k, s, Ti)\
1485 t = a + H(b,c,d) + X[k] + Ti; \
1486 a = ROTATE_LEFT(t, s) + b
1488 /* Do the following 16 operations. */
1489 SET(a, b, c, d, 5, 4, T33);
1490 SET(d, a, b, c, 8, 11, T34);
1491 SET(c, d, a, b, 11, 16, T35);
1492 SET(b, c, d, a, 14, 23, T36);
1493 SET(a, b, c, d, 1, 4, T37);
1494 SET(d, a, b, c, 4, 11, T38);
1495 SET(c, d, a, b, 7, 16, T39);
1496 SET(b, c, d, a, 10, 23, T40);
1497 SET(a, b, c, d, 13, 4, T41);
1498 SET(d, a, b, c, 0, 11, T42);
1499 SET(c, d, a, b, 3, 16, T43);
1500 SET(b, c, d, a, 6, 23, T44);
1501 SET(a, b, c, d, 9, 4, T45);
1502 SET(d, a, b, c, 12, 11, T46);
1503 SET(c, d, a, b, 15, 16, T47);
1504 SET(b, c, d, a, 2, 23, T48);
1507 /* Let [abcd k s t] denote the operation
1508 a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
1509 #define I(x, y, z) ((y) ^ ((x) | ~(z)))
1510 #define SET(a, b, c, d, k, s, Ti)\
1511 t = a + I(b,c,d) + X[k] + Ti; \
1512 a = ROTATE_LEFT(t, s) + b
1514 /* Do the following 16 operations. */
1515 SET(a, b, c, d, 0, 6, T49);
1516 SET(d, a, b, c, 7, 10, T50);
1517 SET(c, d, a, b, 14, 15, T51);
1518 SET(b, c, d, a, 5, 21, T52);
1519 SET(a, b, c, d, 12, 6, T53);
1520 SET(d, a, b, c, 3, 10, T54);
1521 SET(c, d, a, b, 10, 15, T55);
1522 SET(b, c, d, a, 1, 21, T56);
1523 SET(a, b, c, d, 8, 6, T57);
1524 SET(d, a, b, c, 15, 10, T58);
1525 SET(c, d, a, b, 6, 15, T59);
1526 SET(b, c, d, a, 13, 21, T60);
1527 SET(a, b, c, d, 4, 6, T61);
1528 SET(d, a, b, c, 11, 10, T62);
1529 SET(c, d, a, b, 2, 15, T63);
1530 SET(b, c, d, a, 9, 21, T64);
1533 /* Then perform the following additions. (That is increment each
1534 of the four registers by the value it had before this block
1542 Util::md5_init(md5_state_t *pms)
1544 pms->count[0] = pms->count[1] = 0;
1545 pms->abcd[0] = 0x67452301;
1546 pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
1547 pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
1548 pms->abcd[3] = 0x10325476;
1553 Util::md5_append(md5_state_t *pms, const uint8_t *data, int nbytes)
1555 const uint8_t *p = data;
1557 int offset = (pms->count[0] >> 3) & 63;
1558 uint16_t nbits = (uint16_t)(nbytes << 3);
1561 /* Update the message length. */
1562 pms->count[1] += nbytes >> 29;
1563 pms->count[0] += nbits;
1564 if (pms->count[0] < nbits)
1566 /* Process an initial partial block. */
1568 int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
1569 memcpy(pms->buf + offset, p, copy);
1570 if (offset + copy < 64)
1574 md5_process(pms, pms->buf);
1576 /* Process full blocks. */
1577 for (; left >= 64; p += 64, left -= 64)
1578 md5_process(pms, p);
1579 /* Process a final partial block. */
1581 memcpy(pms->buf, p, left);
1584 Util::md5_finish(md5_state_t *pms, uint8_t digest[16])
1586 static const uint8_t pad[64] = {
1587 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1588 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1589 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1590 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1595 /* Save the length before padding. */
1596 for (i = 0; i < 8; ++i)
1597 data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
1598 /* Pad to 56 bytes mod 64. */
1599 md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
1600 /* Append the length. */
1601 md5_append(pms, data, 8);
1602 for (i = 0; i < 16; ++i)
1603 digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
1606 //-------------------------------------------------------------------------
1607 } // end namespace gdcm