use core::ffi;
use core::fmt;
use core::mem;
use core::slice;
use core::write;
use crate::parse::verify_name;
use crate::Encoding;
use crate::EncodingBox;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum NestingLevel {
Top,
Within,
Bottom,
}
impl NestingLevel {
pub(crate) const fn new() -> Self {
Self::Top
}
pub(crate) const fn bitfield(self) -> Self {
self
}
pub(crate) const fn indirection(self, kind: IndirectionKind) -> Self {
match kind {
IndirectionKind::Atomic => Self::Bottom,
IndirectionKind::Pointer => match self {
Self::Top => Self::Within,
Self::Bottom | Self::Within => Self::Bottom,
},
}
}
pub(crate) const fn array(self) -> Self {
self
}
pub(crate) const fn container_include_fields(self) -> Option<Self> {
match self {
Self::Top | Self::Within => {
Some(Self::Within)
}
Self::Bottom => None,
}
}
}
pub(crate) fn compare_encodings<E1: EncodingType, E2: EncodingType>(
enc1: &E1,
enc2: &E2,
level: NestingLevel,
include_all: bool,
) -> bool {
use Helper::*;
let level = if include_all {
NestingLevel::new()
} else {
level
};
match (enc1.helper(), enc2.helper()) {
(Primitive(p1), Primitive(p2)) => p1.equivalents().contains(&p2),
(BitField(size1, Some((offset1, type1))), BitField(size2, Some((offset2, type2)))) => {
size1 == size2
&& offset1 == offset2
&& compare_encodings(type1, type2, level.bitfield(), include_all)
}
(BitField(size1, None), BitField(size2, None)) => size1 == size2,
(BitField(_, _), BitField(_, _)) => false,
(Indirection(kind1, t1), Indirection(kind2, t2)) => {
kind1 == kind2 && compare_encodings(t1, t2, level.indirection(kind1), include_all)
}
(Array(len1, item1), Array(len2, item2)) => {
len1 == len2 && compare_encodings(item1, item2, level.array(), include_all)
}
(Container(kind1, name1, items1), Container(kind2, name2, items2)) => {
kind1 == kind2 && name1 == name2 && {
if let Some(level) = level.container_include_fields() {
if items1.is_empty() || items2.is_empty() {
return true;
}
if items1.len() != items2.len() {
return false;
}
for (item1, item2) in items1.iter().zip(items2.iter()) {
if !compare_encodings(item1, item2, level, include_all) {
return false;
}
}
true
} else {
true
}
}
}
(NoneInvalid, NoneInvalid) => true,
(_, _) => false,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub(crate) enum Primitive {
Char,
Short,
Int,
Long,
LongLong,
UChar,
UShort,
UInt,
ULong,
ULongLong,
Float,
Double,
LongDouble,
FloatComplex,
DoubleComplex,
LongDoubleComplex,
Bool,
Void,
String,
Object,
Block,
Class,
Sel,
Unknown,
}
impl Primitive {
pub(crate) const fn to_str(self) -> &'static str {
use Primitive::*;
match self {
Char => "c",
Short => "s",
Int => "i",
Long => "l",
LongLong => "q",
UChar => "C",
UShort => "S",
UInt => "I",
ULong => "L",
ULongLong => "Q",
Float => "f",
Double => "d",
LongDouble => "D",
FloatComplex => "jf",
DoubleComplex => "jd",
LongDoubleComplex => "jD",
Bool => "B",
Void => "v",
String => "*",
Object => "@",
Block => "@?",
Class => "#",
Sel => ":",
Unknown => "?",
}
}
pub(crate) fn equivalents(&self) -> &[Self] {
match self {
Self::Block | Self::Object | Self::Class => &[Self::Block, Self::Object, Self::Class],
_ => slice::from_ref(self),
}
}
pub(crate) const fn size(self) -> Option<usize> {
match self {
Self::Bool => Some(1),
Self::Char => Some(mem::size_of::<ffi::c_char>()),
Self::UChar => Some(mem::size_of::<ffi::c_uchar>()),
Self::Short => Some(mem::size_of::<ffi::c_short>()),
Self::UShort => Some(mem::size_of::<ffi::c_ushort>()),
Self::Int => Some(mem::size_of::<ffi::c_int>()),
Self::UInt => Some(mem::size_of::<ffi::c_uint>()),
Self::Long => Some(mem::size_of::<ffi::c_long>()),
Self::ULong => Some(mem::size_of::<ffi::c_ulong>()),
Self::LongLong => Some(mem::size_of::<ffi::c_longlong>()),
Self::ULongLong => Some(mem::size_of::<ffi::c_ulonglong>()),
Self::Float => Some(mem::size_of::<ffi::c_float>()),
Self::Double => Some(mem::size_of::<ffi::c_double>()),
#[cfg(any(
target_arch = "x86_64",
all(target_arch = "x86", target_vendor = "apple"),
all(target_arch = "aarch64", not(target_vendor = "apple")),
))]
Self::LongDouble => Some(16),
#[cfg(all(target_arch = "x86", not(target_vendor = "apple")))]
Self::LongDouble => Some(12),
#[cfg(any(
target_arch = "arm",
all(target_arch = "aarch64", target_vendor = "apple"),
))]
Self::LongDouble => Some(8),
Self::FloatComplex => Some(mem::size_of::<ffi::c_float>() * 2),
Self::DoubleComplex => Some(mem::size_of::<ffi::c_double>() * 2),
Self::LongDoubleComplex => match Self::LongDouble.size() {
Some(size) => Some(size * 2),
None => None,
},
Self::String | Self::Object | Self::Block | Self::Class | Self::Sel => {
Some(mem::size_of::<*const ()>())
}
Self::Void | Self::Unknown => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum IndirectionKind {
Atomic,
Pointer,
}
impl IndirectionKind {
pub(crate) const fn prefix(self) -> char {
self.prefix_byte() as char
}
pub(crate) const fn prefix_byte(self) -> u8 {
match self {
Self::Atomic => b'A',
Self::Pointer => b'^',
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum ContainerKind {
Struct,
Union,
}
impl ContainerKind {
pub(crate) const fn start(self) -> char {
self.start_byte() as char
}
pub(crate) const fn end(self) -> char {
self.end_byte() as char
}
pub(crate) const fn start_byte(self) -> u8 {
match self {
Self::Struct => b'{',
Self::Union => b'(',
}
}
pub(crate) const fn end_byte(self) -> u8 {
match self {
Self::Struct => b'}',
Self::Union => b')',
}
}
}
impl fmt::Display for ContainerKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Struct => write!(f, "struct"),
Self::Union => write!(f, "union"),
}
}
}
pub(crate) trait EncodingType: Sized + fmt::Debug {
fn helper(&self) -> Helper<'_, Self>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub(crate) enum Helper<'a, E = Encoding> {
Primitive(Primitive),
BitField(u8, Option<&'a (u64, E)>),
Indirection(IndirectionKind, &'a E),
Array(u64, &'a E),
Container(ContainerKind, &'a str, &'a [E]),
NoneInvalid,
}
impl<E: EncodingType> Helper<'_, E> {
pub(crate) fn fmt(&self, f: &mut fmt::Formatter<'_>, level: NestingLevel) -> fmt::Result {
match self {
Self::Primitive(primitive) => {
write!(f, "{}", primitive.to_str())?;
}
Self::BitField(size, None) => {
write!(f, "b{size}")?;
}
Self::BitField(size, Some((offset, t))) => {
write!(f, "b{offset}")?;
t.helper().fmt(f, level.bitfield())?;
write!(f, "{size}")?;
}
Self::Indirection(kind, t) => {
write!(f, "{}", kind.prefix())?;
t.helper().fmt(f, level.indirection(*kind))?;
}
Self::Array(len, item) => {
write!(f, "[")?;
write!(f, "{len}")?;
item.helper().fmt(f, level.array())?;
write!(f, "]")?;
}
Self::Container(kind, name, items) => {
write!(f, "{}", kind.start())?;
write!(f, "{name}")?;
if let Some(level) = level.container_include_fields() {
write!(f, "=")?;
for item in *items {
item.helper().fmt(f, level)?;
}
}
write!(f, "{}", kind.end())?;
}
Self::NoneInvalid => {}
}
Ok(())
}
pub(crate) fn size(&self, level: NestingLevel) -> Option<usize> {
match self {
Self::NoneInvalid => None,
Self::Primitive(prim) => prim.size(),
Self::BitField(size_bits, off_typ) => Some(
(usize::from(*size_bits).next_power_of_two().max(8) / 8).max(
off_typ
.and_then(|(_, typ)| typ.helper().size(level.bitfield()))
.unwrap_or_default(),
),
),
Self::Indirection(kind, typ) => match kind {
IndirectionKind::Pointer => Some(mem::size_of::<*const ()>()),
IndirectionKind::Atomic => typ.helper().size(level.indirection(*kind)),
},
Self::Array(len, typ) => typ
.helper()
.size(level.array())
.map(|typ_size| *len as usize * typ_size),
Self::Container(kind, _, fields) => {
level
.container_include_fields()
.and_then(|level| match kind {
ContainerKind::Struct => {
fields.iter().map(|field| field.helper().size(level)).sum()
}
ContainerKind::Union => fields
.iter()
.map(|field| field.helper().size(level))
.max()
.flatten(),
})
}
}
}
}
impl Helper<'_> {
pub(crate) const fn new(encoding: &Encoding) -> Self {
use Encoding::*;
match encoding {
Char => Self::Primitive(Primitive::Char),
Short => Self::Primitive(Primitive::Short),
Int => Self::Primitive(Primitive::Int),
Long => Self::Primitive(Primitive::Long),
LongLong => Self::Primitive(Primitive::LongLong),
UChar => Self::Primitive(Primitive::UChar),
UShort => Self::Primitive(Primitive::UShort),
UInt => Self::Primitive(Primitive::UInt),
ULong => Self::Primitive(Primitive::ULong),
ULongLong => Self::Primitive(Primitive::ULongLong),
Float => Self::Primitive(Primitive::Float),
Double => Self::Primitive(Primitive::Double),
LongDouble => Self::Primitive(Primitive::LongDouble),
FloatComplex => Self::Primitive(Primitive::FloatComplex),
DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
Bool => Self::Primitive(Primitive::Bool),
Void => Self::Primitive(Primitive::Void),
String => Self::Primitive(Primitive::String),
Object => Self::Primitive(Primitive::Object),
Block => Self::Primitive(Primitive::Block),
Class => Self::Primitive(Primitive::Class),
Sel => Self::Primitive(Primitive::Sel),
Unknown => Self::Primitive(Primitive::Unknown),
BitField(b, t) => Self::BitField(*b, *t),
Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
Array(len, item) => Self::Array(*len, item),
Struct(name, fields) => {
if !verify_name(name) {
panic!("Struct name was not a valid identifier");
}
Self::Container(ContainerKind::Struct, name, fields)
}
Union(name, members) => {
if !verify_name(name) {
panic!("Union name was not a valid identifier");
}
Self::Container(ContainerKind::Union, name, members)
}
None => Self::NoneInvalid,
}
}
}
impl<'a> Helper<'a, EncodingBox> {
pub(crate) fn from_box(encoding: &'a EncodingBox) -> Self {
use EncodingBox::*;
match encoding {
Char => Self::Primitive(Primitive::Char),
Short => Self::Primitive(Primitive::Short),
Int => Self::Primitive(Primitive::Int),
Long => Self::Primitive(Primitive::Long),
LongLong => Self::Primitive(Primitive::LongLong),
UChar => Self::Primitive(Primitive::UChar),
UShort => Self::Primitive(Primitive::UShort),
UInt => Self::Primitive(Primitive::UInt),
ULong => Self::Primitive(Primitive::ULong),
ULongLong => Self::Primitive(Primitive::ULongLong),
Float => Self::Primitive(Primitive::Float),
Double => Self::Primitive(Primitive::Double),
LongDouble => Self::Primitive(Primitive::LongDouble),
FloatComplex => Self::Primitive(Primitive::FloatComplex),
DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
Bool => Self::Primitive(Primitive::Bool),
Void => Self::Primitive(Primitive::Void),
String => Self::Primitive(Primitive::String),
Object => Self::Primitive(Primitive::Object),
Block => Self::Primitive(Primitive::Block),
Class => Self::Primitive(Primitive::Class),
Sel => Self::Primitive(Primitive::Sel),
Unknown => Self::Primitive(Primitive::Unknown),
BitField(b, t) => Self::BitField(*b, t.as_deref()),
Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
Array(len, item) => Self::Array(*len, item),
Struct(name, fields) => {
if !verify_name(name) {
panic!("Struct name was not a valid identifier");
}
Self::Container(ContainerKind::Struct, name, fields)
}
Union(name, members) => {
if !verify_name(name) {
panic!("Union name was not a valid identifier");
}
Self::Container(ContainerKind::Union, name, members)
}
None => Self::NoneInvalid,
}
}
}
impl EncodingType for Encoding {
fn helper(&self) -> Helper<'_, Self> {
Helper::new(self)
}
}
impl EncodingType for EncodingBox {
fn helper(&self) -> Helper<'_, Self> {
Helper::from_box(self)
}
}