line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $Id$ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Data::YUID::Generator; |
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
64
|
|
5
|
2
|
|
|
2
|
|
9
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
57
|
|
6
|
2
|
|
|
2
|
|
18
|
no warnings qw(deprecated); # for fields |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
89
|
|
7
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
11
|
use vars qw{$VERSION}; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
142
|
|
9
|
|
|
|
|
|
|
$VERSION = "0.01"; |
10
|
2
|
|
|
2
|
|
9
|
use Carp; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
175
|
|
11
|
2
|
|
|
2
|
|
12
|
use Config; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
99
|
|
12
|
|
|
|
|
|
|
|
13
|
2
|
|
|
2
|
|
2143
|
use fields qw(host_id start_time current_time min_id max_id ids make_id); |
|
2
|
|
|
|
|
3734
|
|
|
2
|
|
|
|
|
34
|
|
14
|
|
|
|
|
|
|
|
15
|
2
|
|
|
2
|
|
198
|
use constant EPOCH_OFFSET => 946684800; # Sat, Jan 1 2000 00:00 GMT |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
154
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
## | timestamp | serial | host | |
18
|
|
|
|
|
|
|
## | 36 bits | 12 | 16 | |
19
|
2
|
|
|
2
|
|
10
|
use constant HOST_ID_BITS => 16; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
80
|
|
20
|
2
|
|
|
2
|
|
17
|
use constant TIME_BITS => 36; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
123
|
|
21
|
2
|
|
|
2
|
|
10
|
use constant SERIAL_BITS => 64 - HOST_ID_BITS - TIME_BITS; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
112
|
|
22
|
|
|
|
|
|
|
|
23
|
2
|
|
|
2
|
|
11
|
use constant HOST_SHIFT => HOST_ID_BITS; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
129
|
|
24
|
2
|
|
|
2
|
|
11
|
use constant TIME_SHIFT => HOST_ID_BITS + SERIAL_BITS; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
111
|
|
25
|
2
|
|
|
2
|
|
9
|
use constant SERIAL_SHIFT => HOST_ID_BITS; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
124
|
|
26
|
|
|
|
|
|
|
|
27
|
2
|
|
|
2
|
|
19
|
use constant SERIAL_INCREMENT => 1 << SERIAL_SHIFT; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
149
|
|
28
|
|
|
|
|
|
|
|
29
|
2
|
|
|
2
|
|
11
|
use constant HOST_ID_MAX => (1 << HOST_ID_BITS) - 1; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
134
|
|
30
|
2
|
|
|
2
|
|
10
|
use constant TIME_MAX => (1 << TIME_BITS) - 1; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
123
|
|
31
|
2
|
|
|
2
|
|
11
|
use constant TIME_MAX_SHIFTED => TIME_MAX << TIME_SHIFT; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
110
|
|
32
|
2
|
|
|
2
|
|
10
|
use constant SERIAL_MAX => (1 << SERIAL_BITS) - 1; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
135
|
|
33
|
2
|
|
|
2
|
|
10
|
use constant SERIAL_MAX_SHIFTED => SERIAL_MAX << SERIAL_SHIFT; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
107
|
|
34
|
|
|
|
|
|
|
|
35
|
2
|
|
|
2
|
|
11
|
use constant HOST_MAX => (1 << HOST_ID_BITS) - 1; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
105
|
|
36
|
|
|
|
|
|
|
BEGIN { |
37
|
2
|
|
|
2
|
|
30
|
use Config; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
152
|
|
38
|
2
|
50
|
|
2
|
|
2191
|
unless ($Config{use64bitint}) { |
39
|
0
|
0
|
|
|
|
|
eval "use Math::BigInt; 1;" |
40
|
|
|
|
|
|
|
or croak "Please install Math::BigInt"; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
}; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub new { |
45
|
0
|
|
|
0
|
0
|
|
my Data::YUID::Generator $self = shift; |
46
|
0
|
0
|
|
|
|
|
$self = fields::new( $self ) unless ref $self; |
47
|
|
|
|
|
|
|
|
48
|
0
|
|
|
|
|
|
my $host_id = shift; |
49
|
0
|
0
|
0
|
|
|
|
if( !$host_id ) { |
|
|
0
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
$host_id = int( rand( HOST_ID_MAX ) ); |
51
|
|
|
|
|
|
|
} elsif( $host_id < 0 || $host_id > HOST_ID_MAX ) { |
52
|
0
|
|
|
|
|
|
warn __PACKAGE__ . ": host ID $host_id is not in range of [0," . HOST_ID_MAX . "]\n"; |
53
|
0
|
|
|
|
|
|
return undef; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
0
|
|
|
|
|
|
$self->{ host_id } = $host_id; |
57
|
0
|
|
|
|
|
|
$self->{ start_time } = time; |
58
|
0
|
|
|
|
|
|
$self->{ current_time } = 0; |
59
|
0
|
|
|
|
|
|
$self->{ ids } = {}; |
60
|
0
|
0
|
|
|
|
|
if ($Config{use64bitint}) { |
61
|
0
|
|
|
0
|
|
|
$self->{make_id} = sub { $self->_make_id_64bits(@_) }; |
|
0
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
else { |
64
|
0
|
|
|
0
|
|
|
$self->{make_id} = sub { $self->_make_id_32bits(@_) }; |
|
0
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
0
|
|
|
|
|
|
$self->_sync(); |
68
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
return $self; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _sync { |
74
|
0
|
|
|
0
|
|
|
my $self = shift; |
75
|
0
|
|
|
|
|
|
my $key = shift; |
76
|
0
|
|
|
|
|
|
my $time = time; |
77
|
0
|
0
|
|
|
|
|
return if( $self->{ current_time } == $time ); # FIXME: check for clock skew |
78
|
0
|
|
|
|
|
|
$self->{ current_time } = $time; |
79
|
0
|
|
|
|
|
|
$self->{ min_id } = $self->{make_id}->( 0 ); |
80
|
0
|
|
|
|
|
|
$self->{ max_id } = $self->{make_id}->( SERIAL_MAX ); |
81
|
0
|
0
|
|
|
|
|
delete $self->{ ids }->{ $key } if $key; ## reset current timestamp |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _make_id_64bits ($) { |
86
|
0
|
|
|
0
|
|
|
my $self = shift; |
87
|
0
|
|
0
|
|
|
|
my $serial = shift || 0; |
88
|
|
|
|
|
|
|
return (($self->{ current_time } - EPOCH_OFFSET) << TIME_SHIFT) | |
89
|
0
|
|
|
|
|
|
($serial << SERIAL_SHIFT) | $self->{ host_id }; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _make_id_32bits ($) { |
93
|
0
|
|
|
0
|
|
|
my $self = shift; |
94
|
0
|
|
0
|
|
|
|
my $serial = shift || 0; |
95
|
0
|
|
|
|
|
|
my $id = Math::BigInt->new($self->{ current_time } - EPOCH_OFFSET); |
96
|
|
|
|
|
|
|
return $id->blsft(TIME_SHIFT) |
97
|
|
|
|
|
|
|
->bior(Math::BigInt->new($serial)->blsft(SERIAL_SHIFT)) |
98
|
0
|
|
|
|
|
|
->bior($self->{ host_id }); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub get_id ($) { |
102
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
103
|
0
|
|
0
|
|
|
|
my $key = shift || "_"; |
104
|
0
|
|
|
|
|
|
$self->_sync($key); |
105
|
|
|
|
|
|
|
|
106
|
0
|
0
|
|
|
|
|
if( !exists $self->{ ids }->{ $key } ) { |
107
|
0
|
|
|
|
|
|
$self->{ ids }->{ $key } = $self->{ min_id }; |
108
|
0
|
|
|
|
|
|
return $self->{ ids }->{ $key }; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
return undef |
112
|
0
|
0
|
|
|
|
|
if( $self->{ ids }->{ $key } >= $self->{ max_id } ); |
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
|
|
|
$self->{ ids }->{ $key } += SERIAL_INCREMENT; |
115
|
0
|
|
|
|
|
|
return $self->{ ids }->{ $key }; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
## deconstruct an id in its composing part, using id order: |
119
|
|
|
|
|
|
|
## (ts, serial, host) |
120
|
|
|
|
|
|
|
sub decompose { |
121
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
122
|
0
|
|
|
|
|
|
my $id = shift; |
123
|
0
|
|
|
|
|
|
my $ts = $id >> TIME_SHIFT; |
124
|
0
|
|
|
|
|
|
my $serial = ( $id >> HOST_SHIFT ) & SERIAL_MAX; |
125
|
0
|
|
|
|
|
|
my $host = $id & HOST_MAX; |
126
|
0
|
|
|
|
|
|
return ($ts, $serial, $host); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
1; |