loro_internal/state/
analyzer.rs1use crate::{change::Timestamp, LoroDoc};
2use rustc_hash::FxHashMap;
3use loro_common::ContainerID;
4use rle::HasLength;
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().unwrap();
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().unwrap();
42 let alive_containers = state.get_all_alive_containers();
43 for (&idx, c) in state.iter_all_containers_mut() {
44 let ops_num = ops_nums.get(&idx).unwrap_or(&0);
45 let id = doc.arena().get_container_id(idx).unwrap();
46 let dropped = !alive_containers.contains(&id);
47 containers.insert(
48 id,
49 ContainerAnalysisInfo {
50 depth: c.depth() as u32,
51 dropped,
52 size: c.encode().len() as u32,
53 ops_num: *ops_num as u32,
54 last_edit_time: *last_edit_time.get(&idx).unwrap_or(&0),
55 },
56 );
57 }
58
59 Self { containers }
60 }
61
62 #[allow(unused)]
63 pub fn len(&self) -> usize {
64 self.containers.len()
65 }
66
67 #[allow(unused)]
68 #[must_use]
69 pub fn is_empty(&self) -> bool {
70 self.len() == 0
71 }
72
73 pub fn dropped_len(&self) -> usize {
74 self.containers
75 .iter()
76 .filter(|(_, info)| info.dropped)
77 .count()
78 }
79
80 pub fn tiny_container_len(&self) -> usize {
81 self.containers
82 .iter()
83 .filter(|(_, info)| info.size < 128)
84 .count()
85 }
86
87 pub fn large_container_len(&self) -> usize {
88 self.containers
89 .iter()
90 .filter(|(_, info)| info.size >= 1024)
91 .count()
92 }
93}