Skip to content

Commit c1ef658

Browse files
committed
add support for time
1 parent 195318d commit c1ef658

File tree

7 files changed

+172
-18
lines changed

7 files changed

+172
-18
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ bitcode_derive = { version = "0.6.3", path = "./bitcode_derive", optional = true
1919
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
2020
glam = { version = ">=0.21", default-features = false, optional = true }
2121
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
22+
time = { version = "0.3", default-features = false, features = [ "alloc" ], optional = true }
2223

2324
[dev-dependencies]
2425
arrayvec = { version = "0.7", features = [ "serde" ] }
@@ -37,7 +38,7 @@ zstd = "0.13.0"
3738

3839
[features]
3940
derive = [ "dep:bitcode_derive" ]
40-
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
41+
std = [ "serde?/std", "glam?/std", "arrayvec?/std", "time?/std" ]
4142
default = [ "derive", "std" ]
4243

4344
[package.metadata.docs.rs]

src/derive/datetime.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use bytemuck::CheckedBitPattern;
2+
3+
use super::Decode;
4+
5+
/// A u8 guaranteed to be < 24.
6+
#[derive(Copy, Clone)]
7+
#[repr(transparent)]
8+
pub struct Hour(pub u8);
9+
// Safety: u8 and Hour have the same layout since Hour is #[repr(transparent)].
10+
unsafe impl CheckedBitPattern for Hour {
11+
type Bits = u8;
12+
#[inline(always)]
13+
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
14+
*bits < 24
15+
}
16+
}
17+
impl<'a> Decode<'a> for Hour {
18+
type Decoder = crate::int::CheckedIntDecoder<'a, Hour, u8>;
19+
}
20+
21+
/// A u8 guaranteed to be < 60.
22+
#[derive(Copy, Clone)]
23+
#[repr(transparent)]
24+
pub struct Minute(pub u8);
25+
// Safety: u8 and Minute have the same layout since Minute is #[repr(transparent)].
26+
unsafe impl CheckedBitPattern for Minute {
27+
type Bits = u8;
28+
#[inline(always)]
29+
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
30+
*bits < 60
31+
}
32+
}
33+
impl<'a> Decode<'a> for Minute {
34+
type Decoder = crate::int::CheckedIntDecoder<'a, Minute, u8>;
35+
}
36+
37+
/// A u8 guaranteed to be < 60.
38+
#[derive(Copy, Clone)]
39+
#[repr(transparent)]
40+
pub struct Second(pub u8);
41+
// Safety: u8 and Second have the same layout since Second is #[repr(transparent)].
42+
unsafe impl CheckedBitPattern for Second {
43+
type Bits = u8;
44+
#[inline(always)]
45+
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
46+
*bits < 60
47+
}
48+
}
49+
impl<'a> Decode<'a> for Second {
50+
type Decoder = crate::int::CheckedIntDecoder<'a, Second, u8>;
51+
}
52+
53+
/// A u32 guaranteed to be < 1 billion.
54+
#[derive(Copy, Clone)]
55+
#[repr(transparent)]
56+
pub struct Nanoseconds(pub u32);
57+
// Safety: u32 and Nanoseconds have the same layout since Nanoseconds is #[repr(transparent)].
58+
unsafe impl CheckedBitPattern for Nanoseconds {
59+
type Bits = u32;
60+
#[inline(always)]
61+
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
62+
*bits < 1_000_000_000
63+
}
64+
}
65+
impl<'a> Decode<'a> for Nanoseconds {
66+
type Decoder = crate::int::CheckedIntDecoder<'a, Nanoseconds, u32>;
67+
}

src/derive/duration.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::coder::{Buffer, Decoder, Encoder, Result, View};
2+
use crate::datetime::Nanoseconds;
23
use crate::{Decode, Encode};
34
use alloc::vec::Vec;
45
use bytemuck::CheckedBitPattern;
@@ -32,22 +33,6 @@ impl Encode for Duration {
3233
type Encoder = DurationEncoder;
3334
}
3435

35-
/// A u32 guaranteed to be < 1 billion. Prevents Duration::new from panicking.
36-
#[derive(Copy, Clone)]
37-
#[repr(transparent)]
38-
struct Nanoseconds(u32);
39-
// Safety: u32 and Nanoseconds have the same layout since Nanoseconds is #[repr(transparent)].
40-
unsafe impl CheckedBitPattern for Nanoseconds {
41-
type Bits = u32;
42-
#[inline(always)]
43-
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
44-
*bits < 1_000_000_000
45-
}
46-
}
47-
impl<'a> Decode<'a> for Nanoseconds {
48-
type Decoder = crate::int::CheckedIntDecoder<'a, Nanoseconds, u32>;
49-
}
50-
5136
#[derive(Default)]
5237
pub struct DurationDecoder<'a> {
5338
secs: <u64 as Decode<'a>>::Decoder,
@@ -95,5 +80,5 @@ mod tests {
9580
.map(|(s, n): (_, u32)| Duration::new(s, n % 1_000_000_000))
9681
.collect()
9782
}
98-
crate::bench_encode_decode!(duration_vec: Vec<_>);
83+
crate::bench_encode_decode!(duration_vec: Vec<Duration>);
9984
}

src/derive/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use core::num::NonZeroUsize;
66

77
mod array;
88
pub(crate) mod convert;
9+
pub(crate) mod datetime;
910
mod duration;
1011
mod empty;
1112
mod impls;

src/ext/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ mod arrayvec;
33
#[cfg(feature = "glam")]
44
#[rustfmt::skip] // Makes impl_struct! calls way longer.
55
mod glam;
6+
#[cfg(feature = "time")]
7+
mod time_crate;
68

79
#[allow(unused)]
810
macro_rules! impl_struct {

src/ext/time_crate/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod time;

src/ext/time_crate/time.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use crate::coder::{Buffer, Decoder, Encoder, Result, View};
2+
use crate::datetime::{Hour, Minute, Nanoseconds, Second};
3+
use crate::{Decode, Encode};
4+
use alloc::vec::Vec;
5+
use core::num::NonZeroUsize;
6+
use time::Time;
7+
8+
#[derive(Default)]
9+
pub struct TimeEncoder {
10+
hour: <u8 as Encode>::Encoder,
11+
minute: <u8 as Encode>::Encoder,
12+
second: <u8 as Encode>::Encoder,
13+
nanosecond: <u32 as Encode>::Encoder,
14+
}
15+
impl Encoder<Time> for TimeEncoder {
16+
#[inline(always)]
17+
fn encode(&mut self, t: &Time) {
18+
let (hour, minute, second, nanosecond) = t.as_hms_nano();
19+
self.hour.encode(&hour);
20+
self.minute.encode(&minute);
21+
self.second.encode(&second);
22+
self.nanosecond.encode(&nanosecond);
23+
}
24+
}
25+
impl Buffer for TimeEncoder {
26+
fn collect_into(&mut self, out: &mut Vec<u8>) {
27+
self.hour.collect_into(out);
28+
self.minute.collect_into(out);
29+
self.second.collect_into(out);
30+
self.nanosecond.collect_into(out);
31+
}
32+
33+
fn reserve(&mut self, additional: NonZeroUsize) {
34+
self.hour.reserve(additional);
35+
self.minute.reserve(additional);
36+
self.second.reserve(additional);
37+
self.nanosecond.reserve(additional);
38+
}
39+
}
40+
impl Encode for Time {
41+
type Encoder = TimeEncoder;
42+
}
43+
44+
#[derive(Default)]
45+
pub struct TimeDecoder<'a> {
46+
hour: <Hour as Decode<'a>>::Decoder,
47+
minute: <Minute as Decode<'a>>::Decoder,
48+
second: <Second as Decode<'a>>::Decoder,
49+
nanosecond: <Nanoseconds as Decode<'a>>::Decoder,
50+
}
51+
impl<'a> View<'a> for TimeDecoder<'a> {
52+
fn populate(&mut self, input: &mut &'a [u8], length: usize) -> Result<()> {
53+
self.hour.populate(input, length)?;
54+
self.minute.populate(input, length)?;
55+
self.second.populate(input, length)?;
56+
self.nanosecond.populate(input, length)?;
57+
Ok(())
58+
}
59+
}
60+
impl<'a> Decoder<'a, Time> for TimeDecoder<'a> {
61+
#[inline(always)]
62+
fn decode(&mut self) -> Time {
63+
let Hour(hour) = self.hour.decode();
64+
let Minute(minute) = self.minute.decode();
65+
let Second(second) = self.second.decode();
66+
let Nanoseconds(nanosecond) = self.nanosecond.decode();
67+
// Safety: should not fail because all input values are validated with CheckedBitPattern.
68+
unsafe { Time::from_hms_nano(hour, minute, second, nanosecond).unwrap_unchecked() }
69+
}
70+
}
71+
impl<'a> Decode<'a> for Time {
72+
type Decoder = TimeDecoder<'a>;
73+
}
74+
75+
#[cfg(test)]
76+
mod tests {
77+
#[test]
78+
fn test() {
79+
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 59, 999_999_999))).is_ok());
80+
assert!(crate::decode::<Time>(&crate::encode(&(24, 59, 59, 999_999_999))).is_err());
81+
assert!(crate::decode::<Time>(&crate::encode(&(23, 60, 59, 999_999_999))).is_err());
82+
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 60, 999_999_999))).is_err());
83+
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 59, 1_000_000_000))).is_err());
84+
}
85+
86+
use alloc::vec::Vec;
87+
use time::Time;
88+
fn bench_data() -> Vec<Time> {
89+
crate::random_data(1000)
90+
.into_iter()
91+
.map(|(h, m, s, n): (u8, u8, u8, u32)| {
92+
Time::from_hms_nano(h % 24, m % 60, s % 60, n % 1_000_000_000).unwrap()
93+
})
94+
.collect()
95+
}
96+
crate::bench_encode_decode!(duration_vec: Vec<_>);
97+
}

0 commit comments

Comments
 (0)