File Coverage

blib/lib/BSON/OID.pm
Criterion Covered Total %
statement 75 78 96.1
branch 20 24 83.3
condition 9 9 100.0
subroutine 24 27 88.8
pod 4 5 80.0
total 132 143 92.3


line stmt bran cond sub pod time code
1 71     71   32010 use 5.010001;
  71         250  
2 71     71   349 use strict;
  71         133  
  71         1711  
3 71     71   367 use warnings;
  71         130  
  71         2227  
4              
5             package BSON::OID;
6             # ABSTRACT: BSON type wrapper for Object IDs
7              
8 71     71   358 use version;
  71         125  
  71         328  
9             our $VERSION = 'v1.12.1';
10              
11 71     71   5753 use Carp;
  71         164  
  71         4087  
12 71     71   466 use Config;
  71         163  
  71         3238  
13 71     71   448 use Scalar::Util 'looks_like_number';
  71         168  
  71         3677  
14 71     71   754 use Sys::Hostname;
  71         101543  
  71         3536  
15 71     71   846 use threads::shared; # NOP if threads.pm not loaded
  71         106108  
  71         431  
16 71     71   5231 use Crypt::URandom ();
  71         388368  
  71         3420  
17              
18             use constant {
19             HAS_INT64 => $Config{use64bitint},
20 71         12943 INT64_MAX => 9223372036854775807,
21             INT32_MAX => 2147483647,
22             ZERO_FILL => ("\0" x 8),
23 71     71   631 };
  71         138  
24              
25 71     71   490 use Moo;
  71         635  
  71         457  
26              
27             #pod =attr oid
28             #pod
29             #pod A 12-byte (packed) Object ID (OID) string. If not provided, a new OID
30             #pod will be generated.
31             #pod
32             #pod =cut
33              
34             has 'oid' => (
35             is => 'ro'
36             );
37              
38 71     71   101227 use namespace::clean -except => 'meta';
  71         760862  
  71         463  
39              
40             # OID generation
41             {
42             my $_MAX_INC_VALUE = 0xFFFFFF;
43             my $_MAX_INC_VALUE_PLUS_ONE = 0x01000000;
44             my $_RANDOM_SIZE = 5;
45 67     67   30491 my $_inc : shared;
  67         106584  
  67         48473  
46             {
47             lock($_inc);
48             $_inc = int( rand($_MAX_INC_VALUE) );
49             }
50              
51             # for testing purposes
52             sub __reset_counter {
53 1     1   2 lock($_inc);
54 1         5 $_inc = $_MAX_INC_VALUE - 1;
55             }
56              
57             my $_pid = $$;
58             my $_random = Crypt::URandom::urandom($_RANDOM_SIZE);
59              
60 0     0   0 sub CLONE { $_random = Crypt::URandom::urandom($_RANDOM_SIZE) }
61              
62             #<<<
63             sub _packed_oid {
64 9106 100   9106   20579 my $time = defined $_[0] ? $_[0] : time;
65 9106 50       25398 $_random = Crypt::URandom::urandom($_RANDOM_SIZE) if $$ != $_pid;
66             return pack(
67             'Na5a3',
68             $time,
69             $_random,
70 9106         13378 substr( pack( 'N', do { lock($_inc); $_inc++; $_inc %= $_MAX_INC_VALUE_PLUS_ONE } ), 1, 3)
  9106         12041  
  9106         11634  
  9106         50800  
71             );
72             }
73             sub _packed_oid_special {
74 7     7   13 my ($time, $fill) = @_;
75 7         24 return pack('Na8', $time, $fill);
76             }
77             #>>>
78             }
79              
80             sub BUILD {
81 8962     8962 0 167488 my ($self) = @_;
82              
83 8962 100       20587 $self->{oid} = _packed_oid() unless defined $self->{oid};
84 8962 100       24796 croak "Invalid 'oid' field: OIDs must be 12 bytes"
85             unless length( $self->oid ) == 12;
86 8961         26013 return;
87             }
88              
89             #pod =method new
90             #pod
91             #pod my $oid = BSON::OID->new;
92             #pod
93             #pod my $oid = BSON::OID->new( oid => $twelve_bytes );
94             #pod
95             #pod This is the preferred way to generate an OID. Without arguments, a
96             #pod unique OID will be generated. With a 12-byte string, an object can
97             #pod be created around an existing OID byte-string.
98             #pod
99             #pod =method from_epoch
100             #pod
101             #pod # generate a new OID
102             #pod
103             #pod my $oid = BSON::OID->from_epoch( $epoch, 0); # other bytes zeroed
104             #pod my $oid = BSON::OID->from_epoch( $epoch, $eight_more_bytes );
105             #pod
106             #pod # reset an existing OID
107             #pod
108             #pod $oid->from_epoch( $new_epoch, 0 );
109             #pod $oid->from_epoch( $new_epoch, $eight_more_bytes );
110             #pod
111             #pod B You should not rely on this method for a source of unique IDs.
112             #pod Use this method for query boundaries, only.
113             #pod
114             #pod An OID is a twelve-byte string. Typically, the first four bytes represent
115             #pod integer seconds since the Unix epoch in big-endian format. The remaining
116             #pod bytes ensure uniqueness.
117             #pod
118             #pod With this method, the first argument to this method is an epoch time (in
119             #pod integer seconds). The second argument is the remaining eight-bytes to
120             #pod append to the string.
121             #pod
122             #pod When called as a class method, it returns a new BSON::OID object. When
123             #pod called as an object method, it mutates the existing internal OID value.
124             #pod
125             #pod As a special case, if the second argument is B and zero ("0"),
126             #pod then the remaining bytes will be zeroed.
127             #pod
128             #pod my $oid = BSON::OID->from_epoch(1467545180, 0);
129             #pod
130             #pod This is particularly useful when looking for documents by their insertion
131             #pod date: you can simply look for OIDs which are greater or lower than the one
132             #pod generated with this method.
133             #pod
134             #pod For backwards compatibility with L, if called without a second
135             #pod argument, the method generates the remainder of the fields "like usual".
136             #pod This is equivalent to calling C<< BSON::OID->new >> and replacing the first
137             #pod four bytes with the packed epoch value.
138             #pod
139             #pod # UNSAFE: don't do this unless you have to
140             #pod
141             #pod my $oid = BSON::OID->from_epoch(1467545180);
142             #pod
143             #pod If you insist on creating a unique OID with C, set the
144             #pod remaining eight bytes in a way that guarantees thread-safe uniqueness, such
145             #pod as from a reliable source of randomness (see L).
146             #pod
147             #pod use Crypt::Random 'urandom';
148             #pod my $oid = BSON::OID->from_epoch(1467545180, urandom(8));
149             #pod
150             #pod =cut
151              
152             sub from_epoch {
153 11     11 1 26 my ($self, $epoch, $fill) = @_;
154              
155 11 50       42 croak "BSON::OID::from_epoch expects an epoch in seconds, not '$epoch'"
156             unless looks_like_number( $epoch );
157              
158 11 100 100     62 $fill = ZERO_FILL if defined $fill && looks_like_number($fill) && $fill == 0;
      100        
159              
160 11 100 100     246 croak "BSON::OID expects the second argument to be missing, 0 or an 8-byte string"
161             unless @_ == 2 || length($fill) == 8;
162              
163 10 100       28 my $oid = defined $fill
164             ? _packed_oid_special($epoch, $fill)
165             : _packed_oid($epoch);
166              
167 10 100       26 if (ref $self) {
168 4         8 $self->{oid} = $oid;
169             }
170             else {
171 6         146 $self = $self->new(oid => $oid);
172             }
173              
174 10         60 return $self;
175             }
176              
177             #pod =method hex
178             #pod
179             #pod Returns the C attributes as 24-byte hexadecimal value
180             #pod
181             #pod =cut
182              
183             sub hex {
184 17819     17819 1 3779548 my ($self) = @_;
185             return defined $self->{_hex}
186             ? $self->{_hex}
187 17819 100       337773 : ( $self->{_hex} = unpack( "H*", $self->{oid} ) );
188             }
189              
190             #pod =method get_time
191             #pod
192             #pod Returns a number corresponding to the portion of the C value that
193             #pod represents seconds since the epoch.
194             #pod
195             #pod =cut
196              
197             sub get_time {
198 3     3 1 30 return unpack( "N", substr( $_[0]->{oid}, 0, 4 ) );
199             }
200              
201             #pod =method TO_JSON
202             #pod
203             #pod Returns a string for this OID, with the OID given as 24 hex digits.
204             #pod
205             #pod If the C option is true, it will instead be compatible with
206             #pod MongoDB's L
207             #pod format, which represents it as a document as follows:
208             #pod
209             #pod {"$oid" : "012345678901234567890123"}
210             #pod
211             #pod =cut
212              
213             sub TO_JSON {
214 25 50   25 1 56 return $_[0]->hex unless $ENV{BSON_EXTJSON};
215 25         61 return {'$oid' => $_[0]->hex };
216             }
217              
218             # For backwards compatibility
219             *to_string = \&hex;
220             *value = \&hex;
221              
222             sub _cmp {
223 8     8   476 my ($left, $right, $swap) = @_;
224 8 50       23 ($left, $right) = ($right, $left) if $swap;
225 8         19 return "$left" cmp "$right";
226             }
227              
228             # Legacy MongoDB driver tests check for a PID matching $$, but the new OID
229             # no longer has an embedded PID. To avoid breaking legacy tests, we make
230             # this return the masked PID.
231 0     0   0 sub _get_pid { return $$ & 0xFFFF }
232              
233             # Legacy BSON::XS tests expect to find a _generate_oid, so we provide
234             # one for back-compatibility.
235 0     0   0 sub _generate_oid { _packed_oid() };
236              
237             use overload (
238 71         649 '""' => \&hex,
239             "<=>" => \&_cmp,
240             "cmp" => \&_cmp,
241             fallback => 1,
242 71     71   5060 );
  71         164  
243              
244             1;
245              
246             =pod
247              
248             =encoding UTF-8
249              
250             =head1 NAME
251              
252             BSON::OID - BSON type wrapper for Object IDs
253              
254             =head1 VERSION
255              
256             version v1.12.1
257              
258             =head1 SYNOPSIS
259              
260             use BSON::Types ':all';
261              
262             my $oid = bson_oid();
263             my $oid = bson_oid->from_epoch(1467543496, 0); # for queries only
264              
265             my $bytes = $oid->oid;
266             my $hex = $oid->hex;
267              
268             =head1 DESCRIPTION
269              
270             This module provides a wrapper around a BSON L
271             ID|https://docs.mongodb.com/manual/reference/method/ObjectId/>.
272              
273             =head1 ATTRIBUTES
274              
275             =head2 oid
276              
277             A 12-byte (packed) Object ID (OID) string. If not provided, a new OID
278             will be generated.
279              
280             =head1 METHODS
281              
282             =head2 new
283              
284             my $oid = BSON::OID->new;
285              
286             my $oid = BSON::OID->new( oid => $twelve_bytes );
287              
288             This is the preferred way to generate an OID. Without arguments, a
289             unique OID will be generated. With a 12-byte string, an object can
290             be created around an existing OID byte-string.
291              
292             =head2 from_epoch
293              
294             # generate a new OID
295              
296             my $oid = BSON::OID->from_epoch( $epoch, 0); # other bytes zeroed
297             my $oid = BSON::OID->from_epoch( $epoch, $eight_more_bytes );
298              
299             # reset an existing OID
300              
301             $oid->from_epoch( $new_epoch, 0 );
302             $oid->from_epoch( $new_epoch, $eight_more_bytes );
303              
304             B You should not rely on this method for a source of unique IDs.
305             Use this method for query boundaries, only.
306              
307             An OID is a twelve-byte string. Typically, the first four bytes represent
308             integer seconds since the Unix epoch in big-endian format. The remaining
309             bytes ensure uniqueness.
310              
311             With this method, the first argument to this method is an epoch time (in
312             integer seconds). The second argument is the remaining eight-bytes to
313             append to the string.
314              
315             When called as a class method, it returns a new BSON::OID object. When
316             called as an object method, it mutates the existing internal OID value.
317              
318             As a special case, if the second argument is B and zero ("0"),
319             then the remaining bytes will be zeroed.
320              
321             my $oid = BSON::OID->from_epoch(1467545180, 0);
322              
323             This is particularly useful when looking for documents by their insertion
324             date: you can simply look for OIDs which are greater or lower than the one
325             generated with this method.
326              
327             For backwards compatibility with L, if called without a second
328             argument, the method generates the remainder of the fields "like usual".
329             This is equivalent to calling C<< BSON::OID->new >> and replacing the first
330             four bytes with the packed epoch value.
331              
332             # UNSAFE: don't do this unless you have to
333              
334             my $oid = BSON::OID->from_epoch(1467545180);
335              
336             If you insist on creating a unique OID with C, set the
337             remaining eight bytes in a way that guarantees thread-safe uniqueness, such
338             as from a reliable source of randomness (see L).
339              
340             use Crypt::Random 'urandom';
341             my $oid = BSON::OID->from_epoch(1467545180, urandom(8));
342              
343             =head2 hex
344              
345             Returns the C attributes as 24-byte hexadecimal value
346              
347             =head2 get_time
348              
349             Returns a number corresponding to the portion of the C value that
350             represents seconds since the epoch.
351              
352             =head2 TO_JSON
353              
354             Returns a string for this OID, with the OID given as 24 hex digits.
355              
356             If the C option is true, it will instead be compatible with
357             MongoDB's L
358             format, which represents it as a document as follows:
359              
360             {"$oid" : "012345678901234567890123"}
361              
362             =for Pod::Coverage op_eq to_string value generate_oid BUILD
363              
364             =head1 OVERLOAD
365              
366             The string operator is overloaded so any string operations will actually use
367             the 24-character hex value of the OID. Fallback overloading is enabled.
368              
369             Both numeric comparison (C<< <=> >>) and string comparison (C) are
370             overloaded to do string comparison of the 24-character hex value of the
371             OID. If used with a non-BSON::OID object, be sure to provide a
372             24-character hex string or the results are undefined.
373              
374             =head1 THREADS
375              
376             This module is thread safe.
377              
378             =head1 AUTHORS
379              
380             =over 4
381              
382             =item *
383              
384             David Golden
385              
386             =item *
387              
388             Stefan G.
389              
390             =back
391              
392             =head1 COPYRIGHT AND LICENSE
393              
394             This software is Copyright (c) 2019 by Stefan G. and MongoDB, Inc.
395              
396             This is free software, licensed under:
397              
398             The Apache License, Version 2.0, January 2004
399              
400             =cut
401              
402             __END__