line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
4
|
|
|
4
|
|
237851
|
use v5.26; |
|
4
|
|
|
|
|
21
|
|
2
|
4
|
|
|
4
|
|
17
|
use warnings; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
164
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Twitter::ID; |
5
|
|
|
|
|
|
|
# ABSTRACT: Parse the date from a Twitter Snowflake ID |
6
|
|
|
|
|
|
|
$Twitter::ID::VERSION = '1.00'; |
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
18
|
use Carp qw(croak); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
2155
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $TW_EPOCH = 1288834974657; |
12
|
|
|
|
|
|
|
my $WORKER_BITS = 10; |
13
|
|
|
|
|
|
|
my $SEQUENCE_BITS = 12; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $TIMESTAMP_SHIFT = $WORKER_BITS + $SEQUENCE_BITS; |
16
|
|
|
|
|
|
|
my $WORKER_MASK = (-1 ^ (-1 << $WORKER_BITS)) << $SEQUENCE_BITS; |
17
|
|
|
|
|
|
|
my $SEQUENCE_MASK = -1 ^ (-1 << $SEQUENCE_BITS); |
18
|
|
|
|
|
|
|
my $MAX_WORKER = 1 << $WORKER_BITS; |
19
|
|
|
|
|
|
|
my $MAX_SEQUENCE = 1 << $SEQUENCE_BITS; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $LAST_PRE_SNOWFLAKE_ID = 29700859247; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub new { |
25
|
18
|
|
|
18
|
1
|
7772
|
my ($class, $id) = @_; |
26
|
|
|
|
|
|
|
|
27
|
18
|
100
|
|
|
|
42
|
if (ref $id eq 'HASH') { |
28
|
8
|
|
|
|
|
13
|
my $self = bless \(my $o = 0), $class; |
29
|
8
|
|
|
|
|
23
|
$self->_set( $id->{timestamp}, $id->{worker}, $id->{sequence} ); |
30
|
2
|
|
|
|
|
6
|
return $self; |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
10
|
100
|
|
|
|
19
|
if (! $id) { |
34
|
2
|
|
|
|
|
11
|
return bless \(my $o = 0), $class; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
8
|
|
|
|
|
29
|
bless \$id, $class; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub _set { |
42
|
11
|
|
|
11
|
|
20
|
my ($self, $timestamp, $worker, $sequence) = @_; |
43
|
11
|
|
66
|
|
|
21
|
$timestamp //= $TW_EPOCH; |
44
|
11
|
|
100
|
|
|
25
|
$worker //= 0; |
45
|
11
|
|
100
|
|
|
23
|
$sequence //= 0; |
46
|
|
|
|
|
|
|
|
47
|
11
|
100
|
|
|
|
29
|
croak "Twitter timestamps before $TW_EPOCH unsupported" if $timestamp < $TW_EPOCH; |
48
|
9
|
100
|
100
|
|
|
37
|
croak "Twitter ID components must be positive" if $worker < 0 || $sequence < 0; |
49
|
7
|
100
|
|
|
|
30
|
croak "Worker ID $worker too large (max $MAX_WORKER)" if $worker >= $MAX_WORKER; |
50
|
6
|
100
|
|
|
|
28
|
croak "Sequence number $sequence too large (max $MAX_SEQUENCE)" if $sequence >= $MAX_SEQUENCE; |
51
|
|
|
|
|
|
|
|
52
|
5
|
|
|
|
|
10
|
$$self = ($timestamp - $TW_EPOCH) << $TIMESTAMP_SHIFT |
53
|
|
|
|
|
|
|
| $worker << $SEQUENCE_BITS |
54
|
|
|
|
|
|
|
| $sequence; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub timestamp { |
59
|
11
|
|
|
11
|
1
|
2118
|
my ($self, $timestamp) = @_; |
60
|
|
|
|
|
|
|
|
61
|
11
|
100
|
|
|
|
20
|
if (defined $timestamp) { |
62
|
1
|
|
|
|
|
3
|
$self->_set( $timestamp, $self->worker, $self->sequence ); |
63
|
1
|
|
|
|
|
3
|
return; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
10
|
100
|
|
|
|
29
|
return if $$self <= $LAST_PRE_SNOWFLAKE_ID; |
67
|
|
|
|
|
|
|
|
68
|
6
|
|
|
|
|
16
|
return ($$self >> $TIMESTAMP_SHIFT) + $TW_EPOCH; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub worker { |
73
|
8
|
|
|
8
|
1
|
267
|
my ($self, $worker) = @_; |
74
|
|
|
|
|
|
|
|
75
|
8
|
100
|
|
|
|
22
|
if (defined $worker) { |
76
|
1
|
|
|
|
|
2
|
$self->_set( $self->timestamp, $worker, $self->sequence ); |
77
|
1
|
|
|
|
|
3
|
return; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
7
|
100
|
|
|
|
26
|
return if $$self <= $LAST_PRE_SNOWFLAKE_ID; |
81
|
|
|
|
|
|
|
|
82
|
3
|
|
|
|
|
9
|
return ($$self & $WORKER_MASK) >> $SEQUENCE_BITS; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub sequence { |
87
|
8
|
|
|
8
|
1
|
263
|
my ($self, $sequence) = @_; |
88
|
|
|
|
|
|
|
|
89
|
8
|
100
|
|
|
|
18
|
if (defined $sequence) { |
90
|
1
|
|
|
|
|
2
|
$self->_set( $self->timestamp, $self->worker, $sequence ); |
91
|
1
|
|
|
|
|
3
|
return; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
7
|
100
|
|
|
|
19
|
return if $$self <= $LAST_PRE_SNOWFLAKE_ID; |
95
|
|
|
|
|
|
|
|
96
|
3
|
|
|
|
|
13
|
return $$self & $SEQUENCE_MASK; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub epoch { |
101
|
4
|
|
|
4
|
1
|
12
|
my ($self, $epoch) = @_; |
102
|
|
|
|
|
|
|
|
103
|
4
|
100
|
|
|
|
13
|
croak "epoch() is read-only" if defined $epoch; |
104
|
|
|
|
|
|
|
|
105
|
3
|
|
|
|
|
4
|
my $timestamp = $self->timestamp; |
106
|
3
|
100
|
|
|
|
6
|
return unless defined $timestamp; |
107
|
2
|
|
|
|
|
8
|
return $timestamp / 1000; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
1; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
__END__ |