messagepack_core/timestamp/
decode.rs

1//! Timestamp decoding implementations and tests.
2
3use super::{Timestamp32, Timestamp64, Timestamp96};
4use crate::{
5    Format,
6    decode::{DecodeBorrowed, Error as DecodeError},
7    extension::FixedExtension,
8    io::IoRead,
9};
10
11impl<'de> DecodeBorrowed<'de> for Timestamp32 {
12    type Value = Timestamp32;
13
14    fn decode_borrowed_with_format<R>(
15        format: Format,
16        reader: &mut R,
17    ) -> core::result::Result<Self::Value, DecodeError<R::Error>>
18    where
19        R: IoRead<'de>,
20    {
21        match format {
22            Format::FixExt4 => {}
23            _ => return Err(DecodeError::UnexpectedFormat),
24        }
25        let ext = FixedExtension::<4>::decode_borrowed_with_format(format, reader)?;
26        let timestamp = ext.try_into().map_err(|_| DecodeError::InvalidData)?;
27        Ok(timestamp)
28    }
29}
30
31impl<'de> DecodeBorrowed<'de> for Timestamp64 {
32    type Value = Timestamp64;
33
34    fn decode_borrowed_with_format<R>(
35        format: Format,
36        reader: &mut R,
37    ) -> core::result::Result<Self::Value, DecodeError<R::Error>>
38    where
39        R: IoRead<'de>,
40    {
41        match format {
42            Format::FixExt8 => {}
43            _ => return Err(DecodeError::UnexpectedFormat),
44        }
45        let ext = FixedExtension::<8>::decode_borrowed_with_format(format, reader)?;
46        let timestamp = ext.try_into().map_err(|_| DecodeError::InvalidData)?;
47        Ok(timestamp)
48    }
49}
50
51impl<'de> DecodeBorrowed<'de> for Timestamp96 {
52    type Value = Timestamp96;
53
54    fn decode_borrowed_with_format<R>(
55        format: Format,
56        reader: &mut R,
57    ) -> core::result::Result<Self::Value, DecodeError<R::Error>>
58    where
59        R: IoRead<'de>,
60    {
61        match format {
62            Format::Ext8 => {}
63            _ => return Err(DecodeError::UnexpectedFormat),
64        }
65        const TIMESTAMP96_DATA_LENGTH: usize = 12;
66        let ext =
67            FixedExtension::<TIMESTAMP96_DATA_LENGTH>::decode_borrowed_with_format(format, reader)?;
68        let timestamp = ext.try_into().map_err(|_| DecodeError::InvalidData)?;
69        Ok(timestamp)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::decode::Decode;
77
78    const TIMESTAMP_EXT_TYPE_U8: u8 = 255; // -1
79
80    #[test]
81    fn decode_success_timestamp32() {
82        let secs: u32 = 1234567890;
83        let mut buf = vec![0xd6, TIMESTAMP_EXT_TYPE_U8];
84        buf.extend_from_slice(&secs.to_be_bytes());
85
86        let mut r = crate::io::SliceReader::new(&buf);
87        let ts = Timestamp32::decode(&mut r).unwrap();
88        assert_eq!(ts.seconds(), secs);
89        assert!(r.rest().is_empty());
90    }
91
92    #[test]
93    fn decode_failed_timestamp32_invalid_ext_type() {
94        let secs: u32 = 1;
95        let mut buf = vec![0xd6, 0]; // ext type != -1
96        buf.extend_from_slice(&secs.to_be_bytes());
97
98        let mut r = crate::io::SliceReader::new(&buf);
99        let err = Timestamp32::decode(&mut r).unwrap_err();
100        assert_eq!(err, DecodeError::InvalidData);
101    }
102
103    #[test]
104    fn decode_failed_timestamp32_eof_data() {
105        let secs: u32 = 123;
106        let mut buf = vec![0xd6, TIMESTAMP_EXT_TYPE_U8];
107        buf.extend_from_slice(&secs.to_be_bytes()[..3]); // 1 byte short
108
109        let mut r = crate::io::SliceReader::new(&buf);
110        let err = Timestamp32::decode(&mut r).unwrap_err();
111        assert!(matches!(err, DecodeError::Io(_)));
112    }
113
114    #[test]
115    fn decode_success_timestamp64() {
116        let secs: u64 = 1234567890;
117        let nanos: u32 = 789;
118
119        let data = ((nanos as u64) << 34) | secs;
120        let mut buf = vec![0xd7, TIMESTAMP_EXT_TYPE_U8];
121        buf.extend_from_slice(&data.to_be_bytes());
122
123        let mut r = crate::io::SliceReader::new(&buf);
124        let ts = Timestamp64::decode(&mut r).unwrap();
125        assert_eq!(ts.seconds(), secs);
126        assert_eq!(ts.nanos(), nanos);
127        assert!(r.rest().is_empty());
128    }
129
130    #[test]
131    fn decode_failed_timestamp64_unexpected_format() {
132        let mut buf = vec![0xd6, TIMESTAMP_EXT_TYPE_U8]; // FixExt4, not FixExt8
133        buf.extend_from_slice(&0u64.to_be_bytes());
134
135        let mut r = crate::io::SliceReader::new(&buf);
136        let err = Timestamp64::decode(&mut r).unwrap_err();
137        assert_eq!(err, DecodeError::UnexpectedFormat);
138    }
139
140    #[test]
141    fn decode_failed_timestamp64_invalid_ext_type() {
142        let mut buf = vec![0xd7, 0]; // ext type != -1
143        buf.extend_from_slice(&0u64.to_be_bytes());
144
145        let mut r = crate::io::SliceReader::new(&buf);
146        let err = Timestamp64::decode(&mut r).unwrap_err();
147        assert_eq!(err, DecodeError::InvalidData);
148    }
149
150    #[test]
151    fn decode_failed_timestamp64_eof_data() {
152        let mut buf = vec![0xd7, TIMESTAMP_EXT_TYPE_U8];
153        buf.extend_from_slice(&[0u8; 7]); // 1 byte short
154
155        let mut r = crate::io::SliceReader::new(&buf);
156        let err = Timestamp64::decode(&mut r).unwrap_err();
157        assert!(matches!(err, DecodeError::Io(_)));
158    }
159
160    #[test]
161    fn decode_failed_timestamp64_invalid_nanos() {
162        // Construct data with nanos = 1_000_000_000 (out of spec)
163        let secs: u64 = 0;
164        let nanos: u64 = 1_000_000_000;
165        let data = (nanos << 34) | secs;
166        let mut buf = vec![0xd7, TIMESTAMP_EXT_TYPE_U8];
167        buf.extend_from_slice(&data.to_be_bytes());
168
169        let mut r = crate::io::SliceReader::new(&buf);
170        let err = Timestamp64::decode(&mut r).unwrap_err();
171        assert_eq!(err, DecodeError::InvalidData);
172    }
173
174    #[test]
175    fn decode_success_timestamp96_positive() {
176        let secs: i64 = 123456;
177        let nanos: u32 = 789;
178
179        let mut buf = vec![0xc7, 12, TIMESTAMP_EXT_TYPE_U8];
180        buf.extend_from_slice(&nanos.to_be_bytes());
181        buf.extend_from_slice(&secs.to_be_bytes());
182
183        let mut r = crate::io::SliceReader::new(&buf);
184        let ts = Timestamp96::decode(&mut r).unwrap();
185        assert_eq!(ts.seconds(), secs);
186        assert_eq!(ts.nanos(), nanos);
187        assert!(r.rest().is_empty());
188    }
189
190    #[test]
191    fn decode_success_timestamp96_negative() {
192        let secs: i64 = -123;
193        let nanos: u32 = 42;
194
195        let mut buf = vec![0xc7, 12, TIMESTAMP_EXT_TYPE_U8];
196        buf.extend_from_slice(&nanos.to_be_bytes());
197        buf.extend_from_slice(&secs.to_be_bytes());
198
199        let mut r = crate::io::SliceReader::new(&buf);
200        let ts = Timestamp96::decode(&mut r).unwrap();
201        assert_eq!(ts.seconds(), secs);
202        assert_eq!(ts.nanos(), nanos);
203        assert!(r.rest().is_empty());
204    }
205
206    #[test]
207    fn decode_failed_timestamp96_unexpected_format() {
208        // FixExt8 header instead of Ext8
209        let mut buf = vec![0xd7, TIMESTAMP_EXT_TYPE_U8];
210        buf.extend_from_slice(&[0u8; 8]);
211
212        let mut r = crate::io::SliceReader::new(&buf);
213        let err = Timestamp96::decode(&mut r).unwrap_err();
214        assert_eq!(err, DecodeError::UnexpectedFormat);
215    }
216
217    #[test]
218    fn decode_failed_timestamp96_invalid_length() {
219        // Ext8 length != 12
220        let mut buf = vec![0xc7, 11, TIMESTAMP_EXT_TYPE_U8];
221        buf.extend_from_slice(&[0u8; 11]);
222
223        let mut r = crate::io::SliceReader::new(&buf);
224        let err = Timestamp96::decode(&mut r).unwrap_err();
225        assert_eq!(err, DecodeError::InvalidData);
226    }
227
228    #[test]
229    fn decode_failed_timestamp96_invalid_ext_type() {
230        let secs: i64 = 1;
231        let nanos: u32 = 2;
232
233        let mut buf = vec![0xc7, 12, 0]; // ext type != -1
234        buf.extend_from_slice(&nanos.to_be_bytes());
235        buf.extend_from_slice(&secs.to_be_bytes());
236
237        let mut r = crate::io::SliceReader::new(&buf);
238        let err = Timestamp96::decode(&mut r).unwrap_err();
239        assert_eq!(err, DecodeError::InvalidData);
240    }
241
242    #[test]
243    fn decode_failed_timestamp96_eof_data() {
244        // length says 12 but provide 11
245        let mut buf = vec![0xc7, 12, TIMESTAMP_EXT_TYPE_U8];
246        buf.extend_from_slice(&[0u8; 11]);
247
248        let mut r = crate::io::SliceReader::new(&buf);
249        let err = Timestamp96::decode(&mut r).unwrap_err();
250        assert!(matches!(err, DecodeError::Io(_)));
251    }
252
253    #[test]
254    fn decode_failed_timestamp96_invalid_nanos() {
255        // nanos = 1_000_000_000 should be rejected
256        let nanos: u32 = 1_000_000_000;
257        let secs: i64 = 0;
258        let mut buf = vec![0xc7, 12, TIMESTAMP_EXT_TYPE_U8];
259        buf.extend_from_slice(&nanos.to_be_bytes());
260        buf.extend_from_slice(&secs.to_be_bytes());
261
262        let mut r = crate::io::SliceReader::new(&buf);
263        let err = Timestamp96::decode(&mut r).unwrap_err();
264        assert_eq!(err, DecodeError::InvalidData);
265    }
266}