loro_internal/state/
analyzer.rs

1use 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}