Skip to main content

deser_msgpack/
ext.rs

1use alloc::borrow::Cow;
2use alloc::vec::Vec;
3
4use deser_core::State;
5use deser_core::de::{Deserialize, Slot, default_atom};
6use deser_core::ext::{ExtValue, Extension, Timestamp};
7use deser_core::ser::{Emit, Serialize};
8use deser_core::{Atom, Bytes, Error};
9
10/// The extension type of timestamps.
11pub(crate) const TIMESTAMP: i8 = -1;
12
13/// A MessagePack extension value: a type and binary data.
14///
15/// Extensions whose type the format does not understand are passed through
16/// deser as extension atoms of this type.  Their fallback is the binary
17/// data, so an extension deserializes into `Vec<u8>` if the type is not of
18/// interest.  Values of this type are written as extensions.
19///
20/// ```
21/// use deser_msgpack::Ext;
22///
23/// let bytes = deser_msgpack::to_vec(&Ext::new(7, vec![1, 2, 3])).unwrap();
24/// assert_eq!(bytes, [0xc7, 0x03, 0x07, 0x01, 0x02, 0x03]);
25/// let value: Ext = deser_msgpack::from_slice(&bytes).unwrap();
26/// assert_eq!(value, Ext::new(7, vec![1, 2, 3]));
27/// ```
28///
29/// Timestamps (type `-1`) are converted to and from
30/// [`Timestamp`] instead.  They are still
31/// accepted by this type which then holds their encoding.
32#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct Ext {
34    /// The type of the extension (`-128` to `-1` are reserved by the
35    /// specification).
36    pub kind: i8,
37    /// The data of the extension.
38    pub data: Vec<u8>,
39}
40
41impl Ext {
42    /// Creates an extension value.
43    pub fn new(kind: i8, data: impl Into<Vec<u8>>) -> Ext {
44        Ext {
45            kind,
46            data: data.into(),
47        }
48    }
49}
50
51impl Extension for Ext {
52    fn name(&self) -> &str {
53        "msgpack extension"
54    }
55
56    fn fallback(&self) -> Atom<'_> {
57        Atom::Bytes(Bytes::borrowed(&self.data))
58    }
59}
60
61impl Serialize for Ext {
62    fn serialize<'a>(value: &'a Self, _state: &mut State) -> Result<Emit<'a>, Error> {
63        Ok(Emit::Atom(Atom::Ext(ExtValue::borrowed(value))))
64    }
65}
66
67impl<'de> Deserialize<'de> for Ext {
68    fn deserialize_atom(slot: &mut Slot<Self>, atom: Atom, state: &mut State) -> Result<(), Error> {
69        let value = match atom {
70            Atom::Ext(ref ext) => {
71                if let Some(value) = ext.downcast_ref::<Ext>() {
72                    value.clone()
73                } else if let Some(value) = ext.downcast_ref::<Timestamp>() {
74                    let mut buf = [0; 12];
75                    Ext::new(TIMESTAMP, encode_timestamp(value, &mut buf))
76                } else {
77                    return default_atom(slot, atom, state);
78                }
79            }
80            other => return default_atom(slot, other, state),
81        };
82        slot.set(value);
83        Ok(())
84    }
85
86    fn expecting() -> Cow<'static, str> {
87        Cow::Borrowed("msgpack extension")
88    }
89}
90
91/// Decodes the data of a timestamp extension.
92///
93/// Returns `None` if the data is not a valid timestamp.
94pub(crate) fn decode_timestamp(data: &[u8]) -> Option<Timestamp> {
95    let (seconds, nanosecond) = match data.len() {
96        // timestamp 32: seconds
97        4 => (i64::from(u32::from_be_bytes(data.try_into().unwrap())), 0),
98        // timestamp 64: 30 bits nanoseconds, 34 bits seconds
99        8 => {
100            let value = u64::from_be_bytes(data.try_into().unwrap());
101            ((value & 0x3_ffff_ffff) as i64, (value >> 34) as u32)
102        }
103        // timestamp 96: 32 bits nanoseconds, 64 bits signed seconds
104        12 => (
105            i64::from_be_bytes(data[4..].try_into().unwrap()),
106            u32::from_be_bytes(data[..4].try_into().unwrap()),
107        ),
108        _ => return None,
109    };
110    (nanosecond < 1_000_000_000).then_some(Timestamp {
111        seconds,
112        nanosecond,
113    })
114}
115
116/// Encodes a timestamp in the smallest format.
117pub(crate) fn encode_timestamp<'b>(value: &Timestamp, buf: &'b mut [u8; 12]) -> &'b [u8] {
118    if value.seconds >> 34 == 0 {
119        if value.nanosecond == 0 && value.seconds <= i64::from(u32::MAX) {
120            buf[..4].copy_from_slice(&(value.seconds as u32).to_be_bytes());
121            &buf[..4]
122        } else {
123            let packed = (u64::from(value.nanosecond) << 34) | value.seconds as u64;
124            buf[..8].copy_from_slice(&packed.to_be_bytes());
125            &buf[..8]
126        }
127    } else {
128        buf[..4].copy_from_slice(&value.nanosecond.to_be_bytes());
129        buf[4..].copy_from_slice(&value.seconds.to_be_bytes());
130        &buf[..]
131    }
132}
133
134#[test]
135fn test_timestamp_codec() {
136    for (seconds, nanosecond, len) in [
137        (0, 0, 4),
138        (u32::MAX as i64, 0, 4),
139        (u32::MAX as i64 + 1, 0, 8),
140        (0, 1, 8),
141        ((1 << 34) - 1, 999_999_999, 8),
142        (1 << 34, 0, 12),
143        (-1, 0, 12),
144        (i64::MIN, 999_999_999, 12),
145        (i64::MAX, 0, 12),
146    ] {
147        let value = Timestamp {
148            seconds,
149            nanosecond,
150        };
151        let mut buf = [0; 12];
152        let data = encode_timestamp(&value, &mut buf);
153        assert_eq!(data.len(), len, "{:?}", value);
154        assert_eq!(decode_timestamp(data), Some(value));
155    }
156    // nanoseconds out of range
157    assert_eq!(decode_timestamp(&[0xee, 0x6b, 0x28, 0, 0, 0, 0, 0]), None);
158    assert_eq!(
159        decode_timestamp(&[0x3b, 0x9a, 0xca, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
160        None
161    );
162    assert_eq!(decode_timestamp(&[0, 0, 0]), None);
163}