File Coverage

blib/lib/UUID/Object.pm
Criterion Covered Total %
statement 217 279 77.7
branch 61 90 67.7
condition 15 30 50.0
subroutine 45 53 84.9
pod 0 40 0.0
total 338 492 68.7


line stmt bran cond sub pod time code
1             package UUID::Object;
2              
3 10     10   13532 use strict;
  10         20  
  10         1733  
4 10     10   60 use warnings;
  10         20  
  10         314  
5 10     10   332 use 5.006;
  10         156  
  10         587  
6              
7             our $VERSION = '0.81';
8              
9 10     10   49 use Exporter;
  10         15  
  10         1346  
10             *import = \&Exporter::import;
11              
12             our @EXPORT = qw(
13             uuid_nil
14             uuid_ns_dns
15             uuid_ns_url
16             uuid_ns_oid
17             uuid_ns_x500
18             );
19              
20 10     10   13681 use POSIX qw( floor );
  10         90408  
  10         89  
21 10     10   26720 use MIME::Base64;
  10         9460  
  10         801  
22 10     10   78 use Carp;
  10         24  
  10         1485  
23              
24             use overload (
25 1     1   10 q{""} => sub { $_[0]->as_string },
26 10         202 q{<=>} => \&_compare,
27             q{cmp} => \&_compare,
28             fallback => 1,
29 10     10   30685 );
  10         32779  
30              
31             sub _compare {
32 13     13   346 my ($a, $b) = @_;
33 13         17 local $@;
34              
35 13 100       24 if (eval { $b->isa(__PACKAGE__) }) {
  13         74  
36 11         67 return $$a cmp $$b;
37             }
38              
39             # compare with bare string
40 2 50       6 if (! ref $b) {
41 2         4 eval {
42 2         7 $b = __PACKAGE__->create_from_string($b);
43             };
44 2 50       8 if (! $@) {
45 2         33 return $$a cmp $$b;
46             }
47             }
48              
49 0         0 return -1;
50             }
51              
52             sub clone {
53 6     6 0 847 my $self = shift;
54              
55 6         15 my $data = $$self;
56 6         11 my $result = \$data;
57 6         31 return bless $result, ref $self;
58             }
59              
60             sub create_nil {
61 81     81 0 124 my ($class) = @_;
62 81 100       268 $class = ref $class if ref $class;
63              
64 81         308 my $data = chr(0) x 16;
65 81         569 my $self = \$data;
66              
67 81         263 return bless $self, $class;
68             }
69              
70             sub create {
71 5     5 0 530 my $class = shift;
72 5         14 my $self = $class->create_nil();
73 5         19 $self->assign(@_);
74 5         13 return $self;
75             }
76             *new = *create;
77              
78             sub create_from_binary {
79 1     1 0 3 my ($class, $arg) = @_;
80 1         2 my $self = \$arg;
81 1         7 return bless $self, $class;
82             }
83              
84             sub create_from_binary_np {
85 0     0 0 0 my $class = shift;
86 0         0 my $self = $class->create_nil();
87 0         0 $self->assign_with_binary_np(@_);
88 0         0 return $self;
89             }
90              
91             sub create_from_hex {
92 1     1 0 3 my $class = shift;
93 1         5 my $self = $class->create_nil();
94 1         5 $self->assign_with_hex(@_);
95 1         3 return $self;
96             }
97              
98             sub create_from_string {
99 61     61 0 6736 my $class = shift;
100 61         165 my $self = $class->create_nil();
101 61         160 $self->assign_with_string(@_);
102 61         161 return $self;
103             }
104              
105             sub create_from_base64 {
106 2     2 0 18 my $class = shift;
107 2         9 my $self = $class->create_nil();
108 2         11 $self->assign_with_base64(@_);
109 2         11 return $self;
110             }
111              
112             sub create_from_base64_np {
113 0     0 0 0 my $class = shift;
114 0         0 my $self = $class->create_nil();
115 0         0 $self->assign_with_base64_np(@_);
116 0         0 return $self;
117             }
118              
119             sub create_from_hash {
120 1     1 0 21 my $class = shift;
121 1         6 my $self = $class->create_nil();
122 1         5 $self->assign_with_hash(@_);
123 1         4 return $self;
124             }
125              
126             sub assign {
127 9     9 0 34 my $self = shift;
128 9         15 my $arg = shift;
129              
130 9 100 33     35 if (! defined $arg) {
    50          
    50          
131 1         4 $self->assign_with_object($self->create_nil);
132             }
133 8         112 elsif (eval { $arg->isa(ref $self) }) {
134 0         0 $self->assign_with_object($arg);
135             }
136             elsif (! ref $arg && ! @_) {
137 8 100       98 if (length $arg == 16) {
    100          
    100          
    50          
138 2         5 $self->assign_with_binary($arg);
139             }
140             elsif ($arg =~ m{ \A [0-9a-f]{32} \z }ixmso) {
141 2         6 $self->assign_with_hex($arg);
142             }
143             elsif ($arg =~ m{ \A [0-9a-f]{8} (?: - [0-9a-f]{4} ){3}
144             - [0-9a-f]{12} \z }ixmso) {
145 2         6 $self->assign_with_string($arg);
146             }
147             elsif ($arg =~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) {
148 2         8 $self->assign_with_base64($arg);
149             }
150             else {
151 0         0 croak "invalid format";
152             }
153             }
154             else {
155 0         0 unshift @_, $arg;
156 0         0 $self->assign_with_hash(@_);
157             }
158              
159 9         29 return $self;
160             }
161              
162             sub assign_with_object {
163 3     3 0 7 my ($self, $arg) = @_;
164              
165 3 50       6 if (! eval { $arg->isa(ref $self) }) {
  3         33  
166 0         0 croak "argument must be UUID::Object";
167             }
168              
169 3         8 $$self = $$arg;
170              
171 3         9 return $self;
172             }
173              
174             sub assign_with_binary {
175 76     76 0 261 my ($self, $arg) = @_;
176              
177 76         574 $$self = q{} . $arg;
178              
179 76         162 return $self;
180             }
181              
182             sub assign_with_binary_np {
183 0     0 0 0 my ($self, $arg) = @_;
184              
185 0         0 substr $arg, 0, 4,
186             pack('N', unpack('I', substr($arg, 0, 4)));
187              
188 0         0 substr $arg, 4, 2,
189             pack('n', unpack('S', substr($arg, 4, 2)));
190              
191 0         0 substr $arg, 6, 2,
192             pack('n', unpack('S', substr($arg, 6, 2)));
193              
194 0         0 $$self = q{} . $arg;
195              
196 0         0 return $self;
197             }
198              
199             sub assign_with_hex {
200 68     68 0 245 my ($self, $arg) = @_;
201              
202 68 50       282 if ($arg !~ m{ \A [0-9a-f]{32} \z }ixmso) {
203 0         0 croak "invalid format";
204             }
205              
206 68         365 return $self->assign_with_binary(pack 'H*', $arg);
207             }
208              
209             sub assign_with_string {
210 64     64 0 108 my ($self, $arg) = @_;
211              
212 64         153 $arg =~ tr{-}{}d;
213              
214 64         164 return $self->assign_with_hex($arg);
215             }
216              
217             sub assign_with_base64 {
218 5     5 0 14 my ($self, $arg) = @_;
219              
220 5 50       32 if ($arg !~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) {
221 0         0 croak "invalid format";
222             }
223              
224 5         38 return $self->assign_with_binary(decode_base64($arg));
225             }
226              
227             sub assign_with_base64_np {
228 0     0 0 0 my ($self, $arg) = @_;
229              
230 0 0       0 if ($arg !~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) {
231 0         0 croak "invalid format";
232             }
233              
234 0         0 return $self->assign_with_binary_np(decode_base64($arg));
235             }
236              
237             sub assign_with_hash {
238 2     2 0 11 my $self = shift;
239 2 50 33     16 my $arg = @_ && ref $_[0] eq 'HASH' ? shift : { @_ };
240              
241 2 100       9 if (my $variant = delete $arg->{variant}) {
242 1         6 $self->variant($variant);
243             }
244              
245 2         4 foreach my $key (qw( version
246             time time_low time_mid time_hi
247             clk_seq node )) {
248 14 100       38 if (exists $arg->{$key}) {
249 6         23 $self->$key($arg->{$key});
250             }
251             }
252              
253 2         6 return $self;
254             }
255              
256             sub as_binary {
257 27     27 0 40 return ${$_[0]};
  27         82  
258             }
259              
260             sub as_binary_np {
261 0     0 0 0 my $self = shift;
262              
263 0         0 my $r = $self->as_binary;
264              
265 0         0 substr $r, 0, 4,
266             pack('I', unpack('N', substr($r, 0, 4)));
267              
268 0         0 substr $r, 4, 2,
269             pack('S', unpack('n', substr($r, 4, 2)));
270              
271 0         0 substr $r, 6, 2,
272             pack('S', unpack('n', substr($r, 6, 2)));
273              
274 0         0 return $r;
275             }
276              
277             sub as_hex {
278 1     1 0 3 return scalar unpack 'H*', ${$_[0]};
  1         10  
279             }
280              
281             sub as_string {
282 26     26 0 140 my $u = $_[0]->as_binary;
283 130         380 return join q{-}, map { unpack 'H*', $_ }
  130         8122  
284 26         61 map { substr $u, 0, $_, q{} }
285             ( 4, 2, 2, 2, 6 );
286             }
287              
288             sub as_base64 {
289 1     1 0 3 my $r = encode_base64(${$_[0]});
  1         19  
290              
291 1         6 $r =~ s{\s+}{}gxmso;
292              
293 1         6 return $r;
294             }
295              
296             sub as_base64_np {
297 0     0 0 0 my $data = ${$_[0]};
  0         0  
298              
299 0         0 substr $data, 0, 4,
300             pack('I', unpack('N', substr($data, 0, 4)));
301              
302 0         0 substr $data, 4, 2,
303             pack('S', unpack('n', substr($data, 4, 2)));
304              
305 0         0 substr $data, 6, 2,
306             pack('S', unpack('n', substr($data, 6, 2)));
307              
308 0         0 my $r = encode_base64($data);
309 0         0 $r =~ s{\s+}{}gxmso;
310              
311 0         0 return $r;
312             }
313              
314             sub as_hash {
315 0     0 0 0 my $self = shift;
316              
317 0         0 my $r = {};
318 0         0 foreach my $key (qw( variant version
319             time_low time_mid time_hi
320             clk_seq node )) {
321 0         0 $r->{$key} = $self->$key();
322             }
323              
324 0         0 return $r;
325             }
326              
327             sub as_urn {
328 0     0 0 0 my $self = shift;
329              
330 0         0 return 'urn:uuid:' . $self->as_string;
331             }
332              
333             sub variant {
334 49     49 0 129 my $self = shift;
335              
336 49 100       115 if (@_) {
337 3         7 my $var = shift;
338              
339 3 50 33     37 if ($var !~ m{^\d+$}o || ! grep { $var == $_ } qw( 0 2 6 7 4 )) {
  15         63  
340 0         0 croak "invalid parameter";
341             }
342 3 50       30 $var = 2 if $var == 4;
343              
344 3 50       20 if ($var == 0) {
    50          
345 0         0 substr $$self, 8, 1,
346             chr(ord(substr $$self, 8, 1) & 0x7f);
347             }
348             elsif ($var < 3) {
349 3         63 substr $$self, 8, 1,
350             chr(ord(substr $$self, 8, 1) & 0x3f | $var << 6);
351             }
352             else {
353 0         0 substr $$self, 8, 1,
354             chr(ord(substr $$self, 8, 1) & 0x1f | $var << 5);
355             }
356              
357 3         9 return $var;
358             }
359              
360 46         101 my $var = (ord(substr $$self, 8, 1) & 0xe0) >> 5;
361              
362 46         185 my %varmap = ( 1 => 0, 2 => 0, 3 => 0, 4 => 2, 5 => 2, );
363 46 100       109 if (exists $varmap{$var}) {
364 35         55 $var = $varmap{$var};
365             }
366              
367 46         231 return $var;
368             }
369              
370             sub version {
371 58     58 0 234 my $self = shift;
372              
373 58 100       130 if (@_) {
374 7         10 my $ver = shift;
375              
376 7 50 33     105 if ($ver !~ m{^\d+$}o || $ver < 0 || $ver > 15) {
      33        
377 0         0 croak "invalid parameter";
378             }
379              
380 7         30 substr $$self, 6, 1,
381             chr(ord(substr($$self, 6, 1)) & 0x0f | $ver << 4);
382              
383 7         17 return $ver;
384             }
385              
386 51         283 return (ord(substr($$self, 6, 1)) & 0xf0) >> 4;
387             }
388              
389             sub time_low {
390 6     6 0 11 my $self = shift;
391              
392 6 100       20 if (@_) {
393 3         5 my $arg = shift;
394              
395 3         10 substr $$self, 0, 4, pack('N', $arg);
396              
397 3         8 return $arg;
398             }
399              
400 3         14 return unpack 'N', substr($$self, 0, 4);
401             }
402              
403             sub time_mid {
404 9     9 0 488 my $self = shift;
405              
406 9 100       31 if (@_) {
407 4         8 my $arg = shift;
408              
409 4         14 substr $$self, 4, 2, pack('n', $arg);
410              
411 4         16 return $arg;
412             }
413              
414 5         31 return unpack 'n', substr($$self, 4, 2);
415             }
416              
417             sub time_hi {
418 6     6 0 17 my $self = shift;
419              
420 6 100       20 if (@_) {
421 3         5 my $arg = shift;
422              
423 3 50       9 if ($arg >= 0x1000) {
424 0         0 croak "invalid parameter";
425             }
426              
427 3         21 substr $$self, 6, 2,
428             pack('n', unpack('n', substr($$self, 6, 2)) & 0xf000
429             | $arg);
430              
431 3         8 return $arg;
432             }
433              
434 3         15 return unpack('n', substr($$self, 6, 2)) & 0x0fff;
435             }
436              
437             sub clk_seq {
438 3     3 0 9 my $self = shift;
439              
440 3         10 my $r = unpack 'n', substr($$self, 8, 2);
441              
442 3         8 my $v = $r >> 13;
443 3 50       268 my $w = ($v >= 6) ? 3 # 11x
    50          
444             : ($v >= 4) ? 2 # 10-
445             : 1; # 0--
446              
447 3         5 $w = 16 - $w;
448              
449 3 100       10 if (@_) {
450 2         6 my $arg = shift;
451              
452 2 50       8 if ($arg < 0) {
453 0         0 croak "invalid parameter";
454             }
455              
456 2         6 $arg &= ((1 << $w) - 1);
457              
458 2         8 substr $$self, 8, 2,
459             pack('n', $r & (0xffff - ((1 << $w) - 1)) | $arg);
460              
461 2         7 return $arg;
462             }
463              
464 1         7 return $r & ((1 << $w) - 1);
465             }
466              
467             sub node {
468 3     3 0 541 my $self = shift;
469              
470 3 100       13 if (@_) {
471 2         5 my $arg = shift;
472              
473 2 50       18 if (length $arg == 6) {
    50          
    50          
474             }
475             elsif (length $arg == 12) {
476 0         0 $arg = pack 'H*', $arg;
477             }
478             elsif (length $arg == 17) {
479 2 50       25 if ($arg !~ m{ \A (?: [0-9A-F]{2} ) ([-:]) [0-9A-F]{2}
480             (?: \1 [0-9A-F]{2} ){4}
481             \z }ixmso) {
482 0         0 croak "invalid parameter";
483             }
484              
485 2         8 $arg =~ tr{-:}{}d;
486 2         11 $arg = pack 'H*', $arg;
487             }
488             else {
489 0         0 croak "invalid parameter";
490             }
491              
492 2         7 substr $$self, 10, 6, $arg;
493             }
494              
495 3         17 return join q{:}, map { uc unpack 'H*', $_ }
  18         65  
496             split q{}, substr $$self, 10, 6;
497             }
498              
499             sub _set_time {
500 1     1   2 my ($self, $arg) = @_;
501              
502             # hi = time mod (1000000 / 0x100000000)
503 1         21 my $hi = floor($arg / 65536.0 / 512 * 78125);
504 1         4 $arg -= $hi * 512.0 * 65536 / 78125;
505            
506 1         4 my $low = floor($arg * 10000000.0 + 0.5);
507              
508             # MAGIC offset: 01B2-1DD2-13814000
509 1 50       5 if ($low < 0xec7ec000) {
510 1         3 $low += 0x13814000;
511             }
512             else {
513 0         0 $low -= 0xec7ec000;
514 0         0 $hi ++;
515             }
516              
517 1 50       3 if ($hi < 0x0e4de22e) {
518 1         2 $hi += 0x01b21dd2;
519             }
520             else {
521 0         0 $hi -= 0x0e4de22e; # wrap around
522             }
523              
524 1         6 $self->time_low($low);
525 1         3 $self->time_mid($hi & 0xffff);
526 1         4 $self->time_hi(($hi >> 16) & 0x0fff);
527              
528 1         2 return $self;
529             }
530              
531             sub time {
532 2     2 0 3 my $self = shift;
533              
534 2 100       6 if (@_) {
535 1         5 $self->_set_time(@_);
536             }
537              
538 2         16 my $low = $self->time_low;
539 2         5 my $hi = $self->time_mid | ($self->time_hi << 16);
540              
541             # MAGIC offset: 01B2-1DD2-13814000
542 2 50       7 if ($low >= 0x13814000) {
543 2         3 $low -= 0x13814000;
544             }
545             else {
546 0         0 $low += 0xec7ec000;
547 0         0 $hi --;
548             }
549              
550 2 50       6 if ($hi >= 0x01b21dd2) {
551 2         4 $hi -= 0x01b21dd2;
552             }
553             else {
554 0         0 $hi += 0x0e4de22e; # wrap around
555             }
556              
557 2         3 $low /= 10000000.0;
558 2         3 $hi /= 78125.0 / 512 / 65536; # / 1000000 * 0x100000000
559              
560 2         17 return $hi + $low;
561             }
562              
563             sub is_v1 {
564 5     5 0 20 my $self = shift;
565 5   66     24 return $self->variant == 2 && $self->version == 1;
566             }
567              
568             sub is_v2 {
569 5     5 0 12 my $self = shift;
570 5   66     10 return $self->variant == 2 && $self->version == 2;
571             }
572              
573             sub is_v3 {
574 5     5 0 16 my $self = shift;
575 5   66     12 return $self->variant == 2 && $self->version == 3;
576             }
577              
578             sub is_v4 {
579 5     5 0 10 my $self = shift;
580 5   66     12 return $self->variant == 2 && $self->version == 4;
581             }
582              
583             sub is_v5 {
584 5     5 0 11 my $self = shift;
585 5   66     12 return $self->variant == 2 && $self->version == 5;
586             }
587              
588             {
589             my %uuid_const;
590              
591             my %uuid_const_map = (
592             uuid_nil => '00000000-0000-0000-0000-000000000000',
593             uuid_ns_dns => '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
594             uuid_ns_url => '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
595             uuid_ns_oid => '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
596             uuid_ns_x500 => '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
597             );
598              
599             while (my ($id, $uuid) = each %uuid_const_map) {
600             my $sub
601             = sub {
602 5 50   5   30 if (! defined $uuid_const{$id}) {
603 5         23 $uuid_const{$id}
604             = __PACKAGE__->create_from_string($uuid);
605             }
606              
607 5         18 return $uuid_const{$id}->clone();
608             };
609              
610 10     10   48501 no strict 'refs';
  10         24  
  10         1012  
611             *{__PACKAGE__ . '::' . $id} = $sub;
612             }
613             }
614              
615             1;
616             __END__