use alloc::borrow::Cow;
use alloc::vec::Vec;
use deser_core::State;
use deser_core::de::{Deserialize, Slot, default_atom};
use deser_core::ext::{ExtValue, Extension, Timestamp};
use deser_core::ser::{Emit, Serialize};
use deser_core::{Atom, Bytes, Error};
pub(crate) const TIMESTAMP: i8 = -1;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ext {
pub kind: i8,
pub data: Vec<u8>,
}
impl Ext {
pub fn new(kind: i8, data: impl Into<Vec<u8>>) -> Ext {
Ext {
kind,
data: data.into(),
}
}
}
impl Extension for Ext {
fn name(&self) -> &str {
"msgpack extension"
}
fn fallback(&self) -> Atom<'_> {
Atom::Bytes(Bytes::borrowed(&self.data))
}
}
impl Serialize for Ext {
fn serialize<'a>(value: &'a Self, _state: &mut State) -> Result<Emit<'a>, Error> {
Ok(Emit::Atom(Atom::Ext(ExtValue::borrowed(value))))
}
}
impl<'de> Deserialize<'de> for Ext {
fn deserialize_atom(slot: &mut Slot<Self>, atom: Atom, state: &mut State) -> Result<(), Error> {
let value = match atom {
Atom::Ext(ref ext) => {
if let Some(value) = ext.downcast_ref::<Ext>() {
value.clone()
} else if let Some(value) = ext.downcast_ref::<Timestamp>() {
let mut buf = [0; 12];
Ext::new(TIMESTAMP, encode_timestamp(value, &mut buf))
} else {
return default_atom(slot, atom, state);
}
}
other => return default_atom(slot, other, state),
};
slot.set(value);
Ok(())
}
fn expecting() -> Cow<'static, str> {
Cow::Borrowed("msgpack extension")
}
}
pub(crate) fn decode_timestamp(data: &[u8]) -> Option<Timestamp> {
let (seconds, nanosecond) = match data.len() {
4 => (i64::from(u32::from_be_bytes(data.try_into().unwrap())), 0),
8 => {
let value = u64::from_be_bytes(data.try_into().unwrap());
((value & 0x3_ffff_ffff) as i64, (value >> 34) as u32)
}
12 => (
i64::from_be_bytes(data[4..].try_into().unwrap()),
u32::from_be_bytes(data[..4].try_into().unwrap()),
),
_ => return None,
};
(nanosecond < 1_000_000_000).then_some(Timestamp {
seconds,
nanosecond,
})
}
pub(crate) fn encode_timestamp<'b>(value: &Timestamp, buf: &'b mut [u8; 12]) -> &'b [u8] {
if value.seconds >> 34 == 0 {
if value.nanosecond == 0 && value.seconds <= i64::from(u32::MAX) {
buf[..4].copy_from_slice(&(value.seconds as u32).to_be_bytes());
&buf[..4]
} else {
let packed = (u64::from(value.nanosecond) << 34) | value.seconds as u64;
buf[..8].copy_from_slice(&packed.to_be_bytes());
&buf[..8]
}
} else {
buf[..4].copy_from_slice(&value.nanosecond.to_be_bytes());
buf[4..].copy_from_slice(&value.seconds.to_be_bytes());
&buf[..]
}
}
#[test]
fn test_timestamp_codec() {
for (seconds, nanosecond, len) in [
(0, 0, 4),
(u32::MAX as i64, 0, 4),
(u32::MAX as i64 + 1, 0, 8),
(0, 1, 8),
((1 << 34) - 1, 999_999_999, 8),
(1 << 34, 0, 12),
(-1, 0, 12),
(i64::MIN, 999_999_999, 12),
(i64::MAX, 0, 12),
] {
let value = Timestamp {
seconds,
nanosecond,
};
let mut buf = [0; 12];
let data = encode_timestamp(&value, &mut buf);
assert_eq!(data.len(), len, "{:?}", value);
assert_eq!(decode_timestamp(data), Some(value));
}
assert_eq!(decode_timestamp(&[0xee, 0x6b, 0x28, 0, 0, 0, 0, 0]), None);
assert_eq!(
decode_timestamp(&[0x3b, 0x9a, 0xca, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
None
);
assert_eq!(decode_timestamp(&[0, 0, 0]), None);
}