use core::{cmp::Ordering, fmt, mem};
use crate::{
Def, Facet, FieldBuilder, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut, Repr,
Shape, ShapeBuilder, StructKind, StructType, Type, TypeNameOpts, TypeOpsIndirect, UserType,
VTableIndirect,
};
unsafe fn tuple_debug(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result> {
let shape = ox.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return None,
};
let ptr = ox.ptr();
let mut tuple = f.debug_tuple("");
for field in ty.fields {
let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
let field_ox = unsafe { OxRef::new(field_ptr, field.shape.get()) };
tuple.field(&field_ox);
}
Some(tuple.finish())
}
unsafe fn tuple_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
let shape = ox.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return None,
};
let ptr = ox.ptr();
for field in ty.fields {
let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
let field_shape = field.shape.get();
unsafe { field_shape.call_hash(field_ptr, hasher)? };
}
Some(())
}
unsafe fn tuple_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
let shape = a.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return None,
};
let a_ptr = a.ptr();
let b_ptr = b.ptr();
for field in ty.fields {
let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
let field_shape = field.shape.get();
if !unsafe { field_shape.call_partial_eq(a_field, b_field)? } {
return Some(false);
}
}
Some(true)
}
unsafe fn tuple_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
let shape = a.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return None,
};
let a_ptr = a.ptr();
let b_ptr = b.ptr();
for field in ty.fields {
let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
let field_shape = field.shape.get();
match unsafe { field_shape.call_partial_cmp(a_field, b_field)? } {
Some(Ordering::Equal) => continue,
other => return Some(other),
}
}
Some(Some(Ordering::Equal))
}
unsafe fn tuple_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
let shape = a.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return None,
};
let a_ptr = a.ptr();
let b_ptr = b.ptr();
for field in ty.fields {
let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
let field_shape = field.shape.get();
match unsafe { field_shape.call_cmp(a_field, b_field)? } {
Ordering::Equal => continue,
other => return Some(other),
}
}
Some(Ordering::Equal)
}
unsafe fn tuple_drop(ox: OxPtrMut) {
let shape = ox.shape();
let ty = match shape.ty {
Type::User(UserType::Struct(ref st)) => st,
_ => return,
};
let ptr = ox.ptr();
for field in ty.fields {
let field_ptr = unsafe { PtrMut::new((ptr.as_byte_ptr() as *mut u8).add(field.offset)) };
let field_shape = field.shape.get();
unsafe { field_shape.call_drop_in_place(field_ptr) };
}
}
const TUPLE_VTABLE: VTableIndirect = VTableIndirect {
display: None,
debug: Some(tuple_debug),
hash: Some(tuple_hash),
invariants: None,
parse: None,
parse_bytes: None,
try_from: None,
try_into_inner: None,
try_borrow_inner: None,
partial_eq: Some(tuple_partial_eq),
partial_cmp: Some(tuple_partial_cmp),
cmp: Some(tuple_cmp),
};
static TUPLE_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
drop_in_place: tuple_drop,
default_in_place: None,
clone_into: None,
is_truthy: None,
};
fn tuple_type_name(
shape: &'static Shape,
f: &mut fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> fmt::Result {
let st = match &shape.ty {
Type::User(UserType::Struct(st)) if st.kind == StructKind::Tuple => st,
_ => return write!(f, "(?)"),
};
write!(f, "(")?;
let child_opts = opts.for_children();
for (i, field) in st.fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if let Some(opts) = child_opts {
field.shape.get().write_type_name(f, opts)?;
} else {
write!(f, "…")?;
}
}
if st.fields.len() == 1 {
write!(f, ",")?;
}
write!(f, ")")?;
Ok(())
}
macro_rules! impl_facet_for_tuple {
{
continue from ($($elems:ident.$idx:tt,)+),
remaining ()
} => {};
{
continue from ($($elems:ident.$idx:tt,)+),
remaining ($next:ident.$nextidx:tt, $($remaining:ident.$remainingidx:tt,)*)
} => {
impl_facet_for_tuple! {
impl ($($elems.$idx,)+ $next.$nextidx,),
remaining ($($remaining.$remainingidx,)*)
}
};
{
impl ($($elems:ident.$idx:tt,)+),
remaining ($($remaining:ident.$remainingidx:tt,)*)
} => {
unsafe impl<'a $(, $elems)+> Facet<'a> for ($($elems,)+)
where
$($elems: Facet<'a>,)+
{
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>(
if 1 == [$($elems::SHAPE),+].len() {
"(_,)"
} else {
"(…)"
}
)
.decl_id(crate::DeclId::new(crate::decl_id_hash("#tuple#(…)")))
.type_name(tuple_type_name)
.ty(Type::User(UserType::Struct(StructType {
repr: Repr::default(),
kind: StructKind::Tuple,
fields: &const {[
$(FieldBuilder::new(stringify!($idx), crate::shape_of::<$elems>, mem::offset_of!(Self, $idx)).build(),)+
]}
})))
.def(Def::Undefined)
.vtable_indirect(&TUPLE_VTABLE)
.type_ops_indirect(&TUPLE_TYPE_OPS)
.build()
};
}
impl_facet_for_tuple! {
continue from ($($elems.$idx,)+),
remaining ($($remaining.$remainingidx,)*)
}
};
{ ($first:ident.$firstidx:tt $(, $remaining:ident.$remainingidx:tt)* $(,)?) } => {
impl_facet_for_tuple! {
impl ($first.$firstidx,),
remaining ($($remaining.$remainingidx,)*)
}
};
}
#[cfg(feature = "tuples-12")]
impl_facet_for_tuple! {
(T0.0, T1.1, T2.2, T3.3, T4.4, T5.5, T6.6, T7.7, T8.8, T9.9, T10.10, T11.11)
}
#[cfg(not(feature = "tuples-12"))]
impl_facet_for_tuple! {
(T0.0, T1.1, T2.2, T3.3)
}