1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
/*=========================================================================
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.
=========================================================================*/
#include "gdcmUIDGenerator.h"
#include "gdcmTrace.h"
#include "gdcmSystem.h"
#include <bitset>
#include <cstring>
// FIXME...
#if defined(_WIN32) || defined(__CYGWIN__)
#define HAVE_UUIDCREATE
#else
#define HAVE_UUID_GENERATE
#endif
#ifdef HAVE_UUID_GENERATE
#include "gdcm_uuid.h"
#endif
#ifdef GDCM_HAVE_RPC_H
#include <rpc.h>
#endif
namespace gdcm
{
/*
* This is just plain bad luck. GDCM UID root is 26 byte long
* And all implementation of the DCE UUID (Theodore Y. Ts'o)
* are based on a uint128_t (unsigned char [16]). Which
* means that converted to a base 10 number they require at
* least 39 bytes to fit in memory, since the highest possible
* number is 256**16 - 1 = 340282366920938463463374607431768211455
* Unfortunately root + '.' + suffix should be at most 64 bytes
*
* So to get a full UUID implementation as per RFC 4122
* http://www.ietf.org/rfc/rfc4122.txt we need a shorter
* root...
*
*/
const char UIDGenerator::GDCM_UID[] = "1.2.826.0.1.3680043.2.1143";
std::string UIDGenerator::Root = GetGDCMUID();
// The following contains the *encoded* hardware address (not the raw as in ipconfig/ifconfig)
std::string UIDGenerator::EncodedHardwareAddress; // = System::GetHardwareAddress();
const char *UIDGenerator::GetRoot() { return Root.c_str(); }
void UIDGenerator::SetRoot(const char * root) {
assert( IsValid( root ) );
Root = root;
}
const char *UIDGenerator::GetGDCMUID()
{
return GDCM_UID;
}
/*
* http://www.isthe.com/chongo/tech/comp/fnv/
*/
#define FNV1_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
struct fnv_hash
{
static uint64_t
hash(const char* pBuffer, size_t nByteLen)
{
uint64_t nHashVal = FNV1_64_INIT,
nMagicPrime = 0x00000100000001b3ULL;
const unsigned char* pFirst = ( const unsigned char* )( pBuffer ),
* pLast = pFirst + nByteLen;
while( pFirst < pLast )
{
nHashVal ^= *pFirst++;
nHashVal *= nMagicPrime;
}
return nHashVal;
}
};
/*
Implementation note: You cannot set a root of more than 26 bytes (which should already
enough for most people).
Since implementation is only playing with the first 8bits of the upper
*/
const char* UIDGenerator::Generate()
{
Unique = GetRoot();
// We choose here a value of 26 so that we can still have 37 bytes free to
// set the suffix part which is sufficient to store a 2^(128-8+1)-1 number
if( Unique.empty() || Unique.size() > 62 ) // 62 is simply the highest possible limit
{
// I cannot go any further...
return nullptr;
}
unsigned char uuid[16];
bool r = UIDGenerator::GenerateUUID(uuid);
// This should only happen in some obscure cases. Since the creation of UUID failed
// I should try to go any further and make sure the user's computer crash and burn
// right away
if( !r ) return nullptr;
char randbytesbuf[64];
size_t len = System::EncodeBytes(randbytesbuf, uuid, sizeof(uuid));
assert( len < 64 ); // programmer error
Unique += "."; // This dot is compulsory to separate root from suffix
if( Unique.size() + len > 64 )
{
int idx = 0;
bool found = false;
std::bitset<8> x;
while( !found && idx < 16 ) /* 16 is insane ... oh well */
{
// too bad ! randbytesbuf is too long, let's try to truncate the high bits a little
x = uuid[idx];
unsigned int i = 0;
while( ( Unique.size() + len > 64 ) && i < 8 )
{
x[7-i] = false;
uuid[idx] = (unsigned char)x.to_ulong();
len = System::EncodeBytes(randbytesbuf, uuid, sizeof(uuid));
++i;
}
if( ( Unique.size() + len > 64 ) && i == 8 )
{
// too bad only reducing the 8 bits from uuid[idx] was not enough,
// let's set to zero the following bits...
idx++;
}
else
{
// cool we found enough to stop
found = true;
}
}
if( !found )
{
// Technically this could only happen when root has a length >= 64 ... is it
// even remotely possible ?
gdcmWarningMacro( "Root is too long for current implementation" );
return nullptr;
}
}
// can now safely use randbytesbuf as is, no need to truncate any more:
Unique += randbytesbuf;
assert( IsValid( Unique.c_str() ) );
return Unique.c_str();
}
/* return true on success */
bool UIDGenerator::GenerateUUID(unsigned char *uuid_data)
{
#if defined(HAVE_UUID_GENERATE)
uuid_t g;
uuid_generate(g);
memcpy(uuid_data, g, sizeof(uuid_t));
#elif defined(HAVE_UUID_CREATE)
uint32_t rv;
uuid_t g;
uuid_create(&g, &rv);
if (rv != uuid_s_ok)
return false;
memcpy(uuid_data, &g, sizeof(uuid_t));
#elif defined(HAVE_UUIDCREATE)
if (FAILED(UuidCreate((UUID *)uuid_data)))
{
return false;
}
#else
#error should not happen
#endif
return true;
}
bool UIDGenerator::IsValid(const char *uid_)
{
/*
9.1 UID ENCODING RULES
The DICOM UID encoding rules are defined as follows:
- Each component of a UID is a number and shall consist of one or more digits. The first digit of
each component shall not be zero unless the component is a single digit.
Note: Registration authorities may distribute components with non-significant leading zeroes. The leading
zeroes should be ignored when being encoded (ie. 00029 would be encoded 29).
- Each component numeric value shall be encoded using the characters 0-9 of the Basic G0 Set
of the International Reference Version of ISO 646:1990 (the DICOM default character
repertoire).
- Components shall be separated by the character "." (2EH).
- If ending on an odd byte boundary, except when used for network negotiation (See PS 3.8),
one trailing NULL (00H), as a padding character, shall follow the last component in order to
align the UID on an even byte boundary.
- UID's, shall not exceed 64 total characters, including the digits of each component, separators
between components, and the NULL (00H) padding character if needed.
*/
/*
* FIXME: This is not clear in the standard, but I believe a trailing '.' is not allowed since
* this is considered as a separator for components
*/
if( !uid_ ) return false;
std::string uid = uid_;
if( uid.size() > 64 || uid.empty() )
{
return false;
}
if( uid[0] == '.' || uid[uid.size()-1] == '.' ) // important to do that first
{
return false;
}
if( uid.size() < 3 ) return false;
if( uid[0] == '0' && uid[1] != '.' ) return false;
std::string::size_type i = 0;
for(; i < uid.size(); ++i)
{
if( uid[i] == '.' ) // if test is true we are guarantee that next char is valid (see previous check)
{
// check that next character is neither '0' (except single number) not '.'
if( uid[i+1] == '.' )
{
return false;
}
else if( uid[i+1] == '0' ) // character is guarantee to exist since '.' is not last char
{
// Need to check first if we are not at the end of string
if( i+2 != uid.size() && uid[i+2] != '.' )
{
return false;
}
}
}
else if ( !isdigit( (unsigned char)uid[i] ) )
{
return false;
}
}
// no error found !
return true;
}
} // end namespace gdcm
|