diff --git a/rmp-serde/src/decode.rs b/rmp-serde/src/decode.rs index 382981c1..493a08bd 100644 --- a/rmp-serde/src/decode.rs +++ b/rmp-serde/src/decode.rs @@ -21,7 +21,7 @@ use rmp::decode::{self, DecodeStringError, MarkerReadError, NumValueReadError, R use rmp::Marker; use crate::config::{BinaryConfig, DefaultConfig, HumanReadableConfig, SerializerConfig}; -use crate::MSGPACK_EXT_STRUCT_NAME; +use crate::{MSGPACK_EXT_STRUCT_NAME, MSGPACK_TIMESTAMP_STRUCT_NAME}; /// Enum representing errors that can occur while decoding MessagePack data. #[derive(Debug)] @@ -698,6 +698,11 @@ impl<'de, 'a, R: ReadSlice<'de>, C: SerializerConfig> serde::Deserializer<'de> f return visitor.visit_newtype_struct(ext_de); } + if name == MSGPACK_TIMESTAMP_STRUCT_NAME { + let ts_de = TimestampDeserializer::new(&mut self.rd); + return ts_de.deserialize_any(visitor); + } + visitor.visit_newtype_struct(self) } @@ -1191,3 +1196,34 @@ where let mut de = Deserializer::from_read_ref(rd); Deserialize::deserialize(&mut de) } + +#[derive(Debug)] +pub(crate) struct TimestampDeserializer<'a, R> { + rd: &'a mut R, +} + +impl<'a, R: Read> TimestampDeserializer<'a, R> { + pub(crate) fn new(rd: &'a mut R) -> Self { + Self { + rd, + } + } +} + +impl<'de, 'a, R: Read> de::Deserializer<'de> for TimestampDeserializer<'a, R> { + type Error = Error; + + fn deserialize_any(mut self, visitor: V) -> Result + where + V: Visitor<'de> { + let ts = rmp::decode::read_timestamp(&mut self.rd)?; + + visitor.visit_u128(ts.into_u128()) + } + + forward_to_deserialize_any! { + bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option + seq bytes byte_buf map unit_struct newtype_struct + struct identifier tuple enum ignored_any tuple_struct + } +} diff --git a/rmp-serde/src/encode.rs b/rmp-serde/src/encode.rs index f42b20b7..947a86b9 100644 --- a/rmp-serde/src/encode.rs +++ b/rmp-serde/src/encode.rs @@ -20,7 +20,7 @@ use rmp::{encode, Marker}; use crate::config::{ BinaryConfig, DefaultConfig, HumanReadableConfig, RuntimeConfig, SerializerConfig, StructMapConfig, StructTupleConfig }; -use crate::MSGPACK_EXT_STRUCT_NAME; +use crate::{MSGPACK_EXT_STRUCT_NAME, MSGPACK_TIMESTAMP_STRUCT_NAME}; /// This type represents all possible errors that can occur when serializing or /// deserializing MessagePack data. @@ -685,6 +685,13 @@ where return ext_se.end(); } + if name == MSGPACK_TIMESTAMP_STRUCT_NAME { + let mut ts_se = TimestampSerializer::new(&mut self.wr); + value.serialize(&mut ts_se)?; + + return Ok(()); + } + // Encode as if it's inner type. value.serialize(self) } @@ -1269,3 +1276,186 @@ impl Write for FallibleWriter { Ok(()) } } + +struct TimestampSerializer<'a, W> { + wr: &'a mut W +} + +impl<'a, W: Write> TimestampSerializer<'a, W> { + fn new(wr: &'a mut W) -> Self { + Self { wr } + } +} + +impl<'a, W: Write> serde::Serializer for &'a mut TimestampSerializer<'a, W> { + type Ok = (); + + type Error = Error; + + type SerializeSeq = serde::ser::Impossible<(), Error>; + type SerializeTuple = serde::ser::Impossible<(), Error>; + type SerializeTupleStruct = serde::ser::Impossible<(), Error>; + type SerializeTupleVariant = serde::ser::Impossible<(), Error>; + type SerializeMap = serde::ser::Impossible<(), Error>; + type SerializeStruct = serde::ser::Impossible<(), Error>; + type SerializeStructVariant = serde::ser::Impossible<(), Error>; + + fn serialize_u128(self, v: u128) -> Result { + let ts = rmp::Timestamp::from_u128(v).ok_or_else(|| Error::Syntax(format!("Not a valid Timestamp")))?; + rmp::encode::write_timestamp(self.wr, ts).map_err(|_| Error::Syntax(format!("Not a valid Timestamp")))?; + Ok(()) + } + + fn serialize_bool(self, _v: bool) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_i8(self, _v: i8) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_i16(self, _v: i16) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_i32(self, _v: i32) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_i64(self, _v: i64) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_u8(self, _v: u8) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_u16(self, _v: u16) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_u32(self, _v: u32) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_u64(self, _v: u64) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_f32(self, _v: f32) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_f64(self, _v: f64) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_char(self, _v: char) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_str(self, _v: &str) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_bytes(self, _v: &[u8]) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_none(self) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_some(self, _value: &T) -> Result + where + T: ?Sized + Serialize { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_unit(self) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + ) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::InvalidDataModel("expected u128")) + } +} diff --git a/rmp-serde/src/lib.rs b/rmp-serde/src/lib.rs index 406a257a..d02132ec 100644 --- a/rmp-serde/src/lib.rs +++ b/rmp-serde/src/lib.rs @@ -40,6 +40,7 @@ pub mod encode; /// Value::Ext(2, vec![5])); /// ``` pub const MSGPACK_EXT_STRUCT_NAME: &str = "_ExtStruct"; +pub(crate) const MSGPACK_TIMESTAMP_STRUCT_NAME: &str = "_TimestampForInternalUseOnly"; /// Helper that allows both to encode and decode strings no matter whether they contain valid or /// invalid UTF-8. @@ -335,3 +336,50 @@ impl<'de> Deserialize<'de> for RawRef<'de> { de.deserialize_any(RawRefVisitor) } } + +/// This is a wrapper for `rmp::Timestamp` which is only used for serde. +/// +/// This maps to the serde data model as follows: +/// +/// `struct _TimestampForInternalUseOnly(u128);` +/// +/// This will encode enough information for the Serializer and Deserializer +/// to properly manage the data to and from the msgpack format. +/// +/// This wrapper should ONLY be used with the msgpack Serializer and Deserializer +/// from this library. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TimestampSerde(pub rmp::Timestamp); + +impl Serialize for TimestampSerde { + fn serialize(&self, se: S) -> Result + where + S: serde::Serializer, + { + se.serialize_newtype_struct(MSGPACK_TIMESTAMP_STRUCT_NAME, &self.0.into_u128()) + } +} + +impl<'de> Deserialize<'de> for TimestampSerde { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + struct TimestampVisitor; + + impl de::Visitor<'_> for TimestampVisitor { + type Value = TimestampSerde; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("A valid TimestampSerde") + } + + fn visit_u128(self, v: u128) -> Result + where + E: de::Error, { + Ok(TimestampSerde(rmp::Timestamp::from_u128(v).ok_or_else(|| de::Error::custom("invalid TimestampSerde"))?)) + } + } + deserializer.deserialize_newtype_struct(MSGPACK_TIMESTAMP_STRUCT_NAME, TimestampVisitor) + } +} diff --git a/rmp-serde/tests/decode_derive.rs b/rmp-serde/tests/decode_derive.rs index 0aee2315..bdf86a0c 100644 --- a/rmp-serde/tests/decode_derive.rs +++ b/rmp-serde/tests/decode_derive.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use serde::Deserialize; use rmp_serde::decode::Error; -use rmp_serde::Deserializer; +use rmp_serde::{Deserializer, TimestampSerde}; #[test] fn pass_newtype() { @@ -66,6 +66,42 @@ fn pass_struct() { assert_eq!(Decoded { id: 42, value: 100500 }, actual); } +#[test] +fn pass_timestamp() { + #[derive(Debug, PartialEq, Deserialize)] + struct Decoded { + id: u32, + value: u32, + ts: TimestampSerde, + } + + let vals = [ + (Decoded { + id: 42, + value: 100500, + ts: TimestampSerde(rmp::Timestamp::from_32(9)) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xd6, 0xff, 0x0, 0x0, 0x0, 0x9]), + (Decoded { + id: 42, + value: 100500, + ts: TimestampSerde(rmp::Timestamp::from_64(9, 1).unwrap()) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xd7, 0xff, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x9]), + (Decoded { + id: 42, + value: 100500, + ts: TimestampSerde(rmp::Timestamp::from_96(9, 1).unwrap()) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xc7, 0xc, 0xff, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9]), + ]; + + for (val, raw) in vals { + let cur = Cursor::new(&raw[..]); + let mut de = Deserializer::new(cur); + let actual: Decoded = Deserialize::deserialize(&mut de).unwrap(); + + assert_eq!(val, actual); + } +} + #[test] fn pass_struct_from_map() { #[derive(Debug, PartialEq, Deserialize)] diff --git a/rmp-serde/tests/encode_derive.rs b/rmp-serde/tests/encode_derive.rs index 407c5861..e778e3c2 100644 --- a/rmp-serde/tests/encode_derive.rs +++ b/rmp-serde/tests/encode_derive.rs @@ -1,4 +1,4 @@ -use rmp_serde::Serializer; +use rmp_serde::{Serializer, TimestampSerde}; use serde::Serialize; #[test] @@ -129,6 +129,40 @@ fn pass_struct() { assert_eq!(vec![0x92, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94], buf); } +#[test] +fn pass_timestamp() { + #[derive(Serialize)] + struct Struct { + f1: u32, + f2: u32, + ts: TimestampSerde, + } + + let vals = [ + (Struct { + f1: 42, + f2: 100500, + ts: TimestampSerde(rmp::Timestamp::from_32(9)) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xd6, 0xff, 0x0, 0x0, 0x0, 0x9]), + (Struct { + f1: 42, + f2: 100500, + ts: TimestampSerde(rmp::Timestamp::from_64(9, 1).unwrap()) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xd7, 0xff, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x9]), + (Struct { + f1: 42, + f2: 100500, + ts: TimestampSerde(rmp::Timestamp::from_96(9, 1).unwrap()) + }, vec![0x93, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xc7, 0xc, 0xff, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9]), + ]; + for (val, raw) in vals { + let mut buf = Vec::new(); + val.serialize(&mut Serializer::new(&mut buf)).unwrap(); + + assert_eq!(raw, buf); + } +} + #[test] fn serialize_struct_variant() { #[derive(Serialize)] diff --git a/rmp/src/decode/mod.rs b/rmp/src/decode/mod.rs index 8bc9719f..9b55c1af 100644 --- a/rmp/src/decode/mod.rs +++ b/rmp/src/decode/mod.rs @@ -31,7 +31,7 @@ use std::error; use num_traits::cast::FromPrimitive; -use crate::Marker; +use crate::{Marker, Timestamp}; pub mod bytes; pub use bytes::Bytes; @@ -244,6 +244,79 @@ pub fn read_marker(rd: &mut R) -> Result(rd: &mut R) -> Result> { + let marker = read_marker(rd)?; + let prefix = rd.read_data_i8()?; + match (marker, prefix) { + // timestamp 32 + (Marker::FixExt4, -1) => { + let secs = rd.read_data_u32()?; + Ok(Timestamp::from_32(secs)) + }, + // timestamp 64 + (Marker::FixExt8, -1) => { + let data = rd.read_data_u64()?; + Timestamp::from_combined_64(data) + .ok_or(ValueReadError::TypeMismatch(Marker::Reserved)) + }, + // timestamp 96 + (Marker::Ext8, 12) => { + let prefix = rd.read_data_i8()?; + if prefix == -1 { + let nsecs = rd.read_data_u32()?; + let secs = rd.read_data_i64()?; + Timestamp::from_96(secs, nsecs) + .ok_or(ValueReadError::TypeMismatch(Marker::Reserved)) + } else { + Err(ValueReadError::TypeMismatch(Marker::Reserved)) + } + }, + (Marker::Ext8 | Marker::FixExt4 | Marker::FixExt8, _) => Err(ValueReadError::TypeMismatch(Marker::Reserved)), + (marker, _) => Err(ValueReadError::TypeMismatch(marker)), + } +} + /// Attempts to read a single byte from the given reader and to decode it as a nil value. /// /// According to the MessagePack specification, a nil value is represented as a single `0xc0` byte. diff --git a/rmp/src/encode/mod.rs b/rmp/src/encode/mod.rs index fc3a8d59..e1f64b0d 100644 --- a/rmp/src/encode/mod.rs +++ b/rmp/src/encode/mod.rs @@ -19,7 +19,7 @@ use core::fmt::{self, Debug, Display, Formatter}; #[cfg(feature = "std")] use std::error; -use crate::Marker; +use crate::{Marker, Timestamp}; pub mod buffer; pub use buffer::ByteBuf; @@ -86,6 +86,64 @@ pub fn write_nil(wr: &mut W) -> Result<(), W::Error> { write_marker(wr, Marker::Null).map_err(|e| e.0) } +/// Encodes and attempts to write a timestamp value into the given write. +/// +/// According to the MessagePack specification, a timestamp value is represented as a 32, 64, or 96 bit Extension struct. +/// +/// # Errors +/// +/// This function will return `Error` on any I/O error occurred while writing the timestamp. +/// +/// # Examples +/// +/// ``` +/// use rmp::Timestamp; +/// +/// let mut buf1 = Vec::new(); +/// let mut buf2 = Vec::new(); +/// let mut buf3 = Vec::new(); +/// +/// let ts1 = Timestamp::from_32(0x66c1de7c); +/// let ts2 = Timestamp::from_64(0x66c1de7c, 0x3b9ac9ff).unwrap(); +/// let ts3 = Timestamp::from_96(0x66c1de7c, 0x3b9ac9ff).unwrap(); +/// +/// rmp::encode::write_timestamp(&mut buf1, ts1).ok(); +/// rmp::encode::write_timestamp(&mut buf2, ts2).ok(); +/// rmp::encode::write_timestamp(&mut buf3, ts3).ok(); +/// +/// // FixExt4 with a type of -1 (0xff) +/// assert_eq!(vec![0xd6, 0xff, 0x66, 0xc1, 0xde, 0x7c], buf1); +/// // FixExt8 with a type of -1 (0xff) +/// assert_eq!(vec![0xd7, 0xff, 0xee, 0x6b, 0x27, 0xfc, 0x66, 0xc1, 0xde, 0x7c], buf2); +/// // Ext8 with a size of 12 (0x0c) and a type of -1 (0xff) +/// assert_eq!(vec![0xc7, 0x0c, 0xff, 0x3b, 0x9a, 0xc9, 0xff, 0x00, 0x00, 0x00, 0x00, 0x66, 0xc1, 0xde, 0x7c], buf3); +/// ``` +#[inline] +pub fn write_timestamp(wr: &mut W, timestamp: Timestamp) -> Result<(), DataWriteError> { + match timestamp.size { + 32 => { + write_marker(wr, Marker::FixExt4).map_err(|e| e.0)?; + wr.write_data_i8(-1)?; + wr.write_data_u32(timestamp.secs as u32)?; + }, + 64 => { + write_marker(wr, Marker::FixExt8).map_err(|e| e.0)?; + let data = ((timestamp.nsecs as u64) << 34) | (timestamp.secs as u64); + wr.write_data_i8(-1)?; + wr.write_data_u64(data)?; + }, + 96 => { + write_marker(wr, Marker::Ext8).map_err(|e| e.0)?; + wr.write_data_u8(12)?; + wr.write_data_i8(-1)?; + wr.write_data_u32(timestamp.nsecs)?; + wr.write_data_i64(timestamp.secs)?; + }, + _ => unreachable!(), + } + Ok(()) +} + /// Encodes and attempts to write a bool value into the given write. /// /// According to the MessagePack specification, an encoded boolean value is represented as a single diff --git a/rmp/src/lib.rs b/rmp/src/lib.rs index 91bb862a..d6976b70 100644 --- a/rmp/src/lib.rs +++ b/rmp/src/lib.rs @@ -12,3 +12,90 @@ pub use crate::marker::Marker; /// Version of the MessagePack [spec](http://github.com/msgpack/msgpack/blob/master/spec.md). pub const MSGPACK_VERSION: u32 = 5; + +/// A type for holding Timestamp information +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Timestamp { + size: u8, + secs: i64, + nsecs: u32, +} + +impl Timestamp { + /// Get the size of the Timestamp in bits + #[inline] + pub fn get_bitsize(self) -> u8 { + self.size + } + + /// Get the data to pass to chrono::DateTime::from_timestamp + #[inline] + pub fn into_timestamp(self) -> (i64, u32) { + ( + self.secs, + self.nsecs, + ) + } + + /// Create a 32 bit timestamp using FixExt4 + #[inline] + pub fn from_32(secs: u32) -> Self { + Self { size: 32, secs: i64::from(secs), nsecs: 0 } + } + + /// Create a 64 bit timestamp using FixExt8 with seconds and nanoseconds passed separately + #[inline] + pub fn from_64(secs: i64, nsecs: u32) -> Option { + if secs < 0 || secs > 0x3_ffff_ffff || nsecs > 999_999_999 { + None + } else { + Some(Self { size: 64, secs, nsecs }) + } + } + + /// Create a 64 bit timestamp using FixExt8 from the combined 64 bit data + #[inline] + pub fn from_combined_64(data: u64) -> Option { + // 30 bits fits in u32 + let nsecs = (data >> 34) as u32; + if nsecs > 999_999_999 { + return None + } + // 34 bits fits in i64 + let secs = (data & 0x3_ffff_ffff) as i64; + + Some(Self { size: 64, secs, nsecs }) + } + + /// Create a 96 bit timestamp using Ext8 (len=12) + #[inline] + pub fn from_96(secs: i64, nsecs: u32) -> Option { + if nsecs > 999_999_999 { + None + } else { + Some(Self { size: 96, secs, nsecs }) + } + } + + /// Turns the data into a u128 + #[inline] + pub fn into_u128(self) -> u128 { + ((self.size as u128) << 96) | + ((self.secs as u128) << 32) | + (self.nsecs as u128) + } + + /// Turns the data into a u128 + #[inline] + pub fn from_u128(data: u128) -> Option { + let nsecs = (data & u32::MAX as u128) as u32; + let secs = ((data >> 32) & u64::MAX as u128) as i64; + let size = ((data >> 96) & u8::MAX as u128) as u8; + match size { + 32 => Some(Timestamp::from_32(secs as u32)), + 64 => Timestamp::from_64(secs, nsecs), + 96 => Timestamp::from_96(secs, nsecs), + _ => None, + } + } +}