#
cargo/util/
interning.rs

    1use serde::{Serialize, Serializer};
    2use serde_untagged::UntaggedEnumVisitor;
    3use std::borrow::Borrow;
    4use std::borrow::Cow;
    5use std::cmp::Ordering;
    6use std::collections::HashSet;
    7use std::ffi::OsStr;
    8use std::fmt;
    9use std::hash::{Hash, Hasher};
    10use std::ops::Deref;
    11use std::path::Path;
    12use std::ptr;
    13use std::str;
    14use std::sync::Mutex;
    15use std::sync::OnceLock;
    16
    17pub static INTERNED_DEFAULT: InternedString = InternedString { inner: "default" };
    18
    19fn interned_storage() -> std::sync::MutexGuard<'static, HashSet<&'static str>> {
    20    static STRING_CACHE: OnceLock<Mutex<HashSet<&'static str>>> = OnceLock::new();
    21    STRING_CACHE
    22        .get_or_init(|| {
    23            let mut out: HashSet<&'static str> = Default::default();
    24            out.insert(INTERNED_DEFAULT.as_str());
    25            Mutex::new(out)
    26        })
    27        .lock()
    28        .unwrap()
    29}
    30
    31#[derive(Clone, Copy)]
    32pub struct InternedString {
    33    inner: &'static str,
    34}
    35
    36impl<'a> From<&'a str> for InternedString {
    37    fn from(item: &'a str) -> Self {
    38        InternedString::new(item)
    39    }
    40}
    41
    42impl<'a> From<&'a String> for InternedString {
    43    fn from(item: &'a String) -> Self {
    44        InternedString::new(item)
    45    }
    46}
    47
    48impl From<String> for InternedString {
    49    fn from(item: String) -> Self {
    50        InternedString::from(Cow::Owned(item))
    51    }
    52}
    53
    54impl PartialEq for InternedString {
    55    fn eq(&self, other: &InternedString) -> bool {
    56        ptr::eq(self.as_str(), other.as_str())
    57    }
    58}
    59
    60impl PartialEq<str> for InternedString {
    61    fn eq(&self, other: &str) -> bool {
    62        *self == other
    63    }
    64}
    65
    66impl<'a> PartialEq<&'a str> for InternedString {
    67    fn eq(&self, other: &&str) -> bool {
    68        **self == **other
    69    }
    70}
    71
    72impl<'a> From<Cow<'a, str>> for InternedString {
    73    fn from(cs: Cow<'a, str>) -> Self {
    74        let mut cache = interned_storage();
    75        let s = cache.get(cs.as_ref()).copied().unwrap_or_else(|| {
    76            let s = cs.into_owned().leak();
    77            cache.insert(s);
    78            s
    79        });
    80
    81        InternedString { inner: s }
    82    }
    83}
    84
    85impl Eq for InternedString {}
    86
    87impl InternedString {
    88    pub fn new(s: &str) -> InternedString {
    89        InternedString::from(Cow::Borrowed(s))
    90    }
    91    pub fn as_str(&self) -> &'static str {
    92        self.inner
    93    }
    94}
    95
    96impl Deref for InternedString {
    97    type Target = str;
    98
    99    fn deref(&self) -> &'static str {
    100        self.as_str()
    101    }
    102}
    103
    104impl AsRef<str> for InternedString {
    105    fn as_ref(&self) -> &str {
    106        self.as_str()
    107    }
    108}
    109
    110impl AsRef<OsStr> for InternedString {
    111    fn as_ref(&self) -> &OsStr {
    112        self.as_str().as_ref()
    113    }
    114}
    115
    116impl AsRef<Path> for InternedString {
    117    fn as_ref(&self) -> &Path {
    118        self.as_str().as_ref()
    119    }
    120}
    121
    122impl Hash for InternedString {
    123    // N.B., we can't implement this as `identity(self).hash(state)`,
    124    // because we use this for on-disk fingerprints and so need
    125    // stability across Cargo invocations.
    126    fn hash<H: Hasher>(&self, state: &mut H) {
    127        self.as_str().hash(state);
    128    }
    129}
    130
    131impl Borrow<str> for InternedString {
    132    // If we implement Hash as `identity(self).hash(state)`,
    133    // then this will need to be removed.
    134    fn borrow(&self) -> &str {
    135        self.as_str()
    136    }
    137}
    138
    139impl fmt::Debug for InternedString {
    140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    141        fmt::Debug::fmt(self.as_str(), f)
    142    }
    143}
    144
    145impl fmt::Display for InternedString {
    146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    147        fmt::Display::fmt(self.as_str(), f)
    148    }
    149}
    150
    151impl Ord for InternedString {
    152    fn cmp(&self, other: &InternedString) -> Ordering {
    153        self.as_str().cmp(other.as_str())
    154    }
    155}
    156
    157impl PartialOrd for InternedString {
    158    fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
    159        Some(self.cmp(other))
    160    }
    161}
    162
    163impl Serialize for InternedString {
    164    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    165    where
    166        S: Serializer,
    167    {
    168        serializer.serialize_str(self.inner)
    169    }
    170}
    171
    172impl<'de> serde::Deserialize<'de> for InternedString {
    173    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    174    where
    175        D: serde::Deserializer<'de>,
    176    {
    177        UntaggedEnumVisitor::new()
    178            .expecting("an String like thing")
    179            .string(|value| Ok(value.into()))
    180            .deserialize(deserializer)
    181    }
    182}