loro_common/
id.rs

1use crate::{span::IdSpan, CounterSpan, IdFull, IdLp, IdLpSpan, Lamport};
2
3use super::{Counter, LoroError, PeerID, ID};
4const UNKNOWN: PeerID = 404;
5use std::{
6    fmt::{Debug, Display},
7    ops::RangeBounds,
8};
9
10impl Debug for ID {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        f.write_str(format!("{}@{}", self.counter, self.peer).as_str())
13    }
14}
15
16impl Debug for IdLp {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.write_str(format!("L{}@{}", self.lamport, self.peer).as_str())
19    }
20}
21
22impl Display for ID {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.write_str(format!("{}@{}", self.counter, self.peer).as_str())
25    }
26}
27
28impl Display for IdLp {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.write_str(format!("L{}@{}", self.lamport, self.peer).as_str())
31    }
32}
33
34impl TryFrom<&str> for ID {
35    type Error = LoroError;
36
37    fn try_from(value: &str) -> Result<Self, Self::Error> {
38        if value.split('@').count() != 2 {
39            return Err(LoroError::DecodeError("Invalid ID format".into()));
40        }
41
42        let mut iter = value.split('@');
43        let counter = iter
44            .next()
45            .unwrap()
46            .parse::<Counter>()
47            .map_err(|_| LoroError::DecodeError("Invalid ID format".into()))?;
48        let client_id = iter
49            .next()
50            .unwrap()
51            .parse::<u64>()
52            .map_err(|_| LoroError::DecodeError("Invalid ID format".into()))?;
53        Ok(ID {
54            peer: client_id,
55            counter,
56        })
57    }
58}
59
60impl TryFrom<&str> for IdLp {
61    type Error = LoroError;
62
63    fn try_from(value: &str) -> Result<Self, Self::Error> {
64        if value.split('@').count() != 2 || !value.starts_with('L') {
65            return Err(LoroError::DecodeError("Invalid ID format".into()));
66        }
67
68        let mut iter = value[1..].split('@');
69        let lamport = iter
70            .next()
71            .unwrap()
72            .parse::<Lamport>()
73            .map_err(|_| LoroError::DecodeError("Invalid ID format".into()))?;
74        let client_id = iter
75            .next()
76            .unwrap()
77            .parse::<u64>()
78            .map_err(|_| LoroError::DecodeError("Invalid ID format".into()))?;
79        Ok(IdLp {
80            peer: client_id,
81            lamport,
82        })
83    }
84}
85
86impl PartialOrd for ID {
87    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
88        Some(self.cmp(other))
89    }
90}
91
92impl Ord for ID {
93    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
94        match self.peer.cmp(&other.peer) {
95            core::cmp::Ordering::Equal => self.counter.cmp(&other.counter),
96            ord => ord,
97        }
98    }
99}
100
101pub const ROOT_ID: ID = ID {
102    peer: PeerID::MAX,
103    counter: i32::MAX,
104};
105
106impl From<u128> for ID {
107    fn from(id: u128) -> Self {
108        ID {
109            peer: (id >> 64) as PeerID,
110            counter: id as Counter,
111        }
112    }
113}
114
115impl ID {
116    /// The ID of the null object. This should be use rarely.
117    pub const NONE_ID: ID = ID::new(u64::MAX, 0);
118
119    #[inline]
120    pub const fn new(peer: PeerID, counter: Counter) -> Self {
121        ID { peer, counter }
122    }
123
124    #[inline]
125    pub fn new_root() -> Self {
126        ROOT_ID
127    }
128
129    #[inline]
130    pub fn is_null(&self) -> bool {
131        self.peer == PeerID::MAX
132    }
133
134    #[inline]
135    pub fn to_span(&self, len: usize) -> IdSpan {
136        IdSpan {
137            peer: self.peer,
138            counter: CounterSpan::new(self.counter, self.counter + len as Counter),
139        }
140    }
141
142    #[inline]
143    pub fn unknown(counter: Counter) -> Self {
144        ID {
145            peer: UNKNOWN,
146            counter,
147        }
148    }
149
150    #[inline]
151    pub fn is_unknown(&self) -> bool {
152        self.peer == UNKNOWN
153    }
154
155    #[inline]
156    #[allow(dead_code)]
157    pub(crate) fn is_connected_id(&self, other: &Self, self_len: usize) -> bool {
158        self.peer == other.peer && self.counter + self_len as Counter == other.counter
159    }
160
161    #[inline]
162    pub fn inc(&self, inc: i32) -> Self {
163        ID {
164            peer: self.peer,
165            counter: self.counter.saturating_add(inc),
166        }
167    }
168
169    #[inline]
170    pub fn contains(&self, len: Counter, target: ID) -> bool {
171        self.peer == target.peer
172            && self.counter <= target.counter
173            && target.counter < self.counter + len
174    }
175}
176
177impl From<ID> for u128 {
178    fn from(id: ID) -> Self {
179        ((id.peer as u128) << 64) | id.counter as u128
180    }
181}
182
183impl RangeBounds<ID> for (ID, ID) {
184    fn start_bound(&self) -> std::ops::Bound<&ID> {
185        std::ops::Bound::Included(&self.0)
186    }
187
188    fn end_bound(&self) -> std::ops::Bound<&ID> {
189        std::ops::Bound::Excluded(&self.1)
190    }
191}
192
193impl IdLp {
194    pub const NONE_ID: IdLp = IdLp::new(u64::MAX, 0);
195
196    #[inline]
197    pub const fn new(peer: PeerID, lp: Lamport) -> Self {
198        Self { peer, lamport: lp }
199    }
200
201    pub fn inc(&self, offset: i32) -> IdLp {
202        IdLp {
203            peer: self.peer,
204            lamport: (self.lamport as i32 + offset) as Lamport,
205        }
206    }
207
208    pub fn is_none(&self) -> bool {
209        self.peer == PeerID::MAX
210    }
211}
212
213impl From<IdLp> for IdLpSpan {
214    fn from(value: IdLp) -> Self {
215        IdLpSpan {
216            peer: value.peer,
217            lamport: crate::LamportSpan {
218                start: value.lamport,
219                end: value.lamport + 1,
220            },
221        }
222    }
223}
224
225impl IdFull {
226    pub const NONE_ID: IdFull = IdFull {
227        peer: PeerID::MAX,
228        lamport: 0,
229        counter: 0,
230    };
231
232    pub fn new(peer: PeerID, counter: Counter, lamport: Lamport) -> Self {
233        Self {
234            peer,
235            lamport,
236            counter,
237        }
238    }
239
240    pub fn inc(&self, offset: i32) -> IdFull {
241        IdFull {
242            peer: self.peer,
243            lamport: (self.lamport as i32 + offset) as Lamport,
244            counter: self.counter + offset as Counter,
245        }
246    }
247
248    pub fn id(&self) -> ID {
249        ID {
250            peer: self.peer,
251            counter: self.counter,
252        }
253    }
254
255    pub fn idlp(&self) -> IdLp {
256        IdLp {
257            peer: self.peer,
258            lamport: self.lamport,
259        }
260    }
261}