Skip to content

Commit 783d720

Browse files
committed
WIP: Adding Timestamp decode/encode
1 parent 114b78f commit 783d720

File tree

3 files changed

+164
-2
lines changed

3 files changed

+164
-2
lines changed

rmp/src/decode/mod.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::error;
3131

3232
use num_traits::cast::FromPrimitive;
3333

34-
use crate::Marker;
34+
use crate::{Marker, Timestamp};
3535

3636
pub mod bytes;
3737
pub 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.

rmp/src/encode/mod.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use core::fmt::{self, Debug, Display, Formatter};
1919
#[cfg(feature = "std")]
2020
use std::error;
2121

22-
use crate::Marker;
22+
use crate::{Marker, Timestamp};
2323

2424
pub mod buffer;
2525
pub use buffer::ByteBuf;
@@ -86,6 +86,63 @@ pub fn write_nil<W: RmpWrite>(wr: &mut W) -> Result<(), W::Error> {
8686
write_marker(wr, Marker::Null).map_err(|e| e.0)
8787
}
8888

89+
/// Encodes and attempts to write a timestamp value into the given write.
90+
///
91+
/// According to the MessagePack specification, a timestamp value is represented as a 32, 64, or 96 bit Extension struct.
92+
///
93+
/// # Errors
94+
///
95+
/// This function will return `Error` on any I/O error occurred while writing the timestamp.
96+
///
97+
/// # Examples
98+
///
99+
/// ```
100+
/// use rmp::Timestamp;
101+
///
102+
/// let mut buf1 = Vec::new();
103+
/// let mut buf2 = Vec::new();
104+
/// let mut buf3 = Vec::new();
105+
///
106+
/// let ts1 = Timestamp::T32 { secs: 0x66c1de7c };
107+
/// let ts2 = Timestamp::T64 { nsecs: 0x3b9ac9ff, secs: 0x66c1de7c };
108+
/// let ts3 = Timestamp::T96 { nsecs: 0x3b9ac9ff, secs: 0x66c1de7c };
109+
///
110+
/// rmp::encode::write_timestamp(&mut buf1, ts1).ok();
111+
/// rmp::encode::write_timestamp(&mut buf2, ts2).ok();
112+
/// rmp::encode::write_timestamp(&mut buf3, ts3).ok();
113+
///
114+
/// // FixExt4 with a type of -1 (0xff)
115+
/// assert_eq!(vec![0xd6, 0xff, 0x66, 0xc1, 0xde, 0x7c], buf1);
116+
/// // FixExt8 with a type of -1 (0xff)
117+
/// assert_eq!(vec![0xd7, 0xff, 0xee, 0x6b, 0x27, 0xfc, 0x66, 0xc1, 0xde, 0x7c], buf2);
118+
/// // Ext8 with a size of 12 (0x0c) and a type of -1 (0xff)
119+
/// assert_eq!(vec![0xc7, 0x0c, 0xff, 0x3b, 0x9a, 0xc9, 0xff, 0x00, 0x00, 0x00, 0x00, 0x66, 0xc1, 0xde, 0x7c], buf3);
120+
/// ```
121+
#[inline]
122+
pub fn write_timestamp<W: RmpWrite>(wr: &mut W, timestamp: Timestamp) -> Result<(), DataWriteError<W::Error>> {
123+
match timestamp {
124+
Timestamp::T32 { secs } => {
125+
write_marker(wr, Marker::FixExt4).map_err(|e| e.0)?;
126+
wr.write_data_i8(-1)?;
127+
wr.write_data_u32(secs as u32)?;
128+
},
129+
Timestamp::T64 { nsecs, secs } => {
130+
write_marker(wr, Marker::FixExt8).map_err(|e| e.0)?;
131+
let data = ((nsecs as u64) << 34) | (secs as u64);
132+
wr.write_data_i8(-1)?;
133+
wr.write_data_u64(data)?;
134+
},
135+
Timestamp::T96 { nsecs, secs } => {
136+
write_marker(wr, Marker::Ext8).map_err(|e| e.0)?;
137+
wr.write_data_u8(12)?;
138+
wr.write_data_i8(-1)?;
139+
wr.write_data_u32(nsecs)?;
140+
wr.write_data_i64(secs)?;
141+
},
142+
}
143+
Ok(())
144+
}
145+
89146
/// Encodes and attempts to write a bool value into the given write.
90147
///
91148
/// According to the MessagePack specification, an encoded boolean value is represented as a single

rmp/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@ pub use crate::marker::Marker;
1212

1313
/// Version of the MessagePack [spec](http://github.com/msgpack/msgpack/blob/master/spec.md).
1414
pub const MSGPACK_VERSION: u32 = 5;
15+
16+
/// A type for holding Timestamp information
17+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18+
pub enum Timestamp {
19+
T32 {
20+
secs: i64,
21+
},
22+
T64 {
23+
nsecs: u32,
24+
secs: i64,
25+
},
26+
T96 {
27+
nsecs: u32,
28+
secs: i64,
29+
},
30+
}

0 commit comments

Comments
 (0)