Merge pull request #1944 from declanvk/issue-1705
Implement customer [de]serialization for `IntegerInterner`
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -1338,6 +1338,7 @@ dependencies = [
|
|||||||
"peepmatic-automata",
|
"peepmatic-automata",
|
||||||
"peepmatic-macro",
|
"peepmatic-macro",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_test",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"wast 15.0.0",
|
"wast 15.0.0",
|
||||||
]
|
]
|
||||||
@@ -1881,6 +1882,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_test"
|
||||||
|
version = "1.0.114"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f58190d074af17bd48118303db08afadbd506bc2ba511b4582cebd8f882a9b8d"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|||||||
Binary file not shown.
@@ -18,6 +18,8 @@ serde = { version = "1.0.105", features = ["derive"] }
|
|||||||
thiserror = "1.0.15"
|
thiserror = "1.0.15"
|
||||||
wast = { version = "15.0.0", optional = true }
|
wast = { version = "15.0.0", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serde_test = "1.0.114"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Enable support for a few extra methods that are required by the `peepmatic`
|
# Enable support for a few extra methods that are required by the `peepmatic`
|
||||||
|
|||||||
@@ -5,8 +5,12 @@
|
|||||||
//! few compared to the full range of `u64`) integers we are matching against
|
//! few compared to the full range of `u64`) integers we are matching against
|
||||||
//! here and then reference them by `IntegerId`.
|
//! here and then reference them by `IntegerId`.
|
||||||
|
|
||||||
|
use serde::de::{Deserializer, SeqAccess, Visitor};
|
||||||
|
use serde::ser::{SerializeSeq, Serializer};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::num::{NonZeroU16, NonZeroU32};
|
use std::num::{NonZeroU16, NonZeroU32};
|
||||||
|
|
||||||
/// An identifier for an interned integer.
|
/// An identifier for an interned integer.
|
||||||
@@ -14,7 +18,7 @@ use std::num::{NonZeroU16, NonZeroU32};
|
|||||||
pub struct IntegerId(#[doc(hidden)] pub NonZeroU16);
|
pub struct IntegerId(#[doc(hidden)] pub NonZeroU16);
|
||||||
|
|
||||||
/// An interner for integer values.
|
/// An interner for integer values.
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default)]
|
||||||
pub struct IntegerInterner {
|
pub struct IntegerInterner {
|
||||||
// Note: we use `BTreeMap`s for deterministic serialization.
|
// Note: we use `BTreeMap`s for deterministic serialization.
|
||||||
map: BTreeMap<u64, IntegerId>,
|
map: BTreeMap<u64, IntegerId>,
|
||||||
@@ -71,3 +75,118 @@ impl From<IntegerId> for NonZeroU32 {
|
|||||||
id.0.into()
|
id.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for IntegerInterner {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut seq = serializer.serialize_seq(Some(self.values.len()))?;
|
||||||
|
for p in &self.values {
|
||||||
|
seq.serialize_element(&p)?;
|
||||||
|
}
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for IntegerInterner {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_seq(IntegerInternerVisitor {
|
||||||
|
marker: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IntegerInternerVisitor {
|
||||||
|
marker: PhantomData<fn() -> IntegerInterner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for IntegerInternerVisitor {
|
||||||
|
type Value = IntegerInterner;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
formatter,
|
||||||
|
"a `peepmatic_runtime::integer_interner::IntegerInterner`"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
const DEFAULT_CAPACITY: usize = 16;
|
||||||
|
let capacity = access.size_hint().unwrap_or(DEFAULT_CAPACITY);
|
||||||
|
|
||||||
|
let mut interner = IntegerInterner {
|
||||||
|
map: BTreeMap::new(),
|
||||||
|
values: Vec::with_capacity(capacity),
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(path) = access.next_element::<u64>()? {
|
||||||
|
interner.intern(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(interner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_test::{assert_tokens, Token};
|
||||||
|
use std::iter::successors;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct OrderedIntegerInterner(IntegerInterner);
|
||||||
|
|
||||||
|
impl PartialEq for OrderedIntegerInterner {
|
||||||
|
fn eq(&self, other: &OrderedIntegerInterner) -> bool {
|
||||||
|
self.0.values.iter().eq(other.0.values.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_fib(interner: &mut IntegerInterner, skip: usize, take: usize) {
|
||||||
|
successors(Some((0, 1)), |(a, b): &(u64, u64)| {
|
||||||
|
a.checked_add(*b).map(|c| (*b, c))
|
||||||
|
})
|
||||||
|
.skip(skip)
|
||||||
|
.take(take)
|
||||||
|
.for_each(|(i, _)| {
|
||||||
|
interner.intern(i);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ser_de_empty_interner() {
|
||||||
|
let interner = IntegerInterner::new();
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&OrderedIntegerInterner(interner),
|
||||||
|
&[Token::Seq { len: Some(0) }, Token::SeqEnd],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ser_de_fibonacci_interner() {
|
||||||
|
let mut interner = IntegerInterner::new();
|
||||||
|
intern_fib(&mut interner, 10, 5);
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&OrderedIntegerInterner(interner),
|
||||||
|
&[
|
||||||
|
Token::Seq { len: Some(5) },
|
||||||
|
Token::U64(55),
|
||||||
|
Token::U64(89),
|
||||||
|
Token::U64(144),
|
||||||
|
Token::U64(233),
|
||||||
|
Token::U64(377),
|
||||||
|
Token::SeqEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user