Skip to content

Commit

Permalink
add support for time
Browse files Browse the repository at this point in the history
  • Loading branch information
vnghia committed Sep 20, 2024
1 parent 73c73b0 commit 5eab214
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 18 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bitcode_derive = { version = "0.6.3", path = "./bitcode_derive", optional = true
bytemuck = { version = "1.14", features = [ "min_const_generics", "must_cast" ] }
glam = { version = ">=0.21", default-features = false, optional = true }
serde = { version = "1.0", default-features = false, features = [ "alloc" ], optional = true }
time = { version = "0.3", default-features = false, features = [ "alloc" ], optional = true }

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

[features]
derive = [ "dep:bitcode_derive" ]
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
std = [ "serde?/std", "glam?/std", "arrayvec?/std", "time?/std" ]
default = [ "derive", "std" ]

[package.metadata.docs.rs]
Expand Down
67 changes: 67 additions & 0 deletions src/derive/datetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use bytemuck::CheckedBitPattern;

use super::Decode;

/// A u8 guaranteed to be < 24.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Hour(pub u8);
// Safety: u8 and Hour have the same layout since Hour is #[repr(transparent)].
unsafe impl CheckedBitPattern for Hour {
type Bits = u8;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 24
}
}
impl<'a> Decode<'a> for Hour {
type Decoder = crate::int::CheckedIntDecoder<'a, Hour, u8>;
}

/// A u8 guaranteed to be < 60.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Minute(pub u8);
// Safety: u8 and Minute have the same layout since Minute is #[repr(transparent)].
unsafe impl CheckedBitPattern for Minute {
type Bits = u8;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 60
}
}
impl<'a> Decode<'a> for Minute {
type Decoder = crate::int::CheckedIntDecoder<'a, Minute, u8>;
}

/// A u8 guaranteed to be < 60.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Second(pub u8);
// Safety: u8 and Second have the same layout since Second is #[repr(transparent)].
unsafe impl CheckedBitPattern for Second {
type Bits = u8;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 60
}
}
impl<'a> Decode<'a> for Second {
type Decoder = crate::int::CheckedIntDecoder<'a, Second, u8>;
}

/// A u32 guaranteed to be < 1 billion.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Nanoseconds(pub u32);
// Safety: u32 and Nanoseconds have the same layout since Nanoseconds is #[repr(transparent)].
unsafe impl CheckedBitPattern for Nanoseconds {
type Bits = u32;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 1_000_000_000
}
}
impl<'a> Decode<'a> for Nanoseconds {
type Decoder = crate::int::CheckedIntDecoder<'a, Nanoseconds, u32>;
}
19 changes: 2 additions & 17 deletions src/derive/duration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::coder::{Buffer, Decoder, Encoder, Result, View};
use crate::datetime::Nanoseconds;
use crate::{Decode, Encode};
use alloc::vec::Vec;
use bytemuck::CheckedBitPattern;
Expand Down Expand Up @@ -32,22 +33,6 @@ impl Encode for Duration {
type Encoder = DurationEncoder;
}

/// A u32 guaranteed to be < 1 billion. Prevents Duration::new from panicking.
#[derive(Copy, Clone)]
#[repr(transparent)]
struct Nanoseconds(u32);
// Safety: u32 and Nanoseconds have the same layout since Nanoseconds is #[repr(transparent)].
unsafe impl CheckedBitPattern for Nanoseconds {
type Bits = u32;
#[inline(always)]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
*bits < 1_000_000_000
}
}
impl<'a> Decode<'a> for Nanoseconds {
type Decoder = crate::int::CheckedIntDecoder<'a, Nanoseconds, u32>;
}

#[derive(Default)]
pub struct DurationDecoder<'a> {
secs: <u64 as Decode<'a>>::Decoder,
Expand Down Expand Up @@ -95,5 +80,5 @@ mod tests {
.map(|(s, n): (_, u32)| Duration::new(s, n % 1_000_000_000))
.collect()
}
crate::bench_encode_decode!(duration_vec: Vec<_>);
crate::bench_encode_decode!(duration_vec: Vec<Duration>);
}
1 change: 1 addition & 0 deletions src/derive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use core::num::NonZeroUsize;

mod array;
mod convert;
pub(crate) mod datetime;
mod duration;
mod empty;
mod impls;
Expand Down
2 changes: 2 additions & 0 deletions src/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod arrayvec;
#[cfg(feature = "glam")]
#[rustfmt::skip] // Makes impl_struct! calls way longer.
mod glam;
#[cfg(feature = "time")]
mod time_crate;

#[allow(unused)]
macro_rules! impl_struct {
Expand Down
1 change: 1 addition & 0 deletions src/ext/time_crate/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod time;
97 changes: 97 additions & 0 deletions src/ext/time_crate/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use crate::coder::{Buffer, Decoder, Encoder, Result, View};
use crate::datetime::{Hour, Minute, Nanoseconds, Second};
use crate::{Decode, Encode};
use alloc::vec::Vec;
use core::num::NonZeroUsize;
use time::Time;

#[derive(Default)]
pub struct TimeEncoder {
hour: <u8 as Encode>::Encoder,
minute: <u8 as Encode>::Encoder,
second: <u8 as Encode>::Encoder,
nanosecond: <u32 as Encode>::Encoder,
}
impl Encoder<Time> for TimeEncoder {
#[inline(always)]
fn encode(&mut self, t: &Time) {
let (hour, minute, second, nanosecond) = t.as_hms_nano();
self.hour.encode(&hour);
self.minute.encode(&minute);
self.second.encode(&second);
self.nanosecond.encode(&nanosecond);
}
}
impl Buffer for TimeEncoder {
fn collect_into(&mut self, out: &mut Vec<u8>) {
self.hour.collect_into(out);
self.minute.collect_into(out);
self.second.collect_into(out);
self.nanosecond.collect_into(out);
}

fn reserve(&mut self, additional: NonZeroUsize) {
self.hour.reserve(additional);
self.minute.reserve(additional);
self.second.reserve(additional);
self.nanosecond.reserve(additional);
}
}
impl Encode for Time {
type Encoder = TimeEncoder;
}

#[derive(Default)]
pub struct TimeDecoder<'a> {
hour: <Hour as Decode<'a>>::Decoder,
minute: <Minute as Decode<'a>>::Decoder,
second: <Second as Decode<'a>>::Decoder,
nanosecond: <Nanoseconds as Decode<'a>>::Decoder,
}
impl<'a> View<'a> for TimeDecoder<'a> {
fn populate(&mut self, input: &mut &'a [u8], length: usize) -> Result<()> {
self.hour.populate(input, length)?;
self.minute.populate(input, length)?;
self.second.populate(input, length)?;
self.nanosecond.populate(input, length)?;
Ok(())
}
}
impl<'a> Decoder<'a, Time> for TimeDecoder<'a> {
#[inline(always)]
fn decode(&mut self) -> Time {
let Hour(hour) = self.hour.decode();
let Minute(minute) = self.minute.decode();
let Second(second) = self.second.decode();
let Nanoseconds(nanosecond) = self.nanosecond.decode();
// Safety: should not fail because all input values are validated with CheckedBitPattern.
unsafe { Time::from_hms_nano(hour, minute, second, nanosecond).unwrap_unchecked() }
}
}
impl<'a> Decode<'a> for Time {
type Decoder = TimeDecoder<'a>;
}

#[cfg(test)]
mod tests {
#[test]
fn test() {
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 59, 999_999_999))).is_ok());
assert!(crate::decode::<Time>(&crate::encode(&(24, 59, 59, 999_999_999))).is_err());
assert!(crate::decode::<Time>(&crate::encode(&(23, 60, 59, 999_999_999))).is_err());
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 60, 999_999_999))).is_err());
assert!(crate::decode::<Time>(&crate::encode(&(23, 59, 59, 1_000_000_000))).is_err());
}

use alloc::vec::Vec;
use time::Time;
fn bench_data() -> Vec<Time> {
crate::random_data(1000)
.into_iter()
.map(|(h, m, s, n): (u8, u8, u8, u32)| {
Time::from_hms_nano(h % 24, m % 60, s % 60, n % 1_000_000_000).unwrap()
})
.collect()
}
crate::bench_encode_decode!(duration_vec: Vec<_>);
}

0 comments on commit 5eab214

Please sign in to comment.