Skip to content

Commit

Permalink
Serde support
Browse files Browse the repository at this point in the history
  • Loading branch information
kaphula authored Jan 6, 2025
1 parent 4961737 commit 1563f1f
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 2 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "timer-queue"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
description = "Pure, minimal, and scalable timers"
authors = ["Benjamin Saunders <[email protected]>"]
Expand All @@ -9,14 +9,18 @@ repository = "https://github.com/Ralith/timer-queue"
readme = "README.md"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
serde = ["dep:serde"]

[dependencies]
slab = { version = "0.4.7", default-features = false }
serde = { version = "1.0.217", optional = true }

[dev-dependencies]
proptest = "1"
criterion = "0.4"
rand = { version = "0.8.5", features = ["small_rng"] }
bincode = { version = "1.3.3" }

[[bench]]
name = "bench"
Expand Down
109 changes: 108 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use core::fmt;

use slab::Slab;

#[cfg(feature = "serde")]
use serde::ser::SerializeSeq;

/// Stores values to be yielded at specific times in the future
///
/// Time is expressed as a bare u64 representing an absolute point in time. The caller may use any
Expand Down Expand Up @@ -477,11 +480,74 @@ const SLOTS: usize = 1 << LOG_2_SLOTS;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Timer(usize);

#[cfg(feature = "serde")]
impl<T: serde::Serialize> serde::Serialize for TimerQueue<T> {
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for v in self.iter() {
let t: (u64, &T) = v;
seq.serialize_element(&t)?;
}
seq.end()
}
}

#[cfg(feature = "serde")]
impl<'de, T> serde::Deserialize<'de> for TimerQueue<T>
where
T: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use core::fmt::Formatter;
use core::marker::PhantomData;

struct TimerQueueVisitor<T>(PhantomData<T>);

impl<'de, T> serde::de::Visitor<'de> for TimerQueueVisitor<T>
where
T: serde::Deserialize<'de>,
{
type Value = TimerQueue<T>;

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a sequence of (u64, T) tuples")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut timer_queue = if let Some(size) = seq.size_hint() {
TimerQueue::<T>::with_capacity(size)
} else {
TimerQueue::<T>::new()
};
while let Some((time, value)) = seq.next_element::<(u64, T)>()? {
timer_queue.insert(time, value);
}
Ok(timer_queue)
}
}

deserializer.deserialize_seq(TimerQueueVisitor(PhantomData))
}
}

#[cfg(test)]
mod tests {
extern crate alloc;
extern crate std;

use std::{vec::Vec, collections::HashMap};
use std::{collections::HashMap, vec::Vec};

use super::*;
use proptest::prelude::*;
Expand Down Expand Up @@ -642,6 +708,47 @@ mod tests {
}
}

#[test]
#[cfg(feature = "serde")]
fn serialization() {
const VALUES: [(u64, usize); 17] = [
(23, 5132),
(87, 6),
(45, 7839),
(122, 345),
(67, 12333),
(34, 8),
(90, 234),
(151, 82290),
(56, 32),
(78, 567),
(19, 345),
(22, 78),
(33, 890),
(44, 123),
(51235, 6),
(66, 89),
(727, 890),
];

let mut queue = TimerQueue::<usize>::new();
for (t, v) in VALUES {
queue.insert(t, v);
}
let serialized: Vec<u8> = bincode::serialize(&queue).expect("Serialization failed");
let mut deserialized: TimerQueue<usize> =
bincode::deserialize(&serialized).expect("Deserialization failed");

loop {
let r1 = queue.poll(u64::MAX);
let r2 = deserialized.poll(u64::MAX);
assert!(r1 == r2);
if r1.is_none() {
break;
}
}
}

/// Generates a time whose level/slot is more or less uniformly distributed
fn time() -> impl Strategy<Value = u64> {
((0..LEVELS as u32), (0..SLOTS as u64)).prop_perturb(|(level, mut slot), mut rng| {
Expand Down

0 comments on commit 1563f1f

Please sign in to comment.