|
@@ -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()
|
|
|
+ }
|
|
|
+}
|