@@ -16,6 +16,7 @@ use hex::{self, FromHexError};
16
16
use rand:: { thread_rng, Rng } ;
17
17
18
18
use chrono:: Utc ;
19
+ use lazy_static:: lazy_static;
19
20
20
21
const TIMESTAMP_SIZE : usize = 4 ;
21
22
const PROCESS_ID_SIZE : usize = 5 ;
@@ -27,7 +28,9 @@ const COUNTER_OFFSET: usize = PROCESS_ID_OFFSET + PROCESS_ID_SIZE;
27
28
28
29
const MAX_U24 : usize = 0xFF_FFFF ;
29
30
30
- static OID_COUNTER : AtomicUsize = AtomicUsize :: new ( 0 ) ;
31
+ lazy_static ! {
32
+ static ref OID_COUNTER : AtomicUsize = AtomicUsize :: new( thread_rng( ) . gen_range( 0 , MAX_U24 + 1 ) ) ;
33
+ }
31
34
32
35
/// Errors that can occur during OID construction and generation.
33
36
#[ derive( Debug ) ]
@@ -162,19 +165,10 @@ impl ObjectId {
162
165
// Gets an incremental 3-byte count.
163
166
// Represented in Big Endian.
164
167
fn gen_count ( ) -> [ u8 ; 3 ] {
165
- // Init oid counter
166
- if OID_COUNTER . load ( Ordering :: SeqCst ) == 0 {
167
- let start = thread_rng ( ) . gen_range ( 0 , MAX_U24 + 1 ) ;
168
- OID_COUNTER . store ( start, Ordering :: SeqCst ) ;
169
- }
170
-
171
168
let u_counter = OID_COUNTER . fetch_add ( 1 , Ordering :: SeqCst ) ;
172
169
173
170
// Mod result instead of OID_COUNTER to prevent threading issues.
174
- // Static mutexes are currently unstable; once they have been
175
- // stabilized, one should be used to access OID_COUNTER and
176
- // perform multiple operations atomically.
177
- let u = u_counter % MAX_U24 ;
171
+ let u = u_counter % ( MAX_U24 + 1 ) ;
178
172
179
173
// Convert usize to writable u64, then extract the first three bytes.
180
174
let u_int = u as u64 ;
@@ -224,6 +218,39 @@ fn count_generated_is_big_endian() {
224
218
assert_eq ! ( 0x33u8 , oid. bytes( ) [ COUNTER_OFFSET + 2 ] ) ;
225
219
}
226
220
221
+ #[ test]
222
+ fn test_counter_overflow_u24_max ( ) {
223
+ let _guard = LOCK . run_exclusively ( ) ;
224
+ let start = MAX_U24 ;
225
+ OID_COUNTER . store ( start, Ordering :: SeqCst ) ;
226
+ let oid = ObjectId :: new ( ) ;
227
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET ] ) ;
228
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET + 1 ] ) ;
229
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET + 2 ] ) ;
230
+ // Test counter overflows to 0 when set to MAX_24 + 1
231
+ let oid_new = ObjectId :: new ( ) ;
232
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET ] ) ;
233
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET + 1 ] ) ;
234
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET + 2 ] ) ;
235
+ }
236
+
237
+ #[ test]
238
+ fn test_counter_overflow_usize_max ( ) {
239
+ let _guard = LOCK . run_exclusively ( ) ;
240
+ let start = usize:: max_value ( ) ;
241
+ OID_COUNTER . store ( start, Ordering :: SeqCst ) ;
242
+ // Test counter overflows to u24_max when set to usize_max
243
+ let oid = ObjectId :: new ( ) ;
244
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET ] ) ;
245
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET + 1 ] ) ;
246
+ assert_eq ! ( 0xFFu8 , oid. bytes( ) [ COUNTER_OFFSET + 2 ] ) ;
247
+ // Test counter overflows to 0 when set to usize_max + 1
248
+ let oid_new = ObjectId :: new ( ) ;
249
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET ] ) ;
250
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET + 1 ] ) ;
251
+ assert_eq ! ( 0x00u8 , oid_new. bytes( ) [ COUNTER_OFFSET + 2 ] ) ;
252
+ }
253
+
227
254
#[ cfg( test) ]
228
255
mod test {
229
256
use chrono:: { offset:: TimeZone , Utc } ;
0 commit comments