File Coverage

blib/lib/DBIx/CouchLike/IdGenerator.pm
Criterion Covered Total %
statement 83 85 97.6
branch 9 14 64.2
condition 3 7 42.8
subroutine 22 22 100.0
pod 0 2 0.0
total 117 130 90.0


line stmt bran cond sub pod time code
1             package DBIx::CouchLike::IdGenerator;
2             # original code from Data::YUID::Generator
3              
4 1     1   5 use strict;
  1         1  
  1         41  
5 1     1   5 use warnings;
  1         1  
  1         31  
6 1     1   137244 use Math::BigInt try => 'GMP';
  1         160593  
  1         7  
7              
8 1     1   650827 no warnings qw(deprecated); # for fields
  1         4  
  1         60  
9              
10 1     1   6 use vars qw{$VERSION};
  1         2  
  1         78  
11             $VERSION = "0.02";
12              
13 1     1   1137 use fields qw(host_id start_time current_time min_id max_id ids);
  1         1753  
  1         6  
14              
15 1     1   98 use constant EPOCH_OFFSET => 946684800; # Sat, Jan 1 2000 00:00 GMT
  1         3  
  1         70  
16              
17 1     1   5 use constant HOST_ID_BITS => 16;
  1         2  
  1         47  
18 1     1   6 use constant TIME_BITS => 36;
  1         2  
  1         66  
19 1     1   6 use constant SERIAL_BITS => 64 - HOST_ID_BITS - TIME_BITS;
  1         1  
  1         69  
20              
21 1     1   5 use constant TIME_SHIFT => HOST_ID_BITS + SERIAL_BITS;
  1         3  
  1         71  
22 1     1   5 use constant SERIAL_SHIFT => HOST_ID_BITS;
  1         2  
  1         61  
23              
24 1     1   5 use constant SERIAL_INCREMENT => Math::BigInt->new(1) << SERIAL_SHIFT;
  1         2  
  1         9  
25              
26 1     1   459 use constant HOST_ID_MAX => (Math::BigInt->new(1) << HOST_ID_BITS) - 1;
  1         1  
  1         4  
27 1     1   507 use constant TIME_MAX => (Math::BigInt->new(1) << TIME_BITS) - 1;
  1         2  
  1         7  
28 1     1   671 use constant TIME_MAX_SHIFTED => TIME_MAX << TIME_SHIFT;
  1         2  
  1         80  
29 1     1   4 use constant SERIAL_MAX => (Math::BigInt->new(1) << SERIAL_BITS) - 1;
  1         2  
  1         4  
30 1     1   563 use constant SERIAL_MAX_SHIFTED => SERIAL_MAX << SERIAL_SHIFT;
  1         2  
  1         553  
31              
32              
33             sub new {
34 1     1 0 12 my $self = shift;
35 1 50       9 $self = fields::new( $self ) unless ref $self;
36              
37 1         3971 my $host_id = shift;
38 1 50 0     5 if( !$host_id ) {
    0          
39 1         113 $host_id = int( rand( HOST_ID_MAX ) );
40             } elsif( $host_id < 0 || $host_id > HOST_ID_MAX ) {
41 0         0 warn __PACKAGE__ . ": host ID $host_id is not in range of [0," . HOST_ID_MAX . "]\n";
42 0         0 return;
43             }
44              
45 1         41 $self->{ host_id } = $host_id;
46 1         10 $self->{ start_time } = time;
47 1         2 $self->{ current_time } = 0;
48 1         3 $self->{ ids } = {};
49 1         5 $self->_sync();
50              
51 1         787 return $self;
52             }
53              
54              
55             sub _sync {
56 2001     2001   2686 my $self = shift;
57 2001         4297 my $time = time;
58 2001 100       6484 return if( $self->{ current_time } == $time ); # FIXME: check for clock skew
59 9         25 $self->{ current_time } = $time;
60 9 100       91 $self->{ min_id } = $self->_make_id( 0 ) unless( $self->{ min_id } );
61 9         1383 $self->{ max_id } = $self->_make_id( SERIAL_MAX );
62             }
63              
64              
65             sub _make_id {
66 10     10   30 my $self = shift;
67 10   100     47 my $serial = shift || 0;
68             return
69             ((Math::BigInt->new( $self->{ current_time } - EPOCH_OFFSET )) << TIME_SHIFT)
70 10         372 | (Math::BigInt->new($serial) << SERIAL_SHIFT) | $self->{ host_id };
71             }
72              
73              
74             sub get_id {
75 2000     2000 0 1920670 my $self = shift;
76 2000   50     9683 my $key = shift || "_";
77 2000         4597 $self->_sync();
78              
79 2000 100       21563 if( !exists $self->{ ids }->{ $key } ) {
80 1         6 $self->{ ids }->{ $key } = $self->{ min_id };
81 1         14 return $self->{ ids }->{ $key }->bstr;
82             }
83              
84 1999 50       11490 return if( $self->{ ids }->{ $key } >= $self->{ max_id } );
85              
86 1999         118732 $self->{ ids }->{ $key } += SERIAL_INCREMENT;
87 1999         161722 return $self->{ ids }->{ $key }->bstr;
88             }
89              
90              
91             1;