loro_internal/state/
analyzer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::{change::Timestamp, LoroDoc};
use fxhash::FxHashMap;
use loro_common::ContainerID;
use rle::HasLength;

#[derive(Debug, Clone)]
pub struct DocAnalysis {
    pub containers: FxHashMap<ContainerID, ContainerAnalysisInfo>,
}

#[derive(Debug, Clone)]
pub struct ContainerAnalysisInfo {
    pub size: u32,
    pub dropped: bool,
    pub depth: u32,
    pub ops_num: u32,
    pub last_edit_time: Timestamp,
}

impl DocAnalysis {
    pub fn analyze(doc: &LoroDoc) -> Self {
        let mut ops_nums = FxHashMap::default();
        let mut last_edit_time = FxHashMap::default();
        {
            let oplog = doc.oplog().try_lock().unwrap();
            oplog.change_store().visit_all_changes(&mut |c| {
                for op in c.ops().iter() {
                    let idx = op.container;
                    let info = ops_nums.entry(idx).or_insert(0);
                    *info += op.atom_len();

                    let time = last_edit_time.entry(idx).or_insert(c.timestamp());
                    if *time < c.timestamp() {
                        *time = c.timestamp();
                    }
                }
            });
        }

        let mut containers = FxHashMap::default();
        let mut state = doc.app_state().try_lock().unwrap();
        let alive_containers = state.get_all_alive_containers();
        for (&idx, c) in state.iter_all_containers_mut() {
            let ops_num = ops_nums.get(&idx).unwrap_or(&0);
            let id = doc.arena().get_container_id(idx).unwrap();
            let dropped = !alive_containers.contains(&id);
            containers.insert(
                id,
                ContainerAnalysisInfo {
                    depth: c.depth() as u32,
                    dropped,
                    size: c.encode().len() as u32,
                    ops_num: *ops_num as u32,
                    last_edit_time: *last_edit_time.get(&idx).unwrap_or(&0),
                },
            );
        }

        Self { containers }
    }

    #[allow(unused)]
    pub fn len(&self) -> usize {
        self.containers.len()
    }

    #[allow(unused)]
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn dropped_len(&self) -> usize {
        self.containers
            .iter()
            .filter(|(_, info)| info.dropped)
            .count()
    }

    pub fn tiny_container_len(&self) -> usize {
        self.containers
            .iter()
            .filter(|(_, info)| info.size < 128)
            .count()
    }

    pub fn large_container_len(&self) -> usize {
        self.containers
            .iter()
            .filter(|(_, info)| info.size >= 1024)
            .count()
    }
}