File Coverage

UUID.xs
Criterion Covered Total %
statement 237 253 93.6
branch 66 114 57.8
condition n/a
subroutine n/a
pod n/a
total 303 367 82.5


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 18           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 18 100         if (!inited) {
94 3           get_system_time(&time_last);
95 3           uuids_this_tick = UUIDS_PER_TICK;
96 3           inited = 1;
97             };
98             while (1) {
99 26           get_system_time(&time_now);
100              
101 26 100         if (time_last != time_now) {
102 18           uuids_this_tick = 0;
103 18           time_last = time_now;
104 18           break;
105             };
106 8 50         if (uuids_this_tick < UUIDS_PER_TICK) {
107 0           uuids_this_tick++;
108 0           break;
109             };
110 8           };
111 18           *timestamp = time_now + uuids_this_tick;
112 18           }
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 30           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 30           gettimeofday(&tp, (struct timezone *)0);
161 30           *perl_uuid_time = (tp.tv_sec * I64(10000000)) + (tp.tv_usec * I64(10)) +
162             I64(0x01B21DD213814000);
163             #endif
164 30           }
165              
166 1           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 1           r.hostid = gethostid();
204             # endif
205 1           gettimeofday(&r.t, (struct timezone *)0);
206 1           gethostname(r.hostname, 256);
207             #endif
208              
209 1           ctx = MD5Init();
210 1           MD5Update(ctx, sv_2mortal(newSVpv((char*)&r, sizeof(randomness))));
211 1           MD5Final(seed, ctx);
212 1           }
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             case F_BIN:
224 18           memcpy(buf, &u, sizeof(perl_uuid_t));
225 18           len = sizeof(perl_uuid_t);
226 18           break;
227             case F_STR:
228 3003           sprintf(buf, "%8.8X-%4.4X-%4.4X-%2.2X%2.2X-", (unsigned int)u.time_low, u.time_mid,
229 3003           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             case F_HEX:
235 3           sprintf(buf, "0x%8.8X%4.4X%4.4X%2.2X%2.2X", (unsigned int)u.time_low, u.time_mid,
236 3           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             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             case 1:
246 17           *to++ = base64[(from[0]&0x03)<<4];
247 17           *to++ = '=';
248 17           *to++ = '=';
249 17           break;
250             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             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             default:
264 0           croak("invalid type: %d\n", type);
265             break;
266             }
267 1037           return sv_2mortal(newSVpv(buf,len));
268             }
269              
270 1002           static SV* MD5Init() {
271             SV* res;
272             int rcount;
273              
274 1002           dSP;
275              
276 1002           ENTER; SAVETMPS;
277              
278 1002 50         PUSHMARK(SP);
279 1002 50         XPUSHs(sv_2mortal(newSVpv("Digest::MD5", 0)));
280 1002           PUTBACK;
281              
282 1002           rcount = call_method("new", G_SCALAR);
283 1002           SPAGAIN;
284              
285 1002 50         if ( rcount != 1 )
286 0           croak("couldn't construct new Digest::MD5 object");
287              
288 1002           res = newSVsv(POPs);
289              
290 1002           PUTBACK;
291 1002 50         FREETMPS;
292 1002           LEAVE;
293              
294 1002           return res;
295             };
296              
297 2003           static void MD5Update( SV* ctx, SV* data ) {
298 2003           dSP;
299 2003           ENTER; SAVETMPS;
300              
301 2003 50         PUSHMARK(SP);
302 2003 50         XPUSHs(ctx);
303 2003 50         XPUSHs(data);
304 2003           PUTBACK;
305              
306 2003           call_method("add", G_DISCARD);
307 2003           SPAGAIN;
308              
309 2003           PUTBACK;
310 2003 50         FREETMPS;
311 2003           LEAVE;
312 2003           };
313              
314 1002           static void MD5Final( unsigned char hash[16], SV* ctx ) {
315             int rcount;
316             char* tmp;
317             STRLEN len;
318             SV* retval;
319 1002           dSP;
320              
321 1002           ENTER; SAVETMPS;
322              
323 1002 50         PUSHMARK(SP);
324 1002 50         XPUSHs(sv_2mortal(ctx));
325 1002           PUTBACK;
326              
327 1002           rcount = call_method("digest", G_SCALAR);
328 1002           SPAGAIN;
329              
330 1002 50         if ( rcount != 1 )
331 0           croak("Digest::MD5->digest hasn't returned a scalar");
332              
333 1002           retval = POPs;
334 1002 50         tmp = SvPV(retval, len);
335 1002 50         if ( len != 16 )
336 0           croak("Digest::MD5->digest returned not 16 bytes");
337              
338 1002           memcpy(hash, tmp, len);
339              
340 1002           PUTBACK;
341 1002 50         FREETMPS;
342 1002           LEAVE;
343 1002           };
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 3 100         if ((fd = fopen(UUID_STATE_NV_STORE, "rb"))) {
360 2           fread(&(RETVAL->state), sizeof(uuid_state_t), 1, fd);
361 2           fclose(fd);
362 2           get_current_time(×tamp);
363 2           RETVAL->next_save = timestamp;
364             }
365 3 100         if ((fd = fopen(UUID_NODEID_NV_STORE, "rb"))) {
366 2           pid_t *hate = (pid_t *) &(RETVAL->nodeid);
367 2           fread(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd );
368 2           fclose(fd);
369            
370 2           *hate += getpid();
371             } else {
372 1           get_random_info(seed);
373 1           seed[0] |= 0x80;
374 1           memcpy(&(RETVAL->nodeid), seed, sizeof(uuid_node_t));
375 1           mask = umask(_DEFAULT_UMASK);
376 1 50         if ((fd = fopen(UUID_NODEID_NV_STORE, "wb"))) {
377 1           fwrite(&(RETVAL->nodeid), sizeof(uuid_node_t), 1, fd);
378 1           fclose(fd);
379             };
380 1           umask(mask);
381             }
382 3           errno = 0;
383             #if DU_THREADSAFE
384             MUTEX_LOCK(&instances_mutex);
385             ptable_store(instances, RETVAL, INT2PTR(void *, one));
386             MUTEX_UNLOCK(&instances_mutex);
387             #endif
388             OUTPUT:
389             RETVAL
390              
391             void
392             create(self)
393             uuid_context_t *self;
394             ALIAS:
395             Data::UUID::create_bin = F_BIN
396             Data::UUID::create_str = F_STR
397             Data::UUID::create_hex = F_HEX
398             Data::UUID::create_b64 = F_B64
399             PREINIT:
400             perl_uuid_time_t timestamp;
401             unsigned16 clockseq;
402             perl_uuid_t uuid;
403             FILE *fd;
404             mode_t mask;
405             PPCODE:
406 16           clockseq = self->state.cs;
407 16           get_current_time(×tamp);
408 16 50         if ( self->state.ts == I64(0) ||
    100          
409 16           memcmp(&(self->nodeid), &(self->state.node), sizeof(uuid_node_t)))
410 1           clockseq = true_random();
411 15 50         else if (timestamp <= self->state.ts)
412 0           clockseq++;
413              
414 16           format_uuid_v1(&uuid, clockseq, timestamp, self->nodeid);
415 16           self->state.node = self->nodeid;
416 16           self->state.ts = timestamp;
417 16           self->state.cs = clockseq;
418 16 100         if (timestamp > self->next_save ) {
419 1           mask = umask(_DEFAULT_UMASK);
420 1 50         if((fd = fopen(UUID_STATE_NV_STORE, "wb"))) {
421 1           LOCK(fd);
422 1           fwrite(&(self->state), sizeof(uuid_state_t), 1, fd);
423 1           UNLOCK(fd);
424 1           fclose(fd);
425             }
426 1           umask(mask);
427 1           self->next_save = timestamp + (10 * 10 * 1000 * 1000);
428             }
429 16           ST(0) = make_ret(uuid, ix);
430 16           XSRETURN(1);
431              
432             void
433             create_from_name(self,nsid,name)
434             uuid_context_t *self;
435             perl_uuid_t *nsid;
436             SV *name;
437             ALIAS:
438             Data::UUID::create_from_name_bin = F_BIN
439             Data::UUID::create_from_name_str = F_STR
440             Data::UUID::create_from_name_hex = F_HEX
441             Data::UUID::create_from_name_b64 = F_B64
442             PREINIT:
443             SV *ctx;
444             unsigned char hash[16];
445             perl_uuid_t net_nsid;
446             perl_uuid_t uuid;
447             PPCODE:
448 1001           net_nsid = *nsid;
449 1001           net_nsid.time_low = htonl(net_nsid.time_low);
450 1001           net_nsid.time_mid = htons(net_nsid.time_mid);
451 1001           net_nsid.time_hi_and_version = htons(net_nsid.time_hi_and_version);
452              
453 1001           ctx = MD5Init();
454 1001           MD5Update(ctx, sv_2mortal(newSVpv((char*)&net_nsid, sizeof(perl_uuid_t))));
455 1001           MD5Update(ctx, name);
456 1001           MD5Final(hash, ctx);
457              
458 1001           format_uuid_v3(&uuid, hash);
459 1001           ST(0) = make_ret(uuid, ix);
460 1001           XSRETURN(1);
461              
462             int
463             compare(self,u1,u2)
464             uuid_context_t *self;
465             perl_uuid_t *u1;
466             perl_uuid_t *u2;
467             PREINIT:
468             int i;
469             CODE:
470 2           RETVAL = 0;
471 2 50         CHECK(u1->time_low, u2->time_low);
    0          
472 2 50         CHECK(u1->time_mid, u2->time_mid);
    0          
473 2 50         CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
    0          
474 2 50         CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
    0          
475 2 50         CHECK(u1->clock_seq_low, u2->clock_seq_low);
    0          
476 14 100         for (i = 0; i < 6; i++) {
477 12 50         if (u1->node[i] < u2->node[i])
478 0           RETVAL = -1;
479 12 50         if (u1->node[i] > u2->node[i])
480 0           RETVAL = 1;
481             }
482             OUTPUT:
483             RETVAL
484              
485             void
486             to_string(self,uuid)
487             uuid_context_t *self;
488             perl_uuid_t *uuid;
489             ALIAS:
490             Data::UUID::to_hexstring = F_HEX
491             Data::UUID::to_b64string = F_B64
492             PPCODE:
493 18 50         ST(0) = make_ret(*uuid, ix ? ix : F_STR);
494 18           XSRETURN(1);
495              
496             void
497             from_string(self,str)
498             uuid_context_t *self;
499             char *str;
500             ALIAS:
501             Data::UUID::from_hexstring = F_HEX
502             Data::UUID::from_b64string = F_B64
503             PREINIT:
504             perl_uuid_t uuid;
505             char *from, *to;
506             int c;
507             unsigned int i;
508             unsigned char buf[4];
509             PPCODE:
510 2           switch(ix) {
511             case F_BIN:
512             case F_STR:
513             case F_HEX:
514 1           from = str;
515 1           memset(&uuid, 0x00, sizeof(perl_uuid_t));
516 1 50         if ( from[0] == '0' && from[1] == 'x' )
    50          
517 1           from += 2;
518 17 100         for (i = 0; i < sizeof(perl_uuid_t); i++) {
519 16 50         if (*from == '-')
520 0           from++;
521 16 50         if (sscanf(from, "%2x", &c) != 1)
522 0           croak("from_string(%s) failed...\n", str);
523 16           ((unsigned char*)&uuid)[i] = (unsigned char)c;
524 16           from += 2;
525             }
526 1           uuid.time_low = ntohl(uuid.time_low);
527 1           uuid.time_mid = ntohs(uuid.time_mid);
528 1           uuid.time_hi_and_version = ntohs(uuid.time_hi_and_version);
529 1           break;
530             case F_B64:
531 1           from = str; to = (char*)&uuid;
532 6 50         while(from < (str + strlen(str))) {
533 6           i = 0; memset(buf, 254, 4);
534             do {
535 24           c = index64[(int)*from++];
536 24 50         if (c != 255) buf[i++] = (unsigned char)c;
537 24 100         if (from == (str + strlen(str)))
538 1           break;
539 23 100         } while (i < 4);
540              
541 6 50         if (buf[0] == 254 || buf[1] == 254)
    50          
542             break;
543 6           *to++ = (buf[0] << 2) | ((buf[1] & 0x30) >> 4);
544              
545 6 100         if (buf[2] == 254) break;
546 5           *to++ = ((buf[1] & 0x0F) << 4) | ((buf[2] & 0x3C) >> 2);
547              
548 5 50         if (buf[3] == 254) break;
549 5           *to++ = ((buf[2] & 0x03) << 6) | buf[3];
550             }
551 1           break;
552             default:
553 0           croak("invalid type %d\n", ix);
554             break;
555             }
556 2           ST(0) = make_ret(uuid, F_BIN);
557 2           XSRETURN(1);
558              
559             #if DU_THREADSAFE
560              
561             void
562             CLONE(klass)
563             CODE:
564             MUTEX_LOCK(&instances_mutex);
565             ptable_walk(instances, inc, instances);
566             MUTEX_UNLOCK(&instances_mutex);
567              
568             #endif
569              
570             void
571             DESTROY(self)
572             uuid_context_t *self;
573             PREINIT:
574             #if DU_THREADSAFE
575             UV count;
576             #endif
577             FILE *fd;
578             mode_t mask;
579             CODE:
580             #if DU_THREADSAFE
581             MUTEX_LOCK(&instances_mutex);
582             count = PTR2UV(ptable_fetch(instances, self));
583             count--;
584             ptable_store(instances, self, (void *)count);
585             MUTEX_UNLOCK(&instances_mutex);
586             if (count == 0) {
587             #endif
588 3           mask = umask(_DEFAULT_UMASK);
589 3 50         if ((fd = fopen(UUID_STATE_NV_STORE, "wb"))) {
590 3           LOCK(fd);
591 3           fwrite(&(self->state), sizeof(uuid_state_t), 1, fd);
592 3           UNLOCK(fd);
593 3           fclose(fd);
594             };
595 3           umask(mask);
596 3           PerlMemShared_free(self);
597             #if DU_THREADSAFE
598             }
599             #endif
600              
601             BOOT:
602             {
603 4           HV *stash = gv_stashpv("Data::UUID", 0);
604 4           STRLEN len = sizeof(perl_uuid_t);
605             #if DU_THREADSAFE
606             instances = ptable_new();
607             MUTEX_INIT(&instances_mutex);
608             #endif
609 4           newCONSTSUB(stash, "NameSpace_DNS", newSVpv((char *)&NameSpace_DNS, len));
610 4           newCONSTSUB(stash, "NameSpace_URL", newSVpv((char *)&NameSpace_URL, len));
611 4           newCONSTSUB(stash, "NameSpace_OID", newSVpv((char *)&NameSpace_OID, len));
612 4           newCONSTSUB(stash, "NameSpace_X500", newSVpv((char *)&NameSpace_X500, len));
613             }