package UUID::Object; use strict; use warnings; use 5.006; our $VERSION = '0.80'; use Exporter; *import = \&Exporter::import; our @EXPORT = qw( uuid_nil uuid_ns_dns uuid_ns_url uuid_ns_oid uuid_ns_x500 ); use POSIX qw( floor ); use MIME::Base64; use Carp; use overload ( q{""} => sub { $_[0]->as_string }, q{<=>} => \&_compare, q{cmp} => \&_compare, fallback => 1, ); sub _compare { my ($a, $b) = @_; return $$a cmp $$b; } sub clone { my $self = shift; my $data = $$self; my $result = \$data; return bless $result, ref $self; } sub create_nil { my ($class) = @_; $class = ref $class if ref $class; my $data = chr(0) x 16; my $self = \$data; return bless $self, $class; } sub create { my $class = shift; my $self = $class->create_nil(); $self->assign(@_); return $self; } *new = *create; sub create_from_binary { my ($class, $arg) = @_; my $self = \$arg; return bless $self, $class; } sub create_from_binary_np { my $class = shift; my $self = $class->create_nil(); $self->assign_with_binary_np(@_); return $self; } sub create_from_hex { my $class = shift; my $self = $class->create_nil(); $self->assign_with_hex(@_); return $self; } sub create_from_string { my $class = shift; my $self = $class->create_nil(); $self->assign_with_string(@_); return $self; } sub create_from_base64 { my $class = shift; my $self = $class->create_nil(); $self->assign_with_base64(@_); return $self; } sub create_from_base64_np { my $class = shift; my $self = $class->create_nil(); $self->assign_with_base64_np(@_); return $self; } sub create_from_hash { my $class = shift; my $self = $class->create_nil(); $self->assign_with_hash(@_); return $self; } sub assign { my $self = shift; my $arg = shift; if (! defined $arg) { $self->assign_with_object($self->create_nil); } elsif (eval { $arg->isa(ref $self) }) { $self->assign_with_object($arg); } elsif (! ref $arg && ! @_) { if (length $arg == 16) { $self->assign_with_binary($arg); } elsif ($arg =~ m{ \A [0-9a-f]{32} \z }ixmso) { $self->assign_with_hex($arg); } elsif ($arg =~ m{ \A [0-9a-f]{8} (?: - [0-9a-f]{4} ){3} - [0-9a-f]{12} \z }ixmso) { $self->assign_with_string($arg); } elsif ($arg =~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) { $self->assign_with_base64($arg); } else { croak "invalid format"; } } else { unshift @_, $arg; $self->assign_with_hash(@_); } return $self; } sub assign_with_object { my ($self, $arg) = @_; if (! eval { $arg->isa(ref $self) }) { croak "argument must be UUID::Object"; } $$self = $$arg; return $self; } sub assign_with_binary { my ($self, $arg) = @_; $$self = q{} . $arg; return $self; } sub assign_with_binary_np { my ($self, $arg) = @_; substr $arg, 0, 4, pack('N', unpack('I', substr($arg, 0, 4))); substr $arg, 4, 2, pack('n', unpack('S', substr($arg, 4, 2))); substr $arg, 6, 2, pack('n', unpack('S', substr($arg, 6, 2))); $$self = q{} . $arg; return $self; } sub assign_with_hex { my ($self, $arg) = @_; if ($arg !~ m{ \A [0-9a-f]{32} \z }ixmso) { croak "invalid format"; } return $self->assign_with_binary(pack 'H*', $arg); } sub assign_with_string { my ($self, $arg) = @_; $arg =~ tr{-}{}d; return $self->assign_with_hex($arg); } sub assign_with_base64 { my ($self, $arg) = @_; if ($arg !~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) { croak "invalid format"; } return $self->assign_with_binary(decode_base64($arg)); } sub assign_with_base64_np { my ($self, $arg) = @_; if ($arg !~ m{ \A [+/0-9A-Za-z]{22} == \z }xmso) { croak "invalid format"; } return $self->assign_with_binary_np(decode_base64($arg)); } sub assign_with_hash { my $self = shift; my $arg = @_ && ref $_[0] eq 'HASH' ? shift : { @_ }; if (my $variant = delete $arg->{variant}) { $self->variant($variant); } foreach my $key (qw( version time time_low time_mid time_hi clk_seq node )) { if (exists $arg->{$key}) { $self->$key($arg->{$key}); } } return $self; } sub as_binary { return ${$_[0]}; } sub as_binary_np { my $self = shift; my $r = $self->as_binary; substr $r, 0, 4, pack('I', unpack('N', substr($r, 0, 4))); substr $r, 4, 2, pack('S', unpack('n', substr($r, 4, 2))); substr $r, 6, 2, pack('S', unpack('n', substr($r, 6, 2))); return $r; } sub as_hex { return scalar unpack 'H*', ${$_[0]}; } sub as_string { my $u = $_[0]->as_binary; return join q{-}, map { unpack 'H*', $_ } map { substr $u, 0, $_, q{} } ( 4, 2, 2, 2, 6 ); } sub as_base64 { my $r = encode_base64(${$_[0]}); $r =~ s{\s+}{}gxmso; return $r; } sub as_base64_np { my $data = ${$_[0]}; substr $data, 0, 4, pack('I', unpack('N', substr($data, 0, 4))); substr $data, 4, 2, pack('S', unpack('n', substr($data, 4, 2))); substr $data, 6, 2, pack('S', unpack('n', substr($data, 6, 2))); my $r = encode_base64($data); $r =~ s{\s+}{}gxmso; return $r; } sub as_hash { my $self = shift; my $r = {}; foreach my $key (qw( variant version time_low time_mid time_hi clk_seq node )) { $r->{$key} = $self->$key(); } return $r; } sub as_urn { my $self = shift; return 'urn:uuid:' . $self->as_string; } sub variant { my $self = shift; if (@_) { my $var = shift; if ($var !~ m{^\d+$}o || ! grep { $var == $_ } qw( 0 2 6 7 4 )) { croak "invalid parameter"; } $var = 2 if $var == 4; if ($var == 0) { substr $$self, 8, 1, chr(ord(substr $$self, 8, 1) & 0x7f); } elsif ($var < 3) { substr $$self, 8, 1, chr(ord(substr $$self, 8, 1) & 0x3f | $var << 6); } else { substr $$self, 8, 1, chr(ord(substr $$self, 8, 1) & 0x1f | $var << 5); } return $var; } my $var = (ord(substr $$self, 8, 1) & 0xe0) >> 5; my %varmap = ( 1 => 0, 2 => 0, 3 => 0, 4 => 2, 5 => 2, ); if (exists $varmap{$var}) { $var = $varmap{$var}; } return $var; } sub version { my $self = shift; if (@_) { my $ver = shift; if ($ver !~ m{^\d+$}o || $ver < 0 || $ver > 15) { croak "invalid parameter"; } substr $$self, 6, 1, chr(ord(substr($$self, 6, 1)) & 0x0f | $ver << 4); return $ver; } return (ord(substr($$self, 6, 1)) & 0xf0) >> 4; } sub time_low { my $self = shift; if (@_) { my $arg = shift; substr $$self, 0, 4, pack('N', $arg); return $arg; } return unpack 'N', substr($$self, 0, 4); } sub time_mid { my $self = shift; if (@_) { my $arg = shift; substr $$self, 4, 2, pack('n', $arg); return $arg; } return unpack 'n', substr($$self, 4, 2); } sub time_hi { my $self = shift; if (@_) { my $arg = shift; if ($arg >= 0x1000) { croak "invalid parameter"; } substr $$self, 6, 2, pack('n', unpack('n', substr($$self, 6, 2)) & 0xf000 | $arg); return $arg; } return unpack('n', substr($$self, 6, 2)) & 0x0fff; } sub clk_seq { my $self = shift; my $r = unpack 'n', substr($$self, 8, 2); my $v = $r >> 13; my $w = ($v >= 6) ? 3 # 11x : ($v >= 4) ? 2 # 10- : 1; # 0-- $w = 16 - $w; if (@_) { my $arg = shift; if ($arg < 0) { croak "invalid parameter"; } $arg &= ((1 << $w) - 1); substr $$self, 8, 2, pack('n', $r & (0xffff - ((1 << $w) - 1)) | $arg); return $arg; } return $r & ((1 << $w) - 1); } sub node { my $self = shift; if (@_) { my $arg = shift; if (length $arg == 6) { } elsif (length $arg == 12) { $arg = pack 'H*', $arg; } elsif (length $arg == 17) { if ($arg !~ m{ \A (?: [0-9A-F]{2} ) ([-:]) [0-9A-F]{2} (?: \1 [0-9A-F]{2} ){4} \z }ixmso) { croak "invalid parameter"; } $arg =~ tr{-:}{}d; $arg = pack 'H*', $arg; } else { croak "invalid parameter"; } substr $$self, 10, 6, $arg; } return join q{:}, map { uc unpack 'H*', $_ } split q{}, substr $$self, 10, 6; } sub _set_time { my ($self, $arg) = @_; # hi = time mod (1000000 / 0x100000000) my $hi = floor($arg / 65536.0 / 512 * 78125); $arg -= $hi * 512.0 * 65536 / 78125; my $low = floor($arg * 10000000.0 + 0.5); # MAGIC offset: 01B2-1DD2-13814000 if ($low < 0xec7ec000) { $low += 0x13814000; } else { $low -= 0xec7ec000; $hi ++; } if ($hi < 0x0e4de22e) { $hi += 0x01b21dd2; } else { $hi -= 0x0e4de22e; # wrap around } $self->time_low($low); $self->time_mid($hi & 0xffff); $self->time_hi(($hi >> 16) & 0x0fff); return $self; } sub time { my $self = shift; if (@_) { $self->_set_time(@_); } my $low = $self->time_low; my $hi = $self->time_mid | ($self->time_hi << 16); # MAGIC offset: 01B2-1DD2-13814000 if ($low >= 0x13814000) { $low -= 0x13814000; } else { $low += 0xec7ec000; $hi --; } if ($hi >= 0x01b21dd2) { $hi -= 0x01b21dd2; } else { $hi += 0x0e4de22e; # wrap around } $low /= 10000000.0; $hi /= 78125.0 / 512 / 65536; # / 1000000 * 0x100000000 return $hi + $low; } sub is_v1 { my $self = shift; return $self->variant == 2 && $self->version == 1; } sub is_v2 { my $self = shift; return $self->variant == 2 && $self->version == 2; } sub is_v3 { my $self = shift; return $self->variant == 2 && $self->version == 3; } sub is_v4 { my $self = shift; return $self->variant == 2 && $self->version == 4; } sub is_v5 { my $self = shift; return $self->variant == 2 && $self->version == 5; } { my %uuid_const; my %uuid_const_map = ( uuid_nil => '00000000-0000-0000-0000-000000000000', uuid_ns_dns => '6ba7b810-9dad-11d1-80b4-00c04fd430c8', uuid_ns_url => '6ba7b811-9dad-11d1-80b4-00c04fd430c8', uuid_ns_oid => '6ba7b812-9dad-11d1-80b4-00c04fd430c8', uuid_ns_x500 => '6ba7b814-9dad-11d1-80b4-00c04fd430c8', ); while (my ($id, $uuid) = each %uuid_const_map) { my $sub = sub { if (! defined $uuid_const{$id}) { $uuid_const{$id} = __PACKAGE__->create_from_string($uuid); } return $uuid_const{$id}->clone(); }; no strict 'refs'; *{__PACKAGE__ . '::' . $id} = $sub; } } 1; __END__ =head1 NAME UUID::Object - Universally Unique IDentifier (UUID) Object Class =head1 DESCRIPTION This module is going to be marked as *DEPRECATED*. Do not use this module in your applications / modules. Currently, this implementation is still functional. If you want to know API, please refer to PODs in version 0.04. =head1 FUTURE PLAN =over 2 =item (1) will be renewed module that behaves like a Mix-in module to L =item (2) will be compatibility layer to that module, and be marked as DEPRECATED =item (3) will be withdrawn from CPAN after a while =back =head1 AUTHOR ITO Nobuaki Ebanb@cpan.orgE =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L. version 0.04: L. =cut