File Coverage

UUID.xs
Criterion Covered Total %
statement 213 232 91.8
branch 56 100 56.0
condition n/a
subroutine n/a
pod n/a
total 269 332 81.0


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #include "UUID.h"
5              
6             #if defined __BEOS__ || defined __HAIKU__
7             # undef bool
8             # include
9             #endif
10              
11             #ifdef USE_ITHREADS
12             # define DU_THREADSAFE 1
13             #else
14             # define DU_THREADSAFE 0
15             #endif
16              
17             #if DU_THREADSAFE
18              
19             # define pPTBL pTHX
20             # define pPTBL_ pTHX_
21             # define aPTBL aTHX
22             # define aPTBL_ aTHX_
23              
24             # define PTABLE_VAL_FREE(V) ((void) (V))
25              
26             # include "ptable.h"
27              
28             # define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
29              
30             static ptable *instances;
31             static perl_mutex instances_mutex;
32              
33             static void inc(pTHX_ ptable_ent *ent, void *ud) {
34             UV count = PTR2UV(ent->val);
35             PERL_UNUSED_VAR(ud);
36             ptable_store(instances, ent->key, (void *)++count);
37             }
38              
39             #endif
40              
41             static perl_uuid_t NameSpace_DNS = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */
42             0x6ba7b810,
43             0x9dad,
44             0x11d1,
45             0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
46             };
47              
48             static perl_uuid_t NameSpace_URL = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */
49             0x6ba7b811,
50             0x9dad,
51             0x11d1,
52             0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
53             };
54              
55             static perl_uuid_t NameSpace_OID = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */
56             0x6ba7b812,
57             0x9dad,
58             0x11d1,
59             0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
60             };
61              
62             static perl_uuid_t NameSpace_X500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */
63             0x6ba7b814,
64             0x9dad,
65             0x11d1,
66             0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
67             };
68              
69 16           static void format_uuid_v1(
70             perl_uuid_t *uuid,
71             unsigned16 clock_seq,
72             perl_uuid_time_t timestamp,
73             uuid_node_t node
74             ) {
75 16           uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF);
76 16           uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF);
77 16           uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) &
78             0x0FFF);
79              
80 16           uuid->time_hi_and_version |= (1 << 12);
81 16           uuid->clock_seq_low = clock_seq & 0xFF;
82 16           uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8;
83 16           uuid->clock_seq_hi_and_reserved |= 0x80;
84 16           memcpy(&uuid->node, &node, sizeof uuid->node);
85 16           }
86              
87 16           static void get_current_time(perl_uuid_time_t * timestamp) {
88             perl_uuid_time_t time_now;
89             static perl_uuid_time_t time_last;
90             static unsigned16 uuids_this_tick;
91             static int inited = 0;
92              
93 16 100         if (!inited) {
94 1           get_system_time(&time_last);
95 1           uuids_this_tick = UUIDS_PER_TICK;
96 1           inited = 1;
97             };
98             while (1) {
99 17           get_system_time(&time_now);
100              
101 17 100         if (time_last != time_now) {
102 16           uuids_this_tick = 0;
103 16           time_last = time_now;
104 16           break;
105             };
106 1 50         if (uuids_this_tick < UUIDS_PER_TICK) {
107 0           uuids_this_tick++;
108 0           break;
109             };
110             };
111 16           *timestamp = time_now + uuids_this_tick;
112 16           }
113              
114 1           static unsigned16 true_random(void) {
115             static int inited = 0;
116             perl_uuid_time_t time_now;
117              
118 1 50         if (!inited) {
119 1           get_system_time(&time_now);
120 1           time_now = time_now/UUIDS_PER_TICK;
121 1           srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff));
122 1           inited = 1;
123             };
124 1           return (rand());
125             }
126              
127 1001           static void format_uuid_v3(
128             perl_uuid_t *uuid,
129             unsigned char hash[16]
130             ) {
131 1001           memcpy(uuid, hash, sizeof(perl_uuid_t));
132              
133 1001           uuid->time_low = ntohl(uuid->time_low);
134 1001           uuid->time_mid = ntohs(uuid->time_mid);
135 1001           uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version);
136              
137 1001           uuid->time_hi_and_version &= 0x0FFF;
138 1001           uuid->time_hi_and_version |= (3 << 12);
139 1001           uuid->clock_seq_hi_and_reserved &= 0x3F;
140 1001           uuid->clock_seq_hi_and_reserved |= 0x80;
141 1001           }
142              
143 19           static void get_system_time(perl_uuid_time_t *perl_uuid_time) {
144             #if defined __cygwin__ || defined __MINGW32__ || defined WIN32
145             /* ULARGE_INTEGER time; */
146             LARGE_INTEGER time;
147              
148             /* use QeryPerformanceCounter for +ms resolution - as per Paul Stodghill
149             GetSystemTimeAsFileTime((FILETIME *)&time); */
150             QueryPerformanceCounter(&time);
151             time.QuadPart +=
152             (unsigned __int64) (1000*1000*10) *
153             (unsigned __int64) (60 * 60 * 24) *
154             (unsigned __int64) (17+30+31+365*18+5);
155              
156             *perl_uuid_time = time.QuadPart;
157             #else
158             struct timeval tp;
159              
160 19           gettimeofday(&tp, (struct timezone *)0);
161 19           *perl_uuid_time = (tp.tv_sec * I64(10000000)) + (tp.tv_usec * I64(10)) +
162             I64(0x01B21DD213814000);
163             #endif
164 19           }
165              
166 3           static void get_random_info(unsigned char seed[16]) {
167             SV* ctx;
168             #if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__
169             typedef struct {
170             MEMORYSTATUS m;
171             SYSTEM_INFO s;
172             FILETIME t;
173             LARGE_INTEGER pc;
174             DWORD tc;
175             DWORD l;
176             char hostname[MAX_COMPUTERNAME_LENGTH + 1];
177             } randomness;
178             #else
179             typedef struct {
180             #if defined __BEOS__ || defined __HAIKU__
181             system_info sys_info;
182             #else
183             long hostid;
184             #endif
185             struct timeval t;
186             char hostname[257];
187             } randomness;
188             #endif
189             randomness r;
190              
191             #if defined __cygwin__ || defined __MINGW32__ || defined __MSWin32__
192             GlobalMemoryStatus(&r.m);
193             GetSystemInfo(&r.s);
194             GetSystemTimeAsFileTime(&r.t);
195             QueryPerformanceCounter(&r.pc);
196             r.tc = GetTickCount();
197             r.l = MAX_COMPUTERNAME_LENGTH + 1;
198             GetComputerName(r.hostname, &r.l );
199             #else
200             # if defined __BEOS__ || defined __HAIKU__
201             get_system_info(&r.sys_info);
202             # elif !defined(__ANDROID__)
203 3           r.hostid = gethostid();
204             # endif
205 3           gettimeofday(&r.t, (struct timezone *)0);
206 3           gethostname(r.hostname, 256);
207             #endif
208              
209 3           ctx = MD5Init();
210 3           MD5Update(ctx, sv_2mortal(newSVpv((char*)&r, sizeof(randomness))));
211 3           MD5Final(seed, ctx);
212 3           }
213              
214 1037           static SV* make_ret(const perl_uuid_t u, int type) {
215             char buf[BUFSIZ];
216             const unsigned char *from;
217             unsigned char *to;
218             STRLEN len;
219             int i;
220              
221 1037           memset(buf, 0x00, BUFSIZ);
222 1037           switch(type) {
223 18           case F_BIN:
224 18           memcpy(buf, &u, sizeof(perl_uuid_t));
225 18           len = sizeof(perl_uuid_t);
226 18           break;
227 1001           case F_STR:
228 1001           sprintf(buf, "%8.8X-%4.4X-%4.4X-%2.2X%2.2X-", (unsigned int)u.time_low, u.time_mid,
229 1001           u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low);
230 7007 100         for(i = 0; i < 6; i++ )
231 6006           sprintf(buf+strlen(buf), "%2.2X", u.node[i]);
232 1001           len = strlen(buf);
233 1001           break;
234 1           case F_HEX:
235 1           sprintf(buf, "0x%8.8X%4.4X%4.4X%2.2X%2.2X", (unsigned int)u.time_low, u.time_mid,
236 1           u.time_hi_and_version, u.clock_seq_hi_and_reserved, u.clock_seq_low);
237 7 100         for(i = 0; i < 6; i++ )
238 6           sprintf(buf+strlen(buf), "%2.2X", u.node[i]);
239 1           len = strlen(buf);
240 1           break;
241 17           case F_B64:
242 119 100         for(from = (const unsigned char*)&u, to = (unsigned char*)buf, i = sizeof(u); i > 0; i -= 3, from += 3) {
243 102           *to++ = base64[from[0]>>2];
244 102           switch(i) {
245 17           case 1:
246 17           *to++ = base64[(from[0]&0x03)<<4];
247 17           *to++ = '=';
248 17           *to++ = '=';
249 17           break;
250 0           case 2:
251 0           *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)];
252 0           *to++ = base64[(from[1]&0x0F)<<2];
253 0           *to++ = '=';
254 0           break;
255 85           default:
256 85           *to++ = base64[((from[0]&0x03)<<4) | ((from[1]&0xF0)>>4)];
257 85           *to++ = base64[((from[1]&0x0F)<<2) | ((from[2]&0xC0)>>6)];
258 85           *to++ = base64[(from[2]&0x3F)];
259             }
260             }
261 17           len = strlen(buf);
262 17           break;
263 0           default:
264 0           croak("invalid type: %d\n", type);
265             break;
266             }
267 1037           return sv_2mortal(newSVpv(buf,len));
268             }
269              
270 1004           static SV* MD5Init() {
271             SV* res;
272             int rcount;
273              
274 1004           dSP;
275              
276 1004           ENTER; SAVETMPS;
277              
278 1004 50         PUSHMARK(SP);
279 1004 50         XPUSHs(sv_2mortal(newSVpv("Digest::MD5", 0)));
280 1004           PUTBACK;
281              
282 1004           rcount = call_method("new", G_SCALAR);
283 1004           SPAGAIN;
284              
285 1004 50         if ( rcount != 1 )
286 0           croak("couldn't construct new Digest::MD5 object");
287              
288 1004           res = newSVsv(POPs);
289              
290 1004           PUTBACK;
291 1004 50         FREETMPS;
292 1004           LEAVE;
293              
294 1004           return res;
295             };
296              
297 2005           static void MD5Update( SV* ctx, SV* data ) {
298 2005           dSP;
299 2005           ENTER; SAVETMPS;
300              
301 2005 50         PUSHMARK(SP);
302 2005 50         XPUSHs(ctx);
303 2005 50         XPUSHs(data);
304 2005           PUTBACK;
305              
306 2005           call_method("add", G_DISCARD);
307 2005           SPAGAIN;
308              
309 2005           PUTBACK;
310 2005 50         FREETMPS;
311 2005           LEAVE;
312 2005           };
313              
314 1004           static void MD5Final( unsigned char hash[16], SV* ctx ) {
315             int rcount;
316             char* tmp;
317             STRLEN len;
318             SV* retval;
319 1004           dSP;
320              
321 1004           ENTER; SAVETMPS;
322              
323 1004 50         PUSHMARK(SP);
324 1004 50         XPUSHs(sv_2mortal(ctx));
325 1004           PUTBACK;
326              
327 1004           rcount = call_method("digest", G_SCALAR);
328 1004           SPAGAIN;
329              
330 1004 50         if ( rcount != 1 )
331 0           croak("Digest::MD5->digest hasn't returned a scalar");
332              
333 1004           retval = POPs;
334 1004           tmp = SvPV(retval, len);
335 1004 50         if ( len != 16 )
336 0           croak("Digest::MD5->digest returned not 16 bytes");
337              
338 1004           memcpy(hash, tmp, len);
339              
340 1004           PUTBACK;
341 1004 50         FREETMPS;
342 1004           LEAVE;
343 1004           };
344              
345             MODULE = Data::UUID PACKAGE = Data::UUID
346              
347             PROTOTYPES: DISABLE
348              
349             uuid_context_t*
350             new(class)
351             PREINIT:
352             FILE *fd;
353             unsigned char seed[16];
354             perl_uuid_time_t timestamp;
355             mode_t mask;
356 3           UV one = 1;
357             CODE:
358 3           RETVAL = (uuid_context_t *)PerlMemShared_malloc(sizeof(uuid_context_t));
359              
360 3           get_random_info(seed);
361 3           seed[0] |= 0x80;
362 3           memcpy(&(RETVAL->nodeid), seed, sizeof(uuid_node_t));
363              
364 3           errno = 0;
365             #if DU_THREADSAFE
366             MUTEX_LOCK(&instances_mutex);
367             ptable_store(instances, RETVAL, INT2PTR(void *, one));
368             MUTEX_UNLOCK(&instances_mutex);
369             #endif
370             OUTPUT:
371             RETVAL
372              
373             void
374             create(self)
375             uuid_context_t *self;
376             ALIAS:
377             Data::UUID::create_bin = F_BIN
378             Data::UUID::create_str = F_STR
379             Data::UUID::create_hex = F_HEX
380             Data::UUID::create_b64 = F_B64
381             PREINIT:
382             perl_uuid_time_t timestamp;
383             unsigned16 clockseq;
384             perl_uuid_t uuid;
385             FILE *fd;
386             mode_t mask;
387             PPCODE:
388 16           clockseq = self->state.cs;
389 16           get_current_time(×tamp);
390 16 50         if ( self->state.ts == I64(0) ||
391 16 100         memcmp(&(self->nodeid), &(self->state.node), sizeof(uuid_node_t)))
392 1           clockseq = true_random();
393 15 50         else if (timestamp <= self->state.ts)
394 0           clockseq++;
395              
396 16           format_uuid_v1(&uuid, clockseq, timestamp, self->nodeid);
397 16           self->state.node = self->nodeid;
398 16           self->state.ts = timestamp;
399 16           self->state.cs = clockseq;
400 16           ST(0) = make_ret(uuid, ix);
401 16           XSRETURN(1);
402              
403             void
404             create_from_name(self,nsid,name)
405             uuid_context_t *self;
406             perl_uuid_t *nsid;
407             SV *name;
408             ALIAS:
409             Data::UUID::create_from_name_bin = F_BIN
410             Data::UUID::create_from_name_str = F_STR
411             Data::UUID::create_from_name_hex = F_HEX
412             Data::UUID::create_from_name_b64 = F_B64
413             PREINIT:
414             SV *ctx;
415             unsigned char hash[16];
416             perl_uuid_t net_nsid;
417             perl_uuid_t uuid;
418             PPCODE:
419 1001           net_nsid = *nsid;
420 1001           net_nsid.time_low = htonl(net_nsid.time_low);
421 1001           net_nsid.time_mid = htons(net_nsid.time_mid);
422 1001           net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version);
423              
424 1001           ctx = MD5Init();
425 1001           MD5Update(ctx, sv_2mortal(newSVpv((char*)&net_nsid, sizeof(perl_uuid_t))));
426 1001           MD5Update(ctx, name);
427 1001           MD5Final(hash, ctx);
428              
429 1001           format_uuid_v3(&uuid, hash);
430 1001           ST(0) = make_ret(uuid, ix);
431 1001           XSRETURN(1);
432              
433             int
434             compare(self,u1,u2)
435             uuid_context_t *self;
436             perl_uuid_t *u1;
437             perl_uuid_t *u2;
438             PREINIT:
439             int i;
440             CODE:
441 2           RETVAL = 0;
442 2 50         CHECK(u1->time_low, u2->time_low);
    0          
443 2 50         CHECK(u1->time_mid, u2->time_mid);
    0          
444 2 50         CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
    0          
445 2 50         CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
    0          
446 2 50         CHECK(u1->clock_seq_low, u2->clock_seq_low);
    0          
447 14 100         for (i = 0; i < 6; i++) {
448 12 50         if (u1->node[i] < u2->node[i])
449 0           RETVAL = -1;
450 12 50         if (u1->node[i] > u2->node[i])
451 0           RETVAL = 1;
452             }
453             OUTPUT:
454             RETVAL
455              
456             void
457             to_string(self,uuid)
458             uuid_context_t *self;
459             perl_uuid_t *uuid;
460             ALIAS:
461             Data::UUID::to_hexstring = F_HEX
462             Data::UUID::to_b64string = F_B64
463             PPCODE:
464 18 50         ST(0) = make_ret(*uuid, ix ? ix : F_STR);
465 18           XSRETURN(1);
466              
467             void
468             from_string(self,str)
469             uuid_context_t *self;
470             char *str;
471             ALIAS:
472             Data::UUID::from_hexstring = F_HEX
473             Data::UUID::from_b64string = F_B64
474             PREINIT:
475             perl_uuid_t uuid;
476             char *from, *to;
477             int c;
478             unsigned int i;
479             unsigned char buf[4];
480             PPCODE:
481 2           switch(ix) {
482 1           case F_BIN:
483             case F_STR:
484             case F_HEX:
485 1           from = str;
486 1           memset(&uuid, 0x00, sizeof(perl_uuid_t));
487 1 50         if ( from[0] == '0' && from[1] == 'x' )
    50          
488 1           from += 2;
489 17 100         for (i = 0; i < sizeof(perl_uuid_t); i++) {
490 16 50         if (*from == '-')
491 0           from++;
492 16 50         if (sscanf(from, "%2x", &c) != 1)
493 0           croak("from_string(%s) failed...\n", str);
494 16           ((unsigned char*)&uuid)[i] = (unsigned char)c;
495 16           from += 2;
496             }
497 1           uuid.time_low = ntohl(uuid.time_low);
498 1           uuid.time_mid = ntohs(uuid.time_mid);
499 1           uuid.time_hi_and_version = ntohs(uuid.time_hi_and_version);
500 1           break;
501 1           case F_B64:
502 1           from = str; to = (char*)&uuid;
503 6 50         while(from < (str + strlen(str))) {
504 6           i = 0; memset(buf, 254, 4);
505             do {
506 24           c = index64[(int)*from++];
507 24 50         if (c != 255) buf[i++] = (unsigned char)c;
508 24 100         if (from == (str + strlen(str)))
509 1           break;
510 23 100         } while (i < 4);
511              
512 6 50         if (buf[0] == 254 || buf[1] == 254)
    50          
513             break;
514 6           *to++ = (buf[0] << 2) | ((buf[1] & 0x30) >> 4);
515              
516 6 100         if (buf[2] == 254) break;
517 5           *to++ = ((buf[1] & 0x0F) << 4) | ((buf[2] & 0x3C) >> 2);
518              
519 5 50         if (buf[3] == 254) break;
520 5           *to++ = ((buf[2] & 0x03) << 6) | buf[3];
521             }
522 1           break;
523 0           default:
524 0           croak("invalid type %d\n", ix);
525             break;
526             }
527 2           ST(0) = make_ret(uuid, F_BIN);
528 2           XSRETURN(1);
529              
530             #if DU_THREADSAFE
531              
532             void
533             CLONE(klass)
534             CODE:
535             MUTEX_LOCK(&instances_mutex);
536             ptable_walk(instances, inc, instances);
537             MUTEX_UNLOCK(&instances_mutex);
538              
539             #endif
540              
541             void
542             DESTROY(self)
543             uuid_context_t *self;
544             PREINIT:
545             #if DU_THREADSAFE
546             UV count;
547             #endif
548             FILE *fd;
549             mode_t mask;
550             CODE:
551             #if DU_THREADSAFE
552             MUTEX_LOCK(&instances_mutex);
553             count = PTR2UV(ptable_fetch(instances, self));
554             count--;
555             ptable_store(instances, self, (void *)count);
556             MUTEX_UNLOCK(&instances_mutex);
557             if (count == 0) {
558             #endif
559 3           PerlMemShared_free(self);
560             #if DU_THREADSAFE
561             }
562             #endif
563              
564             BOOT:
565             {
566 4           HV *stash = gv_stashpv("Data::UUID", 0);
567 4           STRLEN len = sizeof(perl_uuid_t);
568             #if DU_THREADSAFE
569             instances = ptable_new();
570             MUTEX_INIT(&instances_mutex);
571             #endif
572 4           newCONSTSUB(stash, "NameSpace_DNS", newSVpv((char *)&NameSpace_DNS, len));
573 4           newCONSTSUB(stash, "NameSpace_URL", newSVpv((char *)&NameSpace_URL, len));
574 4           newCONSTSUB(stash, "NameSpace_OID", newSVpv((char *)&NameSpace_OID, len));
575 4           newCONSTSUB(stash, "NameSpace_X500", newSVpv((char *)&NameSpace_X500, len));
576             }