#![cfg_attr(feature = "nightly", allow(incomplete_features))]
#![cfg_attr(feature = "nightly", feature(unsize, const_generics, doc_cfg))]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::{
fmt::{Debug, Display, Formatter, Result as FmtResult},
marker::PhantomData,
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
ptr,
};
use cfg_if::cfg_if;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use self::storage::RawStorage;
assert_impl_all!(InlineDyn<'static, dyn Debug + Unpin>: Unpin);
assert_impl_all!(InlineDyn<'static, dyn Debug + Send>: Send);
assert_impl_all!(InlineDyn<'static, dyn Debug + Sync>: Sync);
assert_not_impl_any!(InlineDyn<'static, dyn Debug>: Unpin, Send, Sync);
#[cfg(feature = "nightly")]
#[doc(cfg(feature = "nightly"))]
mod nightly;
mod storage;
pub use storage::{Align, Size};
pub type DefaultSize = storage::DefaultSize;
#[doc(hidden)]
pub unsafe trait Coerce<T, U: ?Sized> {
fn coerce(p: *const T) -> *const U;
}
struct VTable<D: ?Sized> {
cast_ref: unsafe fn(*const ()) -> *const D,
cast_mut: unsafe fn(*mut ()) -> *mut D,
}
impl<D: ?Sized> VTable<D> {
unsafe fn with_unsize<'a, C, T: 'a>() -> &'a Self
where C: Coerce<T, D> {
&Self {
cast_ref: |p| C::coerce(p.cast::<T>()),
cast_mut: |p| C::coerce(p.cast::<T>()) as *mut _,
}
}
}
pub struct InlineDyn<'a, D: ?Sized + 'a, S: Size = DefaultSize, A: Align = S> {
metadata: &'a VTable<D>,
storage: RawStorage<S, A>,
_marker: PhantomData<D>,
}
impl<D: ?Sized, S: Size, A: Align> Drop for InlineDyn<'_, D, S, A> {
fn drop(&mut self) {
unsafe {
(self.get_mut() as *mut D).drop_in_place();
}
}
}
impl<'a, D: ?Sized, S: Size, A: Align> InlineDyn<'a, D, S, A> {
#[doc(hidden)]
pub unsafe fn with_unsize<C, T: 'a>(value: T) -> Result<Self, T>
where C: Coerce<T, D> {
Self::with_metadata(VTable::with_unsize::<C, T>(), value)
}
unsafe fn with_metadata<T: 'a>(metadata: &'a VTable<D>, value: T) -> Result<Self, T> {
if (mem::size_of_val(&value) <= mem::size_of::<RawStorage<S, A>>())
&& (mem::align_of_val(&value) <= mem::align_of::<RawStorage<S, A>>())
{
let mut storage = RawStorage::new();
storage.as_mut_ptr().cast::<T>().write(value);
Ok(Self {
metadata,
storage,
_marker: PhantomData,
})
} else {
Err(value)
}
}
pub fn get_ref(&self) -> &D {
unsafe { &*(self.metadata.cast_ref)(self.storage.as_ptr().cast()) }
}
pub fn get_mut(&mut self) -> &mut D {
unsafe { &mut *(self.metadata.cast_mut)(self.storage.as_mut_ptr().cast()) }
}
pub fn grow<U, V>(this: Self) -> InlineDyn<'a, D, U, V>
where
U: Size + typenum::IsGreaterOrEqual<S>,
V: Align + typenum::IsGreaterOrEqual<A>, {
let (size, align) = {
let val = this.get_ref();
(mem::size_of_val(val), mem::align_of_val(val))
};
debug_assert!(size <= mem::size_of::<RawStorage<U, V>>());
debug_assert!(align <= mem::align_of::<RawStorage<U, V>>());
let this = ManuallyDrop::new(this);
let mut storage = RawStorage::<U, V>::new();
unsafe {
ptr::copy_nonoverlapping(this.storage.as_ptr(), storage.as_mut_ptr(), size);
}
InlineDyn {
metadata: this.metadata,
storage,
_marker: PhantomData,
}
}
pub fn try_resize<U, V>(this: Self) -> Result<InlineDyn<'a, D, U, V>, Self>
where
U: Size,
V: Align, {
let (size, align) = {
let val = this.get_ref();
(mem::size_of_val(val), mem::align_of_val(val))
};
if (size <= mem::size_of::<RawStorage<U, V>>())
&& (align <= mem::align_of::<RawStorage<U, V>>())
{
let this = ManuallyDrop::new(this);
let mut storage = RawStorage::<U, V>::new();
unsafe {
ptr::copy_nonoverlapping(this.storage.as_ptr(), storage.as_mut_ptr(), size);
}
Ok(InlineDyn {
metadata: this.metadata,
storage,
_marker: PhantomData,
})
} else {
Err(this)
}
}
}
impl<D: ?Sized, S: Size, A: Align> Deref for InlineDyn<'_, D, S, A> {
type Target = D;
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
impl<D: ?Sized, S: Size, A: Align> DerefMut for InlineDyn<'_, D, S, A> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.get_mut()
}
}
impl<D: Debug + ?Sized, S: Size, A: Align> Debug for InlineDyn<'_, D, S, A> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Debug::fmt(self.get_ref(), f)
}
}
impl<D: Display + ?Sized, S: Size, A: Align> Display for InlineDyn<'_, D, S, A> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(self.get_ref(), f)
}
}
cfg_if! {
if #[cfg(feature = "nightly")] {
#[macro_export]
macro_rules! inline_dyn {
($trait:path $(: $($_arg:ident),*)?; $e:expr) => {{
$crate::InlineDyn::<'_, dyn $trait, _, _>::try_new($e)
}};
}
#[macro_export]
#[cfg(feature = "alloc")]
#[doc(cfg(feature = "alloc"))]
macro_rules! inline_dyn_box {
($trait:path $(: $($_arg:ident),*)?; $e:expr) => {{
$crate::InlineDyn::<'_, dyn $trait>::try_or_box($e)
}};
}
} else {
#[macro_export]
macro_rules! inline_dyn {
($trait:path $(: $($arg:ident),*)?; $e:expr) => {{
fn try_build<'a, $($($arg,)*)* _T, _S, _A>(value: _T) -> Result<$crate::InlineDyn<'a, dyn $trait + 'a, _S, _A>, _T>
where _T: $trait + 'a, _S: $crate::Size, _A: $crate::Align, {
struct Unsize;
unsafe impl<'a, $($($arg,)*)* _T: $trait + 'a> $crate::Coerce<_T, (dyn $trait + 'a)> for Unsize {
fn coerce(p: *const _T) -> *const (dyn $trait + 'a) {
p
}
}
unsafe {
$crate::InlineDyn::with_unsize::<Unsize, _>(value)
}
}
try_build::<$($($arg,)*)* _, _, _>($e)
}};
}
#[macro_export]
#[cfg(feature = "alloc")]
macro_rules! inline_dyn_box {
($trait:path $(: $($arg:ident),*)?; $e:expr) => {{
fn build<'a, $($($arg,)*)* _T>(value: _T) -> $crate::InlineDyn<'a, dyn $trait + 'a>
where _T: $trait + 'a, alloc::boxed::Box<_T>: $trait + 'a {
struct Unsize;
unsafe impl<'a, $($($arg,)*)* _T: $trait + 'a> $crate::Coerce<_T, (dyn $trait + 'a)> for Unsize {
fn coerce(p: *const _T) -> *const (dyn $trait + 'a) {
p
}
}
unsafe {
$crate::InlineDyn::with_unsize::<Unsize, _>(value)
.or_else(|v| $crate::InlineDyn::with_unsize::<Unsize, _>(alloc::boxed::Box::new(v)))
.ok()
.expect("insufficient space for box")
}
}
build::<$($($arg,)*)* _>($e)
}};
}
}
}
macro_rules! impl_try_new {
($trait:path $(, $arg:ident)*) => {
#[cfg(not(feature = "nightly"))]
impl<'a, _S: crate::Size, _A: crate::Align $(, $arg)*> crate::InlineDyn<'a, dyn $trait, _S, _A> {
pub fn try_new<_T: $trait + 'a>(value: _T) -> Result<Self, _T> {
inline_dyn![$trait: $($arg),*; value]
}
}
#[cfg(all(not(feature = "nightly"), feature = "alloc"))]
impl<'a $(, $arg)*> crate::InlineDyn<'a, dyn $trait> {
pub fn try_or_box<_T: $trait + 'a>(value: _T) -> Self
where alloc::boxed::Box<_T>: $trait + 'a {
inline_dyn_box![$trait: $($arg),*; value]
}
}
};
}
pub mod any {
use core::{
any::{Any, TypeId},
mem::ManuallyDrop,
};
use crate::{Align, DefaultSize, Size};
pub type InlineDynAny<S = DefaultSize, A = S> = crate::InlineDyn<'static, dyn Any, S, A>;
#[cfg(not(feature = "nightly"))]
impl<S: Size, A: Align> InlineDynAny<S, A> {
pub fn try_new<T: Any>(value: T) -> Result<Self, T> {
inline_dyn![Any; value]
}
}
impl<D, S, A> crate::InlineDyn<'static, D, S, A>
where
D: Any + ?Sized,
S: Size,
A: Align,
{
pub fn downcast<T: Any>(self) -> Result<T, Self> {
if self.get_ref().type_id() == TypeId::of::<T>() {
let this = ManuallyDrop::new(self);
unsafe { Ok((this.get_ref() as *const D).cast::<T>().read()) }
} else {
Err(self)
}
}
}
}
pub mod convert {
use crate::DefaultSize;
pub type InlineDynAsRef<'a, U, S = DefaultSize, A = S> =
crate::InlineDyn<'a, dyn core::convert::AsRef<U>, S, A>;
pub type InlineDynAsMut<'a, U, S = DefaultSize, A = S> =
crate::InlineDyn<'a, dyn core::convert::AsMut<U>, S, A>;
impl_try_new!(AsRef<U>, U);
impl_try_new!(AsMut<U>, U);
}
pub mod fmt {
use crate::DefaultSize;
pub type InlineDynDebug<'a, S = DefaultSize, A = S> =
crate::InlineDyn<'a, dyn core::fmt::Debug, S, A>;
pub type InlineDynDisplay<'a, S = DefaultSize, A = S> =
crate::InlineDyn<'a, dyn core::fmt::Display, S, A>;
impl_try_new!(core::fmt::Debug);
impl_try_new!(core::fmt::Display);
}
pub mod future {
use crate::{Align, DefaultSize, InlineDyn, Size};
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
pub type InlineDynFuture<'a, O, S = DefaultSize, A = S> =
InlineDyn<'a, dyn Future<Output = O>, S, A>;
impl_try_new!(Future<Output = O>, O);
impl<F, S, A> Future for InlineDyn<'_, F, S, A>
where
F: Future + Unpin + ?Sized,
S: Size,
A: Align,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Pin::new(self.get_mut()).poll(cx)
}
}
}
pub mod hash {
pub type InlineDynHasher<'a, S = crate::DefaultSize, A = S> =
crate::InlineDyn<'a, dyn core::hash::Hasher, S, A>;
}
pub mod iter {
use crate::{Align, DefaultSize, InlineDyn, Size};
pub type InlineDynIterator<'a, I, S = DefaultSize, A = S> =
InlineDyn<'a, dyn Iterator<Item = I>, S, A>;
pub type InlineDynDoubleEndedIterator<'a, I, S = DefaultSize, A = S> =
InlineDyn<'a, dyn DoubleEndedIterator<Item = I>, S, A>;
impl_try_new!(Iterator<Item = I>, I);
impl_try_new!(DoubleEndedIterator<Item = I>, I);
impl<I, S, A> Iterator for InlineDyn<'_, I, S, A>
where
I: Iterator + ?Sized,
S: Size,
A: Align,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.get_mut().next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.get_ref().size_hint()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.get_mut().nth(n)
}
}
impl<I, S, A> DoubleEndedIterator for InlineDyn<'_, I, S, A>
where
I: DoubleEndedIterator + ?Sized,
S: Size,
A: Align,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.get_mut().next_back()
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.get_mut().nth_back(n)
}
}
impl<I, S, A> ExactSizeIterator for InlineDyn<'_, I, S, A>
where
I: ExactSizeIterator + ?Sized,
S: Size,
A: Align,
{
fn len(&self) -> usize {
self.get_ref().len()
}
}
impl<I, S, A> core::iter::FusedIterator for InlineDyn<'_, I, S, A>
where
I: core::iter::FusedIterator + ?Sized,
S: Size,
A: Align,
{
}
}
pub mod ops {
#[macro_export]
macro_rules! InlineDynFn {
(($($Args:ty),*) $(-> $R:ty)?) => {
$crate::InlineDynFn!(($($Args),*) $(-> $R)?; $crate::DefaultSize)
};
(($($Args:ty),*) $(-> $R:ty)?; $S:ty) => {
$crate::InlineDynFn!(($($Args),*) $(-> $R)?; $S, $S)
};
(($($Args:ty),*) $(-> $R:ty)?; $S:ty, $A:ty) => {
$crate::InlineDyn<dyn Fn($($Args),*)$(-> $R)?, $S, $A>
};
}
#[macro_export]
macro_rules! InlineDynFnMut {
(($($Args:ty),*) $(-> $R:ty)?) => {
$crate::InlineDynFnMut!(($($Args),*) $(-> $R)?; $crate::DefaultSize)
};
(($($Args:ty),*) $(-> $R:ty)?; $S:ty) => {
$crate::InlineDyn<'_, dyn FnMut($($Args),*)$(-> $R)?, $S, $S>
};
(($($Args:ty),*) $(-> $R:ty)?; $S:ty, $A:ty) => {
$crate::InlineDyn<'_, dyn FnMut($($Args),*)$(-> $R)?, $S, $A>
};
}
}
cfg_if! {
if #[cfg(feature = "std")] {
#[cfg_attr(feature = "nightly", doc(cfg(feature = "std")))]
pub mod error {
use std::error::Error;
use crate::{Align, InlineDyn, Size};
pub type InlineDynError<'a, S = crate::DefaultSize, A = S> = InlineDyn<'a, dyn Error, S, A>;
impl_try_new!(Error);
#[allow(deprecated)]
impl<E, S, A> Error for InlineDyn<'_, E, S, A>
where
E: Error + ?Sized,
S: Size,
A: Align,
{
fn description(&self) -> &str {
self.get_ref().description()
}
fn cause(&self) -> Option<&dyn Error> {
self.get_ref().cause()
}
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.get_ref().source()
}
}
}
#[cfg_attr(feature = "nightly", doc(cfg(feature = "std")))]
pub mod io {
use crate::{Align, DefaultSize, InlineDyn, Size};
use std::io;
pub type InlineDynRead<'a, S = DefaultSize, A = S> = InlineDyn<'a, dyn io::Read, S, A>;
pub type InlineDynWrite<'a, S = DefaultSize, A = S> = InlineDyn<'a, dyn io::Write, S, A>;
pub type InlineDynSeek<'a, S = DefaultSize, A = S> = InlineDyn<'a, dyn io::Seek, S, A>;
pub type InlineDynBufRead<'a, S = DefaultSize, A = S> = InlineDyn<'a, dyn io::BufRead, S, A>;
impl_try_new!(io::Read);
impl_try_new!(io::Write);
impl_try_new!(io::Seek);
impl_try_new!(io::BufRead);
impl<T, S, A> io::Read for InlineDyn<'_, T, S, A>
where
T: io::Read + ?Sized,
S: Size,
A: Align,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.get_mut().read(buf)
}
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
self.get_mut().read_vectored(bufs)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.get_mut().read_exact(buf)
}
}
impl<T, S, A> io::Write for InlineDyn<'_, T, S, A>
where
T: io::Write + ?Sized,
S: Size,
A: Align,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.get_mut().write(buf)
}
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
self.get_mut().write_vectored(bufs)
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.get_mut().write_all(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.get_mut().flush()
}
}
impl<T, S, A> io::Seek for InlineDyn<'_, T, S, A>
where
T: io::Seek + ?Sized,
S: Size,
A: Align,
{
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.get_mut().seek(pos)
}
}
impl<T, S, A> io::BufRead for InlineDyn<'_, T, S, A>
where
T: io::BufRead + ?Sized,
S: Size,
A: Align,
{
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.get_mut().fill_buf()
}
fn consume(&mut self, amt: usize) {
self.get_mut().consume(amt)
}
}
}
}
}
#[cfg(test)]
mod tests {
use typenum::{U0, U1, U4};
use super::*;
use crate::fmt::{InlineDynDebug, InlineDynDisplay};
#[test]
fn test_simple() {
let val: InlineDynDebug = InlineDynDebug::try_new(42usize).unwrap();
assert_eq!(format!("{:?}", val.get_ref()), "42");
}
#[test]
fn test_drop() {
#[derive(Debug)]
struct Dropper<'a>(&'a mut bool);
impl Drop for Dropper<'_> {
fn drop(&mut self) {
*self.0 = true;
}
}
let mut dropped = false;
{
let dbg: InlineDynDebug = InlineDynDebug::try_new(Dropper(&mut dropped)).unwrap();
assert_eq!(format!("{:?}", dbg), "Dropper(false)");
}
assert_eq!(dropped, true);
}
#[test]
fn test_size_insufficient() {
let res: Result<InlineDynDebug<U0>, _> = InlineDynDebug::try_new(42u8);
assert!(res.is_err());
}
#[test]
fn test_alignment_insufficient() {
let res: Result<InlineDynDebug<U4, U1>, _> = InlineDynDebug::try_new(42u32);
assert!(res.is_err());
}
#[test]
fn test_resize() {
let val: InlineDynDisplay = InlineDynDisplay::try_new(42u8).unwrap();
assert_eq!(val.to_string(), "42");
let val: InlineDynDisplay<U1> = InlineDynDisplay::try_resize(val).ok().unwrap();
assert_eq!(val.to_string(), "42");
}
#[test]
fn test_resize_insufficient() {
let val: InlineDynDisplay = InlineDynDisplay::try_new(42u32).unwrap();
assert_eq!(val.to_string(), "42");
let res: Result<InlineDynDisplay<U1>, _> = InlineDynDisplay::try_resize(val);
assert!(res.is_err());
}
#[test]
#[cfg(feature = "alloc")]
fn test_try_or_box() {
use std::mem;
#[derive(Debug)]
struct LargerThanBox([usize; 5]);
assert!(mem::size_of::<LargerThanBox>() > mem::size_of::<InlineDynDebug>());
let val: InlineDynDebug = InlineDynDebug::try_or_box(LargerThanBox([1, 2, 3, 4, 5]));
assert!(mem::size_of_val(val.get_ref()) < mem::size_of::<LargerThanBox>());
assert_eq!(
format!("{:?}", val.get_ref()),
"LargerThanBox([1, 2, 3, 4, 5])"
);
}
}