-
Notifications
You must be signed in to change notification settings - Fork 413
/
Copy pathlib.rs
151 lines (124 loc) · 4.31 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! TUID: Time-based Unique Identifiers.
//!
//! Time-ordered unique 128-bit identifiers.
//!
//! ## Feature flags
#![doc = document_features::document_features!()]
//!
use arrow2::datatypes::DataType;
use arrow2_convert::{ArrowDeserialize, ArrowSerialize};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ArrowSerialize, ArrowDeserialize)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Tuid {
/// Approximate nanoseconds since epoch.
time_ns: u64,
/// Initialized to something random on each thread,
/// then incremented for each new [`Tuid`] being allocated.
inc: u64,
}
arrow2_convert::arrow_enable_vec_for_type!(Tuid);
// TODO(#1774): shouldn't have to write this manually
impl arrow2_convert::field::ArrowField for Tuid {
type Type = Self;
fn data_type() -> arrow2::datatypes::DataType {
let datatype = arrow2::datatypes::DataType::Struct(<[_]>::into_vec(Box::new([
<u64 as arrow2_convert::field::ArrowField>::field("time_ns"),
<u64 as arrow2_convert::field::ArrowField>::field("inc"),
])));
DataType::Extension("rerun.tuid".into(), Box::new(datatype), None)
}
}
impl std::fmt::Display for Tuid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:032X}", self.as_u128())
}
}
impl std::fmt::Debug for Tuid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:032X}", self.as_u128())
}
}
impl Tuid {
/// All zeroes.
pub const ZERO: Self = Self { time_ns: 0, inc: 0 };
/// All ones.
pub const MAX: Self = Self {
time_ns: u64::MAX,
inc: u64::MAX,
};
#[inline]
#[cfg(not(target_arch = "wasm32"))] // TODO(emilk): implement for wasm32 (needs ms since epoch).
pub fn random() -> Self {
use std::cell::RefCell;
thread_local! {
pub static LATEST_TUID: RefCell<Tuid> = RefCell::new(Tuid{
time_ns: monotonic_nanos_since_epoch(),
// Leave top bit at zero so we have plenty of room to grow.
inc: random_u64() & !(1_u64 << 63),
});
}
LATEST_TUID.with(|latest_tuid| {
let mut latest = latest_tuid.borrow_mut();
let new = Tuid {
time_ns: monotonic_nanos_since_epoch(),
inc: latest.inc + 1,
};
debug_assert!(
latest.time_ns <= new.time_ns,
"Time should be monotonically increasing"
);
*latest = new;
new
})
}
#[inline]
pub fn as_u128(&self) -> u128 {
((self.time_ns as u128) << 64) | (self.inc as u128)
}
#[inline]
pub fn nanoseconds_since_epoch(&self) -> u64 {
self.time_ns
}
}
/// Returns a high-precision, monotonically increasing count that approximates nanoseconds since unix epoch.
#[inline]
#[cfg(not(target_arch = "wasm32"))]
fn monotonic_nanos_since_epoch() -> u64 {
// This can maybe be optimized
use once_cell::sync::Lazy;
use std::time::Instant;
fn epoch_offset_and_start() -> (u64, Instant) {
if let Ok(duration_since_epoch) = std::time::UNIX_EPOCH.elapsed() {
let nanos_since_epoch = duration_since_epoch.as_nanos() as u64;
(nanos_since_epoch, Instant::now())
} else {
// system time is set before 1970. this should be quite rare.
(0, Instant::now())
}
}
static START_TIME: Lazy<(u64, Instant)> = Lazy::new(epoch_offset_and_start);
START_TIME.0 + START_TIME.1.elapsed().as_nanos() as u64
}
#[inline]
#[cfg(not(target_arch = "wasm32"))]
fn random_u64() -> u64 {
let mut bytes = [0_u8; 8];
getrandom::getrandom(&mut bytes).expect("Couldn't get inc");
u64::from_le_bytes(bytes)
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_tuid() {
use std::collections::{BTreeSet, HashSet};
fn is_sorted<T>(data: &[T]) -> bool
where
T: Ord,
{
data.windows(2).all(|w| w[0] <= w[1])
}
let num = 100_000;
let ids: Vec<Tuid> = (0..num).map(|_| Tuid::random()).collect();
assert!(is_sorted(&ids));
assert_eq!(ids.iter().cloned().collect::<HashSet::<Tuid>>().len(), num);
assert_eq!(ids.iter().cloned().collect::<BTreeSet::<Tuid>>().len(), num);
}