loro_internal/state/
analyzer.rs1use crate::{change::Timestamp, LoroDoc};
2use loro_common::ContainerID;
3use rle::HasLength;
4use rustc_hash::FxHashMap;
5
6#[derive(Debug, Clone)]
7pub struct DocAnalysis {
8 pub containers: FxHashMap<ContainerID, ContainerAnalysisInfo>,
9}
10
11#[derive(Debug, Clone)]
12pub struct ContainerAnalysisInfo {
13 pub size: u32,
14 pub dropped: bool,
15 pub depth: u32,
16 pub ops_num: u32,
17 pub last_edit_time: Timestamp,
18}
19
20impl DocAnalysis {
21 pub fn analyze(doc: &LoroDoc) -> Self {
22 let mut ops_nums = FxHashMap::default();
23 let mut last_edit_time = FxHashMap::default();
24 {
25 let oplog = doc.oplog().lock();
26 oplog.change_store().visit_all_changes(&mut |c| {
27 for op in c.ops().iter() {
28 let idx = op.container;
29 let info = ops_nums.entry(idx).or_insert(0);
30 *info += op.atom_len();
31
32 let time = last_edit_time.entry(idx).or_insert(c.timestamp());
33 if *time < c.timestamp() {
34 *time = c.timestamp();
35 }
36 }
37 });
38 }
39
40 let mut containers = FxHashMap::default();
41 let mut state = doc.app_state().lock();
42 let alive_containers = state
43 .get_all_alive_containers()
44 .expect("analyzing a valid document should resolve its container hierarchy");
45 for (idx, c) in state.iter_all_containers_mut() {
46 let ops_num = ops_nums.get(&idx).unwrap_or(&0);
47 let id = doc.arena().get_container_id(idx).unwrap();
48 let dropped = !alive_containers.contains(&id);
49 containers.insert(
50 id,
51 ContainerAnalysisInfo {
52 depth: c.depth() as u32,
53 dropped,
54 size: c.encode().len() as u32,
55 ops_num: *ops_num as u32,
56 last_edit_time: *last_edit_time.get(&idx).unwrap_or(&0),
57 },
58 );
59 }
60
61 Self { containers }
62 }
63
64 #[allow(unused)]
65 pub fn len(&self) -> usize {
66 self.containers.len()
67 }
68
69 #[allow(unused)]
70 #[must_use]
71 pub fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74
75 pub fn dropped_len(&self) -> usize {
76 self.containers
77 .iter()
78 .filter(|(_, info)| info.dropped)
79 .count()
80 }
81
82 pub fn tiny_container_len(&self) -> usize {
83 self.containers
84 .iter()
85 .filter(|(_, info)| info.size < 128)
86 .count()
87 }
88
89 pub fn large_container_len(&self) -> usize {
90 self.containers
91 .iter()
92 .filter(|(_, info)| info.size >= 1024)
93 .count()
94 }
95}