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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
use super::*;

const SSR_REPORT_BLOCK_LENGTH: u16 = 4 + 2 * 2 + 4 * 6 + 4;

/// StatisticsSummaryReportBlock encodes a Statistics Summary Report
/// Block as described in RFC 3611, section 4.6.
///
///  0                   1                   2                   3
///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |     BT=6      |L|D|J|ToH|rsvd.|       block length = 9        |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                        ssrc of source                         |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |          begin_seq            |             end_seq           |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                        lost_packets                           |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                        dup_packets                            |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                         min_jitter                            |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                         max_jitter                            |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                         mean_jitter                           |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |                         dev_jitter                            |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | min_ttl_or_hl | max_ttl_or_hl |mean_ttl_or_hl | dev_ttl_or_hl |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct StatisticsSummaryReportBlock {
    //not included in marshal/unmarshal
    pub loss_reports: bool,
    pub duplicate_reports: bool,
    pub jitter_reports: bool,
    pub ttl_or_hop_limit: TTLorHopLimitType,

    //marshal/unmarshal
    pub ssrc: u32,
    pub begin_seq: u16,
    pub end_seq: u16,
    pub lost_packets: u32,
    pub dup_packets: u32,
    pub min_jitter: u32,
    pub max_jitter: u32,
    pub mean_jitter: u32,
    pub dev_jitter: u32,
    pub min_ttl_or_hl: u8,
    pub max_ttl_or_hl: u8,
    pub mean_ttl_or_hl: u8,
    pub dev_ttl_or_hl: u8,
}

impl fmt::Display for StatisticsSummaryReportBlock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{self:?}")
    }
}

/// TTLorHopLimitType encodes values for the ToH field in
/// a StatisticsSummaryReportBlock
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum TTLorHopLimitType {
    #[default]
    Missing = 0,
    IPv4 = 1,
    IPv6 = 2,
}

impl From<u8> for TTLorHopLimitType {
    fn from(v: u8) -> Self {
        match v {
            1 => TTLorHopLimitType::IPv4,
            2 => TTLorHopLimitType::IPv4,
            _ => TTLorHopLimitType::Missing,
        }
    }
}

impl fmt::Display for TTLorHopLimitType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match *self {
            TTLorHopLimitType::Missing => "[ToH Missing]",
            TTLorHopLimitType::IPv4 => "[ToH = IPv4]",
            TTLorHopLimitType::IPv6 => "[ToH = IPv6]",
        };
        write!(f, "{s}")
    }
}

impl StatisticsSummaryReportBlock {
    pub fn xr_header(&self) -> XRHeader {
        let mut type_specific = 0x00;
        if self.loss_reports {
            type_specific |= 0x80;
        }
        if self.duplicate_reports {
            type_specific |= 0x40;
        }
        if self.jitter_reports {
            type_specific |= 0x20;
        }
        type_specific |= (self.ttl_or_hop_limit as u8 & 0x03) << 3;

        XRHeader {
            block_type: BlockType::StatisticsSummary,
            type_specific,
            block_length: (self.raw_size() / 4 - 1) as u16,
        }
    }
}

impl Packet for StatisticsSummaryReportBlock {
    fn header(&self) -> Header {
        Header::default()
    }

    /// destination_ssrc returns an array of ssrc values that this report block refers to.
    fn destination_ssrc(&self) -> Vec<u32> {
        vec![self.ssrc]
    }

    fn raw_size(&self) -> usize {
        XR_HEADER_LENGTH + SSR_REPORT_BLOCK_LENGTH as usize
    }

    fn as_any(&self) -> &(dyn Any + Send + Sync) {
        self
    }
    fn equal(&self, other: &(dyn Packet + Send + Sync)) -> bool {
        other
            .as_any()
            .downcast_ref::<StatisticsSummaryReportBlock>()
            .map_or(false, |a| self == a)
    }
    fn cloned(&self) -> Box<dyn Packet + Send + Sync> {
        Box::new(self.clone())
    }
}

impl MarshalSize for StatisticsSummaryReportBlock {
    fn marshal_size(&self) -> usize {
        self.raw_size()
    }
}

impl Marshal for StatisticsSummaryReportBlock {
    /// marshal_to encodes the StatisticsSummaryReportBlock in binary
    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
        if buf.remaining_mut() < self.marshal_size() {
            return Err(error::Error::BufferTooShort.into());
        }

        let h = self.xr_header();
        let n = h.marshal_to(buf)?;
        buf = &mut buf[n..];

        buf.put_u32(self.ssrc);
        buf.put_u16(self.begin_seq);
        buf.put_u16(self.end_seq);
        buf.put_u32(self.lost_packets);
        buf.put_u32(self.dup_packets);
        buf.put_u32(self.min_jitter);
        buf.put_u32(self.max_jitter);
        buf.put_u32(self.mean_jitter);
        buf.put_u32(self.dev_jitter);
        buf.put_u8(self.min_ttl_or_hl);
        buf.put_u8(self.max_ttl_or_hl);
        buf.put_u8(self.mean_ttl_or_hl);
        buf.put_u8(self.dev_ttl_or_hl);

        Ok(self.marshal_size())
    }
}

impl Unmarshal for StatisticsSummaryReportBlock {
    /// Unmarshal decodes the StatisticsSummaryReportBlock from binary
    fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
    where
        Self: Sized,
        B: Buf,
    {
        if raw_packet.remaining() < XR_HEADER_LENGTH {
            return Err(error::Error::PacketTooShort.into());
        }

        let xr_header = XRHeader::unmarshal(raw_packet)?;
        let block_length = xr_header.block_length * 4;
        if block_length != SSR_REPORT_BLOCK_LENGTH || raw_packet.remaining() < block_length as usize
        {
            return Err(error::Error::PacketTooShort.into());
        }

        let loss_reports = xr_header.type_specific & 0x80 != 0;
        let duplicate_reports = xr_header.type_specific & 0x40 != 0;
        let jitter_reports = xr_header.type_specific & 0x20 != 0;
        let ttl_or_hop_limit: TTLorHopLimitType = ((xr_header.type_specific & 0x18) >> 3).into();

        let ssrc = raw_packet.get_u32();
        let begin_seq = raw_packet.get_u16();
        let end_seq = raw_packet.get_u16();
        let lost_packets = raw_packet.get_u32();
        let dup_packets = raw_packet.get_u32();
        let min_jitter = raw_packet.get_u32();
        let max_jitter = raw_packet.get_u32();
        let mean_jitter = raw_packet.get_u32();
        let dev_jitter = raw_packet.get_u32();
        let min_ttl_or_hl = raw_packet.get_u8();
        let max_ttl_or_hl = raw_packet.get_u8();
        let mean_ttl_or_hl = raw_packet.get_u8();
        let dev_ttl_or_hl = raw_packet.get_u8();

        Ok(StatisticsSummaryReportBlock {
            loss_reports,
            duplicate_reports,
            jitter_reports,
            ttl_or_hop_limit,

            ssrc,
            begin_seq,
            end_seq,
            lost_packets,
            dup_packets,
            min_jitter,
            max_jitter,
            mean_jitter,
            dev_jitter,
            min_ttl_or_hl,
            max_ttl_or_hl,
            mean_ttl_or_hl,
            dev_ttl_or_hl,
        })
    }
}