root/trunk/tango/scrapple/util/uuid/Uuid.d

Revision 58, 18.4 kB (checked in by maxter, 8 months ago)

Fixed the version setter's parameter.

Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2008 Max Samukha. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         authorS:        Max Samukha
8
9         This module defines a 128-bit Universally Unique Identifier structure
10         conforming to Proposed Stardard RFC-4122.
11
12         Examples:
13
14 ---------------------
15 Uuid uuid = "24bb3c5e-988b-4833-9d9a-77bf84b3284c";
16
17 auto uuid2 = Uuid("24bb3c5e-988b-4833-9d9a-77bf84b3284c");
18
19 char[Uuid.StringLength] buf;
20 uuid.toString(buf);
21
22 Stdout(uuid).newline;
23
24 // Convert to an array of bytes for sending over a network
25 ubyte[Uuid.StringLength] ubytes;
26 uuid.toOctets(ubytes);
27 ---------------------
28
29
30 *******************************************************************************/
31 module tango.scrapple.util.uuid.Uuid;
32
33 import tango.core.Exception;
34
35 private
36 {
37     const char[] InvalidUuidMsg = "Invalid UUID string format";
38     const char[] Digits = "0123456789abcdef";
39 }
40
41 private bool hexToUbyte(char[] hex, out ubyte ub)
42 {
43     if (hex.length != 2)
44         return false;
45
46     char c1 = hex[0];
47     char c2 = hex[1];
48
49     if (c1 >= 'A' && c1 <='F')
50         c1 += 32;
51     if (c2 >= 'A' && c2 <='F')
52         c2 += 32;
53
54     int cCount;
55
56     foreach (i, d; Digits)
57     {
58         if (d == c1)
59         {
60             ub = ub | i << 4;
61             cCount++;
62         }
63
64         if (d == c2)
65         {
66             ub |= i;
67             cCount++;
68         }
69
70         if (cCount == 2)
71             break;
72     }
73
74     return cCount == 2;
75 }
76
77 private void ubyteToHex(ubyte ub, char[] hex)
78 {
79     assert (hex.length == 2);
80
81     hex[0] = Digits[ub >> 4];
82     hex[1] = Digits[ub & 0x0F];
83 }
84
85 /*****************************************************************************************
86    The UUID variants. The UUID variant determines the layout of the UUID.
87 *****************************************************************************************/
88 enum UuidVariant : ubyte
89 {
90     /// Reserved, NCS backward compatibility.
91     Ncs,
92     /// The UUID is compatible with RFC-4122.
93     Standard,
94     /// Reserved, _Microsoft backward compatibility.
95     Microsoft,
96     /// Reserved for future definition.
97     Future
98 }
99
100 /*****************************************************************************************
101     Versions of a standard UUID.
102 *****************************************************************************************/
103 enum UuidVersion : ubyte
104 {
105     /// The time-based version.
106     TimeBased = 1,
107     /// The DCE security version, with embedded POSIX UIDs.
108     Dce = 2,
109     /// The name-based version that uses MD5 hashing.
110     NameBasedMd5 = 3,
111     /// The randomly or pseudo-randomly generated version.
112     Random = 4,
113     /// The name-based version specified that uses SHA1 hashing.
114     NameBasedSha1 = 5
115 }
116
117 /*****************************************************************************************
118     The UUID structure.
119 *****************************************************************************************/
120 struct Uuid
121 {
122     align(1):
123
124     /// The low field of the timestamp.
125     uint timeLow;
126     /// The middle field of the timestamp.
127     ushort timeMid;
128     /// The high field of the timestamp multiplexed with the version number.
129     ushort versionTimeHigh;
130     /// The high field of the clock sequence multiplexed with the variant.
131     ubyte variantClockSeqHigh;
132     /// The low field of the clock sequence.
133     ubyte clockSeqLow;
134     /// The spatially unique node identifier.
135     ubyte[6] node;
136
137     enum : size_t
138     {
139         /*****************************************************************************************
140             The length of the string representation of a UUID (36 characters).
141         *****************************************************************************************/
142         StringLength = 36
143     }
144
145     /*****************************************************************************************
146         Changes the byte order of the UUID fields.
147     *****************************************************************************************/
148     void swap()
149     {
150         ubyte octet;
151         ubyte* octets = cast(ubyte*)this;
152
153         //timeLow
154         octet = octets[3];
155         octets[3] = octets[0];
156         octets[0] = octet;
157         octet = octets[2];
158         octets[2] = octets[1];
159         octets[1] = octet;
160
161         //timeMid
162         octet = octets[5];
163         octets[5] = octets[4];
164         octets[4] = octet;
165
166         //verAndTimeHi
167         octet = octets[7];
168         octets[7] = octets[6];
169         octets[6] = octet;
170     }
171
172     /*****************************************************************************************
173         Gets the UUID _variant.
174     *****************************************************************************************/
175     UuidVariant variant()
176     {
177         if ((variantClockSeqHigh & 0b1000_0000) == 0)
178             return UuidVariant.Ncs;
179         if ((variantClockSeqHigh & 0b1100_0000) == 0b1000_0000)
180             return UuidVariant.Standard;
181         if ((variantClockSeqHigh & 0b1110_0000) == 0b1100_0000)
182             return UuidVariant.Microsoft;
183
184         return UuidVariant.Future;
185     }
186
187     /*****************************************************************************************
188         Sets the UUID _variant.
189
190         Params:
191             v  = UUID _variant.
192     *****************************************************************************************/
193     UuidVariant variant(UuidVariant v)
194     {
195         if (v == UuidVariant.Ncs)
196             variantClockSeqHigh &= 0b0111_1111;
197         else if (v == UuidVariant.Standard)
198             variantClockSeqHigh = (variantClockSeqHigh | 0b1100_0000) & 0b1011_1111;
199         else if (v == UuidVariant.Microsoft)
200             variantClockSeqHigh = (variantClockSeqHigh | 0b1110_0000) & 0b1101_1111;
201         else if (v == UuidVariant.Future)
202             variantClockSeqHigh = (variantClockSeqHigh | 0b1110_0000) & 0b1111_1111;
203         else
204             assert(false, "Unknown UUID variant.");
205
206         return v;
207     }
208
209     /*****************************************************************************************
210         Gets the UUID version.
211     *****************************************************************************************/
212     UuidVersion ver()
213     {
214         return cast(UuidVersion)(versionTimeHigh >> 12);
215     }
216
217     /*****************************************************************************************
218         Sets the UUID version.
219
220         Params:
221             v  = UUID version.
222     *****************************************************************************************/
223     UuidVersion ver(UuidVersion v)
224     {
225         versionTimeHigh = (versionTimeHigh & 0x0FFF) | (v << 12);
226         return v;
227     }
228
229     /*****************************************************************************************
230         Gets the string representation of this UUID.
231     *****************************************************************************************/
232     char[] toString()
233     {
234         return toString(null);
235     }
236
237     /*****************************************************************************************
238         Fills buf with the string representation of this UUID. If the length of buf is zero,
239         allocates a new buffer.
240
241         Returns:
242             The UUID string.
243
244         Params:
245             buf     = Buffer to recieve the UUID string.
246
247         Throws: IllegalArgumentException if the length of the buffer is not zero and
248             is less than 36 characters.
249     *****************************************************************************************/
250     char[] toString(char[] buf)
251     {
252         if (!buf.length)
253             buf = new char[36];
254         else if (buf.length < 36)
255             throw new IllegalArgumentException("Supplied buffer is too small.");
256
257         version (LittleEndian)
258         {
259             Uuid temp = *this;
260             temp.swap();
261             ubyte* octets = cast(ubyte*)&temp;
262         }
263         else
264             ubyte* octets = cast(ubyte*)this;
265
266         size_t cIdx, octIdx;
267         while (cIdx < 36)
268         {
269             if (cIdx == 8 || cIdx == 13 || cIdx == 18 || cIdx == 23)
270             {
271                 buf[cIdx] = '-';
272                 cIdx++;
273                 continue;
274             }
275
276             ubyteToHex(octets[octIdx], buf[cIdx..cIdx + 2]);
277             cIdx += 2;
278             octIdx++;
279         }
280
281         return buf;
282     }
283
284     /*****************************************************************************************
285         Gets the URN representation of this UUID.
286
287         Params:
288             buf         = Buffer to receive the result. If the length of buf is zero,
289                           a new buffer is dynamically allocated.
290         Returns:
291             Reference to the buffer.
292
293         Throws: IllegalArgumentException if the length of the supplied buffer is not zero and
294             not big enough for the URN (45 chars).
295     *****************************************************************************************/
296     char[] toUrn(char[] buf = null)
297     {
298         char[] urn;
299
300         if (!buf.length)
301             buf = new char[45];
302         else if (buf.length < 45)
303             throw new IllegalArgumentException("Supplied buffer is too small.");
304
305         buf[0..9] = "urn:uuid:";
306         toString(urn[9..$]);
307     }
308
309     /*****************************************************************************************
310       Fills the supplied buffer with the UUID _octets arranged in network order. A new array is
311       created if the length of the buffer is zero.
312
313       Params:
314             octets           = Buffer to recieve the result,
315
316       Returns: Reference to the array.
317
318       Throws: IllegalArgumentException if the length of the supplied array is not zero and not big enough
319               for the UUID _octets (16 bytes).
320     *****************************************************************************************/
321     ubyte[] toOctets(ubyte[] octets = null)
322     {
323         Uuid* pUuid;
324
325         if (!octets.length)
326             pUuid = new Uuid;
327         else if (octets.length >= 16)
328             pUuid = cast(Uuid*)octets.ptr;
329         else
330             throw new IllegalArgumentException("Supplied array is too small");
331
332         *pUuid = *this;
333
334         version (LittleEndian)
335             pUuid.swap();
336
337         return (cast(ubyte*)pUuid)[0..16];
338     }
339
340     /*****************************************************************************************
341         Parses the UUID string and assigns the result to this UUID.
342         Params:
343             str         = UUID string.
344
345 Examples:
346 ---------
347 Uuid uuid;
348 uuid = "24bb3c5e-988b-4833-9d9a-77bf84b3284c";
349 ---------
350     *****************************************************************************************/
351     Uuid opAssign(char[] str)
352     {
353         *this = opCall(str);
354         return *this;
355     }
356
357     /*****************************************************************************************
358         Assigns the UUID _octets arranged in network byte order to this UUID .
359         Params:
360             octets = UUID _octets in network byte order.
361     *****************************************************************************************/
362     Uuid opAssign(ubyte[] octets)
363     {
364         *this = opCall(octets);
365         return *this;
366     }
367
368
369     /*****************************************************************************************
370         Creates a UUID from UUID field values.
371     *****************************************************************************************/
372     static Uuid opCall(uint timeLow, ushort timeMid, ushort versionTimeHigh,
373         ubyte variantClockSeqHigh, ubyte clockSeqLow, ubyte[6] node)
374     {
375         Uuid uuid;
376
377         uuid.timeLow = timeLow;
378         uuid.timeMid = timeMid;
379         uuid.versionTimeHigh = versionTimeHigh;
380         uuid.variantClockSeqHigh = variantClockSeqHigh;
381         uuid.clockSeqLow = clockSeqLow;
382         uuid.node[] = node;
383
384         return uuid;
385     }
386
387     /*****************************************************************************************
388         Creates a new UUID from the supplied string.
389
390         Throws: IllegalArgumentException if the input string is not a valid
391             UUID string.
392     *****************************************************************************************/
393     static Uuid opCall(char[] str)
394     {
395         Uuid result;
396
397         if (str.length != 36 || str[8] != '-' || str[13] != '-' ||
398                 str[18] != '-' || str[23] != '-')
399         {
400             throw new IllegalArgumentException(InvalidUuidMsg);
401         }
402
403         ubyte* octets = cast(ubyte*)&result;
404
405         uint cIdx, octIdx;
406         ubyte ub;
407         while (octIdx < 16)
408         {
409             if (cIdx == 8 || cIdx == 13 || cIdx == 18 || cIdx == 23)
410             {
411                 cIdx++;
412                 continue;
413             }
414
415             if (!hexToUbyte(str[cIdx..cIdx + 2], ub))
416                 throw new IllegalArgumentException(InvalidUuidMsg);
417
418             octets[octIdx] = ub;
419
420             cIdx += 2;
421             octIdx++;
422         }
423
424         version (LittleEndian)
425             result.swap();
426
427         return result;
428     }
429
430     /*****************************************************************************************
431         Creates a UUID from _octets arranged in network byte order.
432         This is the reverse of toOctets().
433
434         Params:
435             octets      = UUID _octets in network byte order.
436
437         Throws:
438             IllegalArgumentException if the length of octets is less than 16 bytes.
439     *****************************************************************************************/
440     static Uuid opCall(ubyte[] octets)
441     {
442         if (octets.length < 16)
443             throw new IllegalArgumentException("Number of supplied octets is less than 16.");
444
445         version (LittleEndian)
446         {
447             Uuid result = *(cast(Uuid*)octets.ptr);
448             result.swap();
449             return result;
450         }
451         else
452             return *(cast(Uuid*)octets.ptr);
453     }
454
455     /*****************************************************************************************
456         Returns the hash code of this UUID.
457     *****************************************************************************************/
458     int toHash()
459     {
460         return timeLow; // provides a decent distribution
461     }
462
463     /*****************************************************************************************
464         Compares this UUID with uuid.
465     *****************************************************************************************/
466     int opCmp(Uuid uuid)
467     {
468         if (timeLow < uuid.timeLow)
469             return -1;
470         else if (timeLow > uuid.timeLow)
471             return 1;
472
473         if (timeMid < uuid.timeMid)
474             return -1;
475         else if (timeMid > uuid.timeMid)
476             return 1;
477
478         if (timeMid < uuid.timeMid)
479             return -1;
480         else if (timeMid > uuid.timeMid)
481             return 1;
482
483         if (versionTimeHigh < uuid.versionTimeHigh)
484             return -1;
485         else if (versionTimeHigh > uuid.versionTimeHigh)
486             return 1;
487
488         ubyte* p = cast(ubyte*)this;
489         ubyte* pUuid = cast(ubyte*)&uuid;
490
491         for (uint i = 8; i < 16; i++)
492         {
493             if (p[i] < pUuid[i])
494                 return -1;
495             else if (p[i] > pUuid[i])
496                 return 1;
497         }
498
499         return 0;
500     }
501
502     /*****************************************************************************************
503         Returns true if all UUID fields are equal to zero.
504     *****************************************************************************************/
505     bool isNil()
506     {
507         ulong* ulongs = cast(ulong*)this;
508         return !(ulongs[0] || ulongs[1]);
509     }
510 }
511
512 debug (UuidUnitTest)
513 {
514     unittest
515     {
516         static assert(Uuid.sizeof == 16);
517         ubyte ub;
518
519         hexToUbyte("AF", ub);
520         assert(ub == 0xAF);
521
522         char[] hex = new char[2];
523         ubyteToHex(ub, hex);
524         assert(hex == "af");
525
526         const char[] testUuidStr = "24bb3c5e-988b-4833-9d9a-77bf84b3284c";
527         const char[] testUuidStr2 = "24bb3c5e-988b-4833-9d9a-80bf84b3284c";
528
529         Uuid uuid;
530         assert(uuid.isNil);
531
532         uuid = testUuidStr;
533         assert(!uuid.isNil);
534
535         char[36] buffer;
536         uuid.toString(buffer);
537         assert(buffer == testUuidStr);
538
539         uuid.ver = UuidVersion.TimeBased;
540         assert(uuid.ver == UuidVersion.TimeBased);
541
542         auto uuid2 = Uuid(uuid.toString);
543         assert(uuid == uuid2);
544
545         uuid = testUuidStr;
546         assert(uuid.toString == testUuidStr);
547
548         ubyte[] octets = uuid.toOctets;
549
550         assert(octets[0] == 0x24);
551         assert(octets[1] == 0xbb);
552         assert(octets[2] == 0x3c);
553         assert(octets[3] == 0x5e);
554         assert(octets[4] == 0x98);
555         assert(octets[5] == 0x8b);
556         assert(octets[6] == 0x48);
557         assert(octets[7] == 0x33);
558         assert(octets[8] == 0x9d);
559         assert(octets[9] == 0x9a);
560         assert(octets[10] == 0x77);
561         assert(octets[11] == 0xbf);
562         assert(octets[12] == 0x84);
563         assert(octets[13] == 0xb3);
564         assert(octets[14] == 0x28);
565         assert(octets[15] == 0x4c);
566
567         uuid2 = cast(Uuid)octets;
568         assert(uuid == uuid2);
569
570         uuid2 = testUuidStr2;
571         assert(uuid2 > uuid && uuid < uuid2);
572
573         uuid = uuid2;
574         auto v = uuid.variant;
575
576         uuid.variant = UuidVariant.Future;
577         assert(uuid.variant == UuidVariant.Future);
578
579         uuid.variant = UuidVariant.Microsoft;
580         assert(uuid.variant == UuidVariant.Microsoft);
581
582         uuid.variant = UuidVariant.Standard;
583         assert(uuid.variant == UuidVariant.Standard);
584
585         uuid.variant = UuidVariant.Ncs;
586         assert(uuid.variant == UuidVariant.Ncs);
587
588         uuid.variant = v;
589         assert (uuid == uuid2);
590     }
591 }
592
593 debug (UuidStandalone)
594 {
595     import
596         tango.scrapple.util.uuid.NativeUuidGen,
597         tango.scrapple.util.uuid.RandomUuidGen,
598         tango.scrapple.util.uuid.NameUuidGen;
599
600     void main()
601     {
602     }
603 }
Note: See TracBrowser for help on using the browser.