@@ -31,7 +31,7 @@ use std::error;
3131
3232use num_traits:: cast:: FromPrimitive ;
3333
34- use crate :: Marker ;
34+ use crate :: { Marker , Timestamp } ;
3535
3636pub mod bytes;
3737pub use bytes:: Bytes ;
@@ -244,6 +244,95 @@ pub fn read_marker<R: RmpRead>(rd: &mut R) -> Result<Marker, MarkerReadError<R::
244244 Ok ( Marker :: from_u8 ( rd. read_u8 ( ) ?) )
245245}
246246
247+ /// Attempts to read an Extension struct from the given reader and to decode it as a Timestamp value.
248+ ///
249+ /// According to the MessagePack specification, there are 3 types of Timestamp, 32, 64, and 96.
250+ ///
251+ /// # Errors
252+ ///
253+ /// This function will return `ValueReadError` on any I/O error while reading the Timestamp,
254+ /// except the EINTR, which is handled internally.
255+ ///
256+ /// It also returns `ValueReadError::TypeMismatch` if the actual type is not equal with the
257+ /// expected one, indicating you with the actual type.
258+ ///
259+ /// # Note
260+ ///
261+ /// This function will silently retry on every EINTR received from the underlying `Read` until
262+ /// successful read.
263+ ///
264+ /// # Examples
265+ ///
266+ /// ```
267+ /// use rmp::Timestamp;
268+ ///
269+ /// // FixExt4 with a type of -1 (0xff)
270+ /// let mut buf1 = [0xd6, 0xff, 0x66, 0xc1, 0xde, 0x7c];
271+ /// // FixExt8 with a type of -1 (0xff)
272+ /// let mut buf2 = [0xd7, 0xff, 0xee, 0x6b, 0x27, 0xfc, 0x66, 0xc1, 0xde, 0x7c];
273+ /// // Ext8 with a size of 12 (0x0c) and a type of -1 (0xff)
274+ /// let mut buf3 = [0xc7, 0x0c, 0xff, 0x3b, 0x9a, 0xc9, 0xff, 0x00, 0x00, 0x00, 0x00, 0x66, 0xc1, 0xde, 0x7c];
275+ ///
276+ /// let ts1_expected = Timestamp::T32 { secs: 0x66c1de7c };
277+ /// let ts2_expected = Timestamp::T64 { nsecs: 0x3b9ac9ff, secs: 0x66c1de7c };
278+ /// let ts3_expected = Timestamp::T96 { nsecs: 0x3b9ac9ff, secs: 0x66c1de7c };
279+ ///
280+ /// let ts1_result = rmp::decode::read_timestamp(&mut buf1.as_slice()).unwrap();
281+ /// let ts2_result = rmp::decode::read_timestamp(&mut buf2.as_slice()).unwrap();
282+ /// let ts3_result = rmp::decode::read_timestamp(&mut buf3.as_slice()).unwrap();
283+ ///
284+ /// assert_eq!(ts1_expected, ts1_result);
285+ /// assert_eq!(ts2_expected, ts2_result);
286+ /// assert_eq!(ts3_expected, ts3_result);
287+ /// ```
288+ pub fn read_timestamp < R : RmpRead > ( rd : & mut R ) -> Result < Timestamp , ValueReadError < R :: Error > > {
289+ let marker = read_marker ( rd) ?;
290+ let prefix = rd. read_data_i8 ( ) ?;
291+ match ( marker, prefix) {
292+ // timestamp 32
293+ ( Marker :: FixExt4 , -1 ) => {
294+ let secs = rd. read_data_u32 ( ) ? as i64 ;
295+ Ok ( Timestamp :: T32 {
296+ secs,
297+ } )
298+ } ,
299+ // timestamp 64
300+ ( Marker :: FixExt8 , -1 ) => {
301+ let data = rd. read_data_u64 ( ) ?;
302+ // 30 bits fits in u32
303+ let nsecs = ( data >> 34 ) as u32 ;
304+ if nsecs > 999_999_999 {
305+ return Err ( ValueReadError :: TypeMismatch ( Marker :: Reserved ) )
306+ }
307+ // 34 bits fits in i64
308+ let secs = ( data & 0x3_ffff_ffff ) as i64 ;
309+ Ok ( Timestamp :: T64 {
310+ secs,
311+ nsecs,
312+ } )
313+ } ,
314+ // timestamp 96
315+ ( Marker :: Ext8 , 12 ) => {
316+ let prefix = rd. read_data_i8 ( ) ?;
317+ if prefix == -1 {
318+ let nsecs = rd. read_data_u32 ( ) ?;
319+ if nsecs > 999_999_999 {
320+ return Err ( ValueReadError :: TypeMismatch ( Marker :: Reserved ) )
321+ }
322+ let secs = rd. read_data_i64 ( ) ?;
323+ Ok ( Timestamp :: T96 {
324+ secs,
325+ nsecs,
326+ } )
327+ } else {
328+ Err ( ValueReadError :: TypeMismatch ( Marker :: Reserved ) )
329+ }
330+ } ,
331+ ( Marker :: Ext8 | Marker :: FixExt4 | Marker :: FixExt8 , _) => Err ( ValueReadError :: TypeMismatch ( Marker :: Reserved ) ) ,
332+ ( marker, _) => Err ( ValueReadError :: TypeMismatch ( marker) ) ,
333+ }
334+ }
335+
247336/// Attempts to read a single byte from the given reader and to decode it as a nil value.
248337///
249338/// According to the MessagePack specification, a nil value is represented as a single `0xc0` byte.
0 commit comments