From b3e2c3548dc5fce7bc508ad734f1c780bfd5b0ab Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 29 Jan 2023 15:53:48 +0100 Subject: [PATCH] Make objc2_encode target-agnostic --- crates/objc2-encode/CHANGELOG.md | 8 ++ crates/objc2-encode/src/encoding.rs | 117 +++++++++++++--------- crates/objc2-encode/src/encoding_box.rs | 10 +- crates/objc2-encode/src/helper.rs | 45 +++++---- crates/objc2-encode/src/parse.rs | 126 ++++++++++++++++++++++-- crates/objc2-encode/src/static_str.rs | 59 ++++++++--- crates/objc2/src/encode.rs | 2 +- crates/tests/extern/encode_utils.m | 39 +++++++- crates/tests/src/test_encode_utils.rs | 86 ++++++++++++---- 9 files changed, 377 insertions(+), 115 deletions(-) diff --git a/crates/objc2-encode/CHANGELOG.md b/crates/objc2-encode/CHANGELOG.md index 662d9e22f..c748e4628 100644 --- a/crates/objc2-encode/CHANGELOG.md +++ b/crates/objc2-encode/CHANGELOG.md @@ -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 diff --git a/crates/objc2-encode/src/encoding.rs b/crates/objc2-encode/src/encoding.rs index dbfdbea23..ebfe3166a 100644 --- a/crates/objc2-encode/src/encoding.rs +++ b/crates/objc2-encode/src/encoding.rs @@ -39,95 +39,99 @@ use crate::EncodingBox; // See #[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 @@ -135,7 +139,8 @@ pub enum Encoding { } 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; @@ -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 = { @@ -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() { @@ -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() { diff --git a/crates/objc2-encode/src/encoding_box.rs b/crates/objc2-encode/src/encoding_box.rs index 10206234f..8efd24dc4 100644 --- a/crates/objc2-encode/src/encoding_box.rs +++ b/crates/objc2-encode/src/encoding_box.rs @@ -77,13 +77,13 @@ pub enum EncodingBox { /// Same as [`Encoding::Unknown`]. Unknown, /// Same as [`Encoding::BitField`]. - BitField(u8, Option>), + BitField(u8, Option>), /// Same as [`Encoding::Pointer`]. Pointer(Box), /// Same as [`Encoding::Atomic`]. Atomic(Box), /// Same as [`Encoding::Array`]. - Array(usize, Box), + Array(u64, Box), /// Same as [`Encoding::Struct`]. Struct(String, Option>), /// Same as [`Encoding::Union`]. @@ -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(); @@ -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, ""); } } diff --git a/crates/objc2-encode/src/helper.rs b/crates/objc2-encode/src/helper.rs index 7b7e8f0b2..7901a2929 100644 --- a/crates/objc2-encode/src/helper.rs +++ b/crates/objc2-encode/src/helper.rs @@ -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 } @@ -83,23 +84,19 @@ pub(crate) fn compare_encodings( 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) } @@ -263,9 +260,9 @@ 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), } @@ -273,9 +270,11 @@ impl 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)) @@ -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()), diff --git a/crates/objc2-encode/src/parse.rs b/crates/objc2-encode/src/parse.rs index a2cc8189c..f495c43ec 100644 --- a/crates/objc2-encode/src/parse.rs +++ b/crates/objc2-encode/src/parse.rs @@ -130,6 +130,14 @@ impl<'a> Parser<'a> { self.data.as_bytes().get(self.split_point).copied() } + fn try_peek2(&self) -> Option<(u8, u8)> { + let bytes = self.data.as_bytes(); + Some(( + *bytes.get(self.split_point)?, + *bytes.get(self.split_point + 1)?, + )) + } + fn advance(&mut self) { self.split_point += 1; } @@ -191,7 +199,7 @@ impl Parser<'_> { Ok(&self.data[old_split_point..self.split_point]) } - fn parse_usize(&mut self) -> Result { + fn parse_u64(&mut self) -> Result { self.chomp_digits()? .parse() .map_err(|_| ErrorKind::IntegerTooLarge) @@ -224,8 +232,16 @@ impl Parser<'_> { Some(()) } - fn expect_usize(&mut self, int: usize) -> Option<()> { - if self.parse_usize().ok()? == int { + fn expect_u64(&mut self, int: u64) -> Option<()> { + if self.parse_u64().ok()? == int { + Some(()) + } else { + None + } + } + + fn expect_u8(&mut self, int: u8) -> Option<()> { + if self.parse_u8().ok()? == int { Some(()) } else { None @@ -236,10 +252,15 @@ impl Parser<'_> { let helper = Helper::new(enc, level); match helper { Helper::Primitive(primitive) => self.expect_str(primitive.to_str()), - Helper::BitField(b, _type, _level) => { - // TODO: Use the type on GNUStep (nesting level?) + Helper::BitField(size, Some((offset, t)), level) => { self.expect_byte(b'b')?; - self.expect_usize(b as usize) + self.expect_u64(*offset)?; + self.expect_encoding(t, level)?; + self.expect_u8(size) + } + Helper::BitField(size, None, _level) => { + self.expect_byte(b'b')?; + self.expect_u8(size) } Helper::Indirection(kind, t, level) => { self.expect_byte(kind.prefix_byte())?; @@ -247,7 +268,7 @@ impl Parser<'_> { } Helper::Array(len, item, level) => { self.expect_byte(b'[')?; - self.expect_usize(len)?; + self.expect_u64(len)?; self.expect_encoding(item, level)?; self.expect_byte(b']') } @@ -363,13 +384,21 @@ impl Parser<'_> { b'?' => EncodingBox::Unknown, b'b' => { - // TODO: Parse type here on GNUStep - EncodingBox::BitField(self.parse_u8()?, None) + let size_or_offset = self.parse_u64()?; + if let Some((size, ty)) = self.try_parse_bitfield_gnustep()? { + let offset = size_or_offset; + EncodingBox::BitField(size, Some(Box::new((offset, ty)))) + } else { + let size = size_or_offset + .try_into() + .map_err(|_| ErrorKind::IntegerTooLarge)?; + EncodingBox::BitField(size, None) + } } b'^' => EncodingBox::Pointer(Box::new(self.parse_encoding()?)), b'A' => EncodingBox::Atomic(Box::new(self.parse_encoding()?)), b'[' => { - let len = self.parse_usize()?; + let len = self.parse_u64()?; let item = self.parse_encoding()?; self.expect_byte(b']').ok_or(ErrorKind::WrongEndArray)?; EncodingBox::Array(len, Box::new(item)) @@ -387,6 +416,40 @@ impl Parser<'_> { b => return Err(ErrorKind::Unknown(b)), }) } + + fn try_parse_bitfield_gnustep(&mut self) -> Result> { + if let Some((b1, b2)) = self.try_peek2() { + // Try to parse the encoding. + // + // The encoding is always an integral type. + let ty = match b1 { + b'c' => EncodingBox::Char, + b's' => EncodingBox::Short, + b'i' => EncodingBox::Int, + b'l' => EncodingBox::Long, + b'q' => EncodingBox::LongLong, + b'C' => EncodingBox::UChar, + b'S' => EncodingBox::UShort, + b'I' => EncodingBox::UInt, + b'L' => EncodingBox::ULong, + b'Q' => EncodingBox::ULongLong, + b'B' => EncodingBox::Bool, + _ => return Ok(None), + }; + // And then check if a digit follows that (which the size would + // always contain). + if !b2.is_ascii_digit() { + return Ok(None); + } + // We have a size; so let's advance... + self.advance(); + // ...and parse it for real. + let size = self.parse_u8()?; + Ok(Some((size, ty))) + } else { + Ok(None) + } + } } #[cfg(test)] @@ -398,6 +461,7 @@ mod tests { fn parse_container() { const KIND: ContainerKind = ContainerKind::Struct; + #[track_caller] fn assert_name(enc: &str, expected: Result<(&str, Option>)>) { let mut parser = Parser::new(enc); assert_eq!(parser.parse_container(KIND), expected); @@ -417,4 +481,46 @@ mod tests { assert_name("abc", Err(ErrorKind::WrongEndContainer(KIND))); assert_name("abc)def", Err(ErrorKind::WrongEndContainer(KIND))); } + + #[test] + fn parse_bitfield() { + #[track_caller] + fn assert_bitfield(enc: &str, expected: Result) { + let mut parser = Parser::new(enc); + assert_eq!( + parser + .parse_encoding() + .and_then(|enc| parser.expect_empty().map(|()| enc)), + expected + ); + } + + assert_bitfield("b8", Ok(EncodingBox::BitField(8, None))); + assert_bitfield("b8C", Err(ErrorKind::NotAllConsumed)); + assert_bitfield( + "b8C4", + Ok(EncodingBox::BitField( + 4, + Some(Box::new((8, EncodingBox::UChar))), + )), + ); + + assert_bitfield( + "{s=b8C}", + Ok(EncodingBox::Struct( + "s".into(), + Some(vec![EncodingBox::BitField(8, None), EncodingBox::UChar]), + )), + ); + + assert_bitfield("b2000", Err(ErrorKind::IntegerTooLarge)); + assert_bitfield( + "b2000c100", + Ok(EncodingBox::BitField( + 100, + Some(Box::new((2000, EncodingBox::Char))), + )), + ); + assert_bitfield("b2000C257", Err(ErrorKind::IntegerTooLarge)); + } } diff --git a/crates/objc2-encode/src/static_str.rs b/crates/objc2-encode/src/static_str.rs index aa8ff99b4..3782ba7ab 100644 --- a/crates/objc2-encode/src/static_str.rs +++ b/crates/objc2-encode/src/static_str.rs @@ -2,7 +2,7 @@ use crate::helper::{Helper, NestingLevel}; use super::Encoding; -pub(crate) const fn static_int_str_len(mut n: u128) -> usize { +pub(crate) const fn static_int_str_len(mut n: u64) -> usize { let mut i = 0; if n == 0 { return 1; @@ -14,7 +14,7 @@ pub(crate) const fn static_int_str_len(mut n: u128) -> usize { i } -pub(crate) const fn static_int_str_array(mut n: u128) -> [u8; RES] { +pub(crate) const fn static_int_str_array(mut n: u64) -> [u8; RES] { let mut res: [u8; RES] = [0; RES]; let mut i = 0; if n == 0 { @@ -43,13 +43,15 @@ pub(crate) const fn static_encoding_str_len(encoding: &Encoding, level: NestingL match Helper::new(encoding, level) { Primitive(primitive) => primitive.to_str().len(), - BitField(b, _type, _level) => { - // TODO: Use the type on GNUStep (nesting level?) - 1 + static_int_str_len(b as u128) + BitField(size, None, _level) => 1 + static_int_str_len(size as u64), + BitField(size, Some((offset, t)), level) => { + 1 + static_int_str_len(*offset) + + static_encoding_str_len(t, level) + + static_int_str_len(size as u64) } Indirection(_kind, t, level) => 1 + static_encoding_str_len(t, level), Array(len, item, level) => { - 1 + static_int_str_len(len as u128) + static_encoding_str_len(item, level) + 1 + 1 + static_int_str_len(len) + static_encoding_str_len(item, level) + 1 } Container(_, name, items, level) => { let mut res = 1 + name.len(); @@ -84,15 +86,46 @@ pub(crate) const fn static_encoding_str_array( i += 1; } } - BitField(b, _type, _level) => { - // TODO: Use the type on GNUStep (nesting level?) + BitField(size, None, _level) => { res[res_i] = b'b'; res_i += 1; let mut i = 0; // We use 3 even though it creates an oversized array - let arr = static_int_str_array::<3>(b as u128); - while i < static_int_str_len(b as u128) { + let arr = static_int_str_array::<3>(size as u64); + while i < static_int_str_len(size as u64) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + } + BitField(size, Some((offset, t)), level) => { + res[res_i] = b'b'; + res_i += 1; + + let mut i = 0; + // We use 20 even though it creates an oversized array + let arr = static_int_str_array::<20>(*offset); + while i < static_int_str_len(*offset) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + + let mut i = 0; + // We use LEN even though it creates an oversized array + // This could probably be reduced to 1 + let arr = static_encoding_str_array::(t, level); + while i < static_encoding_str_len(t, level) { + res[res_i] = arr[i]; + res_i += 1; + i += 1; + } + + let mut i = 0; + // We use 3 even though it creates an oversized array + let arr = static_int_str_array::<3>(size as u64); + while i < static_int_str_len(size as u64) { res[res_i] = arr[i]; res_i += 1; i += 1; @@ -119,8 +152,8 @@ pub(crate) const fn static_encoding_str_array( let mut i = 0; // We use 20 even though it creates an oversized array - let arr = static_int_str_array::<20>(len as u128); - while i < static_int_str_len(len as u128) { + let arr = static_int_str_array::<20>(len); + while i < static_int_str_len(len) { res[res_i] = arr[i]; res_i += 1; i += 1; @@ -182,7 +215,7 @@ mod tests { macro_rules! const_int_str { ($n:expr) => {{ - const X: [u8; static_int_str_len($n as u128)] = static_int_str_array($n as u128); + const X: [u8; static_int_str_len($n)] = static_int_str_array($n); unsafe { core::str::from_utf8_unchecked(&X) } }}; } diff --git a/crates/objc2/src/encode.rs b/crates/objc2/src/encode.rs index 37a918dfc..89d44fcea 100644 --- a/crates/objc2/src/encode.rs +++ b/crates/objc2/src/encode.rs @@ -489,7 +489,7 @@ unsafe impl RefEncode for NonNull { unsafe impl OptionEncode for NonNull {} unsafe impl Encode for [T; LENGTH] { - const ENCODING: Encoding = Encoding::Array(LENGTH, &T::ENCODING); + const ENCODING: Encoding = Encoding::Array(LENGTH as u64, &T::ENCODING); } unsafe impl RefEncode for [T; LENGTH] { diff --git a/crates/tests/extern/encode_utils.m b/crates/tests/extern/encode_utils.m index 3ce30b4b5..b3778ccfb 100644 --- a/crates/tests/extern/encode_utils.m +++ b/crates/tests/extern/encode_utils.m @@ -102,11 +102,46 @@ // Bit field struct bitfield { - unsigned int a: 1; - unsigned int b: 30; + int8_t b1 : 5; + int16_t : 0; + int8_t b2 : 2; }; ENCODING(BITFIELD, struct bitfield); +struct bitfield_all_types { + struct bitfield nested; + + char b_char : 1; + short b_short : 1; + int b_int : 1; + long b_long : 1; + long long b_long_long : 1; + + signed char b_signed_char : 1; + signed short b_signed_short : 1; + signed int b_signed_int : 1; + signed long b_signed_long : 1; + signed long long b_signed_long_long : 1; + + unsigned char b_unsigned_char : 1; + unsigned short b_unsigned_short : 1; + unsigned int b_unsigned_int : 1; + unsigned long b_unsigned_long : 1; + unsigned long long b_unsigned_long_long : 1; + + bool b_bool : 1; +}; +ENCODING(BITFIELD_ALL_TYPES, struct bitfield_all_types); + +// Ill-supported in compilers +// #if __has_builtin(__int128_t) +// struct bitfield_128 { +// __int128_t b_signed : 1; +// __uint128_t b_unsigned : 1; +// }; +// ENCODING(BITFIELD_128, struct bitfield_128); +// #endif + // Union union union_ { diff --git a/crates/tests/src/test_encode_utils.rs b/crates/tests/src/test_encode_utils.rs index bb6e18b7e..96ed71b5f 100644 --- a/crates/tests/src/test_encode_utils.rs +++ b/crates/tests/src/test_encode_utils.rs @@ -100,6 +100,71 @@ const WITH_ATOMIC_INNER: Encoding = Encoding::Struct( ], ); +#[cfg(feature = "apple")] +const BITFIELD: Encoding = Encoding::Struct( + "bitfield", + &[ + Encoding::BitField(5, None), + Encoding::BitField(0, None), + Encoding::BitField(2, None), + ], +); +#[cfg(feature = "apple")] +const BITFIELD_ALL_TYPES: Encoding = Encoding::Struct( + "bitfield_all_types", + &[ + BITFIELD, + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + Encoding::BitField(1, None), + ], +); +#[cfg(feature = "gnustep-1-7")] +const BITFIELD: Encoding = Encoding::Struct( + "bitfield", + &[ + Encoding::BitField(5, Some(&(0, i8::ENCODING))), + Encoding::BitField(0, Some(&(16, i16::ENCODING))), + Encoding::BitField(2, Some(&(16, i8::ENCODING))), + ], +); +#[cfg(feature = "gnustep-1-7")] +const BITFIELD_ALL_TYPES: Encoding = Encoding::Struct( + "bitfield_all_types", + &[ + BITFIELD, + Encoding::BitField(1, Some(&(24, Encoding::Char))), + Encoding::BitField(1, Some(&(25, Encoding::Short))), + Encoding::BitField(1, Some(&(26, Encoding::Int))), + Encoding::BitField(1, Some(&(27, Encoding::C_LONG))), + Encoding::BitField(1, Some(&(28, Encoding::LongLong))), + Encoding::BitField(1, Some(&(29, Encoding::Char))), + Encoding::BitField(1, Some(&(30, Encoding::Short))), + Encoding::BitField(1, Some(&(31, Encoding::Int))), + Encoding::BitField(1, Some(&(32, Encoding::C_LONG))), + Encoding::BitField(1, Some(&(33, Encoding::LongLong))), + Encoding::BitField(1, Some(&(34, Encoding::UChar))), + Encoding::BitField(1, Some(&(35, Encoding::UShort))), + Encoding::BitField(1, Some(&(36, Encoding::UInt))), + Encoding::BitField(1, Some(&(37, Encoding::C_ULONG))), + Encoding::BitField(1, Some(&(38, Encoding::ULongLong))), + Encoding::BitField(1, Some(&(39, Encoding::Bool))), + ], +); + assert_types! { // C types @@ -174,14 +239,8 @@ assert_types! { // Bitfields - #[cfg(feature = "apple")] - BITFIELD => enc Encoding::Struct( - "bitfield", - &[ - Encoding::BitField(1, &Encoding::UInt), - Encoding::BitField(30, &Encoding::UInt), - ], - ), + BITFIELD => enc BITFIELD, + BITFIELD_ALL_TYPES => enc BITFIELD_ALL_TYPES, // Unions @@ -243,14 +302,3 @@ assert_types! { // SIGNED_INT_128 => i128, // UNSIGNED_INT_128 => u128, } - -// Bitfields - -#[cfg(feature = "gnustep-1-7")] -mod bitfields { - use super::*; - - assert_inner!(str ENCODING_BITFIELD => "{bitfield=b0I1b1I30}"); - assert_inner!(str ENCODING_BITFIELD_POINTER => "^{bitfield=b0I1b1I30}"); - assert_inner!(str ENCODING_BITFIELD_ATOMIC => "A{bitfield}"); -}