Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"e8391ac7ae2a5c701636e9369c5909340f930ace6d9227eadd94ba88b37f0fdd","Cargo.lock":"90988905fc575a458a243b36ae2277d487c6c99703c41b199b5ca5a0ae746939","Cargo.toml":"3007205192dbd967f1f2020c6501699530cac1f63721ae87ce7be1ec2264288e","README.md":"89a1402faf702ad0a4a69f8019e885bc90eb39c58a81c87d3023ab89832202d6","examples/core_graphics.rs":"9c219e6907a14230eee24cdf8690ebd5e17e66ff147362795ec4c08f3a1a265e","examples/ns_string.rs":"e62539091a1973df2a764a05c2532065f1613c76bb2a6bb4c531e7c87cfc28d7","examples/ns_uinteger.rs":"7a5b1c49fa6770997f1ca58619cccfb4a5c239639c5ed6fbcdcc262e13344cfa","examples/opaque_type.rs":"9dbcfde73c050c5bffc9016d7e3a4b98b7a9389067fd49aaecea243e6f0ced07","src/__bool.rs":"c75312eb4862788e6b8baf39dc022e36cfe1d5f681e5a34a4bdd6675ea399478","src/encode.rs":"c2b69ef25119a558a07bb642858ea00fb873ad1507e7373afb07683ee3c3d7d8","src/encoding.rs":"ff19fe45fe3c4fb33627267cb176dfb54454dcdd719e0fc4402cefd0f3e13ed2","src/helper.rs":"2bb406940c58c354f1c9f1850e0941c483562013ffa58a9637a0bee3986ee505","src/lib.rs":"7ee68b5822bdff295c177ce5dd12692f4b84b237d31437d991f214386d0a7f1d","src/parse.rs":"1243d0102865c66cc721499222baf008d23081b8df6c7553b313e3281c7cfea8","src/static_str.rs":"21294faaaf3a08c11e7806d025aa3884220b6f6e7611a6d351b1a4023a5a7d29"},"package":"abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512"}

View file

@ -0,0 +1,195 @@
# Changelog
Notable changes to this crate will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
## 2.0.0-pre.2 - 2022-08-28
### Added
* Added `EncodeConvert` trait to help with correctly handling `BOOL`/`bool`.
### Changed
* **BREAKING**: Remove the lifetime specifier from `Encoding`, since the non
-`'static` version was essentially useless.
### Fixed
* Fixed the encoding output and comparison of structs behind pointers.
### Removed
* **BREAKING**: `bool` (and `AtomicBool`) no longer implements `Encode`, since
that was difficult to use correctly. See the `EncodeConvert` trait, or use
`objc2::runtime::Bool` instead.
## 2.0.0-pre.1 - 2022-07-19
### Added
* Added `Encoding::Atomic`.
* Implement `Encode` and `RefEncode` for `std::sync::atomic` types.
### Changed
* **BREAKING**: Renamed `Encoding::C_U_LONG` to `Encoding::C_ULONG`.
## 2.0.0-pre.0 - 2022-06-13
### Added
* Added `Encoding::C_LONG` and `Encoding::C_U_LONG` to help with platform
compatibility; use these instead of `c_long::ENCODING` and
`c_ulong::ENCODING`.
* Implement `Encode` and `RefEncode` for `MaybeUninit<T>`, where `T` is
properly bound.
### Changed
* **BREAKING**: Sealed the `EncodeArguments` trait.
* **BREAKING**: Add type argument to `Encoding::BitField`.
### Removed
* **BREAKING**: Removed `PartialEq` impl between `str` and `Encoding` since it
was incorrect (it violated the trait requirements).
* **BREAKING**: Removed `Encode` and `RefEncode` implementations for `Pin`
since it may not be sound.
## 2.0.0-beta.2 - 2022-01-03
### Added
* Implement `Hash` for `Encoding`.
### Changed
* Improved documentation.
## 2.0.0-beta.1 - 2021-12-22
### Added
* `Encoding::equivalent_to`, `Encoding::equivalent_to_str` and
`Encoding::equivalent_to_start_of_str` methods for more precise comparison
semantics.
* Added `Encode` and `RefEncode` implementations for `Option` function
pointers.
### Changed
* Discourage comparing `str` with `Encoding` using `PartialEq`. This trait
impl might get removed in a future version.
## 2.0.0-beta.0 - 2021-11-22
### Added
* **BREAKING**: Add `Encoding::LongDouble`, `Encoding::FloatComplex`,
`Encoding::DoubleComplex` and `Encoding::LongDoubleComplex`.
* Implement `RefEncode` for all number types that implement `Encode` (`bool`,
`i8`, `usize`, `f32`, `NonZeroU32`, and so on).
* Implement `RefEncode` for `*const c_void` and `*mut c_void` (allowing
`void**` in C).
* Implement `Encode` and `RefEncode` for `Wrapping<T>`, where `T` is properly
bound.
### Changed
* **BREAKING**: Make `Encoding` `#[non_exhaustive]`. This will help us in
evolving the API while minimizing further breaking changes.
* Discourage using `bool::ENCODING`; use `objc2::Bool::ENCODING` instead.
* Discourage using `()::ENCODING` for anything other than as a function return
type.
## 2.0.0-alpha.1 - 2021-09-01
### Added
* Improved documentation.
* Add `RefEncode` trait, which represents types whoose pointers has an
encoding. This means you now only have to implement `RefEncode`, and not
both `&Encode` and `&mut Encode`.
Additionally, encodings of pointers to pointers (to pointers, and so on) are
now supported.
* Implement `Encode` for `NonZeroX` and `Option<NonZeroX>` integer types.
* Implement `RefEncode` for arrays.
* Implement `Encode` and `RefEncode` for (where `T` is properly bound):
- `ManuallyDrop<T>`
- `Pin<T>`
- `NonNull<T>`
- `Option<NonNull<T>>`
* Add `EncodeArguments` trait, to represent an ordered group of functions
arguments, where each argument has an Objective-C type-encoding.
Previously in the `objc` crate.
* Implement `Encode` and `RefEncode` for some `extern "C" fn` pointers.
### Removed
* **BREAKING**: Removed automatic `*const T: Encode` and `*mut T: Encode`
impls when when `&T: Encode` and `&mut T: Encode` was implemented.
Implement `T: RefEncode` instead!
## 2.0.0-alpha.0 - 2021-09-01
### Added
* Improved documentation.
* Support for targets with pointer-width 16
* Implement `Encode` for all array lengths using const-generics.
* Implement `Encode` for unsized pointer types as well.
### Changed
* **BREAKING**: Forked the project, so it is now available under the name
`objc2-encode`.
* **BREAKING**: Changed type in `Encoding::BitField` from `u32` to `u8`.
* **BREAKING**: Changed type in `Encoding::Array` from `u32` to `usize`.
* **BREAKING**: Loosen `'static` bounds on references implementing `Encode`.
## [1.1.0] (`objc-encode` crate) - 2019-10-16
### Added
* Implement `Encode` for arrays with up to 32 elements.
### Changed
* Simplify internal encoding comparison.
## [1.0.0] (`objc-encode` crate) - 2019-03-25
### Added
* Implement `PartialEq` between `Encoding` and `&str`.
### Changed
* **BREAKING**: Make `Encoding` an enum instead of a trait, yielding a vastly
different design. This makes use of associated constants.
* **BREAKING**: Rename `Encode::CODE` to `Encode::ENCODING`.
* Update to Rust 2018.
### Removed
* `libc` dependency.
## [0.0.3] (`objc-encode` crate) - 2017-04-30
### Fixed
* Compilation on versions prior to Rust `1.15`.
## [0.0.2] (`objc-encode` crate) - 2017-02-20
### Added
* **BREAKING**: `Display` requirement for encodings.
* Implement `PartialEq` for encodings.
* Implement `Encode` for pointers when references do.
### Fixed
* `IndexEncodingsComparator`.
* Compilation with older Rust versions.
## [0.0.1] (`objc-encode` crate) - 2017-02-19
Initial version.
[1.1.0]: https://github.com/madsmtm/objc2/compare/objc-encode-1.0.0...objc-encode-1.1.0
[1.0.0]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.3...objc-encode-1.0.0
[0.0.3]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.2...objc-encode-0.0.3
[0.0.2]: https://github.com/madsmtm/objc2/compare/objc-encode-0.0.1...objc-encode-0.0.2
[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/objc-encode-0.0.1

16
third-party/vendor/objc2-encode/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "objc-sys"
version = "0.2.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7"
[[package]]
name = "objc2-encode"
version = "2.0.0-pre.2"
dependencies = [
"objc-sys",
]

View file

@ -0,0 +1,83 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "objc2-encode"
version = "2.0.0-pre.2"
authors = [
"Steven Sheldon",
"Mads Marquart <mads@marquart.dk>",
]
description = "Objective-C type-encodings"
documentation = "https://docs.rs/objc2-encode/"
readme = "README.md"
keywords = [
"objective-c",
"macos",
"ios",
"encode",
]
categories = [
"development-tools::ffi",
"encoding",
"no-std",
"os::macos-apis",
]
license = "MIT"
repository = "https://github.com/madsmtm/objc2"
resolver = "2"
[package.metadata.docs.rs]
default-target = "x86_64-apple-darwin"
targets = [
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"aarch64-apple-ios",
"x86_64-apple-ios",
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
]
[dependencies.objc-sys]
version = "=0.2.0-beta.2"
default-features = false
[features]
alloc = ["objc-sys/alloc"]
apple = ["objc-sys/apple"]
default = [
"std",
"apple",
]
gnustep-1-7 = ["objc-sys/gnustep-1-7"]
gnustep-1-8 = [
"gnustep-1-7",
"objc-sys/gnustep-1-8",
]
gnustep-1-9 = [
"gnustep-1-8",
"objc-sys/gnustep-1-9",
]
gnustep-2-0 = [
"gnustep-1-9",
"objc-sys/gnustep-2-0",
]
gnustep-2-1 = [
"gnustep-2-0",
"objc-sys/gnustep-2-1",
]
std = [
"alloc",
"objc-sys/std",
]
unstable-c-unwind = []

View file

@ -0,0 +1,19 @@
# `objc2-encode`
[![Latest version](https://badgen.net/crates/v/objc2-encode)](https://crates.io/crates/objc2-encode)
[![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt)
[![Documentation](https://docs.rs/objc2-encode/badge.svg)](https://docs.rs/objc2-encode/)
[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml)
Objective-C type-encoding in Rust.
This crates provides the equivalent of the Objective-C `@encode` directive,
and functions for comparing these encodings.
Additionally, it provides traits for annotating types that has an Objective-C
encoding.
See [the docs](https://docs.rs/objc2-encode/) for a more thorough overview.
This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2),
see that for related crates.

View file

@ -0,0 +1,41 @@
use objc2_encode::{Encode, Encoding};
#[cfg(target_pointer_width = "32")]
type CGFloat = f32;
#[cfg(target_pointer_width = "64")]
type CGFloat = f64;
#[repr(C)]
struct CGPoint {
x: CGFloat,
y: CGFloat,
}
unsafe impl Encode for CGPoint {
const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
#[repr(C)]
struct CGSize {
width: CGFloat,
height: CGFloat,
}
unsafe impl Encode for CGSize {
const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
#[repr(C)]
struct CGRect {
origin: CGPoint,
size: CGSize,
}
unsafe impl Encode for CGRect {
const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]);
}
fn main() {
println!("{}", CGRect::ENCODING);
}

View file

@ -0,0 +1,25 @@
use objc2_encode::{Encode, Encoding, RefEncode};
/// We don't know the size of NSString, so we can only hold pointers to it.
///
/// TODO: Use [`extern type`][rfc-1861] when that gets stabilized.
///
/// [rfc-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html
#[repr(C)]
struct NSString {
_priv: [u8; 0],
}
/// Implement `RefEncode` for pointers and references to the string.
unsafe impl RefEncode for NSString {
const ENCODING_REF: Encoding = Encoding::Object;
}
fn main() {
println!("{}", <*const NSString>::ENCODING);
println!("{}", <*mut NSString>::ENCODING);
println!("{}", <&NSString>::ENCODING);
println!("{}", <&mut NSString>::ENCODING);
println!("{}", Option::<&NSString>::ENCODING);
println!("{}", Option::<&mut NSString>::ENCODING);
}

View file

@ -0,0 +1,32 @@
//! Implementing `Encode` and `RefEncode` for `NSUInteger`.
//!
//! Note that in this case `NSUInteger` could actually just be a type alias
//! for `usize`, and that's already available under `objc2::ffi::NSUInteger`.
use objc2_encode::{Encode, Encoding, RefEncode};
#[repr(transparent)]
struct NSUInteger {
_inner: usize,
}
// SAFETY: `NSUInteger` has the same `repr` as `usize`.
unsafe impl Encode for NSUInteger {
/// Running `@encode(NSUInteger)` gives `Q` on 64-bit systems and `I` on
/// 32-bit systems. This corresponds exactly to `usize`, which is also how
/// we've defined our struct.
const ENCODING: Encoding = usize::ENCODING;
}
// SAFETY: `&NSUInteger` has the same representation as `&usize`.
unsafe impl RefEncode for NSUInteger {
/// Running `@encode(NSUInteger*)` gives `^Q` on 64-bit systems and `^I`
/// on 32-bit systems. So implementing `RefEncode` as a plain pointer is
/// correct.
const ENCODING_REF: Encoding = Encoding::Pointer(&NSUInteger::ENCODING);
}
fn main() {
assert!(NSUInteger::ENCODING.equivalent_to_str("Q"));
assert!(<&NSUInteger>::ENCODING.equivalent_to_str("^Q"));
assert!(<&NSUInteger>::ENCODING.equivalent_to_str("r^Q"));
}

View file

@ -0,0 +1,34 @@
//! Implementing `RefEncode` for `NSDecimal`.
use objc2_encode::{Encoding, RefEncode};
/// We choose in this case to represent `NSDecimal` as an opaque struct
/// (and in the future as an `extern type`) because we don't know much
/// about the internals.
///
/// Therefore we do not implement `Encode`, but when implementing `RefEncode`
/// the type-encoding still has to be correct.
#[repr(C)]
struct NSDecimal {
_priv: [u8; 0],
}
// SAFETY: `&NSDecimal` is a pointer.
unsafe impl RefEncode for NSDecimal {
// Running `@encode` on `NSDecimal*` on my 64-bit system gives `^{?=cCCC[38C]}`.
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct(
"?",
&[
Encoding::Char,
Encoding::UChar,
Encoding::UChar,
Encoding::UChar,
Encoding::Array(38, &Encoding::UChar),
],
));
}
fn main() {
assert!(NSDecimal::ENCODING_REF.equivalent_to_str("^{?=cCCC[38C]}"));
// Does not compile:
// println!("{:?}", NSDecimal::ENCODING);
}

View file

@ -0,0 +1,190 @@
//! This belongs in `objc2`, but it is put here to make `EncodeConvert` work
//! properly!
use core::fmt;
use crate::{Encode, EncodeConvert, Encoding, RefEncode};
/// The Objective-C `BOOL` type.
///
/// This is a thin wrapper-type over [`objc_sys::BOOL`]. It is intended that
/// you convert this into a Rust [`bool`] with the [`Bool::as_bool`] method as
/// soon as possible.
///
/// This is FFI-safe and can be used directly with `msg_send!` and `extern`
/// functions.
///
/// Note that this is able to contain more states than `bool` on some
/// platforms, but these cases should not be relied on!
#[repr(transparent)]
// We don't implement comparison traits because they could be implemented with
// two slightly different semantics:
// - `self.as_bool().cmp(other.as_bool())`
// - `self.value.cmp(other.value)`
// And it is not immediately clear for users which one was chosen.
#[derive(Copy, Clone, Default)]
pub struct Bool {
value: objc_sys::BOOL,
}
impl Bool {
/// The equivalent of [`true`] for Objective-C's `BOOL` type.
pub const YES: Self = Self::from_raw(objc_sys::YES);
/// The equivalent of [`false`] for Objective-C's `BOOL` type.
pub const NO: Self = Self::from_raw(objc_sys::NO);
/// Creates an Objective-C boolean from a Rust boolean.
#[inline]
pub const fn new(value: bool) -> Self {
// true as BOOL => 1 (YES)
// false as BOOL => 0 (NO)
let value = value as objc_sys::BOOL;
Self { value }
}
/// Creates this from a boolean value received from a raw Objective-C API.
#[inline]
pub const fn from_raw(value: objc_sys::BOOL) -> Self {
Self { value }
}
/// Retrieves the inner [`objc_sys::BOOL`] boolean type, to be used in raw
/// Objective-C APIs.
#[inline]
pub const fn as_raw(self) -> objc_sys::BOOL {
self.value
}
/// Returns `true` if `self` is [`NO`][Self::NO].
///
/// You should prefer using [`as_bool`][Self::as_bool].
#[inline]
pub const fn is_false(self) -> bool {
!self.as_bool()
}
/// Returns `true` if `self` is not [`NO`][Self::NO].
///
/// You should prefer using [`as_bool`][Self::as_bool].
#[inline]
pub const fn is_true(self) -> bool {
self.as_bool()
}
/// Converts this into the [`bool`] equivalent.
#[inline]
pub const fn as_bool(self) -> bool {
// Always compare with 0 (NO)
// This is what happens with the `!` operator / when using `if` in C.
self.value != objc_sys::NO
}
}
impl From<bool> for Bool {
#[inline]
fn from(b: bool) -> Bool {
Bool::new(b)
}
}
impl From<Bool> for bool {
#[inline]
fn from(b: Bool) -> bool {
b.as_bool()
}
}
impl fmt::Debug for Bool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.as_bool() { "YES" } else { "NO" })
}
}
// SAFETY: `Bool` is `repr(transparent)`.
unsafe impl Encode for Bool {
// i8::__ENCODING == Encoding::Char
// u8::__ENCODING == Encoding::UChar
// bool::__ENCODING == Encoding::Bool
// i32::__ENCODING == Encoding::Int
const ENCODING: Encoding = objc_sys::BOOL::__ENCODING;
}
// Note that we shouldn't delegate to `BOOL`'s `ENCODING_REF` since `BOOL` is
// sometimes `i8`/`u8`, and their `ENCODING_REF`s are `Encoding::String`,
// which is incorrect for `BOOL`:
//
// ```objc
// @encode(BOOL); // -> "c", "C" or "B"
// @encode(BOOL*); // -> "^c", "^C" or "^B"
// @encode(char); // -> "c" or "C"
// @encode(char*); // -> "*"
// ```
unsafe impl RefEncode for Bool {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_basic() {
let b = Bool::new(true);
assert!(b.as_bool());
assert!(b.is_true());
assert!(!b.is_false());
assert!(bool::from(b));
assert_eq!(b.as_raw() as usize, 1);
let b = Bool::new(false);
assert!(!b.as_bool());
assert!(!b.is_true());
assert!(b.is_false());
assert!(!bool::from(b));
assert_eq!(b.as_raw() as usize, 0);
}
#[test]
fn test_associated_constants() {
let b = Bool::YES;
assert!(b.as_bool());
assert!(b.is_true());
assert_eq!(b.as_raw() as usize, 1);
let b = Bool::NO;
assert!(!b.as_bool());
assert!(b.is_false());
assert_eq!(b.as_raw() as usize, 0);
}
#[test]
fn test_impls() {
let b: Bool = Default::default();
assert!(!b.as_bool());
assert!(b.is_false());
assert!(Bool::from(true).as_bool());
assert!(Bool::from(true).is_true());
assert!(Bool::from(false).is_false());
assert!(Bool::from(true).is_true());
assert!(Bool::from(false).is_false());
}
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", Bool::from(true)), "YES");
assert_eq!(format!("{:?}", Bool::from(false)), "NO");
}
#[test]
// Test on platform where we know the type of BOOL
#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86_64"))]
fn test_outside_normal() {
let b = Bool::from_raw(42);
assert!(b.is_true());
assert!(!b.is_false());
assert_eq!(b.as_raw(), 42);
}
}

View file

@ -0,0 +1,841 @@
use core::ffi::c_void;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::num::{
NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
};
use core::ptr::NonNull;
use core::sync::atomic;
use crate::Encoding;
use crate::__bool::Bool;
/// Types that have an Objective-C type-encoding.
///
/// Usually you will want to implement [`RefEncode`] as well.
///
/// If your type is an opaque type you should not need to implement this;
/// there you will only need [`RefEncode`].
///
/// # Safety
///
/// The type must be FFI-safe, meaning a C-compatible `repr` (`repr(C)`,
/// `repr(u8)`, `repr(transparent)` where the inner types are C-compatible,
/// and so on). See the [nomicon on other `repr`s][reprs].
///
/// Objective-C will make assumptions about the type (like its size, alignment
/// and ABI) from its encoding, so the implementer must verify that the
/// encoding is accurate.
///
/// Concretely, [`Self::ENCODING`] must match the result of running `@encode`
/// in Objective-C with the type in question.
///
/// You should also beware of having [`Drop`] types implement this, since when
/// passed to Objective-C via. `objc2::msg_send!` their destructor will not be
/// called!
///
/// # Examples
///
/// Implementing for a struct:
///
/// ```
/// # use objc2_encode::{Encode, Encoding, RefEncode};
/// # use core::ffi::c_void;
/// #
/// #[repr(C)]
/// struct MyType {
/// a: i32,
/// b: f64,
/// c: *const c_void,
/// }
///
/// unsafe impl Encode for MyType {
/// const ENCODING: Encoding = Encoding::Struct(
/// // The name of the type that Objective-C sees.
/// "MyType",
/// &[
/// // Delegate to field's implementations.
/// // The order is the same as in the definition.
/// i32::ENCODING,
/// f64::ENCODING,
/// <*const c_void>::ENCODING,
/// ],
/// );
/// }
///
/// // Note: You would also implement `RefEncode` for this type.
/// ```
///
/// [reprs]: https://doc.rust-lang.org/nomicon/other-reprs.html
pub unsafe trait Encode {
/// The Objective-C type-encoding for this type.
const ENCODING: Encoding;
}
/// Types whoose references has an Objective-C type-encoding.
///
/// Implementing this for `T` provides [`Encode`] implementations for:
/// - `*const T`
/// - `*mut T`
/// - `&T`
/// - `&mut T`
/// - `NonNull<T>`
/// - `Option<&T>`
/// - `Option<&mut T>`
/// - `Option<NonNull<T>>`
///
/// # Reasoning behind this trait's existence
///
/// External crates cannot implement [`Encode`] for pointers or [`Option`]s
/// containing references, so instead, they can implement this trait.
/// Additionally it would be very cumbersome if every type had to implement
/// [`Encode`] for all possible pointer types.
///
/// Finally, having this trait allows for much cleaner generic code that need
/// to represent types that can be encoded as pointers.
///
/// # Safety
///
/// References to the object must be FFI-safe.
///
/// See the nomicon entry on [representing opaque structs][opaque] for
/// information on how to represent objects that you don't know the layout of
/// (or use `extern type` ([RFC-1861]) if you're using nightly).
///
/// Objective-C will make assumptions about the type (like its size, alignment
/// and ABI) from its encoding, so the implementer must verify that the
/// encoding is accurate.
///
/// Concretely, [`Self::ENCODING_REF`] must match the result of running
/// `@encode` in Objective-C with a pointer to the type in question.
///
/// [opaque]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
/// [RFC-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html
pub unsafe trait RefEncode {
/// The Objective-C type-encoding for a reference of this type.
///
/// Should be one of [`Encoding::Object`], [`Encoding::Block`],
/// [`Encoding::Class`], [`Encoding::Pointer`], [`Encoding::Sel`] or
/// [`Encoding::Unknown`].
///
/// # Examples
///
/// This is usually implemented either as an object pointer:
/// ```
/// # use objc2_encode::{Encoding, RefEncode};
/// # #[repr(C)]
/// # struct MyObject {
/// # _priv: [u8; 0],
/// # }
/// # unsafe impl RefEncode for MyObject {
/// const ENCODING_REF: Encoding = Encoding::Object;
/// # }
/// ```
///
/// Or as a pointer to the type, delegating the rest to the [`Encode`]
/// implementation:
/// ```
/// # use objc2_encode::{Encode, Encoding, RefEncode};
/// # #[repr(transparent)]
/// # struct MyType(i32);
/// # unsafe impl Encode for MyType {
/// # const ENCODING: Encoding = i32::ENCODING;
/// # }
/// # unsafe impl RefEncode for MyType {
/// const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
/// # }
/// ```
const ENCODING_REF: Encoding;
}
// TODO: Implement for `PhantomData` and `PhantomPinned`?
/// Simple helper for implementing [`Encode`].
macro_rules! encode_impls {
($($t:ty => $e:ident,)*) => ($(
unsafe impl Encode for $t {
const ENCODING: Encoding = Encoding::$e;
}
)*);
}
encode_impls!(
i8 => Char,
i16 => Short,
i32 => Int,
i64 => LongLong,
u8 => UChar,
u16 => UShort,
u32 => UInt,
u64 => ULongLong,
f32 => Float,
f64 => Double,
// TODO: i128 & u128
// https://github.com/rust-lang/rust/issues/54341
);
// TODO: Structs in core::arch?
/// To allow usage as the return type of generic functions.
///
/// You should not rely on this encoding to exist for any other purpose (since
/// `()` is not FFI-safe)!
// TODO: Figure out a way to remove this - maybe with a `EncodeReturn` trait?
unsafe impl Encode for () {
const ENCODING: Encoding = Encoding::Void;
}
// UI tests of this is too brittle.
#[cfg(doctest)]
/// ```
/// use objc2_encode::Encode;
/// <()>::ENCODING; // TODO: Make this fail as well
/// ```
/// ```should_fail
/// use core::ffi::c_void;
/// use objc2_encode::Encode;
/// <c_void>::ENCODING;
/// ```
/// ```should_fail
/// use objc2_encode::Encode;
/// <*const ()>::ENCODING;
/// ```
/// ```should_fail
/// use core::ffi::c_void;
/// use objc2_encode::Encode;
/// <&c_void>::ENCODING;
/// ```
extern "C" {}
macro_rules! encode_impls_size {
($($t:ty => ($t16:ty, $t32:ty, $t64:ty),)*) => ($(
#[doc = concat!("The encoding of [`", stringify!($t), "`] varies based on the target pointer width.")]
unsafe impl Encode for $t {
#[cfg(target_pointer_width = "16")]
const ENCODING: Encoding = <$t16>::ENCODING;
#[cfg(target_pointer_width = "32")]
const ENCODING: Encoding = <$t32>::ENCODING;
#[cfg(target_pointer_width = "64")]
const ENCODING: Encoding = <$t64>::ENCODING;
}
)*);
}
encode_impls_size!(
isize => (i16, i32, i64),
usize => (u16, u32, u64),
);
/// Simple helper for implementing [`RefEncode`].
macro_rules! pointer_refencode_impl {
($($t:ty),*) => ($(
unsafe impl RefEncode for $t {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
)*);
}
pointer_refencode_impl!(i16, i32, i64, isize, u16, u32, u64, usize, f32, f64);
/// Pointers to [`i8`] use the special [`Encoding::String`] encoding.
unsafe impl RefEncode for i8 {
const ENCODING_REF: Encoding = Encoding::String;
}
/// Pointers to [`u8`] use the special [`Encoding::String`] encoding.
unsafe impl RefEncode for u8 {
const ENCODING_REF: Encoding = Encoding::String;
}
/// Simple helper for implementing [`Encode`] for nonzero integer types.
macro_rules! encode_impls_nonzero {
($($nonzero:ident => $type:ty,)*) => ($(
unsafe impl Encode for $nonzero {
const ENCODING: Encoding = <$type>::ENCODING;
}
unsafe impl Encode for Option<$nonzero> {
const ENCODING: Encoding = <$type>::ENCODING;
}
unsafe impl RefEncode for $nonzero {
const ENCODING_REF: Encoding = <$type>::ENCODING_REF;
}
unsafe impl RefEncode for Option<$nonzero> {
const ENCODING_REF: Encoding = <$type>::ENCODING_REF;
}
)*);
}
encode_impls_nonzero!(
NonZeroI8 => i8,
NonZeroI16 => i16,
NonZeroI32 => i32,
NonZeroI64 => i64,
NonZeroIsize => isize,
NonZeroU8 => u8,
NonZeroU16 => u16,
NonZeroU32 => u32,
NonZeroU64 => u64,
NonZeroUsize => usize,
);
/// Simple helper for implementing for atomic types.
macro_rules! encode_atomic_impls {
($(
$(#[$m:meta])*
$atomic:ident => $type:ty,
)*) => ($(
// SAFETY: C11 `_Atomic` types use compatible synchronization
// primitives, and the atomic type is guaranteed to have the same
// in-memory representation as the underlying type.
$(#[$m])*
unsafe impl Encode for atomic::$atomic {
const ENCODING: Encoding = Encoding::Atomic(&<$type>::ENCODING);
}
$(#[$m])*
unsafe impl RefEncode for atomic::$atomic {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
)*);
}
encode_atomic_impls!(
#[cfg(target_has_atomic = "8")]
AtomicI8 => i8,
#[cfg(target_has_atomic = "8")]
AtomicU8 => u8,
#[cfg(target_has_atomic = "16")]
AtomicI16 => i16,
#[cfg(target_has_atomic = "16")]
AtomicU16 => u16,
#[cfg(target_has_atomic = "32")]
AtomicI32 => i32,
#[cfg(target_has_atomic = "32")]
AtomicU32 => u32,
#[cfg(target_has_atomic = "64")]
AtomicI64 => i64,
#[cfg(target_has_atomic = "64")]
AtomicU64 => u64,
// TODO
// #[cfg(target_has_atomic = "128")]
// AtomicI128 => i128,
// #[cfg(target_has_atomic = "128")]
// AtomicU128 => u128,
#[cfg(target_has_atomic = "ptr")]
AtomicIsize => isize,
#[cfg(target_has_atomic = "ptr")]
AtomicUsize => usize,
);
// SAFETY: Guaranteed to have the same in-memory representation as `*mut T`.
#[cfg(target_has_atomic = "ptr")]
unsafe impl<T: RefEncode> Encode for atomic::AtomicPtr<T> {
const ENCODING: Encoding = Encoding::Atomic(&T::ENCODING_REF);
}
#[cfg(target_has_atomic = "ptr")]
unsafe impl<T: RefEncode> RefEncode for atomic::AtomicPtr<T> {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
/// [`Encode`] is implemented manually for `*const c_void`, instead of
/// implementing [`RefEncode`], to discourage creating `&c_void`.
unsafe impl Encode for *const c_void {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Void);
}
unsafe impl RefEncode for *const c_void {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
/// [`Encode`] is implemented manually for `*mut c_void`, instead of
/// implementing [`RefEncode`], to discourage creating `&mut c_void`.
unsafe impl Encode for *mut c_void {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Void);
}
unsafe impl RefEncode for *mut c_void {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<T: Encode, const LENGTH: usize> Encode for [T; LENGTH] {
const ENCODING: Encoding = Encoding::Array(LENGTH, &T::ENCODING);
}
unsafe impl<T: Encode, const LENGTH: usize> RefEncode for [T; LENGTH] {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
macro_rules! encode_impls_transparent {
($($t:ident<T $(: ?$b:ident)?>,)*) => ($(
unsafe impl<T: Encode $(+ ?$b)?> Encode for $t<T> {
const ENCODING: Encoding = T::ENCODING;
}
unsafe impl<T: RefEncode $(+ ?$b)?> RefEncode for $t<T> {
const ENCODING_REF: Encoding = T::ENCODING_REF;
}
)*);
}
encode_impls_transparent! {
// SAFETY: Guaranteed to have the same layout as `T`, and is subject to
// the same layout optimizations as `T`.
// TODO: With specialization: `impl Encode for ManuallyDrop<Box<T>>`
ManuallyDrop<T: ?Sized>,
// The fact that this has `repr(no_niche)` has no effect on us, since we
// don't implement `Encode` generically over `Option`.
// (e.g. an `Option<UnsafeCell<&u8>>` impl is not available).
// The inner field is not public, so may not be stable.
// TODO: UnsafeCell<T>,
// The inner field is not public, so may not be safe.
// TODO: Pin<T>,
// SAFETY: Guaranteed to have the same size, alignment, and ABI as `T`.
MaybeUninit<T>,
// SAFETY: Guaranteed to have the same layout and ABI as `T`.
Wrapping<T>,
// It might have requirements that would disourage this impl?
// TODO: Cell<T>
// TODO: Types that need to be made repr(transparent) first:
// - core::cell::Ref?
// - core::cell::RefCell?
// - core::cell::RefMut?
// - core::panic::AssertUnwindSafe<T>
// TODO: core::num::Saturating when that is stabilized
// TODO: core::cmp::Reverse?
}
/// Helper for implementing `Encode`/`RefEncode` for pointers to types that
/// implement `RefEncode`.
///
/// Using `?Sized` is safe here because we delegate to other implementations
/// (which will verify that the implementation is safe for the unsized type).
macro_rules! encode_pointer_impls {
(unsafe impl<T: RefEncode> $x:ident for Pointer<T> {
const $c:ident = $e:expr;
}) => (
unsafe impl<T: RefEncode + ?Sized> $x for *const T {
const $c: Encoding = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for *mut T {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a T {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a mut T {
const $c: Encoding = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for NonNull<T> {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for Option<&'a T> {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for Option<&'a mut T> {
const $c: Encoding = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for Option<NonNull<T>> {
const $c: Encoding = $e;
}
);
}
// Implement `Encode` for types that are `RefEncode`.
//
// This allows users to implement `Encode` for custom types that have a
// specific encoding as a pointer, instead of having to implement it for each
// pointer-like type in turn.
encode_pointer_impls!(
unsafe impl<T: RefEncode> Encode for Pointer<T> {
const ENCODING = T::ENCODING_REF;
}
);
// Implement `RefEncode` for pointers to types that are `RefEncode`.
//
// This implements `Encode` for pointers to pointers (to pointers, and so on),
// which would otherwise be very cumbersome to do manually.
encode_pointer_impls!(
unsafe impl<T: RefEncode> RefEncode for Pointer<T> {
const ENCODING_REF = Encoding::Pointer(&T::ENCODING_REF);
}
);
/// Helper for implementing [`Encode`]/[`RefEncode`] for function pointers
/// whoose arguments implement [`Encode`].
///
/// Ideally we'd implement it for all function pointers, but due to coherence
/// issues, see <https://github.com/rust-lang/rust/issues/56105>, function
/// pointers that are higher-ranked over lifetimes don't get implemented.
///
/// We could fix it by adding those impls and allowing `coherence_leak_check`,
/// but it would have to be done for _all_ references, `Option<&T>` and such as
/// well. So trying to do it quickly requires generating a polynomial amount of
/// implementations, which IMO is overkill for such a small issue.
///
/// Using `?Sized` is probably not safe here because C functions can only take
/// and return items with a known size.
macro_rules! encode_fn_pointer_impl {
(@ $FnTy: ty, $($Arg: ident),*) => {
unsafe impl<Ret: Encode, $($Arg: Encode),*> Encode for $FnTy {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Unknown);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> RefEncode for $FnTy {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> Encode for Option<$FnTy> {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Unknown);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> RefEncode for Option<$FnTy> {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
};
(# $abi:literal; $($Arg: ident),+) => {
// Normal functions
encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
// Variadic functions
encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
};
(# $abi:literal; ) => {
// No variadic functions with 0 parameters
encode_fn_pointer_impl!(@ extern $abi fn() -> Ret, );
encode_fn_pointer_impl!(@ unsafe extern $abi fn() -> Ret, );
};
($($Arg: ident),*) => {
encode_fn_pointer_impl!(# "C"; $($Arg),*);
#[cfg(feature = "unstable-c-unwind")]
encode_fn_pointer_impl!(# "C-unwind"; $($Arg),*);
};
}
encode_fn_pointer_impl!();
encode_fn_pointer_impl!(A);
encode_fn_pointer_impl!(A, B);
encode_fn_pointer_impl!(A, B, C);
encode_fn_pointer_impl!(A, B, C, D);
encode_fn_pointer_impl!(A, B, C, D, E);
encode_fn_pointer_impl!(A, B, C, D, E, F);
encode_fn_pointer_impl!(A, B, C, D, E, F, G);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
mod convert_private {
use super::*;
pub trait Sealed {}
impl<T: Encode> Sealed for T {}
impl Sealed for bool {}
}
/// Represents types that can easily be converted to/from an [`Encode`] type.
///
/// This is implemented specially for [`bool`] to allow using that as
/// Objective-C `BOOL`, where it would otherwise not be allowed (since they
/// are not ABI compatible).
///
/// This is mostly an implementation detail, and hence the trait is sealed.
/// Open an issue if you know a use-case where this restrition should be
/// lifted!
pub trait EncodeConvert: convert_private::Sealed {
/// The inner type that this can be converted to and from.
#[doc(hidden)]
type __Inner: Encode;
/// The actual encoding this type has.
#[doc(hidden)]
const __ENCODING: Encoding;
#[doc(hidden)]
fn __from_inner(inner: Self::__Inner) -> Self;
#[doc(hidden)]
fn __into_inner(self) -> Self::__Inner;
}
impl<T: Encode> EncodeConvert for T {
type __Inner = Self;
const __ENCODING: Encoding = Self::ENCODING;
#[inline]
fn __from_inner(inner: Self::__Inner) -> Self {
inner
}
#[inline]
fn __into_inner(self) -> Self::__Inner {
self
}
}
impl EncodeConvert for bool {
type __Inner = Bool;
const __ENCODING: Encoding = Encoding::Bool;
#[inline]
fn __from_inner(inner: Self::__Inner) -> Self {
inner.as_bool()
}
#[inline]
fn __into_inner(self) -> Self::__Inner {
Bool::new(self)
}
}
mod args_private {
pub trait Sealed {}
}
/// Types that represent an ordered group of function arguments, where each
/// argument has an Objective-C type-encoding, or can be converted from one.
///
/// This is implemented for tuples of up to 12 arguments, where each argument
/// implements [`EncodeConvert`]. It is primarily used to make generic code
/// a bit easier.
///
/// Note that tuples themselves don't implement [`Encode`] directly, because
/// they're not FFI-safe!
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
///
///
/// # Safety
///
/// You cannot rely on this trait for ensuring that the arguments all
/// are [`Encode`], since they may only implement [`EncodeConvert`]. Use your
/// own trait and add [`Encode`] bounds on that.
pub unsafe trait EncodeArguments: args_private::Sealed {
/// The encodings for the arguments.
const ENCODINGS: &'static [Encoding];
}
macro_rules! encode_args_impl {
($($Arg: ident),*) => {
impl<$($Arg: EncodeConvert),*> args_private::Sealed for ($($Arg,)*) {}
unsafe impl<$($Arg: EncodeConvert),*> EncodeArguments for ($($Arg,)*) {
const ENCODINGS: &'static [Encoding] = &[
// T::__Inner::ENCODING => T::ENCODING
// bool::__Inner::ENCODING => Bool::ENCODING
$($Arg::__Inner::ENCODING),*
];
}
};
}
encode_args_impl!();
encode_args_impl!(A);
encode_args_impl!(A, B);
encode_args_impl!(A, B, C);
encode_args_impl!(A, B, C, D);
encode_args_impl!(A, B, C, D, E);
encode_args_impl!(A, B, C, D, E, F);
encode_args_impl!(A, B, C, D, E, F, G);
encode_args_impl!(A, B, C, D, E, F, G, H);
encode_args_impl!(A, B, C, D, E, F, G, H, I);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
#[cfg(test)]
mod tests {
use super::*;
use core::any::TypeId;
use core::sync::atomic::*;
#[test]
fn test_c_string() {
assert_eq!(i8::ENCODING, Encoding::Char);
assert_eq!(u8::ENCODING, Encoding::UChar);
assert_eq!(<*const i8>::ENCODING, Encoding::String);
assert_eq!(<&u8>::ENCODING, Encoding::String);
assert_eq!(i8::ENCODING_REF, Encoding::String);
assert_eq!(i8::ENCODING_REF, Encoding::String);
assert_eq!(
<*const *const i8>::ENCODING,
Encoding::Pointer(&Encoding::String)
);
assert_eq!(<&&u8>::ENCODING, Encoding::Pointer(&Encoding::String));
}
#[test]
fn test_i32() {
assert_eq!(i32::ENCODING, Encoding::Int);
assert_eq!(<&i32>::ENCODING, Encoding::Pointer(&Encoding::Int));
assert_eq!(
<&&i32>::ENCODING,
Encoding::Pointer(&Encoding::Pointer(&Encoding::Int))
);
}
#[test]
fn test_atomic() {
assert_eq!(AtomicI32::ENCODING, Encoding::Atomic(&Encoding::Int));
assert_eq!(
AtomicI32::ENCODING_REF,
Encoding::Pointer(&Encoding::Atomic(&Encoding::Int))
);
assert_eq!(
AtomicPtr::<i32>::ENCODING,
Encoding::Atomic(&Encoding::Pointer(&Encoding::Int))
);
assert_eq!(AtomicI8::ENCODING, Encoding::Atomic(&Encoding::Char));
assert_eq!(
AtomicI8::ENCODING_REF,
Encoding::Pointer(&Encoding::Atomic(&Encoding::Char))
);
assert_eq!(
AtomicPtr::<i8>::ENCODING,
Encoding::Atomic(&Encoding::String)
);
}
#[test]
fn test_void() {
// TODO: Remove this
assert_eq!(<()>::ENCODING, Encoding::Void);
assert_eq!(
<*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Void)
);
assert_eq!(
<&*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Pointer(&Encoding::Void))
);
}
#[test]
fn test_transparent() {
assert_eq!(<ManuallyDrop<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<ManuallyDrop<&u8>>::ENCODING, u8::ENCODING_REF);
assert_eq!(<ManuallyDrop<Option<&u8>>>::ENCODING, u8::ENCODING_REF);
assert_eq!(<&ManuallyDrop<Option<&u8>>>::ENCODING, <&&u8>::ENCODING);
// assert_eq!(<UnsafeCell<u8>>::ENCODING, u8::ENCODING);
// assert_eq!(<Pin<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<MaybeUninit<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<Wrapping<u8>>::ENCODING, u8::ENCODING);
// Shouldn't compile
// assert_eq!(<Option<UnsafeCell<&u8>>>::ENCODING, <&u8>::ENCODING);
}
#[test]
fn test_extern_fn_pointer() {
assert_eq!(
<extern "C" fn()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<extern "C" fn(x: ()) -> ()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<Option<unsafe extern "C" fn()>>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
#[cfg(feature = "unstable-c-unwind")]
assert_eq!(
<extern "C-unwind" fn()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
}
#[test]
fn test_extern_fn_pointer_elided_lifetime() {
fn impls_encode<T: Encode>(_x: T) {}
extern "C" fn my_fn1(_x: &i32) {}
extern "C" fn my_fn2(_x: &i32, _y: &u8) {}
extern "C" fn my_fn3(x: &u8) -> &u8 {
x
}
extern "C" fn my_fn4<'a, 'b>(x: &'a u8, _y: &'b i32) -> &'a u8 {
x
}
impls_encode(my_fn1 as extern "C" fn(_));
impls_encode(my_fn2 as extern "C" fn(_, _));
impls_encode(my_fn3 as extern "C" fn(_) -> _);
impls_encode(my_fn4 as extern "C" fn(_, _) -> _);
}
#[test]
fn convert_normally_noop() {
assert_eq!(
TypeId::of::<<i32 as EncodeConvert>::__Inner>(),
TypeId::of::<i32>()
);
assert_eq!(<i32 as EncodeConvert>::__from_inner(42), 42);
assert_eq!(42i32.__into_inner(), 42);
}
#[test]
fn convert_i8() {
assert_eq!(
TypeId::of::<<i8 as EncodeConvert>::__Inner>(),
TypeId::of::<i8>()
);
assert_eq!(<i8 as EncodeConvert>::__from_inner(-3), -3);
assert_eq!((-3i32).__into_inner(), -3);
}
#[test]
fn convert_bool() {
assert!(!<bool as EncodeConvert>::__from_inner(Bool::NO));
assert!(<bool as EncodeConvert>::__from_inner(Bool::YES));
assert!(!false.__into_inner().as_bool());
assert!(true.__into_inner().as_bool());
assert_eq!(bool::__ENCODING, Encoding::Bool);
assert_eq!(
<bool as EncodeConvert>::__Inner::__ENCODING,
<bool as EncodeConvert>::__Inner::ENCODING
);
#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86_64"))]
assert_eq!(<bool as EncodeConvert>::__Inner::ENCODING, Encoding::Char);
}
#[test]
fn test_encode_arguments() {
assert!(<()>::ENCODINGS.is_empty());
assert_eq!(<(i8,)>::ENCODINGS, &[i8::ENCODING]);
assert_eq!(<(i8, u32)>::ENCODINGS, &[i8::ENCODING, u32::ENCODING]);
}
}

View file

@ -0,0 +1,594 @@
use core::fmt;
use crate::helper::{Helper, NestingLevel};
use crate::parse;
/// An Objective-C type-encoding.
///
/// Can be retrieved in Objective-C for a type `T` using the `@encode(T)`
/// directive.
/// ```objc
/// NSLog(@"Encoding of NSException: %s", @encode(NSException));
/// ```
///
/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`]
/// into its string representation, that the the `@encode` directive would
/// return. This can be used conveniently through the `to_string` method:
///
/// ```
/// use objc2_encode::Encoding;
/// assert_eq!(Encoding::Int.to_string(), "i");
/// ```
///
/// For more information on the string value of an encoding, see [Apple's
/// documentation][ocrtTypeEncodings].
///
/// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
///
/// # Examples
///
/// Comparing an encoding to a string from the Objective-C runtime:
///
/// ```
/// use objc2_encode::Encoding;
/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]"));
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
// 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.
Char,
/// A C `short`. Corresponds to the `s` code.
Short,
/// A C `int`. Corresponds to the `i` code.
Int,
/// 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.
LongLong,
/// A C `unsigned char`. Corresponds to the `C` code.
UChar,
/// A C `unsigned short`. Corresponds to the `S` code.
UShort,
/// A C `unsigned int`. Corresponds to the `I` code.
UInt,
/// A C `unsigned long`. Corresponds to the `L` code.
ULong,
/// A C `unsigned long long`. Corresponds to the `Q` code.
ULongLong,
/// A C `float`. Corresponds to the `f` code.
Float,
/// A C `double`. Corresponds to the `d` code.
Double,
/// A C `long double`. Corresponds to the `D` code.
LongDouble,
/// A C `float _Complex`. Corresponds to the `jf` code.
FloatComplex,
/// A C `_Complex` or `double _Complex`. Corresponds to the `jd` code.
DoubleComplex,
/// A C `long double _Complex`. Corresponds to the `jD` code.
LongDoubleComplex,
// TODO: Complex(&Encoding) ???
/// A C++ `bool` / C99 `_Bool`. Corresponds to the `B` code.
Bool,
/// A C `void`. Corresponds to the `v` code.
Void,
/// A C `char *`. Corresponds to the `*` code.
String,
/// An Objective-C object (`id`). Corresponds to the `@` code.
Object,
/// An Objective-C block. Corresponds to the `@?` code.
Block,
/// An Objective-C class (`Class`). Corresponds to the `#` code.
Class,
/// An Objective-C selector (`SEL`). Corresponds to the `:` code.
Sel,
/// 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 num` code.
BitField(u8, &'static Encoding),
/// A pointer to the given type.
///
/// Corresponds to the `^ type` code.
Pointer(&'static Encoding),
/// A C11 [`_Atomic`] type.
///
/// 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),
/// 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.
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.
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`).
///
/// 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;
/// then `c_long::ENCODING` would just work.
///
/// Unfortunately, `long` have a different encoding than `int` when it is
/// 32 bits wide; the [`l`][`Encoding::Long`] encoding.
pub const C_LONG: Self = {
// Alternative: `mem::size_of::<c_long>() == 4`
// That would exactly match what `clang` does:
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/AST/ASTContext.cpp#L7245
if cfg!(any(target_pointer_width = "32", windows)) {
// @encode(long) = 'l'
Self::Long
} else {
// @encode(long) = 'q'
Self::LongLong
}
};
/// The encoding of [`c_ulong`](`std::os::raw::c_ulong`).
///
/// See [`Encoding::C_LONG`] for explanation.
pub const C_ULONG: Self = {
if cfg!(any(target_pointer_width = "32", windows)) {
// @encode(unsigned long) = 'L'
Self::ULong
} else {
// @encode(unsigned long) = 'Q'
Self::ULongLong
}
};
/// Check if one encoding is equivalent to another.
///
/// Currently, equivalence testing requires that the encodings are equal,
/// except for qualifiers. This may be changed in the future to e.g.
/// ignore struct names, to allow structs behind multiple pointers to be
/// considered equivalent, or similar changes that may be required because
/// of limitations in Objective-C compiler implementations.
///
/// For example, you should not rely on two equivalent encodings to have
/// the same size or ABI - that is provided on a best-effort basis.
pub fn equivalent_to(&self, other: &Self) -> bool {
equivalent_to(self, other, NestingLevel::new())
}
/// Check if an encoding is equivalent to the given string representation.
///
/// See [`Encoding::equivalent_to`] for details about the meaning of
/// "equivalence".
pub fn equivalent_to_str(&self, s: &str) -> bool {
// if the given encoding can be successfully removed from the start
// and an empty string remains, they were fully equivalent!
if let Some(res) = self.equivalent_to_start_of_str(s) {
res.is_empty()
} else {
false
}
}
/// Check if an encoding is equivalent to the start of the given string
/// representation.
///
/// If it is equivalent, the remaining part of the string is returned.
/// Otherwise this returns [`None`].
///
/// See [`Encoding::equivalent_to`] for details about the meaning of
/// "equivalence".
pub fn equivalent_to_start_of_str<'a>(&self, s: &'a str) -> Option<&'a str> {
// strip leading qualifiers
let s = s.trim_start_matches(parse::QUALIFIERS);
// TODO: Allow missing/"?" names in structs and unions?
parse::rm_enc_prefix(s, self, NestingLevel::new())
}
}
fn equivalent_to(enc1: &Encoding, enc2: &Encoding, level: NestingLevel) -> bool {
// Note: Ideally `Block` and sequence of `Object, Unknown` in struct
// should compare equal, but we don't bother since in practice a plain
// `Unknown` will never appear.
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) => {
// TODO: Use the type on GNUStep (nesting level?)
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())
}
}
}
/// Formats this [`Encoding`] in a similar way that the `@encode` directive
/// would ordinarily do.
///
/// You should not rely on the output of this to be stable across versions. It
/// may change if found to be required to be compatible with exisiting
/// Objective-C compilers.
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;
// Check PartialEq
assert_eq!(E, E);
// Check Display
assert_eq!(E.to_string(), $string);
// Check equivalence comparisons
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));
)*
// Negative checks
$(
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));
)*
// Check static str
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";
// Qualifiers
~"rc";
~"nc";
~"Nc";
~"oc";
~"Oc";
~"Rc";
~"Vc";
!"ri";
}
fn block() {
Encoding::Block;
"@?";
}
fn object() {
Encoding::Object;
!Encoding::Block;
"@";
!"@?";
}
fn unknown() {
Encoding::Unknown;
!Encoding::Block;
"?";
}
// Note: A raw `?` cannot happen in practice, since functions can only
// be accessed through pointers, and that will yield `^?`
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=}";
// TODO: Unsure about this
!"{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?}";
}
}
}

View file

@ -0,0 +1,205 @@
use crate::Encoding;
#[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 {
// TODO: Is this correct?
self
}
pub(crate) const fn indirection(self, kind: IndirectionKind) -> Self {
match kind {
// Move all the way down
IndirectionKind::Atomic => Self::Bottom,
// Move one step down
IndirectionKind::Pointer => match self {
Self::Top => Self::Within,
Self::Bottom | Self::Within => Self::Bottom,
},
}
}
pub(crate) const fn array(self) -> Self {
// TODO: Is this correct?
self
}
pub(crate) const fn container(self) -> Option<Self> {
match self {
// Move one step down, and output
Self::Top => Some(Self::Within),
// Output
Self::Within => Some(Self::Within),
// Don't output
Self::Bottom => None,
}
}
}
#[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 => "?",
}
}
}
#[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')',
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub(crate) enum Helper<'a> {
Primitive(Primitive),
BitField(u8, &'a Encoding),
Indirection(IndirectionKind, &'a Encoding),
Array(usize, &'a Encoding),
Container(ContainerKind, &'a str, &'a [Encoding]),
}
impl<'a> Helper<'a> {
pub(crate) const fn new(encoding: &'a 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) => Self::Container(ContainerKind::Struct, name, fields),
Union(name, members) => Self::Container(ContainerKind::Union, name, members),
}
}
}

View file

@ -0,0 +1,117 @@
//! # Objective-C type-encoding
//!
//! This is re-exported into the top level of `objc2`.
//!
//! The Objective-C directive `@encode` encodes types as strings, and this is
//! used in various places in the runtime.
//!
//! This crate provides the [`Encoding`] type to efficiently describe and
//! compare these type-encodings.
//!
//! Additionally it provides traits for annotating types that has an
//! Objective-C encoding: Specifically [`Encode`] for structs, [`RefEncode`]
//! for references and [`EncodeArguments`] for function arguments.
//!
//! This crate is exported under the [`objc2`] crate as `objc2::encode`, so
//! usually you would just use it from there.
//!
//! [`objc2`]: https://crates.io/crates/objc2
//!
//!
//! ## Example
//!
//! Implementing [`Encode`] and [`RefEncode`] for a custom type:
//!
//! ```
//! use objc2_encode::{Encode, Encoding, RefEncode};
//! // or from objc2:
//! // use objc2::{Encode, Encoding, RefEncode};
//!
//! #[repr(C)]
//! struct MyStruct {
//! a: f32, // float
//! b: i16, // int16_t
//! }
//!
//! unsafe impl Encode for MyStruct {
//! const ENCODING: Encoding = Encoding::Struct(
//! "MyStruct", // Must use the same name as defined in C header files
//! &[
//! f32::ENCODING, // Same as Encoding::Float
//! i16::ENCODING, // Same as Encoding::Short
//! ],
//! );
//! }
//!
//! // @encode(MyStruct) -> "{MyStruct=fs}"
//! assert!(MyStruct::ENCODING.equivalent_to_str("{MyStruct=fs}"));
//!
//! unsafe impl RefEncode for MyStruct {
//! // Note that if `MyStruct` is an Objective-C instance, this should
//! // be `Encoding::Object`.
//! const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
//! }
//!
//! // @encode(MyStruct*) -> "^{MyStruct=fs}"
//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}"));
//! ```
//!
//! See the [`examples`] folder for more complex usage.
//!
//! [`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples
//!
//!
//! ## Caveats
//!
//! We've taken the pragmatic approach with [`Encode`] and [`RefEncode`], and
//! have implemented it for as many types as possible (instead of defining a
//! bunch of subtraits for very specific purposes). However, that might
//! sometimes be slightly surprising.
//!
//! The primary example is [`()`][`unit`], which doesn't make sense as a
//! method argument, but is a very common return type, and hence implements
//! [`Encode`].
//!
//!
//! ## Further resources
//!
//! - [Objective-C, Encoding and You](https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de).
//! - [Apple's documentation on Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html).
//! - [How are the digits in ObjC method type encoding calculated?](https://stackoverflow.com/a/11527925)
//! - [`clang`'s source code for generating `@encode`](https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850).
#![no_std]
#![warn(elided_lifetimes_in_paths)]
#![warn(missing_docs)]
#![deny(non_ascii_idents)]
#![warn(unreachable_pub)]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(clippy::cargo)]
#![warn(clippy::ptr_as_ptr)]
// Update in Cargo.toml as well.
#![doc(html_root_url = "https://docs.rs/objc2-encode/2.0.0-pre.2")]
#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}
#[cfg(any(feature = "std", doc))]
extern crate std;
#[cfg(any(feature = "alloc", test, doc))]
extern crate alloc;
#[doc(hidden)]
pub mod __bool;
mod encode;
mod encoding;
mod helper;
mod parse;
// Will be used at some point when generic constants are available
#[allow(dead_code)]
mod static_str;
pub use self::encode::{Encode, EncodeArguments, EncodeConvert, RefEncode};
pub use self::encoding::Encoding;

View file

@ -0,0 +1,67 @@
//! Parsing encodings from their string representation.
#![deny(unsafe_code)]
use crate::helper::{Helper, NestingLevel};
use crate::Encoding;
pub(crate) const QUALIFIERS: &[char] = &[
'r', // const
'n', // in
'N', // inout
'o', // out
'O', // bycopy
'R', // byref
'V', // oneway
];
pub(crate) fn rm_enc_prefix<'a>(
s: &'a str,
enc: &Encoding,
level: NestingLevel,
) -> Option<&'a str> {
use Helper::*;
match Helper::new(enc) {
Primitive(primitive) => s.strip_prefix(primitive.to_str()),
BitField(b, _type) => {
// TODO: Use the type on GNUStep (nesting level?)
let s = s.strip_prefix('b')?;
rm_int_prefix(s, b as usize)
}
Indirection(kind, t) => {
let s = s.strip_prefix(kind.prefix())?;
rm_enc_prefix(s, t, level.indirection(kind))
}
Array(len, item) => {
let mut s = s;
s = s.strip_prefix('[')?;
s = rm_int_prefix(s, len)?;
s = rm_enc_prefix(s, item, level.array())?;
s.strip_prefix(']')
}
Container(kind, name, fields) => {
let mut s = s;
s = s.strip_prefix(kind.start())?;
s = s.strip_prefix(name)?;
if let Some(level) = level.container() {
s = s.strip_prefix('=')?;
for field in fields {
s = rm_enc_prefix(s, field, level)?;
}
}
s.strip_prefix(kind.end())
}
}
}
fn chomp_int(s: &str) -> Option<(usize, &str)> {
// Chomp until we hit a non-digit
let (num, t) = match s.find(|c: char| !c.is_ascii_digit()) {
Some(i) => s.split_at(i),
None => (s, ""),
};
num.parse().map(|n| (n, t)).ok()
}
fn rm_int_prefix(s: &str, other: usize) -> Option<&str> {
chomp_int(s).and_then(|(n, t)| if other == n { Some(t) } else { None })
}

View file

@ -0,0 +1,208 @@
use crate::helper::{Helper, NestingLevel};
use super::Encoding;
pub(crate) const fn static_int_str_len(mut n: u128) -> usize {
let mut i = 0;
if n == 0 {
return 1;
}
while n > 0 {
n /= 10;
i += 1;
}
i
}
pub(crate) const fn static_int_str_array<const RES: usize>(mut n: u128) -> [u8; RES] {
let mut res: [u8; RES] = [0; RES];
let mut i = 0;
if n == 0 {
res[0] = b'0';
return res;
}
while n > 0 {
res[i] = b'0' + (n % 10) as u8;
n /= 10;
i += 1;
}
let mut rev: [u8; RES] = [0; RES];
let mut rev_i = 0;
while 0 < i {
i -= 1;
rev[rev_i] = res[i];
n /= 10;
rev_i += 1;
}
rev
}
pub(crate) const fn static_encoding_str_len(encoding: &Encoding, level: NestingLevel) -> usize {
use Helper::*;
match Helper::new(encoding) {
Primitive(primitive) => primitive.to_str().len(),
BitField(b, _type) => {
// TODO: Use the type on GNUStep (nesting level?)
1 + static_int_str_len(b as u128)
}
Indirection(kind, t) => 1 + static_encoding_str_len(t, level.indirection(kind)),
Array(len, item) => {
1 + static_int_str_len(len as u128) + static_encoding_str_len(item, level.array()) + 1
}
Container(_, name, items) => {
let mut res = 1 + name.len();
if let Some(level) = level.container() {
res += 1;
let mut i = 0;
while i < items.len() {
res += static_encoding_str_len(&items[i], level);
i += 1;
}
}
res + 1
}
}
}
pub(crate) const fn static_encoding_str_array<const LEN: usize>(
encoding: &Encoding,
level: NestingLevel,
) -> [u8; LEN] {
use Helper::*;
let mut res: [u8; LEN] = [0; LEN];
let mut res_i = 0;
match Helper::new(encoding) {
Primitive(primitive) => {
let s = primitive.to_str().as_bytes();
let mut i = 0;
while i < s.len() {
res[i] = s[i];
i += 1;
}
}
BitField(b, _type) => {
// TODO: Use the type on GNUStep (nesting 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) {
res[res_i] = arr[i];
res_i += 1;
i += 1;
}
}
Indirection(kind, t) => {
res[res_i] = kind.prefix_byte();
res_i += 1;
let mut i = 0;
// We use LEN even though it creates an oversized array
let arr = static_encoding_str_array::<LEN>(t, level.indirection(kind));
while i < static_encoding_str_len(t, level.indirection(kind)) {
res[res_i] = arr[i];
res_i += 1;
i += 1;
}
}
Array(len, item) => {
let mut res_i = 0;
res[res_i] = 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>(len as u128);
while i < static_int_str_len(len as u128) {
res[res_i] = arr[i];
res_i += 1;
i += 1;
}
let mut i = 0;
// We use LEN even though it creates an oversized array
let arr = static_encoding_str_array::<LEN>(item, level.array());
while i < static_encoding_str_len(item, level.array()) {
res[res_i] = arr[i];
res_i += 1;
i += 1;
}
res[res_i] = b']';
}
Container(kind, name, items) => {
let mut res_i = 0;
res[res_i] = kind.start_byte();
res_i += 1;
let mut name_i = 0;
let name = name.as_bytes();
while name_i < name.len() {
res[res_i] = name[name_i];
res_i += 1;
name_i += 1;
}
if let Some(level) = level.container() {
res[res_i] = b'=';
res_i += 1;
let mut items_i = 0;
while items_i < items.len() {
// We use LEN even though it creates an oversized array
let field_res = static_encoding_str_array::<LEN>(&items[items_i], level);
let mut item_res_i = 0;
while item_res_i < static_encoding_str_len(&items[items_i], level) {
res[res_i] = field_res[item_res_i];
res_i += 1;
item_res_i += 1;
}
items_i += 1;
}
}
res[res_i] = kind.end_byte();
}
};
res
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! const_int_str {
($n:expr) => {{
const X: [u8; static_int_str_len($n as u128)] = static_int_str_array($n as u128);
unsafe { core::str::from_utf8_unchecked(&X) }
}};
}
#[test]
fn test_const_int_str() {
const STR_0: &str = const_int_str!(0);
const STR_4: &str = const_int_str!(4);
const STR_42: &str = const_int_str!(42);
const STR_100: &str = const_int_str!(100);
const STR_999: &str = const_int_str!(999);
const STR_1236018655: &str = const_int_str!(1236018655);
assert_eq!(STR_0, "0");
assert_eq!(STR_4, "4");
assert_eq!(STR_42, "42");
assert_eq!(STR_100, "100");
assert_eq!(STR_999, "999");
assert_eq!(STR_1236018655, "1236018655");
}
// static encoding tests are in `encoding.rs`
}