Skip to content

Commit 59161c5

Browse files
authored
Implement serde support for StringPool (#12563)
* Implement `serde` support for `StringPool` * review feedback * fix accidental usage of `std`
1 parent 1a9339e commit 59161c5

1 file changed

Lines changed: 110 additions & 11 deletions

File tree

crates/environ/src/string_pool.rs

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ use crate::{
88
use core::{fmt, mem, num::NonZeroU32};
99
use wasmtime_core::alloc::TryClone;
1010

11+
/// An interned string associated with a particular string in a `StringPool`.
12+
///
13+
/// Allows for $O(1)$ equality tests, $O(1)$ hashing, and $O(1)$
14+
/// arbitrary-but-stable ordering.
15+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16+
pub struct Atom {
17+
index: NonZeroU32,
18+
}
19+
1120
/// A pool of interned strings.
1221
///
1322
/// Insert new strings with [`StringPool::insert`] to get an `Atom` that is
@@ -44,15 +53,6 @@ impl Drop for StringPool {
4453
}
4554
}
4655

47-
/// An interned string associated with a particular string in a `StringPool`.
48-
///
49-
/// Allows for $O(1)$ equality tests, $O(1)$ hashing, and $O(1)$
50-
/// arbitrary-but-stable ordering.
51-
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52-
pub struct Atom {
53-
index: NonZeroU32,
54-
}
55-
5656
impl fmt::Debug for StringPool {
5757
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5858
struct Strings<'a>(&'a StringPool);
@@ -101,6 +101,59 @@ impl core::ops::Index<Atom> for StringPool {
101101
}
102102
}
103103

104+
impl serde::ser::Serialize for StringPool {
105+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106+
where
107+
S: serde::Serializer,
108+
{
109+
serde::ser::Serialize::serialize(&*self.strings, serializer)
110+
}
111+
}
112+
113+
impl<'de> serde::de::Deserialize<'de> for StringPool {
114+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115+
where
116+
D: serde::Deserializer<'de>,
117+
{
118+
struct Visitor;
119+
impl<'de> serde::de::Visitor<'de> for Visitor {
120+
type Value = StringPool;
121+
122+
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
123+
f.write_str("a `StringPool` sequence of strings")
124+
}
125+
126+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
127+
where
128+
A: serde::de::SeqAccess<'de>,
129+
{
130+
use serde::de::Error as _;
131+
132+
let mut pool = StringPool::new();
133+
134+
if let Some(len) = seq.size_hint() {
135+
pool.map.reserve(len).map_err(|oom| A::Error::custom(oom))?;
136+
pool.strings
137+
.reserve(len)
138+
.map_err(|oom| A::Error::custom(oom))?;
139+
}
140+
141+
while let Some(s) = seq.next_element::<String>()? {
142+
debug_assert_eq!(s.len(), s.capacity());
143+
let s = s.into_boxed_str().map_err(|oom| A::Error::custom(oom))?;
144+
if !pool.map.contains_key(&*s) {
145+
pool.insert_new_boxed_str(s)
146+
.map_err(|oom| A::Error::custom(oom))?;
147+
}
148+
}
149+
150+
Ok(pool)
151+
}
152+
}
153+
deserializer.deserialize_seq(Visitor)
154+
}
155+
}
156+
104157
impl StringPool {
105158
/// Create a new, empty pool.
106159
pub fn new() -> Self {
@@ -123,15 +176,21 @@ impl StringPool {
123176
.into_boxed_str()
124177
.expect("reserved exact capacity, so shouldn't need to realloc");
125178

179+
self.insert_new_boxed_str(owned)
180+
}
181+
182+
fn insert_new_boxed_str(&mut self, owned: Box<str>) -> Result<Atom, OutOfMemory> {
183+
debug_assert!(!self.map.contains_key(&*owned));
184+
126185
let index = self.strings.len();
127186
let atom = Atom::new(index);
128-
self.strings.push(owned).expect("reserved capacity");
187+
self.strings.push(owned)?;
129188

130189
// SAFETY: We never expose this borrow and never mutate or reallocate
131190
// strings once inserted into the pool.
132191
let s = unsafe { mem::transmute::<&str, &'static str>(&self.strings[index]) };
133192

134-
let old = self.map.insert(s, atom).expect("reserved capacity");
193+
let old = self.map.insert(s, atom)?;
135194
debug_assert!(old.is_none());
136195

137196
Ok(atom)
@@ -169,6 +228,25 @@ impl fmt::Debug for Atom {
169228
}
170229
}
171230

231+
impl serde::ser::Serialize for Atom {
232+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
233+
where
234+
S: serde::Serializer,
235+
{
236+
serde::ser::Serialize::serialize(&self.index, serializer)
237+
}
238+
}
239+
240+
impl<'de> serde::de::Deserialize<'de> for Atom {
241+
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
242+
where
243+
D: serde::Deserializer<'de>,
244+
{
245+
let index = serde::de::Deserialize::deserialize(deserializer)?;
246+
Ok(Self { index })
247+
}
248+
}
249+
172250
impl Atom {
173251
fn new(index: usize) -> Self {
174252
assert!(index < usize::try_from(u32::MAX).unwrap());
@@ -235,4 +313,25 @@ mod tests {
235313

236314
Ok(())
237315
}
316+
317+
#[test]
318+
fn roundtrip_serialize_deserialize() -> Result<()> {
319+
let mut pool = StringPool::new();
320+
let a = pool.insert("a")?;
321+
let b = pool.insert("b")?;
322+
let c = pool.insert("c")?;
323+
324+
let bytes = postcard::to_allocvec(&(pool, a, b, c))?;
325+
let (pool, a2, b2, c2) = postcard::from_bytes::<(StringPool, Atom, Atom, Atom)>(&bytes)?;
326+
327+
assert_eq!(&pool[a], "a");
328+
assert_eq!(&pool[b], "b");
329+
assert_eq!(&pool[c], "c");
330+
331+
assert_eq!(&pool[a2], "a");
332+
assert_eq!(&pool[b2], "b");
333+
assert_eq!(&pool[c2], "c");
334+
335+
Ok(())
336+
}
238337
}

0 commit comments

Comments
 (0)