1use std::{fmt::Display, io::Write};
2
3use arbitrary::Arbitrary;
4use enum_as_inner::EnumAsInner;
5
6use nonmax::{NonMaxI32, NonMaxU32};
7use serde::{Deserialize, Serialize};
8
9mod error;
10mod id;
11mod internal_string;
12mod logging;
13mod macros;
14mod span;
15mod value;
16
17pub use error::{LoroEncodeError, LoroError, LoroResult, LoroTreeError};
18pub use internal_string::InternalString;
19pub use logging::log::*;
20#[doc(hidden)]
21pub use rustc_hash::FxHashMap;
22pub use span::*;
23pub use value::{
24 to_value, LoroBinaryValue, LoroListValue, LoroMapValue, LoroStringValue, LoroValue,
25};
26
27pub type PeerID = u64;
29pub type Counter = i32;
31pub type Lamport = u32;
33
34#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
36pub struct ID {
37 pub peer: PeerID,
38 pub counter: Counter,
39}
40
41impl ID {
42 pub fn to_bytes(&self) -> [u8; 12] {
43 let mut bytes = [0; 12];
44 bytes[..8].copy_from_slice(&self.peer.to_be_bytes());
45 bytes[8..].copy_from_slice(&self.counter.to_be_bytes());
46 bytes
47 }
48
49 pub fn from_bytes(bytes: &[u8]) -> Self {
50 if bytes.len() != 12 {
51 panic!(
52 "Invalid ID bytes. Expected 12 bytes but got {} bytes",
53 bytes.len()
54 );
55 }
56
57 Self {
58 peer: u64::from_be_bytes(bytes[..8].try_into().unwrap()),
59 counter: i32::from_be_bytes(bytes[8..].try_into().unwrap()),
60 }
61 }
62}
63
64#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
66pub struct CompactId {
67 pub peer: PeerID,
68 pub counter: NonMaxI32,
69}
70
71pub fn check_root_container_name(name: &str) -> bool {
73 !name.is_empty() && name.char_indices().all(|(_, x)| x != '/' && x != '\0')
74}
75
76impl CompactId {
77 pub fn new(peer: PeerID, counter: Counter) -> Self {
78 Self {
79 peer,
80 counter: NonMaxI32::new(counter).unwrap(),
81 }
82 }
83
84 pub fn to_id(&self) -> ID {
85 ID {
86 peer: self.peer,
87 counter: self.counter.get(),
88 }
89 }
90
91 pub fn inc(&self, start: i32) -> CompactId {
92 Self {
93 peer: self.peer,
94 counter: NonMaxI32::new(start + self.counter.get()).unwrap(),
95 }
96 }
97}
98
99impl TryFrom<ID> for CompactId {
100 type Error = ID;
101
102 fn try_from(id: ID) -> Result<Self, ID> {
103 if id.counter == i32::MAX {
104 return Err(id);
105 }
106
107 Ok(Self::new(id.peer, id.counter))
108 }
109}
110
111#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord)]
114pub struct IdLp {
115 pub lamport: Lamport,
116 pub peer: PeerID,
117}
118
119impl IdLp {
120 pub fn compact(self) -> CompactIdLp {
121 CompactIdLp::new(self.peer, self.lamport)
122 }
123}
124
125#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
127pub struct CompactIdLp {
128 pub peer: PeerID,
129 pub lamport: NonMaxU32,
130}
131
132impl CompactIdLp {
133 pub fn new(peer: PeerID, lamport: Lamport) -> Self {
134 Self {
135 peer,
136 lamport: NonMaxU32::new(lamport).unwrap(),
137 }
138 }
139
140 pub fn to_id(&self) -> IdLp {
141 IdLp {
142 peer: self.peer,
143 lamport: self.lamport.get(),
144 }
145 }
146}
147
148impl TryFrom<IdLp> for CompactIdLp {
149 type Error = IdLp;
150
151 fn try_from(id: IdLp) -> Result<Self, IdLp> {
152 if id.lamport == u32::MAX {
153 return Err(id);
154 }
155
156 Ok(Self::new(id.peer, id.lamport))
157 }
158}
159
160#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
162pub struct IdFull {
163 pub peer: PeerID,
164 pub lamport: Lamport,
165 pub counter: Counter,
166}
167
168#[derive(Hash, PartialEq, Eq, Clone, Serialize, Deserialize, EnumAsInner)]
180pub enum ContainerID {
181 Root {
183 name: InternalString,
184 container_type: ContainerType,
185 },
186 Normal {
187 peer: PeerID,
188 counter: Counter,
189 container_type: ContainerType,
190 },
191}
192
193impl ContainerID {
194 pub fn encode<W: Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
195 match self {
196 Self::Root {
197 name,
198 container_type,
199 } => {
200 let first_byte = container_type.to_u8() | 0b10000000;
201 writer.write_all(&[first_byte])?;
202 leb128::write::unsigned(writer, name.len() as u64)?;
203 writer.write_all(name.as_bytes())?;
204 }
205 Self::Normal {
206 peer,
207 counter,
208 container_type,
209 } => {
210 let first_byte = container_type.to_u8();
211 writer.write_all(&[first_byte])?;
212 writer.write_all(&peer.to_le_bytes())?;
213 writer.write_all(&counter.to_le_bytes())?;
214 }
215 }
216
217 Ok(())
218 }
219
220 pub fn to_bytes(&self) -> Vec<u8> {
221 let mut bytes = Vec::with_capacity(13);
223 self.encode(&mut bytes).unwrap();
224 bytes
225 }
226
227 pub fn from_bytes(bytes: &[u8]) -> Self {
228 let first_byte = bytes[0];
229 let container_type = ContainerType::try_from_u8(first_byte & 0b01111111).unwrap();
230 let is_root = (first_byte & 0b10000000) != 0;
231
232 let mut reader = &bytes[1..];
233 match is_root {
234 true => {
235 let name_len = leb128::read::unsigned(&mut reader).unwrap();
236 let name = InternalString::from(
237 std::str::from_utf8(&reader[..name_len as usize]).unwrap(),
238 );
239 Self::Root {
240 name,
241 container_type,
242 }
243 }
244 false => {
245 let peer = PeerID::from_le_bytes(reader[..8].try_into().unwrap());
246 let counter = i32::from_le_bytes(reader[8..12].try_into().unwrap());
247 Self::Normal {
248 peer,
249 counter,
250 container_type,
251 }
252 }
253 }
254 }
255
256 const LORO_CONTAINER_ID_PREFIX: &str = "🦜:";
257 pub fn to_loro_value_string(&self) -> String {
258 format!("{}{}", Self::LORO_CONTAINER_ID_PREFIX, self)
259 }
260
261 pub fn try_from_loro_value_string(s: &str) -> Option<Self> {
262 if let Some(s) = s.strip_prefix(Self::LORO_CONTAINER_ID_PREFIX) {
263 Self::try_from(s).ok()
264 } else {
265 None
266 }
267 }
268}
269
270impl std::fmt::Debug for ContainerID {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 Self::Root {
274 name,
275 container_type,
276 } => {
277 write!(f, "Root(\"{name}\" {container_type:?})")
278 }
279 Self::Normal {
280 peer,
281 counter,
282 container_type,
283 } => {
284 write!(f, "Normal({container_type:?} {counter}@{peer})")
285 }
286 }
287 }
288}
289
290#[derive(Arbitrary, Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
293pub enum ContainerType {
294 Text,
295 Map,
296 List,
297 MovableList,
298 Tree,
299 #[cfg(feature = "counter")]
300 Counter,
301 Unknown(u8),
302}
303
304impl ContainerType {
305 #[cfg(feature = "counter")]
306 pub const ALL_TYPES: [ContainerType; 6] = [
307 ContainerType::Map,
308 ContainerType::List,
309 ContainerType::Text,
310 ContainerType::Tree,
311 ContainerType::MovableList,
312 ContainerType::Counter,
313 ];
314 #[cfg(not(feature = "counter"))]
315 pub const ALL_TYPES: [ContainerType; 5] = [
316 ContainerType::Map,
317 ContainerType::List,
318 ContainerType::Text,
319 ContainerType::Tree,
320 ContainerType::MovableList,
321 ];
322
323 pub fn default_value(&self) -> LoroValue {
324 match self {
325 ContainerType::Map => LoroValue::Map(Default::default()),
326 ContainerType::List => LoroValue::List(Default::default()),
327 ContainerType::Text => LoroValue::String(Default::default()),
328 ContainerType::Tree => LoroValue::List(Default::default()),
329 ContainerType::MovableList => LoroValue::List(Default::default()),
330 #[cfg(feature = "counter")]
331 ContainerType::Counter => LoroValue::Double(0.),
332 ContainerType::Unknown(_) => unreachable!(),
333 }
334 }
335
336 pub fn to_u8(self) -> u8 {
337 match self {
338 ContainerType::Map => 0,
339 ContainerType::List => 1,
340 ContainerType::Text => 2,
341 ContainerType::Tree => 3,
342 ContainerType::MovableList => 4,
343 #[cfg(feature = "counter")]
344 ContainerType::Counter => 5,
345 ContainerType::Unknown(k) => k,
346 }
347 }
348
349 pub fn try_from_u8(v: u8) -> LoroResult<Self> {
350 match v {
351 0 => Ok(ContainerType::Map),
352 1 => Ok(ContainerType::List),
353 2 => Ok(ContainerType::Text),
354 3 => Ok(ContainerType::Tree),
355 4 => Ok(ContainerType::MovableList),
356 #[cfg(feature = "counter")]
357 5 => Ok(ContainerType::Counter),
358 x => Ok(ContainerType::Unknown(x)),
359 }
360 }
361}
362
363#[derive(Serialize, Deserialize)]
364#[serde(rename = "ContainerType")]
365enum ContainerTypeSerdeRepr {
366 Text,
367 Map,
368 List,
369 MovableList,
370 Tree,
371 #[cfg(feature = "counter")]
372 Counter,
373 Unknown(u8),
374}
375
376fn historical_container_type_to_byte(c: ContainerType) -> u8 {
379 match c {
380 ContainerType::Text => 0,
381 ContainerType::Map => 1,
382 ContainerType::List => 2,
383 ContainerType::MovableList => 3,
384 ContainerType::Tree => 4,
385 #[cfg(feature = "counter")]
386 ContainerType::Counter => 5,
387 ContainerType::Unknown(k) => k,
388 }
389}
390
391fn historical_try_byte_to_container(byte: u8) -> ContainerType {
392 match byte {
393 0 => ContainerType::Text,
394 1 => ContainerType::Map,
395 2 => ContainerType::List,
396 3 => ContainerType::MovableList,
397 4 => ContainerType::Tree,
398 #[cfg(feature = "counter")]
399 5 => ContainerType::Counter,
400 _ => ContainerType::Unknown(byte),
401 }
402}
403
404impl From<ContainerType> for ContainerTypeSerdeRepr {
405 fn from(value: ContainerType) -> Self {
406 match value {
407 ContainerType::Text => Self::Text,
408 ContainerType::Map => Self::Map,
409 ContainerType::List => Self::List,
410 ContainerType::MovableList => Self::MovableList,
411 ContainerType::Tree => Self::Tree,
412 #[cfg(feature = "counter")]
413 ContainerType::Counter => Self::Counter,
414 ContainerType::Unknown(value) => Self::Unknown(value),
415 }
416 }
417}
418
419impl From<ContainerTypeSerdeRepr> for ContainerType {
420 fn from(value: ContainerTypeSerdeRepr) -> Self {
421 match value {
422 ContainerTypeSerdeRepr::Text => ContainerType::Text,
423 ContainerTypeSerdeRepr::Map => ContainerType::Map,
424 ContainerTypeSerdeRepr::List => ContainerType::List,
425 ContainerTypeSerdeRepr::MovableList => ContainerType::MovableList,
426 ContainerTypeSerdeRepr::Tree => ContainerType::Tree,
427 #[cfg(feature = "counter")]
428 ContainerTypeSerdeRepr::Counter => ContainerType::Counter,
429 ContainerTypeSerdeRepr::Unknown(value) => ContainerType::Unknown(value),
430 }
431 }
432}
433
434impl Serialize for ContainerType {
435 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
436 where
437 S: serde::Serializer,
438 {
439 if serializer.is_human_readable() {
440 ContainerTypeSerdeRepr::from(*self).serialize(serializer)
441 } else {
442 serializer.serialize_u8(historical_container_type_to_byte(*self))
443 }
444 }
445}
446
447impl<'de> Deserialize<'de> for ContainerType {
448 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
449 where
450 D: serde::Deserializer<'de>,
451 {
452 if deserializer.is_human_readable() {
453 let repr = ContainerTypeSerdeRepr::deserialize(deserializer)?;
454 Ok(repr.into())
455 } else {
456 let value = u8::deserialize(deserializer)?;
457 Ok(historical_try_byte_to_container(value))
458 }
459 }
460}
461
462pub type IdSpanVector = rustc_hash::FxHashMap<PeerID, CounterSpan>;
463
464mod container {
465 use super::*;
466
467 impl Display for ContainerType {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 f.write_str(match self {
470 ContainerType::Map => "Map",
471 ContainerType::List => "List",
472 ContainerType::MovableList => "MovableList",
473 ContainerType::Text => "Text",
474 ContainerType::Tree => "Tree",
475 #[cfg(feature = "counter")]
476 ContainerType::Counter => "Counter",
477 ContainerType::Unknown(k) => return f.write_fmt(format_args!("Unknown({k})")),
478 })
479 }
480 }
481
482 impl Display for ContainerID {
483 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
484 match self {
485 ContainerID::Root {
486 name,
487 container_type,
488 } => f.write_fmt(format_args!("cid:root-{name}:{container_type}"))?,
489 ContainerID::Normal {
490 peer,
491 counter,
492 container_type,
493 } => f.write_fmt(format_args!(
494 "cid:{id}:{container_type}",
495 id = ID::new(*peer, *counter),
496 container_type = container_type
497 ))?,
498 };
499 Ok(())
500 }
501 }
502
503 impl TryFrom<&str> for ContainerID {
504 type Error = ();
505
506 fn try_from(mut s: &str) -> Result<Self, Self::Error> {
507 if !s.starts_with("cid:") {
508 return Err(());
509 }
510
511 s = &s[4..];
512 if s.starts_with("root-") {
513 s = &s[5..];
515 let split = s.rfind(':').ok_or(())?;
516 if split == 0 {
517 return Err(());
518 }
519 let kind = ContainerType::try_from(&s[split + 1..]).map_err(|_| ())?;
520 let name = &s[..split];
521 Ok(ContainerID::Root {
522 name: name.into(),
523 container_type: kind,
524 })
525 } else {
526 let mut iter = s.split(':');
527 let id = iter.next().ok_or(())?;
528 let kind = iter.next().ok_or(())?;
529 if iter.next().is_some() {
530 return Err(());
531 }
532
533 let id = ID::try_from(id).map_err(|_| ())?;
534 let kind = ContainerType::try_from(kind).map_err(|_| ())?;
535 Ok(ContainerID::Normal {
536 peer: id.peer,
537 counter: id.counter,
538 container_type: kind,
539 })
540 }
541 }
542 }
543
544 impl ContainerID {
545 #[inline]
546 pub fn new_normal(id: ID, container_type: ContainerType) -> Self {
547 ContainerID::Normal {
548 peer: id.peer,
549 counter: id.counter,
550 container_type,
551 }
552 }
553
554 #[inline]
555 pub fn new_root(name: &str, container_type: ContainerType) -> Self {
556 if !check_root_container_name(name) {
557 panic!(
558 "Invalid root container name, it should not be empty or contain '/' or '\\0'"
559 );
560 } else {
561 ContainerID::Root {
562 name: name.into(),
563 container_type,
564 }
565 }
566 }
567
568 #[inline]
569 pub fn name(&self) -> &InternalString {
570 match self {
571 ContainerID::Root { name, .. } => name,
572 ContainerID::Normal { .. } => unreachable!(),
573 }
574 }
575
576 #[inline]
577 pub fn container_type(&self) -> ContainerType {
578 match self {
579 ContainerID::Root { container_type, .. } => *container_type,
580 ContainerID::Normal { container_type, .. } => *container_type,
581 }
582 }
583
584 pub fn is_unknown(&self) -> bool {
585 matches!(self.container_type(), ContainerType::Unknown(_))
586 }
587 }
588
589 impl TryFrom<&str> for ContainerType {
590 type Error = LoroError;
591
592 fn try_from(value: &str) -> Result<Self, Self::Error> {
593 match value {
594 "Map" | "map" => Ok(ContainerType::Map),
595 "List" | "list" => Ok(ContainerType::List),
596 "Text" | "text" => Ok(ContainerType::Text),
597 "Tree" | "tree" => Ok(ContainerType::Tree),
598 "MovableList" | "movableList" => Ok(ContainerType::MovableList),
599 #[cfg(feature = "counter")]
600 "Counter" | "counter" => Ok(ContainerType::Counter),
601 a => {
602 if a.ends_with(')') {
603 let start = a.find('(').ok_or_else(|| {
604 LoroError::DecodeError(
605 format!("Invalid container type string \"{value}\"").into(),
606 )
607 })?;
608 let k = a[start+1..a.len() - 1].parse().map_err(|_| {
609 LoroError::DecodeError(
610 format!("Unknown container type \"{value}\". The valid options are Map|List|Text|Tree|MovableList.").into(),
611 )
612 })?;
613 match ContainerType::try_from_u8(k) {
614 Ok(k) => Ok(k),
615 Err(_) => Ok(ContainerType::Unknown(k)),
616 }
617 } else {
618 Err(LoroError::DecodeError(
619 format!("Unknown container type \"{value}\". The valid options are Map|List|Text|Tree|MovableList.").into(),
620 ))
621 }
622 }
623 }
624 }
625 }
626}
627
628pub const DELETED_TREE_ROOT: TreeID = TreeID {
632 peer: PeerID::MAX,
633 counter: Counter::MAX,
634};
635
636#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
646
647pub struct TreeID {
648 pub peer: PeerID,
649 pub counter: Counter,
651}
652
653impl TreeID {
654 #[inline(always)]
655 pub fn new(peer: PeerID, counter: Counter) -> Self {
656 Self { peer, counter }
657 }
658
659 pub const fn delete_root() -> Self {
661 DELETED_TREE_ROOT
662 }
663
664 pub fn is_deleted_root(&self) -> bool {
666 self == &DELETED_TREE_ROOT
667 }
668
669 pub fn from_id(id: ID) -> Self {
670 Self {
671 peer: id.peer,
672 counter: id.counter,
673 }
674 }
675
676 pub fn id(&self) -> ID {
677 ID {
678 peer: self.peer,
679 counter: self.counter,
680 }
681 }
682
683 pub fn associated_meta_container(&self) -> ContainerID {
684 ContainerID::new_normal(self.id(), ContainerType::Map)
685 }
686}
687
688impl Display for TreeID {
689 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
690 self.id().fmt(f)
691 }
692}
693
694impl TryFrom<&str> for TreeID {
695 type Error = LoroError;
696 fn try_from(value: &str) -> Result<Self, Self::Error> {
697 let id = ID::try_from(value)?;
698 Ok(TreeID {
699 peer: id.peer,
700 counter: id.counter,
701 })
702 }
703}
704
705#[cfg(feature = "wasm")]
706pub mod wasm {
707 use crate::{LoroError, TreeID};
708 use wasm_bindgen::JsValue;
709 impl From<TreeID> for JsValue {
710 fn from(value: TreeID) -> Self {
711 JsValue::from_str(&format!("{value}"))
712 }
713 }
714
715 impl TryFrom<JsValue> for TreeID {
716 type Error = LoroError;
717 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
718 let id = value.as_string().unwrap();
719 TreeID::try_from(id.as_str())
720 }
721 }
722}
723
724#[cfg(test)]
725mod test {
726 use crate::{ContainerID, ContainerType, ID};
727
728 #[test]
729 fn test_container_id_convert_to_and_from_str() {
730 let id = ContainerID::Root {
731 name: "name".into(),
732 container_type: crate::ContainerType::Map,
733 };
734 let id_str = id.to_string();
735 assert_eq!(id_str.as_str(), "cid:root-name:Map");
736 assert_eq!(ContainerID::try_from(id_str.as_str()).unwrap(), id);
737
738 let id = ContainerID::Normal {
739 counter: 10,
740 peer: 255,
741 container_type: crate::ContainerType::Map,
742 };
743 let id_str = id.to_string();
744 assert_eq!(id_str.as_str(), "cid:10@255:Map");
745 assert_eq!(ContainerID::try_from(id_str.as_str()).unwrap(), id);
746
747 let id = ContainerID::try_from("cid:root-a:b:c:Tree").unwrap();
748 assert_eq!(
749 id,
750 ContainerID::new_root("a:b:c", crate::ContainerType::Tree)
751 );
752 }
753
754 #[test]
755 fn test_convert_invalid_container_id_str() {
756 assert!(ContainerID::try_from("cid:root-:Map").is_err());
757 assert!(ContainerID::try_from("cid:0@:Map").is_err());
758 assert!(ContainerID::try_from("cid:@:Map").is_err());
759 assert!(ContainerID::try_from("cid:x@0:Map").is_err());
760 assert!(ContainerID::try_from("id:0@0:Map").is_err());
761 assert!(ContainerID::try_from("cid:0@0:Unknown(6)").is_ok());
762 }
763
764 #[test]
765 fn test_container_id_encode_and_decode() {
766 let id = ContainerID::new_normal(ID::new(1, 2), ContainerType::Map);
767 let bytes = id.to_bytes();
768 assert_eq!(ContainerID::from_bytes(&bytes), id);
769
770 let id = ContainerID::new_normal(ID::new(u64::MAX, i32::MAX), ContainerType::Text);
771 let bytes = id.to_bytes();
772 assert_eq!(ContainerID::from_bytes(&bytes), id);
773
774 let id = ContainerID::new_root("test_root", ContainerType::List);
775 let bytes = id.to_bytes();
776 assert_eq!(ContainerID::from_bytes(&bytes), id);
777
778 let id = ContainerID::new_normal(ID::new(0, 0), ContainerType::MovableList);
779 let bytes = id.to_bytes();
780 assert_eq!(ContainerID::from_bytes(&bytes), id);
781
782 let id = ContainerID::new_root(&"x".repeat(1024), ContainerType::Tree);
783 let bytes = id.to_bytes();
784 assert_eq!(ContainerID::from_bytes(&bytes), id);
785
786 #[cfg(feature = "counter")]
787 {
788 let id = ContainerID::new_normal(ID::new(42, 100), ContainerType::Counter);
789 let bytes = id.to_bytes();
790 assert_eq!(ContainerID::from_bytes(&bytes), id);
791 }
792
793 let id = ContainerID::new_normal(ID::new(1, 1), ContainerType::Unknown(100));
794 let bytes = id.to_bytes();
795 assert_eq!(ContainerID::from_bytes(&bytes), id);
796 }
797}