1#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
5use std::{convert::TryInto, time::SystemTime};
6use std::{
7 fmt,
8 str::FromStr,
9 sync::{
10 atomic::{AtomicUsize, Ordering},
11 LazyLock,
12 },
13};
14
15use rand::{random, rng, Rng};
16
17use crate::error::{Error, Result};
18
19const TIMESTAMP_SIZE: usize = 4;
20const PROCESS_ID_SIZE: usize = 5;
21const COUNTER_SIZE: usize = 3;
22
23const TIMESTAMP_OFFSET: usize = 0;
24const PROCESS_ID_OFFSET: usize = TIMESTAMP_OFFSET + TIMESTAMP_SIZE;
25const COUNTER_OFFSET: usize = PROCESS_ID_OFFSET + PROCESS_ID_SIZE;
26
27const MAX_U24: usize = 0xFF_FFFF;
28
29static OID_COUNTER: LazyLock<AtomicUsize> =
30 LazyLock::new(|| AtomicUsize::new(rng().random_range(0..=MAX_U24)));
31
32#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
101pub struct ObjectId {
102 id: [u8; 12],
103}
104
105impl Default for ObjectId {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111impl FromStr for ObjectId {
112 type Err = Error;
113
114 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
115 Self::parse_str(s)
116 }
117}
118
119impl From<[u8; 12]> for ObjectId {
120 fn from(bytes: [u8; 12]) -> Self {
121 Self { id: bytes }
122 }
123}
124
125impl ObjectId {
126 pub fn new() -> Self {
130 let timestamp = Self::gen_timestamp();
131 let process_id = Self::gen_process_id();
132 let counter = Self::gen_count();
133
134 Self::from_parts(timestamp, process_id, counter)
135 }
136
137 pub const fn from_bytes(bytes: [u8; 12]) -> ObjectId {
139 ObjectId { id: bytes }
140 }
141
142 pub fn from_parts(seconds_since_epoch: u32, process_id: [u8; 5], counter: [u8; 3]) -> Self {
146 let mut bytes = [0; 12];
147
148 bytes[TIMESTAMP_OFFSET..(TIMESTAMP_OFFSET + TIMESTAMP_SIZE)]
149 .clone_from_slice(&u32::to_be_bytes(seconds_since_epoch));
150 bytes[PROCESS_ID_OFFSET..(PROCESS_ID_OFFSET + PROCESS_ID_SIZE)]
151 .clone_from_slice(&process_id);
152 bytes[COUNTER_OFFSET..(COUNTER_OFFSET + COUNTER_SIZE)].clone_from_slice(&counter);
153
154 Self::from_bytes(bytes)
155 }
156
157 pub fn parse_str(s: impl AsRef<str>) -> Result<ObjectId> {
159 let s = s.as_ref();
160
161 let bytes: Vec<u8> =
162 hex::decode(s.as_bytes()).map_err(|e| Error::from_hex_error(e, s.len()))?;
163 if bytes.len() != 12 {
164 Err(Error::oid_invalid_length(bytes.len()))
165 } else {
166 let mut byte_array: [u8; 12] = [0; 12];
167 byte_array[..].copy_from_slice(&bytes[..]);
168 Ok(ObjectId::from_bytes(byte_array))
169 }
170 }
171
172 pub fn timestamp(&self) -> crate::DateTime {
174 let mut buf = [0; 4];
175 buf.copy_from_slice(&self.id[0..4]);
176 let seconds_since_epoch = u32::from_be_bytes(buf);
177
178 crate::DateTime::from_millis(seconds_since_epoch as i64 * 1000)
180 }
181
182 pub const fn bytes(&self) -> [u8; 12] {
184 self.id
185 }
186
187 pub fn to_hex(self) -> String {
189 hex::encode(self.id)
190 }
191
192 fn gen_timestamp() -> u32 {
194 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
195 let timestamp: u32 = (js_sys::Date::now() / 1000.0) as u32;
196 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
197 let timestamp: u32 = SystemTime::now()
198 .duration_since(SystemTime::UNIX_EPOCH)
199 .expect("system clock is before 1970")
200 .as_secs()
201 .try_into()
202 .unwrap(); timestamp
205 }
206
207 fn gen_process_id() -> [u8; 5] {
209 static BUF: LazyLock<[u8; 5]> = LazyLock::new(random);
210
211 *BUF
212 }
213
214 fn gen_count() -> [u8; 3] {
217 let u_counter = OID_COUNTER.fetch_add(1, Ordering::SeqCst);
218
219 let u = u_counter % (MAX_U24 + 1);
221
222 let u_int = u as u64;
224
225 let buf = u_int.to_be_bytes();
226 let buf_u24: [u8; 3] = [buf[5], buf[6], buf[7]];
227 buf_u24
228 }
229}
230
231impl fmt::Display for ObjectId {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 f.write_str(&self.to_hex())
234 }
235}
236
237impl fmt::Debug for ObjectId {
238 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
239 f.debug_tuple("ObjectId").field(&self.to_hex()).finish()
240 }
241}
242
243#[cfg(test)]
244use crate::tests::LOCK;
245
246#[test]
247fn count_generated_is_big_endian() {
248 let _guard = LOCK.run_exclusively();
249 let start = 1_122_866;
250 OID_COUNTER.store(start, Ordering::SeqCst);
251
252 let count_bytes = ObjectId::gen_count();
254
255 let mut buf: [u8; 4] = [0; 4];
256 buf[1..=COUNTER_SIZE].clone_from_slice(&count_bytes[..COUNTER_SIZE]);
257
258 let count = u32::from_be_bytes(buf);
259 assert_eq!(start as u32, count);
260
261 let oid = ObjectId::new();
263
264 assert_eq!(0x11u8, oid.bytes()[COUNTER_OFFSET]);
265 assert_eq!(0x22u8, oid.bytes()[COUNTER_OFFSET + 1]);
266 assert_eq!(0x33u8, oid.bytes()[COUNTER_OFFSET + 2]);
267}
268
269#[test]
270fn test_counter_overflow_u24_max() {
271 let _guard = LOCK.run_exclusively();
272 let start = MAX_U24;
273 OID_COUNTER.store(start, Ordering::SeqCst);
274 let oid = ObjectId::new();
275 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET]);
276 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET + 1]);
277 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET + 2]);
278 let oid_new = ObjectId::new();
280 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET]);
281 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET + 1]);
282 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET + 2]);
283}
284
285#[test]
286fn test_counter_overflow_usize_max() {
287 let _guard = LOCK.run_exclusively();
288 let start = usize::MAX;
289 OID_COUNTER.store(start, Ordering::SeqCst);
290 let oid = ObjectId::new();
292 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET]);
293 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET + 1]);
294 assert_eq!(0xFFu8, oid.bytes()[COUNTER_OFFSET + 2]);
295 let oid_new = ObjectId::new();
297 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET]);
298 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET + 1]);
299 assert_eq!(0x00u8, oid_new.bytes()[COUNTER_OFFSET + 2]);
300}
301
302#[cfg(test)]
303mod test {
304 use time::macros::datetime;
305
306 #[test]
307 fn test_display() {
308 let id = super::ObjectId::parse_str("53e37d08776f724e42000000").unwrap();
309
310 assert_eq!(format!("{}", id), "53e37d08776f724e42000000")
311 }
312
313 #[test]
314 fn test_debug() {
315 let id = super::ObjectId::parse_str("53e37d08776f724e42000000").unwrap();
316
317 assert_eq!(
318 format!("{:?}", id),
319 "ObjectId(\"53e37d08776f724e42000000\")"
320 );
321 assert_eq!(
322 format!("{:#?}", id),
323 "ObjectId(\n \"53e37d08776f724e42000000\",\n)"
324 );
325 }
326
327 #[test]
328 fn test_timestamp() {
329 let id = super::ObjectId::parse_str("000000000000000000000000").unwrap();
330 assert_eq!(datetime!(1970-01-01 0:00 UTC), id.timestamp().to_time_0_3());
332
333 let id = super::ObjectId::parse_str("7FFFFFFF0000000000000000").unwrap();
334 assert_eq!(
336 datetime!(2038-01-19 3:14:07 UTC),
337 id.timestamp().to_time_0_3()
338 );
339
340 let id = super::ObjectId::parse_str("800000000000000000000000").unwrap();
341 assert_eq!(
343 datetime!(2038-01-19 3:14:08 UTC),
344 id.timestamp().to_time_0_3()
345 );
346
347 let id = super::ObjectId::parse_str("FFFFFFFF0000000000000000").unwrap();
348 assert_eq!(
350 datetime!(2106-02-07 6:28:15 UTC),
351 id.timestamp().to_time_0_3()
352 );
353 }
354}