use core::fmt;
use crate::helper::{Helper, NestingLevel};
use crate::parse;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive] pub enum Encoding {
Char,
Short,
Int,
Long,
LongLong,
UChar,
UShort,
UInt,
ULong,
ULongLong,
Float,
Double,
LongDouble,
FloatComplex,
DoubleComplex,
LongDoubleComplex,
Bool,
Void,
String,
Object,
Block,
Class,
Sel,
Unknown,
BitField(u8, &'static Encoding),
Pointer(&'static Encoding),
Atomic(&'static Encoding),
Array(usize, &'static Encoding),
Struct(&'static str, &'static [Encoding]),
Union(&'static str, &'static [Encoding]),
}
impl Encoding {
pub const C_LONG: Self = {
if cfg!(any(target_pointer_width = "32", windows)) {
Self::Long
} else {
Self::LongLong
}
};
pub const C_ULONG: Self = {
if cfg!(any(target_pointer_width = "32", windows)) {
Self::ULong
} else {
Self::ULongLong
}
};
pub fn equivalent_to(&self, other: &Self) -> bool {
equivalent_to(self, other, NestingLevel::new())
}
pub fn equivalent_to_str(&self, s: &str) -> bool {
if let Some(res) = self.equivalent_to_start_of_str(s) {
res.is_empty()
} else {
false
}
}
pub fn equivalent_to_start_of_str<'a>(&self, s: &'a str) -> Option<&'a str> {
let s = s.trim_start_matches(parse::QUALIFIERS);
parse::rm_enc_prefix(s, self, NestingLevel::new())
}
}
fn equivalent_to(enc1: &Encoding, enc2: &Encoding, level: NestingLevel) -> bool {
use Helper::*;
match (Helper::new(enc1), Helper::new(enc2)) {
(Primitive(p1), Primitive(p2)) => p1 == p2,
(BitField(b1, type1), BitField(b2, type2)) => {
b1 == b2 && equivalent_to(type1, type2, level.bitfield())
}
(Indirection(kind1, t1), Indirection(kind2, t2)) => {
kind1 == kind2 && equivalent_to(t1, t2, level.indirection(kind1))
}
(Array(len1, item1), Array(len2, item2)) => {
len1 == len2 && equivalent_to(item1, item2, level.array())
}
(Container(kind1, name1, fields1), Container(kind2, name2, fields2)) => {
if kind1 != kind2 {
return false;
}
if name1 != name2 {
return false;
}
if let Some(level) = level.container() {
if fields1.len() != fields2.len() {
return false;
}
for (field1, field2) in fields1.iter().zip(fields2.iter()) {
if !equivalent_to(field1, field2, level) {
return false;
}
}
}
true
}
(_, _) => false,
}
}
fn display_fmt(this: &Encoding, f: &mut fmt::Formatter<'_>, level: NestingLevel) -> fmt::Result {
use Helper::*;
match Helper::new(this) {
Primitive(primitive) => f.write_str(primitive.to_str()),
BitField(b, _type) => {
write!(f, "b{}", b)
}
Indirection(kind, t) => {
write!(f, "{}", kind.prefix())?;
display_fmt(t, f, level.indirection(kind))
}
Array(len, item) => {
write!(f, "[")?;
write!(f, "{}", len)?;
display_fmt(item, f, level.array())?;
write!(f, "]")
}
Container(kind, name, fields) => {
write!(f, "{}", kind.start())?;
write!(f, "{}", name)?;
if let Some(level) = level.container() {
write!(f, "=")?;
for field in fields {
display_fmt(field, f, level)?;
}
}
write!(f, "{}", kind.end())
}
}
}
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
display_fmt(self, f, NestingLevel::new())
}
}
#[cfg(test)]
mod tests {
use super::Encoding;
use crate::helper::NestingLevel;
use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
use alloc::string::ToString;
fn send_sync<T: Send + Sync>() {}
#[test]
fn test_send_sync() {
send_sync::<Encoding>();
}
#[test]
fn smoke() {
assert!(Encoding::Short.equivalent_to_str("s"));
}
#[test]
fn qualifiers() {
assert!(Encoding::Void.equivalent_to_str("v"));
assert!(Encoding::Void.equivalent_to_str("Vv"));
assert!(Encoding::String.equivalent_to_str("*"));
assert!(Encoding::String.equivalent_to_str("r*"));
}
macro_rules! assert_enc {
($(
fn $name:ident() {
$encoding:expr;
$(
~$equivalent_encoding:expr;
)*
$(
!$not_encoding:expr;
)*
$string:literal;
$(
~$equivalent_string:expr;
)*
$(
!$not_string:literal;
)*
}
)+) => {$(
#[test]
fn $name() {
const E: Encoding = $encoding;
assert_eq!(E, E);
assert_eq!(E.to_string(), $string);
assert!(E.equivalent_to(&E));
assert!(E.equivalent_to_str($string));
assert_eq!(E.equivalent_to_start_of_str(concat!($string, "xyz")), Some("xyz"));
$(
assert!(E.equivalent_to(&$equivalent_encoding));
assert!(E.equivalent_to_str(&$equivalent_encoding.to_string()));
)*
$(
assert!(E.equivalent_to_str($equivalent_string));
)*
$(
assert_ne!(E, $not_encoding);
assert!(!E.equivalent_to(&$not_encoding));
assert!(!E.equivalent_to_str(&$not_encoding.to_string()));
)*
$(
assert!(!E.equivalent_to_str(&$not_string));
)*
const STATIC_ENCODING_DATA: [u8; static_encoding_str_len(&E, NestingLevel::new())] = static_encoding_str_array(&E, NestingLevel::new());
const STATIC_ENCODING_STR: &str = unsafe { core::str::from_utf8_unchecked(&STATIC_ENCODING_DATA) };
assert_eq!(STATIC_ENCODING_STR, $string);
}
)+};
}
assert_enc! {
fn int() {
Encoding::Int;
!Encoding::Char;
"i";
}
fn char() {
Encoding::Char;
!Encoding::Int;
"c";
~"rc";
~"nc";
~"Nc";
~"oc";
~"Oc";
~"Rc";
~"Vc";
!"ri";
}
fn block() {
Encoding::Block;
"@?";
}
fn object() {
Encoding::Object;
!Encoding::Block;
"@";
!"@?";
}
fn unknown() {
Encoding::Unknown;
!Encoding::Block;
"?";
}
fn object_unknown_in_struct() {
Encoding::Struct("S", &[Encoding::Block, Encoding::Object, Encoding::Unknown]);
"{S=@?@?}";
}
fn double() {
Encoding::Double;
"d";
}
fn bitfield() {
Encoding::BitField(32, &Encoding::Int);
!Encoding::Int;
!Encoding::BitField(33, &Encoding::Int);
"b32";
!"b32a";
!"b";
!"b-32";
}
fn atomic() {
Encoding::Atomic(&Encoding::Int);
!Encoding::Pointer(&Encoding::Int);
!Encoding::Atomic(&Encoding::Char);
!Encoding::Atomic(&Encoding::Atomic(&Encoding::Int));
"Ai";
}
fn atomic_string() {
Encoding::Atomic(&Encoding::String);
"A*";
}
fn pointer() {
Encoding::Pointer(&Encoding::Int);
!Encoding::Atomic(&Encoding::Int);
!Encoding::Pointer(&Encoding::Char);
!Encoding::Pointer(&Encoding::Pointer(&Encoding::Int));
"^i";
}
fn array() {
Encoding::Array(12, &Encoding::Int);
!Encoding::Int;
!Encoding::Array(11, &Encoding::Int);
!Encoding::Array(12, &Encoding::Char);
"[12i]";
!"[12i";
}
fn struct_() {
Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]);
!Encoding::Union("SomeStruct", &[Encoding::Char, Encoding::Int]);
!Encoding::Int;
!Encoding::Struct("SomeStruct", &[Encoding::Int]);
!Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int, Encoding::Int]);
!Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]);
!Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]);
"{SomeStruct=ci}";
!"{SomeStruct=ci";
!"{SomeStruct}";
!"{SomeStruct=}";
}
fn struct_unicode() {
Encoding::Struct("☃", &[Encoding::Char]);
"{☃=c}";
}
fn pointer_struct() {
Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
!Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
!Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
"^{SomeStruct=ci}";
!"^{SomeStruct=ci";
!"^{SomeStruct}";
!"^{SomeStruct=}";
}
fn pointer_pointer_struct() {
Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int])));
~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char])));
!Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int])));
"^^{SomeStruct}";
!"^^{SomeStruct=ci}";
!"^^{SomeStruct=ii}";
!"^^{SomeStruct=ci";
!"^^{SomeStruct=}";
}
fn atomic_struct() {
Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
!Encoding::Atomic(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
"A{SomeStruct}";
!"A{SomeStruct=ci}";
!"A{SomeStruct=ci";
!"A{SomeStruct=}";
}
fn empty_struct() {
Encoding::Struct("SomeStruct", &[]);
"{SomeStruct=}";
!"{SomeStruct}";
}
fn union_() {
Encoding::Union("Onion", &[Encoding::Char, Encoding::Int]);
!Encoding::Struct("Onion", &[Encoding::Char, Encoding::Int]);
!Encoding::Int;
!Encoding::Union("Onion", &[Encoding::Int, Encoding::Char]);
!Encoding::Union("AnotherUnion", &[Encoding::Char, Encoding::Int]);
"(Onion=ci)";
!"(Onion=ci";
}
fn nested() {
Encoding::Struct(
"A",
&[
Encoding::Struct("B", &[Encoding::Int]),
Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
Encoding::Char,
],
);
~Encoding::Struct(
"A",
&[
Encoding::Struct("B", &[Encoding::Int]),
Encoding::Pointer(&Encoding::Struct("C", &[])),
Encoding::Char,
],
);
"{A={B=i}^{C}c}";
!"{A={B=i}^{C=d}c}";
!"{A={B=i}^{C=i}c}";
!"{A={B=i}^{C=d}c";
}
fn nested_pointer() {
Encoding::Pointer(&Encoding::Struct(
"A",
&[
Encoding::Struct("B", &[Encoding::Int]),
Encoding::Pointer(&Encoding::Struct("C", &[Encoding::Double])),
],
));
"^{A={B=i}^{C}}";
!"^{A={B}^{C}}";
!"^{A={B=i}^{C=d}}";
}
fn various() {
Encoding::Struct(
"abc",
&[
Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
Encoding::Union("def", &[Encoding::Block]),
Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, &Encoding::Int))),
Encoding::Unknown,
]
);
"{abc=^[8B](def=@?)^^b255?}";
}
}
}