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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use serde::ser::{Serialize, SerializeMap, Serializer};

use crate::{ProcessHandle, Timestamp};

/// A counter. Can be created with [`Profile::add_counter`](crate::Profile::add_counter).
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct CounterHandle(pub(crate) usize);

#[derive(Debug)]
pub struct Counter {
    name: String,
    category: String,
    description: String,
    process: ProcessHandle,
    pid: String,
    samples: CounterSamples,
}

impl Counter {
    pub fn new(
        name: &str,
        category: &str,
        description: &str,
        process: ProcessHandle,
        pid: &str,
    ) -> Self {
        Counter {
            name: name.to_owned(),
            category: category.to_owned(),
            description: description.to_owned(),
            process,
            pid: pid.to_owned(),
            samples: CounterSamples::new(),
        }
    }

    pub fn process(&self) -> ProcessHandle {
        self.process
    }

    pub fn add_sample(
        &mut self,
        timestamp: Timestamp,
        value_delta: f64,
        number_of_operations_delta: u32,
    ) {
        self.samples
            .add_sample(timestamp, value_delta, number_of_operations_delta)
    }

    pub fn as_serializable(&self, main_thread_index: usize) -> impl Serialize + '_ {
        SerializableCounter {
            counter: self,
            main_thread_index,
        }
    }
}

struct SerializableCounter<'a> {
    counter: &'a Counter,
    /// The index of the main thread for the counter's process in the profile threads list.
    main_thread_index: usize,
}

impl<'a> Serialize for SerializableCounter<'a> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("category", &self.counter.category)?;
        map.serialize_entry("name", &self.counter.name)?;
        map.serialize_entry("description", &self.counter.description)?;
        map.serialize_entry("mainThreadIndex", &self.main_thread_index)?;
        map.serialize_entry("pid", &self.counter.pid)?;
        map.serialize_entry(
            "sampleGroups",
            &[SerializableCounterSampleGroup(self.counter)],
        )?;
        map.end()
    }
}

struct SerializableCounterSampleGroup<'a>(&'a Counter);

impl<'a> Serialize for SerializableCounterSampleGroup<'a> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("id", &0)?; // It's not clear what the meaning of this ID is.
        map.serialize_entry("samples", &self.0.samples)?;
        map.end()
    }
}

#[derive(Debug)]
struct CounterSamples {
    time: Vec<Timestamp>,
    number: Vec<u32>,
    count: Vec<f64>,
}

impl CounterSamples {
    pub fn new() -> Self {
        Self {
            time: Vec::new(),
            number: Vec::new(),
            count: Vec::new(),
        }
    }

    pub fn add_sample(
        &mut self,
        timestamp: Timestamp,
        value_delta: f64,
        number_of_operations_delta: u32,
    ) {
        self.time.push(timestamp);
        self.count.push(value_delta);
        self.number.push(number_of_operations_delta);
    }
}

impl Serialize for CounterSamples {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let len = self.time.len();
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("length", &len)?;
        map.serialize_entry("count", &self.count)?;
        map.serialize_entry("number", &self.number)?;
        map.serialize_entry("time", &self.time)?;
        map.end()
    }
}