| 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__ |