use core::fmt;
use std::borrow::Cow;
#[cfg(feature = "cstr")]
use std::ffi::{CStr, CString};
#[cfg(feature = "osstr")]
use std::ffi::{OsStr, OsString};
#[cfg(feature = "path")]
use std::path::{Path, PathBuf};
use self::boxed::PinBox;
pub struct Interned<T: 'static + ?Sized>(Slice<T>);
impl From<Cow<'static, str>> for Interned<str> {
#[inline]
fn from(cow: Cow<'static, str>) -> Self {
Self(cow.into())
}
}
#[cfg(feature = "bytes")]
impl From<Cow<'static, [u8]>> for Interned<[u8]> {
#[inline]
fn from(cow: Cow<'static, [u8]>) -> Self {
Self(cow.into())
}
}
#[cfg(feature = "cstr")]
impl From<Cow<'static, CStr>> for Interned<CStr> {
#[inline]
fn from(cow: Cow<'static, CStr>) -> Self {
Self(cow.into())
}
}
#[cfg(feature = "osstr")]
impl From<Cow<'static, OsStr>> for Interned<OsStr> {
#[inline]
fn from(cow: Cow<'static, OsStr>) -> Self {
Self(cow.into())
}
}
#[cfg(feature = "path")]
impl From<Cow<'static, Path>> for Interned<Path> {
#[inline]
fn from(cow: Cow<'static, Path>) -> Self {
Self(cow.into())
}
}
impl<T> Interned<T>
where
T: ?Sized,
{
#[inline]
pub fn as_slice(&self) -> &T {
self.0.as_slice()
}
#[inline]
pub unsafe fn as_static_slice(&self) -> &'static T {
unsafe { self.0.as_static_slice() }
}
}
impl fmt::Debug for Interned<str> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "bytes")]
impl fmt::Debug for Interned<[u8]> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "cstr")]
impl fmt::Debug for Interned<CStr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "osstr")]
impl fmt::Debug for Interned<OsStr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "path")]
impl fmt::Debug for Interned<Path> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
enum Slice<T: 'static + ?Sized> {
Static(&'static T),
Owned(PinBox<T>),
}
impl<T> From<&'static T> for Slice<T>
where
T: ?Sized,
{
#[inline]
fn from(slice: &'static T) -> Self {
Self::Static(slice)
}
}
impl From<String> for Slice<str> {
#[inline]
fn from(owned: String) -> Self {
Self::Owned(PinBox::new(owned.into_boxed_str()))
}
}
impl From<Cow<'static, str>> for Slice<str> {
#[inline]
fn from(cow: Cow<'static, str>) -> Self {
match cow {
Cow::Borrowed(slice) => slice.into(),
Cow::Owned(owned) => owned.into(),
}
}
}
#[cfg(feature = "bytes")]
impl From<Vec<u8>> for Slice<[u8]> {
#[inline]
fn from(owned: Vec<u8>) -> Self {
Self::Owned(PinBox::new(owned.into_boxed_slice()))
}
}
#[cfg(feature = "bytes")]
impl From<Cow<'static, [u8]>> for Slice<[u8]> {
#[inline]
fn from(cow: Cow<'static, [u8]>) -> Self {
match cow {
Cow::Borrowed(slice) => slice.into(),
Cow::Owned(owned) => owned.into(),
}
}
}
#[cfg(feature = "cstr")]
impl From<CString> for Slice<CStr> {
#[inline]
fn from(owned: CString) -> Self {
Self::Owned(PinBox::new(owned.into_boxed_c_str()))
}
}
#[cfg(feature = "cstr")]
impl From<Cow<'static, CStr>> for Slice<CStr> {
#[inline]
fn from(cow: Cow<'static, CStr>) -> Self {
match cow {
Cow::Borrowed(slice) => slice.into(),
Cow::Owned(owned) => owned.into(),
}
}
}
#[cfg(feature = "osstr")]
impl From<OsString> for Slice<OsStr> {
#[inline]
fn from(owned: OsString) -> Self {
Self::Owned(PinBox::new(owned.into_boxed_os_str()))
}
}
#[cfg(feature = "osstr")]
impl From<Cow<'static, OsStr>> for Slice<OsStr> {
#[inline]
fn from(cow: Cow<'static, OsStr>) -> Self {
match cow {
Cow::Borrowed(slice) => slice.into(),
Cow::Owned(owned) => owned.into(),
}
}
}
#[cfg(feature = "path")]
impl From<PathBuf> for Slice<Path> {
#[inline]
fn from(owned: PathBuf) -> Self {
Self::Owned(PinBox::new(owned.into_boxed_path()))
}
}
#[cfg(feature = "path")]
impl From<Cow<'static, Path>> for Slice<Path> {
#[inline]
fn from(cow: Cow<'static, Path>) -> Self {
match cow {
Cow::Borrowed(slice) => slice.into(),
Cow::Owned(owned) => owned.into(),
}
}
}
impl<T> Slice<T>
where
T: ?Sized,
{
#[inline]
fn as_slice(&self) -> &T {
match self {
Self::Static(slice) => slice,
Self::Owned(owned) => {
unsafe { owned.as_ref() }
}
}
}
#[inline]
unsafe fn as_static_slice(&self) -> &'static T {
match self {
Self::Static(slice) => slice,
Self::Owned(owned) => {
unsafe {
owned.as_ref()
}
}
}
}
}
impl fmt::Debug for Slice<str> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.as_slice())
}
}
#[cfg(feature = "bytes")]
impl fmt::Debug for Slice<[u8]> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{:?}", String::from_utf8_lossy(self.as_slice()))
} else {
write!(f, "{:?}", self.as_slice())
}
}
}
#[cfg(feature = "cstr")]
impl fmt::Debug for Slice<CStr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(
f,
"{:?}",
String::from_utf8_lossy(self.as_slice().to_bytes())
)
} else {
write!(f, "{:?}", self.as_slice())
}
}
}
#[cfg(feature = "osstr")]
impl fmt::Debug for Slice<OsStr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{:?}", self.as_slice().to_string_lossy())
} else {
write!(f, "{:?}", self.as_slice())
}
}
}
#[cfg(feature = "path")]
impl fmt::Debug for Slice<Path> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{:?}", self.as_slice().to_string_lossy())
} else {
write!(f, "{:?}", self.as_slice())
}
}
}
mod boxed {
use core::fmt;
use core::marker::PhantomData;
use core::ptr::NonNull;
pub(crate) struct PinBox<T: ?Sized> {
ptr: NonNull<T>,
_marker: PhantomData<Box<T>>,
}
impl<T: ?Sized> PinBox<T> {
#[inline]
pub(crate) fn new(x: Box<T>) -> Self {
let ptr = Box::into_raw(x);
let ptr = unsafe { NonNull::new_unchecked(ptr) };
Self {
ptr,
_marker: PhantomData,
}
}
#[inline]
pub(crate) unsafe fn as_ref<'a>(&self) -> &'a T {
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> Drop for PinBox<T> {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.ptr.as_ptr()));
}
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for PinBox<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = unsafe { self.as_ref() };
s.fmt(f)
}
}
unsafe impl<T> Send for PinBox<T>
where
T: ?Sized,
Box<T>: Send,
{
}
unsafe impl<T> Sync for PinBox<T>
where
T: ?Sized,
Box<T>: Sync,
{
}
#[cfg(test)]
mod tests {
use core::fmt::Write;
use super::PinBox;
#[test]
fn test_drop() {
let x = "abc".to_string().into_boxed_str();
let x = PinBox::new(x);
drop(x);
}
#[test]
fn test_as_ref() {
let x = "abc".to_string().into_boxed_str();
let x = PinBox::new(x);
assert_eq!(unsafe { x.as_ref() }, "abc");
}
#[test]
fn test_debug_format() {
let x = "abc".to_string().into_boxed_str();
let x = PinBox::new(x);
let mut buf = String::new();
write!(&mut buf, "{x:?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
}
}
#[cfg(test)]
mod tests {
use core::fmt::Write;
use std::borrow::Cow;
#[cfg(feature = "cstr")]
use std::ffi::CStr;
#[cfg(feature = "osstr")]
use std::ffi::OsStr;
#[cfg(feature = "path")]
use std::path::Path;
use super::Interned;
#[test]
fn test_interned_static_str_debug_format() {
let s = Interned::from(Cow::Borrowed("abc"));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
#[test]
fn test_interned_owned_str_debug_format() {
let s = Interned::<str>::from(Cow::Owned("abc".to_string()));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
#[test]
#[cfg(feature = "bytes")]
fn test_interned_static_bytes_debug_format() {
let s = Interned::from(Cow::Borrowed(&b"abc"[..]));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "[97, 98, 99]");
let s = Interned::from(Cow::Borrowed(&b"\xFF"[..]));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "[255]");
let s = Interned::from(Cow::Borrowed(&b"abc"[..]));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::from(Cow::Borrowed(&b"\xFF"[..]));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"\u{FFFD}\"");
}
#[test]
#[cfg(feature = "bytes")]
fn test_interned_owned_bytes_debug_format() {
let s = Interned::<[u8]>::from(Cow::Owned(b"abc".to_vec()));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "[97, 98, 99]");
let s = Interned::<[u8]>::from(Cow::Owned(b"\xFF".to_vec()));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "[255]");
let s = Interned::<[u8]>::from(Cow::Owned(b"abc".to_vec()));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::<[u8]>::from(Cow::Owned(b"\xFF".to_vec()));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"\u{FFFD}\"");
}
#[test]
#[cfg(feature = "cstr")]
fn test_interned_static_cstr_debug_format() {
let s = Interned::from(Cow::Borrowed(
CStr::from_bytes_with_nul(b"abc\x00").unwrap(),
));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::from(Cow::Borrowed(
CStr::from_bytes_with_nul(b"\xFF\x00").unwrap(),
));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, r#""\xff""#);
let s = Interned::from(Cow::Borrowed(
CStr::from_bytes_with_nul(b"abc\x00").unwrap(),
));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::from(Cow::Borrowed(
CStr::from_bytes_with_nul(b"\xFF\x00").unwrap(),
));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"\u{FFFD}\"");
}
#[test]
#[cfg(feature = "cstr")]
fn test_interned_owned_cstring_debug_format() {
let s = Interned::<CStr>::from(Cow::Owned(
CStr::from_bytes_with_nul(b"abc\x00").unwrap().to_owned(),
));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::<CStr>::from(Cow::Owned(
CStr::from_bytes_with_nul(b"\xFF\x00").unwrap().to_owned(),
));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, r#""\xff""#);
let s = Interned::<CStr>::from(Cow::Owned(
CStr::from_bytes_with_nul(b"abc\x00").unwrap().to_owned(),
));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::<CStr>::from(Cow::Owned(
CStr::from_bytes_with_nul(b"\xFF\x00").unwrap().to_owned(),
));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"\u{FFFD}\"");
}
#[test]
#[cfg(feature = "osstr")]
fn test_interned_static_osstr_debug_format() {
let s = Interned::from(Cow::Borrowed(OsStr::new("abc")));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::from(Cow::Borrowed(OsStr::new("abc")));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
#[test]
#[cfg(feature = "osstr")]
fn test_interned_owned_osstring_debug_format() {
let s = Interned::<OsStr>::from(Cow::Owned(OsStr::new("abc").to_owned()));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::<OsStr>::from(Cow::Owned(OsStr::new("abc").to_owned()));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
#[test]
#[cfg(feature = "path")]
fn test_interned_static_path_debug_format() {
let s = Interned::from(Cow::Borrowed(Path::new("abc")));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::from(Cow::Borrowed(Path::new("abc")));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
#[test]
#[cfg(feature = "path")]
fn test_interned_owned_pathbuf_debug_format() {
let s = Interned::<Path>::from(Cow::Owned(Path::new("abc").to_owned()));
let mut buf = String::new();
write!(&mut buf, "{s:?}").unwrap();
assert_eq!(buf, "\"abc\"");
let s = Interned::<Path>::from(Cow::Owned(Path::new("abc").to_owned()));
let mut buf = String::new();
write!(&mut buf, "{s:#?}").unwrap();
assert_eq!(buf, "\"abc\"");
}
}