Browse Source

Fix serde for StoredOsString.

Kestrel 2 months ago
parent
commit
65ce757707
2 changed files with 37 additions and 38 deletions
  1. 1 1
      Cargo.toml
  2. 36 37
      src/lib.rs

+ 1 - 1
Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "stringstore"
-version = "0.1.2"
+version = "0.1.3"
 edition = "2021"
 author = "kestrel <kestrel@flying-kestrel.ca>"
 repository = "https://git.flying-kestrel.ca/kestrel/stringstore"

+ 36 - 37
src/lib.rs

@@ -10,23 +10,22 @@
 //! - the set of string values you care about changes significantly over time, or
 //! - your usage pattern leans towards constructing strings and only rarely comparing them.
 //!
-//! Conceptually, a [`StoredString`][stored-string] is a lightweight reference to a string inside a
+//! Conceptually, a [`StoredString`] is a lightweight reference to a string inside a
 //! global string storage. The intended use is for rapid equality checks between strings drawn from
 //! a small set, where the overhead of byte-by-byte string comparison is overkill.
 //!
-//! More concretely, each [`StoredString`][stored-string] contains a pointer to a string stored in
+//! More concretely, each [`StoredString`] contains a pointer to a string stored in
 //! a global hash table. Since the hash table guarantees uniqueness, a string comparison can be
 //! reduced to simply a pointer comparison. Constructing a `StoredString` involves a mutex lock and
 //! hash table lookup, but copying one is nearly free as it is simply a pointer copy. No reference
 //! tracking is performed, so any strings added to the global hash table **will remain allocated
 //! until program exit**.
 //!
-//! [stored-string]: struct.StoredString.html
-//!
-//! To avoid complications stemming from strings with different semantics sharing the same unique
+//! To avoid potential complications stemming from strings with different semantics sharing the same unique
 //! pointer value, `StoredString` is abstracted across a [`NamespaceTag`](trait.NamespaceTag.html)
 //! implementation. This introduces no runtime overhead, but allows for catching many errors at
-//! compile-time. As a concrete example; this is perfectly accepted:
+//! compile-time, and still shares pointer values internally. As a concrete example; this is
+//! perfectly accepted:
 //!
 //! ```rust
 //! # use stringstore::{StoredString,NamespaceTag};
@@ -159,24 +158,22 @@ impl<Tag> StoredString<Tag> {
 }
 
 #[cfg(feature = "serde")]
-impl<'de, Tag> serde::de::Visitor<'de> for StoredString<Tag> {
-    type Value = Self;
+struct StoredStringVisitor<Tag>(std::marker::PhantomData<Tag>);
+
+#[cfg(feature = "serde")]
+impl<'de, Tag: 'static> serde::de::Visitor<'de> for StoredStringVisitor<Tag> {
+    type Value = StoredString<Tag>;
+
     fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
-        formatter.write_fmt(format_args!("stored string"))
+        write!(formatter, "stored string")
     }
 
-    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
-    where
-        E: serde::de::Error,
-    {
-        Ok(Self::new(v))
+    fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
+        Ok(StoredString::new(v))
     }
 
-    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
-    where
-        E: serde::de::Error,
-    {
-        Ok(Self::new(v.as_str()))
+    fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
+        Ok(v.into())
     }
 }
 
@@ -186,7 +183,7 @@ impl<'de, Tag> serde::de::Deserialize<'de> for StoredString<Tag> {
     where
         D: serde::Deserializer<'de>,
     {
-        deserializer.deserialize_str(Self::new(""))
+        deserializer.deserialize_str(StoredStringVisitor::<Tag>(std::marker::PhantomData))
     }
 }
 
@@ -196,7 +193,7 @@ impl<Tag> serde::ser::Serialize for StoredString<Tag> {
     where
         S: serde::Serializer,
     {
-        serializer.serialize_str(self.name)
+        serializer.serialize_str(self.as_str())
     }
 }
 
@@ -350,24 +347,19 @@ impl<Tag> StoredOsString<Tag> {
 }
 
 #[cfg(feature = "serde")]
-impl<'de, Tag> serde::de::Visitor<'de> for StoredOsString<Tag> {
-    type Value = Self;
+struct StoredOsStringVisitor<Tag>(std::marker::PhantomData<Tag>);
+
+#[cfg(feature = "serde")]
+impl<'de, Tag: 'static> serde::de::Visitor<'de> for StoredOsStringVisitor<Tag> {
+    type Value = StoredOsString<Tag>;
+
     fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
-        formatter.write_fmt(format_args!("stored string"))
+        write!(formatter, "stored OsString")
     }
 
-    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
-    where
-        E: serde::de::Error,
-    {
-        Ok(Self::new(v))
-    }
 
-    fn visit_string<E>(self, v: OsString) -> Result<Self::Value, E>
-    where
-        E: serde::de::Error,
-    {
-        Ok(Self::new(v.as_str()))
+    fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
+        Ok(StoredOsString::from(v))
     }
 }
 
@@ -377,7 +369,7 @@ impl<'de, Tag> serde::de::Deserialize<'de> for StoredOsString<Tag> {
     where
         D: serde::Deserializer<'de>,
     {
-        deserializer.deserialize_str(Self::new(""))
+        deserializer.deserialize_bytes(StoredOsStringVisitor::<Tag>(std::marker::PhantomData))
     }
 }
 
@@ -387,7 +379,7 @@ impl<Tag> serde::ser::Serialize for StoredOsString<Tag> {
     where
         S: serde::Serializer,
     {
-        serializer.serialize_str(self.name)
+        serializer.serialize_bytes(self.as_encoded_bytes())
     }
 }
 
@@ -409,3 +401,10 @@ impl<'l, Tag: 'static> From<&'l OsStr> for StoredOsString<Tag> {
     }
 }
 
+#[cfg(target_os = "linux")]
+impl<'l, Tag: 'static> From<&'l [u8]> for StoredOsString<Tag> {
+    fn from(value: &'l [u8]) -> Self {
+        use std::os::unix::ffi::OsStrExt;
+        OsStr::from_bytes(value).into()
+    }
+}