#![allow(clippy::unreadable_literal)]
#[doc(hidden)]
pub extern crate hstr;
#[doc(hidden)]
pub extern crate once_cell;
use std::{
borrow::{Borrow, Cow},
cell::UnsafeCell,
fmt::{self, Display, Formatter},
hash::{Hash, Hasher},
mem::transmute,
ops::Deref,
rc::Rc,
};
pub use hstr::wtf8;
use once_cell::sync::Lazy;
use serde::Serializer;
use wtf8::Wtf8;
pub use crate::{fast::UnsafeAtom, wtf8_atom::Wtf8Atom};
mod fast;
mod wtf8_atom;
#[derive(Clone, Default)]
#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
#[repr(transparent)]
pub struct Atom(hstr::Atom);
impl PartialEq for Atom {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Atom {}
impl Hash for Atom {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
#[cfg(feature = "encoding-impl")]
impl cbor4ii::core::enc::Encode for Atom {
#[inline]
fn encode<W: cbor4ii::core::enc::Write>(
&self,
writer: &mut W,
) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
self.as_str().encode(writer)
}
}
#[cfg(feature = "encoding-impl")]
impl<'de> cbor4ii::core::dec::Decode<'de> for Atom {
#[inline]
fn decode<R: cbor4ii::core::dec::Read<'de>>(
reader: &mut R,
) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
let s = <&str>::decode(reader)?;
Ok(Atom::new(s))
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for Atom {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let sym = u.arbitrary::<String>()?;
if sym.is_empty() {
return Err(arbitrary::Error::NotEnoughData);
}
Ok(Self(hstr::Atom::from(sym)))
}
}
fn _asserts() {
fn _assert_send<T: Send>() {}
fn _assert_sync<T: Sync>() {}
_assert_send::<Atom>();
_assert_sync::<Atom>();
}
impl Atom {
#[inline(always)]
pub fn new<S>(s: S) -> Self
where
hstr::Atom: From<S>,
{
Atom(hstr::Atom::from(s))
}
#[inline]
pub fn to_ascii_lowercase(&self) -> Self {
Self(self.0.to_ascii_lowercase())
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
pub unsafe fn from_wtf8_unchecked(s: Wtf8Atom) -> Self {
Atom(unsafe { hstr::Atom::from_wtf8_unchecked(s.0) })
}
}
impl Deref for Atom {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
macro_rules! impl_eq {
($T:ty) => {
impl PartialEq<$T> for Atom {
#[inline]
fn eq(&self, other: &$T) -> bool {
&**self == &**other
}
}
};
}
macro_rules! impl_from {
($T:ty) => {
impl From<$T> for Atom {
fn from(s: $T) -> Self {
Atom::new(s)
}
}
};
}
impl From<hstr::Atom> for Atom {
#[inline(always)]
fn from(s: hstr::Atom) -> Self {
Atom(s)
}
}
impl From<Atom> for hstr::Wtf8Atom {
#[inline(always)]
fn from(s: Atom) -> Self {
hstr::Wtf8Atom::from(&*s)
}
}
impl PartialEq<str> for Atom {
#[inline]
fn eq(&self, other: &str) -> bool {
self.0 == *other
}
}
impl_eq!(&'_ str);
impl_eq!(Box<str>);
impl_eq!(std::sync::Arc<str>);
impl_eq!(Rc<str>);
impl_eq!(Cow<'_, str>);
impl_eq!(String);
impl_from!(&'_ str);
impl_from!(Box<str>);
impl_from!(String);
impl_from!(Cow<'_, str>);
impl AsRef<str> for Atom {
fn as_ref(&self) -> &str {
self
}
}
impl fmt::Debug for Atom {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl Display for Atom {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl PartialOrd for Atom {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Atom {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl Borrow<Wtf8Atom> for Atom {
fn borrow(&self) -> &Wtf8Atom {
const _: () = assert!(std::mem::size_of::<Atom>() == std::mem::size_of::<Wtf8Atom>());
const _: () = assert!(std::mem::align_of::<Atom>() == std::mem::align_of::<Wtf8Atom>());
unsafe { transmute::<&Atom, &Wtf8Atom>(self) }
}
}
impl serde::ser::Serialize for Atom {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self)
}
}
impl<'de> serde::de::Deserialize<'de> for Atom {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer).map(Self::new)
}
}
#[macro_export]
macro_rules! atom {
($s:tt) => {{
$crate::Atom::from($crate::hstr::atom!($s))
}};
}
#[macro_export]
macro_rules! lazy_atom {
($s:tt) => {{
$crate::Atom::from($crate::hstr::atom!($s))
}};
}
impl PartialEq<Atom> for str {
#[inline]
fn eq(&self, other: &Atom) -> bool {
*self == other.0
}
}
#[cfg(feature = "rkyv-impl")]
impl rkyv::Archive for Atom {
type Archived = rkyv::string::ArchivedString;
type Resolver = rkyv::string::StringResolver;
#[allow(clippy::unit_arg)]
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
rkyv::string::ArchivedString::resolve_from_str(self, resolver, out)
}
}
#[cfg(feature = "rkyv-impl")]
impl<S: rancor::Fallible + rkyv::ser::Writer + ?Sized> rkyv::Serialize<S> for Atom
where
<S as rancor::Fallible>::Error: rancor::Source,
{
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
rkyv::string::ArchivedString::serialize_from_str(self.as_str(), serializer)
}
}
#[cfg(feature = "rkyv-impl")]
impl<D> rkyv::Deserialize<Atom, D> for rkyv::string::ArchivedString
where
D: ?Sized + rancor::Fallible,
{
fn deserialize(&self, _: &mut D) -> Result<Atom, <D as rancor::Fallible>::Error> {
Ok(Atom::new(self.as_str()))
}
}
#[doc(hidden)]
pub type CahcedAtom = Lazy<Atom>;
pub type StaticString = Atom;
#[derive(Default)]
pub struct AtomStore(hstr::AtomStore);
impl AtomStore {
#[inline]
pub fn atom<'a>(&mut self, s: impl Into<Cow<'a, str>>) -> Atom {
Atom(self.0.atom(s))
}
#[inline]
pub fn wtf8_atom<'a>(&mut self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
Wtf8Atom(self.0.wtf8_atom(s))
}
}
#[derive(Default)]
pub struct AtomStoreCell(UnsafeCell<AtomStore>);
impl AtomStoreCell {
#[inline]
pub fn atom<'a>(&self, s: impl Into<Cow<'a, str>>) -> Atom {
let s: Cow<'a, str> = s.into();
unsafe { (*self.0.get()).atom(s) }
}
#[inline]
pub fn wtf8_atom<'a>(&self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
let s: Cow<'a, Wtf8> = s.into();
unsafe { (*self.0.get()).wtf8_atom(s) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn equality_variants() {
assert_eq!(Atom::from("inline"), Atom::from("inline"));
assert_ne!(Atom::from("inline"), Atom::from("inlinf"));
let mut store = AtomStore::default();
let dynamic = store.atom("Hello, beautiful world!");
let dynamic_same_store_equal = store.atom("Hello, beautiful world!");
let mut other_store = AtomStore::default();
let dynamic_other_store_equal = other_store.atom("Hello, beautiful world!");
let dynamic_different = other_store.atom("Hello, different world!");
assert_eq!(dynamic, dynamic_same_store_equal);
assert_eq!(dynamic, dynamic_other_store_equal);
assert_ne!(dynamic, dynamic_different);
assert_eq!(dynamic, "Hello, beautiful world!");
}
}
#[cfg(feature = "shrink-to-fit")]
impl shrink_to_fit::ShrinkToFit for Atom {
#[inline(always)]
fn shrink_to_fit(&mut self) {}
}