Skip to content

Commit

Permalink
Make objc2_encode target-agnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 29, 2023
1 parent b226269 commit b3e2c35
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 115 deletions.
8 changes: 8 additions & 0 deletions crates/objc2-encode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Made the crate `no_std` compatible.
* Made the crate platform-agnostic.

### Changed
* **BREAKING**: Moved the `Encode`, `RefEncode`, `EncodeArguments`,
`EncodeConvert`, `OptionEncode` to `objc2`.
* **BREAKING**: `Encoding::BitField` now allows omitting the type using an
`Option`.
* **BREAKING**: Changed length field in `Encoding::Array` from `usize` to
`u64` (to be platform-agnostic).

### Fixed
* Fixed `Encoding::BitField` encoding parsing.


## 2.0.0-pre.3 - 2022-12-24
Expand Down
117 changes: 73 additions & 44 deletions crates/objc2-encode/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,103 +39,108 @@ use crate::EncodingBox;
// See <https://en.cppreference.com/w/c/language/type>
#[non_exhaustive] // Maybe we're missing some encodings?
pub enum Encoding {
/// A C `char`. Corresponds to the `c` code.
/// A C `char`. Corresponds to the `"c"` code.
Char,
/// A C `short`. Corresponds to the `s` code.
/// A C `short`. Corresponds to the `"s"` code.
Short,
/// A C `int`. Corresponds to the `i` code.
/// A C `int`. Corresponds to the `"i"` code.
Int,
/// A C `long`. Corresponds to the `l` code.
/// A C `long`. Corresponds to the `"l"` code.
///
/// This is treated as a 32-bit quantity in 64-bit programs.
// TODO: What does that mean??
Long,
/// A C `long long`. Corresponds to the `q` code.
/// A C `long long`. Corresponds to the `"q"` code.
LongLong,
/// A C `unsigned char`. Corresponds to the `C` code.
/// A C `unsigned char`. Corresponds to the `"C"` code.
UChar,
/// A C `unsigned short`. Corresponds to the `S` code.
/// A C `unsigned short`. Corresponds to the `"S"` code.
UShort,
/// A C `unsigned int`. Corresponds to the `I` code.
/// A C `unsigned int`. Corresponds to the `"I"` code.
UInt,
/// A C `unsigned long`. Corresponds to the `L` code.
/// A C `unsigned long`. Corresponds to the `"L"` code.
ULong,
/// A C `unsigned long long`. Corresponds to the `Q` code.
/// A C `unsigned long long`. Corresponds to the `"Q"` code.
ULongLong,
/// A C `float`. Corresponds to the `f` code.
/// A C `float`. Corresponds to the `"f"` code.
Float,
/// A C `double`. Corresponds to the `d` code.
/// A C `double`. Corresponds to the `"d"` code.
Double,
/// A C `long double`. Corresponds to the `D` code.
/// A C `long double`. Corresponds to the `"D"` code.
LongDouble,
/// A C `float _Complex`. Corresponds to the `jf` code.
/// A C `float _Complex`. Corresponds to the `"j" "f"` code.
FloatComplex,
/// A C `_Complex` or `double _Complex`. Corresponds to the `jd` code.
/// A C `_Complex` or `double _Complex`. Corresponds to the `"j" "d"` code.
DoubleComplex,
/// A C `long double _Complex`. Corresponds to the `jD` code.
/// A C `long double _Complex`. Corresponds to the `"j" "D"` code.
LongDoubleComplex,
// TODO: Complex(&Encoding) ???
/// A C++ `bool` / C99 `_Bool`. Corresponds to the `B` code.
/// A C++ `bool` / C99 `_Bool`. Corresponds to the `"B"` code.
Bool,
/// A C `void`. Corresponds to the `v` code.
/// A C `void`. Corresponds to the `"v"` code.
Void,
/// A C `char *`. Corresponds to the `*` code.
/// A C `char *`. Corresponds to the `"*"` code.
String,
/// An Objective-C object (`id`). Corresponds to the `@` code.
/// An Objective-C object (`id`). Corresponds to the `"@"` code.
Object,
/// An Objective-C block. Corresponds to the `@?` code.
/// An Objective-C block. Corresponds to the `"@" "?"` code.
Block,
/// An Objective-C class (`Class`). Corresponds to the `#` code.
/// An Objective-C class (`Class`). Corresponds to the `"#"` code.
Class,
/// An Objective-C selector (`SEL`). Corresponds to the `:` code.
/// An Objective-C selector (`SEL`). Corresponds to the `":"` code.
Sel,
/// An unknown type. Corresponds to the `?` code.
/// An unknown type. Corresponds to the `"?"` code.
///
/// This is usually used to encode functions.
Unknown,
/// A bitfield with the given number of bits, and the given type.
///
/// The type is not currently used, but may be in the future for better
/// compatibility with Objective-C runtimes.
/// Corresponds to the `"b" size` code.
///
/// Corresponds to the `b num` code.
BitField(u8, &'static Encoding),
/// On GNUStep, this uses the `"b" offset type size` code, so this
/// contains an `Option` that should be set for that. Only integral types
/// are possible for the type.
///
/// A `BitField(_, Some(_))` and a `BitField(_, None)` do _not_ compare
/// equal; instead, you should set the bitfield depending on the target
/// platform.
BitField(u8, Option<&'static (u64, Encoding)>),
/// A pointer to the given type.
///
/// Corresponds to the `^ type` code.
/// Corresponds to the `"^" type` code.
Pointer(&'static Encoding),
/// A C11 [`_Atomic`] type.
///
/// Corresponds to the `A type` code. Not all encodings are possible in
/// Corresponds to the `"A" type` code. Not all encodings are possible in
/// this.
///
/// [`_Atomic`]: https://en.cppreference.com/w/c/language/atomic
Atomic(&'static Encoding),
/// An array with the given length and type.
///
/// Corresponds to the `[len type]` code.
Array(usize, &'static Encoding),
/// Corresponds to the `"[" length type "]"` code.
Array(u64, &'static Encoding),
/// A struct with the given name and fields.
///
/// The order of the fields must match the order of the order in this.
///
/// It is not uncommon for the name to be `"?"`.
///
/// Corresponds to the `{name=fields...}` code.
/// Corresponds to the `"{" name "=" fields... "}"` code.
Struct(&'static str, &'static [Encoding]),
/// A union with the given name and fields.
///
/// The order of the fields must match the order of the order in this.
///
/// Corresponds to the `(name=fields...)` code.
/// Corresponds to the `"(" name "=" fields... ")"` code.
Union(&'static str, &'static [Encoding]),
// "Vector" types have the '!' encoding, but are not implemented in clang

// TODO: `t` and `T` codes for i128 and u128?
}

impl Encoding {
/// The encoding of [`c_long`](`std::os::raw::c_long`).
/// The encoding of [`c_long`](`std::os::raw::c_long`) on the current
/// target.
///
/// Ideally the encoding of `long` would just be the same as `int` when
/// it's 32 bits wide and the same as `long long` when it is 64 bits wide;
Expand All @@ -156,7 +161,8 @@ impl Encoding {
}
};

/// The encoding of [`c_ulong`](`std::os::raw::c_ulong`).
/// The encoding of [`c_ulong`](`std::os::raw::c_ulong`) on the current
/// target.
///
/// See [`Encoding::C_LONG`] for explanation.
pub const C_ULONG: Self = {
Expand Down Expand Up @@ -374,13 +380,35 @@ mod tests {
}
fn bitfield() {
Encoding::BitField(32, &Encoding::Int);
Encoding::BitField(4, None);
!Encoding::Int;
!Encoding::BitField(5, None);
!Encoding::BitField(4, Some(&(0, Encoding::Bool)));
"b4";
!"b4a";
!"b4c";
!"b4B";
!"b";
!"b-4";
!"b0B4";
}
fn bitfield_gnustep() {
Encoding::BitField(4, Some(&(16, Encoding::Bool)));
!Encoding::Int;
!Encoding::BitField(33, &Encoding::Int);
"b32";
!"b32a";
!Encoding::BitField(4, None);
!Encoding::BitField(5, Some(&(16, Encoding::Bool)));
!Encoding::BitField(4, Some(&(20, Encoding::Bool)));
!Encoding::BitField(4, Some(&(16, Encoding::Char)));
"b16B4";
!"b4";
!"b16B";
!"b20B4";
!"b16B5";
!"b16c4";
!"b4a";
!"b";
!"b-32";
!"b-4";
}
fn atomic() {
Expand Down Expand Up @@ -517,11 +545,12 @@ mod tests {
&[
Encoding::Pointer(&Encoding::Array(8, &Encoding::Bool)),
Encoding::Union("def", &[Encoding::Block]),
Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, &Encoding::Int))),
Encoding::Pointer(&Encoding::Pointer(&Encoding::BitField(255, None))),
Encoding::Char,
Encoding::Unknown,
]
);
"{abc=^[8B](def=@?)^^b255?}";
"{abc=^[8B](def=@?)^^b255c?}";
}
fn identifier() {
Expand Down
10 changes: 7 additions & 3 deletions crates/objc2-encode/src/encoding_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ pub enum EncodingBox {
/// Same as [`Encoding::Unknown`].
Unknown,
/// Same as [`Encoding::BitField`].
BitField(u8, Option<Box<Self>>),
BitField(u8, Option<Box<(u64, Self)>>),
/// Same as [`Encoding::Pointer`].
Pointer(Box<Self>),
/// Same as [`Encoding::Atomic`].
Atomic(Box<Self>),
/// Same as [`Encoding::Array`].
Array(usize, Box<Self>),
Array(u64, Box<Self>),
/// Same as [`Encoding::Struct`].
Struct(String, Option<Vec<Self>>),
/// Same as [`Encoding::Union`].
Expand Down Expand Up @@ -236,7 +236,7 @@ mod tests {

#[test]
fn parse_part_of_string() {
let mut s = "{a}c";
let mut s = "{a}cb0i16";

let expected = EncodingBox::Struct("a".into(), None);
let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
Expand All @@ -246,6 +246,10 @@ mod tests {
let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
assert_eq!(expected, actual);

let expected = EncodingBox::BitField(16, Some(Box::new((0, EncodingBox::Int))));
let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
assert_eq!(expected, actual);

assert_eq!(s, "");
}
}
45 changes: 22 additions & 23 deletions crates/objc2-encode/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ impl NestingLevel {
}

const fn bitfield(self) -> Self {
// TODO: Is this correct?
// This is a bit irrelevant, since bitfields can only contain integral
// types
self
}

Expand Down Expand Up @@ -83,23 +84,19 @@ pub(crate) fn compare_encodings<E1: EncodingType, E2: EncodingType>(

match (enc1.helper(level1), enc2.helper(level2)) {
(Primitive(p1), Primitive(p2)) => p1 == p2,
(BitField(b1, type1, level1), BitField(b2, type2, level2)) => {
b1 == b2
&& match (type1, type2) {
(None, None) => true,
(Some(type1), Some(type2)) => {
compare_encodings(type1, level1, type2, level2, include_all)
}
// The type-encoding of a bitfield is always either
// available, or it is not (depends on platform); so if it
// was available in one, but not the other, we should
// compare the encodings unequal.
//
// However, since bitfield types are not really supported
// ATM, we'll do the opposite, and compare them equal.
_ => true,
}
(
BitField(size1, Some((offset1, type1)), level1),
BitField(size2, Some((offset2, type2)), level2),
) => {
size1 == size2
&& offset1 == offset2
&& compare_encodings(type1, level1, type2, level2, include_all)
}
(BitField(size1, None, _level1), BitField(size2, None, _level2)) => size1 == size2,
// The type-encoding of a bitfield is always either available, or it
// is not (depends on platform); so if it was available in one, but
// not the other, we should compare the encodings unequal.
(BitField(_, _, _), BitField(_, _, _)) => false,
(Indirection(kind1, t1, level1), Indirection(kind2, t2, level2)) => {
kind1 == kind2 && compare_encodings(t1, level1, t2, level2, include_all)
}
Expand Down Expand Up @@ -263,19 +260,21 @@ pub(crate) trait EncodingType: Sized + fmt::Debug {
#[non_exhaustive]
pub(crate) enum Helper<'a, E = Encoding> {
Primitive(Primitive),
BitField(u8, Option<&'a E>, NestingLevel),
BitField(u8, Option<&'a (u64, E)>, NestingLevel),
Indirection(IndirectionKind, &'a E, NestingLevel),
Array(usize, &'a E, NestingLevel),
Array(u64, &'a E, NestingLevel),
Container(ContainerKind, &'a str, Option<&'a [E]>, NestingLevel),
}

impl<E: EncodingType> fmt::Display for Helper<'_, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Primitive(primitive) => write!(f, "{}", primitive.to_str()),
Self::BitField(b, _type, _level) => {
// TODO: Use the type on GNUStep (nesting level?)
write!(f, "b{b}")
Self::BitField(size, None, _level) => {
write!(f, "b{size}")
}
Self::BitField(size, Some((offset, t)), level) => {
write!(f, "b{offset}{}{size}", t.helper(*level))
}
Self::Indirection(kind, t, level) => {
write!(f, "{}{}", kind.prefix(), t.helper(*level))
Expand Down Expand Up @@ -326,7 +325,7 @@ impl Helper<'_> {
Class => Self::Primitive(Primitive::Class),
Sel => Self::Primitive(Primitive::Sel),
Unknown => Self::Primitive(Primitive::Unknown),
BitField(b, t) => Self::BitField(*b, Some(t), level.bitfield()),
BitField(b, t) => Self::BitField(*b, *t, level.bitfield()),
Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t, level.pointer()),
Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t, level.atomic()),
Array(len, item) => Self::Array(*len, item, level.array()),
Expand Down
Loading

0 comments on commit b3e2c35

Please sign in to comment.