idable/
lib.rs

1//! The code here is referenced with [AppFlowy-Collab].
2//! `wait_next_millis` make timestamp|sequenceis crdt/increased.
3use std::{
4    sync::atomic::{
5        AtomicU64,
6        Ordering::{Acquire, Release, SeqCst},
7    },
8    time::SystemTime,
9};
10
11pub const EPOCH: u64 = 1637806706000;
12#[cfg(not(test))]
13const SEQUENCE_BITS: u8 = 12;
14#[cfg(test)]
15const SEQUENCE_BITS: u8 = 1;
16const TIMESTAMP_SHIFT: u8 = SEQUENCE_BITS;
17const SEQUENCE_MASK: u64 = (1 << SEQUENCE_BITS) - 1;
18
19pub type NID = u64;
20pub type SIDGEN = AtomicU64;
21pub type SID = u64;
22
23/// Represents a sequential number generator.
24#[derive(Default)]
25pub struct Seq(SIDGEN);
26// unsafe impl Send for Seq {}
27// unsafe impl Sync for Seq {}
28impl Seq {
29    /// Creates a new `Seq` instance with an initial value of 1.
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// use idable::Seq;
35    ///
36    /// let mut seq = Seq::new();
37    /// ```
38    pub fn new() -> Seq {
39        Seq(SIDGEN::new(0))
40    }
41
42    /// Generates the next sequential ID.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use idable::Seq;
48    ///
49    /// let mut seq = Seq::new();
50    /// let next_id = seq.next_id();
51    /// ```
52    pub fn next_id(&self) -> SID {
53        self.0.fetch_add(1, SeqCst)
54    }
55
56    /// Resets the sequential number to its initial value of 1.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// use idable::Seq;
62    ///
63    /// let mut seq = Seq::new();
64    /// seq.reset();
65    /// ```
66    pub fn reset(&self) {
67        self.0.store(0, Release);
68    }
69}
70impl From<SID> for Seq {
71    fn from(value: SID) -> Self {
72        Seq(SIDGEN::new(value))
73    }
74}
75/// Represents a timestamped sequence generator.
76#[derive(Default)]
77pub struct TimestampSeq {
78    sequence: AtomicU64,
79    last_cycle_timestamp: AtomicU64,
80}
81// unsafe impl Send for TimestampSeq {}
82// unsafe impl Sync for TimestampSeq {}
83fn get_timestamp() -> u64 {
84    SystemTime::now()
85        .duration_since(SystemTime::UNIX_EPOCH)
86        .expect("Clock moved backwards!")
87        .as_millis() as u64
88}
89impl TimestampSeq {
90    /// Creates a new `TimestampSeq` instance with default values.
91    pub fn new() -> TimestampSeq {
92        TimestampSeq::default()
93    }
94
95    fn wait_next_millis(&self) {
96        let last_timestamp = self.last_cycle_timestamp.load(Acquire);
97        while get_timestamp() <= last_timestamp {}
98    }
99
100    /// Generates the next unique ID based on the timestamp and sequence number.
101    ///
102    /// The generated ID is a combination of timestamp and sequence number, ensuring uniqueness.
103    /// * Note that it is not guaranteed to be in increasing order。
104    /// # Examples
105    ///
106    /// ```
107    /// use idable::TimestampSeq;
108    ///
109    /// let mut timestamp_seq = TimestampSeq::new();
110    ///
111    /// // Generate the next unique ID.
112    /// let unique_id = timestamp_seq.next_id();
113    ///
114    /// // Print the generated unique ID.
115    /// println!("Generated Unique ID: {}", unique_id);
116    /// ```
117    pub fn next_id(&self) -> u64 {
118        let sequence = self.sequence.fetch_add(1, SeqCst) & SEQUENCE_MASK;
119        let mut new_timestamp = get_timestamp();
120        // If the sequence goes one cycle, check if the timestamp hasn't changed yet
121        if sequence == 0 {
122            let last_timestamp = self.last_cycle_timestamp.load(Acquire);
123            if last_timestamp == new_timestamp {
124                self.wait_next_millis();
125                new_timestamp = get_timestamp();
126            }
127            self.last_cycle_timestamp.fetch_max(new_timestamp, Release);
128        }
129        (new_timestamp - EPOCH) << TIMESTAMP_SHIFT | sequence
130    }
131}
132#[cfg(any(test, feature = "for-test"))]
133pub fn into_parts(timestamp: u64) -> (u64, u64) {
134    (timestamp >> TIMESTAMP_SHIFT, timestamp & SEQUENCE_MASK)
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    // Helper function to wait for the next millisecond
142    fn wait_for_next_millis() {
143        std::thread::sleep(std::time::Duration::from_millis(1));
144    }
145
146    #[test]
147    fn test_next_generates_unique_ids() {
148        let timestamp_seq = TimestampSeq::new();
149
150        // Generate multiple unique IDs and ensure they are different.
151        let id1 = timestamp_seq.next_id();
152        let id2 = timestamp_seq.next_id();
153        let id3 = timestamp_seq.next_id();
154
155        assert_ne!(id1, id2);
156        assert_ne!(id2, id3);
157        assert_ne!(id1, id3);
158    }
159
160    #[test]
161    fn test_next_increases_sequence() {
162        let timestamp_seq = TimestampSeq::new();
163
164        // Generate IDs and ensure the sequence increases.
165        let id1 = timestamp_seq.next_id();
166        let id2 = timestamp_seq.next_id();
167
168        assert!(id2 > id1);
169    }
170
171    #[test]
172    fn test_next_does_not_repeat_ids() {
173        let timestamp_seq = TimestampSeq::new();
174
175        // Generate multiple IDs and ensure no repetition.
176        let id1 = timestamp_seq.next_id();
177        let id2 = timestamp_seq.next_id();
178        let id3 = timestamp_seq.next_id();
179        let id4 = timestamp_seq.next_id();
180
181        assert_ne!(id1, id2);
182        assert_ne!(id2, id3);
183        assert_ne!(id3, id4);
184        assert_ne!(id1, id4);
185        println!("{id1} {id2} {id3} {id4}");
186        println!(
187            "{:?} {:?} {:?} {:?}",
188            into_parts(id1),
189            into_parts(id2),
190            into_parts(id3),
191            into_parts(id4)
192        );
193    }
194
195    #[test]
196    fn test_next_wait_for_next_millis() {
197        let timestamp_seq = TimestampSeq::new();
198
199        // Generate two IDs in quick succession and ensure the second one has a greater timestamp.
200        let id1 = timestamp_seq.next_id();
201        wait_for_next_millis();
202        let id2 = timestamp_seq.next_id();
203
204        let timestamp1 = id1 >> TIMESTAMP_SHIFT;
205        let timestamp2 = id2 >> TIMESTAMP_SHIFT;
206
207        assert!(timestamp2 > timestamp1);
208    }
209}