objc2_encode/
helper.rs

1use core::ffi;
2use core::fmt;
3use core::mem;
4use core::slice;
5use core::write;
6
7use crate::parse::verify_name;
8use crate::Encoding;
9use crate::EncodingBox;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub(crate) enum NestingLevel {
13    Top,
14    Within,
15    Bottom,
16}
17
18impl NestingLevel {
19    pub(crate) const fn new() -> Self {
20        Self::Top
21    }
22
23    pub(crate) const fn bitfield(self) -> Self {
24        // This is a bit irrelevant, since bitfields can only contain integral
25        // types
26        self
27    }
28
29    pub(crate) const fn indirection(self, kind: IndirectionKind) -> Self {
30        match kind {
31            // Move all the way down
32            IndirectionKind::Atomic => Self::Bottom,
33            // Move one step down
34            IndirectionKind::Pointer => match self {
35                Self::Top => Self::Within,
36                Self::Bottom | Self::Within => Self::Bottom,
37            },
38        }
39    }
40
41    pub(crate) const fn array(self) -> Self {
42        // TODO: Is this correct?
43        self
44    }
45
46    pub(crate) const fn container_include_fields(self) -> Option<Self> {
47        match self {
48            Self::Top | Self::Within => {
49                // Move top one step down
50                Some(Self::Within)
51            }
52            Self::Bottom => None,
53        }
54    }
55}
56
57pub(crate) fn compare_encodings<E1: EncodingType, E2: EncodingType>(
58    enc1: &E1,
59    enc2: &E2,
60    level: NestingLevel,
61    include_all: bool,
62) -> bool {
63    use Helper::*;
64    // Note: Ideally `Block` and sequence of `Object, Unknown` in struct
65    // should compare equivalent, but we don't bother since in practice a
66    // plain `Unknown` will never appear.
67
68    let level = if include_all {
69        NestingLevel::new()
70    } else {
71        level
72    };
73
74    match (enc1.helper(), enc2.helper()) {
75        (Primitive(p1), Primitive(p2)) => p1.equivalents().contains(&p2),
76        (BitField(size1, Some((offset1, type1))), BitField(size2, Some((offset2, type2)))) => {
77            size1 == size2
78                && offset1 == offset2
79                && compare_encodings(type1, type2, level.bitfield(), include_all)
80        }
81        (BitField(size1, None), BitField(size2, None)) => size1 == size2,
82        // The type-encoding of a bitfield is always either available, or it
83        // is not (depends on platform); so if it was available in one, but
84        // not the other, we should compare the encodings unequal.
85        (BitField(_, _), BitField(_, _)) => false,
86        (Indirection(kind1, t1), Indirection(kind2, t2)) => {
87            kind1 == kind2 && compare_encodings(t1, t2, level.indirection(kind1), include_all)
88        }
89        (Array(len1, item1), Array(len2, item2)) => {
90            len1 == len2 && compare_encodings(item1, item2, level.array(), include_all)
91        }
92        (Container(kind1, name1, items1), Container(kind2, name2, items2)) => {
93            kind1 == kind2 && name1 == name2 && {
94                if let Some(level) = level.container_include_fields() {
95                    // If either container is empty, then they are equivalent
96                    if items1.is_empty() || items2.is_empty() {
97                        return true;
98                    }
99                    if items1.len() != items2.len() {
100                        return false;
101                    }
102                    for (item1, item2) in items1.iter().zip(items2.iter()) {
103                        if !compare_encodings(item1, item2, level, include_all) {
104                            return false;
105                        }
106                    }
107                    true
108                } else {
109                    true
110                }
111            }
112        }
113        (NoneInvalid, NoneInvalid) => true,
114        (_, _) => false,
115    }
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
119#[non_exhaustive]
120pub(crate) enum Primitive {
121    Char,
122    Short,
123    Int,
124    Long,
125    LongLong,
126    UChar,
127    UShort,
128    UInt,
129    ULong,
130    ULongLong,
131    Float,
132    Double,
133    LongDouble,
134    FloatComplex,
135    DoubleComplex,
136    LongDoubleComplex,
137    Bool,
138    Void,
139    String,
140    Object,
141    Block,
142    Class,
143    Sel,
144    Unknown,
145}
146
147impl Primitive {
148    pub(crate) const fn to_str(self) -> &'static str {
149        use Primitive::*;
150        match self {
151            Char => "c",
152            Short => "s",
153            Int => "i",
154            Long => "l",
155            LongLong => "q",
156            UChar => "C",
157            UShort => "S",
158            UInt => "I",
159            ULong => "L",
160            ULongLong => "Q",
161            Float => "f",
162            Double => "d",
163            LongDouble => "D",
164            FloatComplex => "jf",
165            DoubleComplex => "jd",
166            LongDoubleComplex => "jD",
167            Bool => "B",
168            Void => "v",
169            String => "*",
170            Object => "@",
171            Block => "@?",
172            Class => "#",
173            Sel => ":",
174            Unknown => "?",
175        }
176    }
177
178    /// Classes, blocks and objects can all be used in places where `id` is
179    /// expected (i.e. where the encoding is for objects).
180    ///
181    /// So to support those use-cases, we compare them as equivalent.
182    pub(crate) fn equivalents(&self) -> &[Self] {
183        match self {
184            Self::Block | Self::Object | Self::Class => &[Self::Block, Self::Object, Self::Class],
185            _ => slice::from_ref(self),
186        }
187    }
188
189    pub(crate) const fn size(self) -> Option<usize> {
190        match self {
191            // Under all the considered targets, `_Bool` is sized and aligned
192            // to a single byte. See:
193            // https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+BoolWidth&type=code
194            // Obj-C's `BOOL` is `signed char`, i.e. `c`, so will fall in the
195            // below case. See: https://developer.apple.com/documentation/objectivec/bool?language=objc
196            Self::Bool => Some(1),
197            // Numbers.
198            Self::Char => Some(mem::size_of::<ffi::c_char>()),
199            Self::UChar => Some(mem::size_of::<ffi::c_uchar>()),
200            Self::Short => Some(mem::size_of::<ffi::c_short>()),
201            Self::UShort => Some(mem::size_of::<ffi::c_ushort>()),
202            Self::Int => Some(mem::size_of::<ffi::c_int>()),
203            Self::UInt => Some(mem::size_of::<ffi::c_uint>()),
204            Self::Long => Some(mem::size_of::<ffi::c_long>()),
205            Self::ULong => Some(mem::size_of::<ffi::c_ulong>()),
206            Self::LongLong => Some(mem::size_of::<ffi::c_longlong>()),
207            Self::ULongLong => Some(mem::size_of::<ffi::c_ulonglong>()),
208            Self::Float => Some(mem::size_of::<ffi::c_float>()),
209            Self::Double => Some(mem::size_of::<ffi::c_double>()),
210            // https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+LongDoubleWidth&type=code
211            #[cfg(any(
212                target_arch = "x86_64",
213                all(target_arch = "x86", target_vendor = "apple"),
214                all(target_arch = "aarch64", not(target_vendor = "apple")),
215            ))]
216            Self::LongDouble => Some(16),
217            #[cfg(all(target_arch = "x86", not(target_vendor = "apple")))]
218            Self::LongDouble => Some(12),
219            #[cfg(any(
220                target_arch = "arm",
221                all(target_arch = "aarch64", target_vendor = "apple"),
222            ))]
223            Self::LongDouble => Some(8),
224            Self::FloatComplex => Some(mem::size_of::<ffi::c_float>() * 2),
225            Self::DoubleComplex => Some(mem::size_of::<ffi::c_double>() * 2),
226            Self::LongDoubleComplex => match Self::LongDouble.size() {
227                Some(size) => Some(size * 2),
228                None => None,
229            },
230            // Pointers.
231            Self::String | Self::Object | Self::Block | Self::Class | Self::Sel => {
232                Some(mem::size_of::<*const ()>())
233            }
234            // Nothing.
235            Self::Void | Self::Unknown => None,
236        }
237    }
238}
239
240#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
241pub(crate) enum IndirectionKind {
242    Atomic,
243    Pointer,
244}
245
246impl IndirectionKind {
247    pub(crate) const fn prefix(self) -> char {
248        self.prefix_byte() as char
249    }
250
251    pub(crate) const fn prefix_byte(self) -> u8 {
252        match self {
253            Self::Atomic => b'A',
254            Self::Pointer => b'^',
255        }
256    }
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
260pub(crate) enum ContainerKind {
261    Struct,
262    Union,
263}
264
265impl ContainerKind {
266    pub(crate) const fn start(self) -> char {
267        self.start_byte() as char
268    }
269
270    pub(crate) const fn end(self) -> char {
271        self.end_byte() as char
272    }
273
274    pub(crate) const fn start_byte(self) -> u8 {
275        match self {
276            Self::Struct => b'{',
277            Self::Union => b'(',
278        }
279    }
280
281    pub(crate) const fn end_byte(self) -> u8 {
282        match self {
283            Self::Struct => b'}',
284            Self::Union => b')',
285        }
286    }
287}
288
289impl fmt::Display for ContainerKind {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        match self {
292            Self::Struct => write!(f, "struct"),
293            Self::Union => write!(f, "union"),
294        }
295    }
296}
297
298pub(crate) trait EncodingType: Sized + fmt::Debug {
299    fn helper(&self) -> Helper<'_, Self>;
300}
301
302#[derive(Clone, Debug, PartialEq, Eq, Hash)]
303#[non_exhaustive]
304pub(crate) enum Helper<'a, E = Encoding> {
305    Primitive(Primitive),
306    BitField(u8, Option<&'a (u64, E)>),
307    Indirection(IndirectionKind, &'a E),
308    Array(u64, &'a E),
309    Container(ContainerKind, &'a str, &'a [E]),
310    NoneInvalid,
311}
312
313impl<E: EncodingType> Helper<'_, E> {
314    pub(crate) fn fmt(&self, f: &mut fmt::Formatter<'_>, level: NestingLevel) -> fmt::Result {
315        match self {
316            Self::Primitive(primitive) => {
317                write!(f, "{}", primitive.to_str())?;
318            }
319            Self::BitField(size, None) => {
320                write!(f, "b{size}")?;
321            }
322            Self::BitField(size, Some((offset, t))) => {
323                write!(f, "b{offset}")?;
324                t.helper().fmt(f, level.bitfield())?;
325                write!(f, "{size}")?;
326            }
327            Self::Indirection(kind, t) => {
328                write!(f, "{}", kind.prefix())?;
329                t.helper().fmt(f, level.indirection(*kind))?;
330            }
331            Self::Array(len, item) => {
332                write!(f, "[")?;
333                write!(f, "{len}")?;
334                item.helper().fmt(f, level.array())?;
335                write!(f, "]")?;
336            }
337            Self::Container(kind, name, items) => {
338                write!(f, "{}", kind.start())?;
339                write!(f, "{name}")?;
340                if let Some(level) = level.container_include_fields() {
341                    write!(f, "=")?;
342                    for item in *items {
343                        item.helper().fmt(f, level)?;
344                    }
345                }
346                write!(f, "{}", kind.end())?;
347            }
348            Self::NoneInvalid => {}
349        }
350        Ok(())
351    }
352
353    pub(crate) fn size(&self, level: NestingLevel) -> Option<usize> {
354        // TODO: alignment?
355        match self {
356            Self::NoneInvalid => None,
357            Self::Primitive(prim) => prim.size(),
358            Self::BitField(size_bits, off_typ) => Some(
359                (usize::from(*size_bits).next_power_of_two().max(8) / 8).max(
360                    off_typ
361                        .and_then(|(_, typ)| typ.helper().size(level.bitfield()))
362                        .unwrap_or_default(),
363                ),
364            ),
365            Self::Indirection(kind, typ) => match kind {
366                IndirectionKind::Pointer => Some(mem::size_of::<*const ()>()),
367                IndirectionKind::Atomic => typ.helper().size(level.indirection(*kind)),
368            },
369            Self::Array(len, typ) => typ
370                .helper()
371                .size(level.array())
372                .map(|typ_size| *len as usize * typ_size),
373            Self::Container(kind, _, fields) => {
374                level
375                    .container_include_fields()
376                    .and_then(|level| match kind {
377                        ContainerKind::Struct => {
378                            fields.iter().map(|field| field.helper().size(level)).sum()
379                        }
380                        ContainerKind::Union => fields
381                            .iter()
382                            .map(|field| field.helper().size(level))
383                            .max()
384                            .flatten(),
385                    })
386            }
387        }
388    }
389}
390
391impl Helper<'_> {
392    pub(crate) const fn new(encoding: &Encoding) -> Self {
393        use Encoding::*;
394        match encoding {
395            Char => Self::Primitive(Primitive::Char),
396            Short => Self::Primitive(Primitive::Short),
397            Int => Self::Primitive(Primitive::Int),
398            Long => Self::Primitive(Primitive::Long),
399            LongLong => Self::Primitive(Primitive::LongLong),
400            UChar => Self::Primitive(Primitive::UChar),
401            UShort => Self::Primitive(Primitive::UShort),
402            UInt => Self::Primitive(Primitive::UInt),
403            ULong => Self::Primitive(Primitive::ULong),
404            ULongLong => Self::Primitive(Primitive::ULongLong),
405            Float => Self::Primitive(Primitive::Float),
406            Double => Self::Primitive(Primitive::Double),
407            LongDouble => Self::Primitive(Primitive::LongDouble),
408            FloatComplex => Self::Primitive(Primitive::FloatComplex),
409            DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
410            LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
411            Bool => Self::Primitive(Primitive::Bool),
412            Void => Self::Primitive(Primitive::Void),
413            String => Self::Primitive(Primitive::String),
414            Object => Self::Primitive(Primitive::Object),
415            Block => Self::Primitive(Primitive::Block),
416            Class => Self::Primitive(Primitive::Class),
417            Sel => Self::Primitive(Primitive::Sel),
418            Unknown => Self::Primitive(Primitive::Unknown),
419            BitField(b, t) => Self::BitField(*b, *t),
420            Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
421            Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
422            Array(len, item) => Self::Array(*len, item),
423            Struct(name, fields) => {
424                if !verify_name(name) {
425                    panic!("Struct name was not a valid identifier");
426                }
427                Self::Container(ContainerKind::Struct, name, fields)
428            }
429            Union(name, members) => {
430                if !verify_name(name) {
431                    panic!("Union name was not a valid identifier");
432                }
433                Self::Container(ContainerKind::Union, name, members)
434            }
435            None => Self::NoneInvalid,
436        }
437    }
438}
439
440impl<'a> Helper<'a, EncodingBox> {
441    pub(crate) fn from_box(encoding: &'a EncodingBox) -> Self {
442        use EncodingBox::*;
443        match encoding {
444            Char => Self::Primitive(Primitive::Char),
445            Short => Self::Primitive(Primitive::Short),
446            Int => Self::Primitive(Primitive::Int),
447            Long => Self::Primitive(Primitive::Long),
448            LongLong => Self::Primitive(Primitive::LongLong),
449            UChar => Self::Primitive(Primitive::UChar),
450            UShort => Self::Primitive(Primitive::UShort),
451            UInt => Self::Primitive(Primitive::UInt),
452            ULong => Self::Primitive(Primitive::ULong),
453            ULongLong => Self::Primitive(Primitive::ULongLong),
454            Float => Self::Primitive(Primitive::Float),
455            Double => Self::Primitive(Primitive::Double),
456            LongDouble => Self::Primitive(Primitive::LongDouble),
457            FloatComplex => Self::Primitive(Primitive::FloatComplex),
458            DoubleComplex => Self::Primitive(Primitive::DoubleComplex),
459            LongDoubleComplex => Self::Primitive(Primitive::LongDoubleComplex),
460            Bool => Self::Primitive(Primitive::Bool),
461            Void => Self::Primitive(Primitive::Void),
462            String => Self::Primitive(Primitive::String),
463            Object => Self::Primitive(Primitive::Object),
464            Block => Self::Primitive(Primitive::Block),
465            Class => Self::Primitive(Primitive::Class),
466            Sel => Self::Primitive(Primitive::Sel),
467            Unknown => Self::Primitive(Primitive::Unknown),
468            BitField(b, t) => Self::BitField(*b, t.as_deref()),
469            Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
470            Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
471            Array(len, item) => Self::Array(*len, item),
472            Struct(name, fields) => {
473                if !verify_name(name) {
474                    panic!("Struct name was not a valid identifier");
475                }
476                Self::Container(ContainerKind::Struct, name, fields)
477            }
478            Union(name, members) => {
479                if !verify_name(name) {
480                    panic!("Union name was not a valid identifier");
481                }
482                Self::Container(ContainerKind::Union, name, members)
483            }
484            None => Self::NoneInvalid,
485        }
486    }
487}
488
489impl EncodingType for Encoding {
490    fn helper(&self) -> Helper<'_, Self> {
491        Helper::new(self)
492    }
493}
494
495impl EncodingType for EncodingBox {
496    fn helper(&self) -> Helper<'_, Self> {
497        Helper::from_box(self)
498    }
499}