Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"998a091c4482ecaf0d4d2f99cb61ea7edfcae91e30a9c65e419c974c44e97fe4","Cargo.lock":"c2d878335c2920d350d861cec5dd4ee60dab3d2a4481b1c6851d55c7412ec233","Cargo.toml":"cf9a8cc812710344d7fe618a5e615530baeeceae5c2826b9d5bb557dc419f159","README.md":"e980a74b88a62193f7908df3c27e7915f38559870f5e033ddb77fbed65e024a5","src/encoding.rs":"fef7c5d6b4911edf74e49d0c04ff4478211daa47d9a6b442a855907809344f9b","src/encoding_box.rs":"ebeef60bf38dc37ad37a7305978225e12c76274ebf7d399b1dc26aa54afd6366","src/helper.rs":"fb6b0ec5ced5922b144eefd92568e5ff65472af995fcd71457166ca305a9ccff","src/lib.rs":"6d526334bf38275d812335c1f18542adfb5c0a247a4b822342e6770a03c5491d","src/parse.rs":"2b1b995653f09e33b112dad74e97dfcb70c6e760e4c663e3c65c1d767009caaf","src/static_str.rs":"22cc1d7cb7901a05263115efc47b91ddd6f4022f583d5d353556bbbc372f1679"},"package":"ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"}

295
vendor/objc2-encode/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,295 @@
# 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
## 4.1.0 - 2025-01-22
### Added
* Added `Encoding::size`, which computes the size of the encoded type for the
current target.
### Changed
* Equivalence comparisons now consider `Encoding::Class`, `Encoding::Object`
and `Encoding::Block` as equivalent.
## 4.0.3 - 2024-05-21
### Fixed
* Fixed an issue with publishing using an older version of Cargo that didn't
handle the `lints.workspace = true` Cargo setup properly.
## 4.0.2 - 2024-05-21 (Yanked)
### Added
* Added `Encoding::None`, which represents encodings where the host compiler
(i.e. `clang`) couldn't generate an encoding for a given type.
This is useful when working with SIMD types.
## 4.0.1 - 2024-04-17
### Changed
* Build documentation on docs.rs on more Apple platforms.
## 4.0.0 - 2023-12-03
### Changed
* **BREAKING**: Changed the type of `EncodingBox::Struct` and
`EncodingBox::Union` to no longer contain an `Option`.
### Fixed
* Fixed encoding equivalence between empty structs and unions.
* Parse (and ignore) extended type information.
## 3.0.0 - 2023-07-31
### Fixed
* Bumped version number to ensure that this crate can be compiled together
with code that depends on pre-releases of `2.0.0`.
## 2.0.0 - 2023-06-20
### Added
* Improved documentation slightly.
## 2.0.0-pre.4 - 2023-02-07
### 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
### Added
* Added `EncodingBox` for dynamically parsing encodings and creating them on
the heap.
* Added `ParseError`, a custom type that represents errors during encoding
parsing.
* Added `Encoding::equivalent_to_box` for comparing `Encoding` and
`EncodingBox`.
* Implemented `Encode` and `RefEncode` for `NonNull<c_void>`.
* Added `OptionEncode` to help with implementing `Encode` and `RefEncode` for
`Option`.
### Changed
* **BREAKING**: Verify that the name in `Encoding::Struct` and
`Encoding::Union` is a valid C identifier.
### Removed
* **BREAKING**: `Encoding` no longer implements `Copy`, though it is still
`Clone`.
* **BREAKING**: Removed `Encoding::equivalent_to_start_of_str`, since it
wasn't really useful.
## 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 whose 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

7
vendor/objc2-encode/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "objc2-encode"
version = "4.1.0"

81
vendor/objc2-encode/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,81 @@
# 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"
rust-version = "1.71"
name = "objc2-encode"
version = "4.1.0"
authors = ["Mads Marquart <mads@marquart.dk>"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Objective-C type-encoding representation and parsing"
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"
[package.metadata.docs.rs]
default-target = "aarch64-apple-darwin"
targets = [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"aarch64-apple-ios",
"aarch64-apple-tvos",
"aarch64-apple-watchos",
"aarch64-apple-ios-macabi",
"aarch64-apple-visionos",
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
]
[package.metadata.release]
shared-version = false
tag-prefix = "objc-encode"
[features]
alloc = []
default = ["std"]
std = ["alloc"]
[lib]
name = "objc2_encode"
path = "src/lib.rs"
[lints.clippy]
ptr_as_ptr = "warn"
redundant_feature_names = "allow"
[lints.clippy.cargo]
level = "warn"
priority = -1
[lints.rust]
elided_lifetimes_in_paths = "warn"
missing_copy_implementations = "warn"
non_ascii_idents = "deny"
unreachable_pub = "warn"
unsafe_op_in_unsafe_fn = "deny"

16
vendor/objc2-encode/README.md vendored Normal file
View File

@@ -0,0 +1,16 @@
# `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)](https://github.com/madsmtm/objc2/blob/master/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 types to parse and compare Objective-C type-encodings
created with the `@encode` directive.
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.

726
vendor/objc2-encode/src/encoding.rs vendored Normal file
View File

@@ -0,0 +1,726 @@
use core::fmt;
use crate::helper::{compare_encodings, Helper, NestingLevel};
use crate::parse::Parser;
use crate::EncodingBox;
/// 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]"));
/// ```
// Not `Copy`, since this may one day be merged with `EncodingBox`
#[allow(missing_copy_implementations)]
#[derive(Clone, 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, see
/// [`Encoding::C_LONG`].
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.
///
/// See [`Encoding::C_ULONG`].
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 `"j" "f"` code.
FloatComplex,
/// A C `_Complex` or `double _Complex`. Corresponds to the `"j" "d"` code.
DoubleComplex,
/// A C `long double _Complex`. Corresponds to the `"j" "D"` code.
LongDoubleComplex,
/// 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.
///
/// Some compilers may choose to store the name of the class in instance
/// variables and properties as `"@" class_name`, see [Extended Type Info
/// in Objective-C][ext] (note that this does not include generics).
///
/// Such class names are currently ignored by `objc2-encode`.
///
/// [ext]: https://bou.io/ExtendedTypeInfoInObjC.html
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.
///
/// Corresponds to the `"b" size` code.
///
/// 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 correctly depending on the
/// target platform.
BitField(u8, Option<&'static (u64, 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 `"[" 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.
///
/// Note that the `=` may be omitted in some situations; this is
/// considered equal to the case where there are no fields.
Struct(&'static str, &'static [Encoding]),
/// A union with the given name and members.
///
/// The order of the members must match the order of the order in this.
///
/// Corresponds to the `"(" name "=" members... ")"` code.
///
/// Note that the `=` may be omitted in some situations; this is
/// considered equal to the case where there are no members.
Union(&'static str, &'static [Encoding]),
/// The type does not have an Objective-C encoding.
///
/// This is usually only used on types where Clang fails to generate the
/// Objective-C encoding, like SIMD types marked with
/// `__attribute__((__ext_vector_type__(1)))`.
None,
// TODO: "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`) on the current
/// target.
///
/// Ideally the encoding of `long` would 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 = {
// TODO once `core::ffi::c_long` is in MSRV
// `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`) on the current
/// target.
///
/// 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 mostly requires that the encodings are
/// equal, except for:
/// - Any leading qualifiers that the encoding may have.
/// - Structs or unions behind multiple pointers are considered
/// equivalent, since Objective-C compilers strip this information to
/// avoid unnecessary nesting.
/// - Structs or unions with no fields/members are considered to represent
/// "opqaue" types, and will therefore be equivalent to all other
/// structs / unions.
/// - [`Object`], [`Block`] and [`Class`] compare as equivalent.
///
/// The comparison may be changed in the future to e.g. ignore struct
/// names 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.
///
/// [`Object`]: Self::Object
/// [`Block`]: Self::Block
/// [`Class`]: Self::Class
pub fn equivalent_to(&self, other: &Self) -> bool {
compare_encodings(self, other, NestingLevel::new(), false)
}
/// 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 {
let mut parser = Parser::new(s);
parser.strip_leading_qualifiers();
if let Some(()) = parser.expect_encoding(self, NestingLevel::new()) {
// if the given encoding can be successfully removed from the
// start and an empty string remains, they were fully equivalent!
parser.is_empty()
} else {
false
}
}
/// Check if an encoding is equivalent to a boxed encoding.
///
/// See [`Encoding::equivalent_to`] for details about the meaning of
/// "equivalence".
pub fn equivalent_to_box(&self, other: &EncodingBox) -> bool {
compare_encodings(self, other, NestingLevel::new(), false)
}
/// Computes the theoretical size in bytes of the represented value type.
///
/// The size is only valid for the current target.
///
/// This does not currently consider alignment, i.e. everything is
/// considered packed, but that may change in the future.
pub fn size(&self) -> Option<usize> {
Helper::new(self).size(NestingLevel::new())
}
}
/// 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 existing
/// Objective-C compilers.
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Helper::new(self).fmt(f, NestingLevel::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
use alloc::boxed::Box;
use alloc::string::ToString;
use alloc::vec;
use core::str::FromStr;
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, "equal");
// Check Display
assert_eq!(E.to_string(), $string, "equal to string");
// Check encoding box
let boxed = EncodingBox::from_str($string).expect("parse");
assert_eq!(boxed.to_string(), $string, "parsed");
// Check equivalence comparisons
assert!(E.equivalent_to(&E), "equivalent self");
assert!(E.equivalent_to_str($string), "equivalent self string {}", $string);
assert!(E.equivalent_to_box(&boxed), "equivalent self boxed");
$(
assert!(E.equivalent_to(&$equivalent_encoding), "equivalent encoding {}", $equivalent_encoding);
assert!(E.equivalent_to_str(&$equivalent_encoding.to_string()), "equivalent encoding string");
let boxed = EncodingBox::from_str(&$equivalent_encoding.to_string()).expect("parse equivalent encoding");
assert!(E.equivalent_to_box(&boxed), "equivalent encoding boxed");
)*
$(
assert!(E.equivalent_to_str($equivalent_string), "equivalent string {}", $equivalent_string);
let boxed = EncodingBox::from_str($equivalent_string).expect("parse equivalent string");
assert!(E.equivalent_to_box(&boxed), "equivalent string boxed");
)*
// Negative checks
$(
assert_ne!(E, $not_encoding, "not equal");
assert!(!E.equivalent_to(&$not_encoding), "not equivalent encoding");
assert!(!E.equivalent_to_str(&$not_encoding.to_string()), "not equivalent encoding string");
let boxed = EncodingBox::from_str(&$not_encoding.to_string()).expect("parse not equivalent encoding");
assert!(!E.equivalent_to_box(&boxed), "not equivalent boxed");
)*
$(
assert!(!E.equivalent_to_str(&$not_string), "not equivalent 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, "static");
}
)+};
}
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;
~Encoding::Class;
~Encoding::Object;
!Encoding::Unknown;
"@?";
}
fn object() {
Encoding::Object;
~Encoding::Block;
~Encoding::Class;
!Encoding::Sel;
"@";
~"@\"AnyClassName\"";
~"@\"\""; // Empty class name
~"@?";
~"#";
!"@\"MyClassName";
!"@MyClassName\"";
}
fn unknown() {
Encoding::Unknown;
!Encoding::Block;
"?";
}
fn double() {
Encoding::Double;
"d";
}
fn bitfield() {
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(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-4";
}
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::Struct("SomeStruct", &[]);
!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=}";
!"{SomeStruct}";
!"{SomeStruct=ic}";
!"{SomeStruct=malformed";
}
fn pointer_struct() {
Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
~Encoding::Pointer(&Encoding::Struct("SomeStruct", &[]));
!Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
!Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
"^{SomeStruct=ci}";
~"^{SomeStruct=}";
!"^{SomeStruct}";
!"^{SomeStruct=ic}";
!"^{SomeStruct=malformed";
}
fn pointer_pointer_struct() {
Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int])));
~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[])));
~Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char])));
!Encoding::Pointer(&Encoding::Pointer(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int])));
"^^{SomeStruct}";
!"^^{SomeStruct=}";
!"^^{SomeStruct=ci}";
!"^^{SomeStruct=ic}";
!"^^{AnotherName=ic}";
!"^^{SomeStruct=malformed";
}
fn atomic_struct() {
Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Char, Encoding::Int]));
~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[]));
~Encoding::Atomic(&Encoding::Struct("SomeStruct", &[Encoding::Int, Encoding::Char]));
!Encoding::Atomic(&Encoding::Struct("AnotherName", &[Encoding::Char, Encoding::Int]));
"A{SomeStruct}";
!"A{SomeStruct=}";
!"A{SomeStruct=ci}";
!"A{SomeStruct=ic}";
!"A{SomeStruct=malformed";
}
fn empty_struct() {
Encoding::Struct("SomeStruct", &[]);
"{SomeStruct=}";
~"{SomeStruct=ci}";
!"{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, None))),
Encoding::Char,
Encoding::Unknown,
]
);
"{abc=^[8B](def=@?)^^b255c?}";
~"{abc=}";
!"{abc}";
}
fn identifier() {
Encoding::Struct("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", &[]);
"{_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789=}";
}
// Regression test. The encoding of the `CGLContextObj` object changed
// between versions of macOS. As such, this is something that we must
// be prepared to handle.
fn cgl_context_obj() {
Encoding::Pointer(&Encoding::Struct("_CGLContextObject", &[]));
"^{_CGLContextObject=}";
~"^{_CGLContextObject=^{__GLIContextRec}{__GLIFunctionDispatchRec=^?^?^?^?^?}^{_CGLPrivateObject}^v}";
!"^{_CGLContextObject}";
!"^{SomeOtherStruct=}";
}
fn none() {
Encoding::None;
"";
!"?";
}
fn none_in_array() {
Encoding::Array(42, &Encoding::None);
!Encoding::Array(42, &Encoding::Unknown);
"[42]";
!"[42i]";
}
fn none_in_pointer() {
Encoding::Pointer(&Encoding::None);
!Encoding::Pointer(&Encoding::Unknown);
"^";
!"";
!"^i";
}
fn none_in_pointer_in_array() {
Encoding::Array(42, &Encoding::Pointer(&Encoding::None));
"[42^]";
}
fn class() {
Encoding::Class;
~Encoding::Object;
~Encoding::Block;
!Encoding::Sel;
"#";
~"@?";
~"@";
!"a";
}
}
#[test]
#[should_panic = "Struct name was not a valid identifier"]
fn struct_empty() {
let _ = Encoding::Struct("", &[]).to_string();
}
#[test]
#[should_panic = "Struct name was not a valid identifier"]
fn struct_unicode() {
let _ = Encoding::Struct("", &[Encoding::Char]).to_string();
}
#[test]
#[should_panic = "Union name was not a valid identifier"]
fn union_invalid_identifier() {
let _ = Encoding::Union("a-b", &[Encoding::Char]).equivalent_to_str("(☃=c)");
}
// Note: A raw `?` cannot happen in practice, since functions can only
// be accessed through pointers, and that will yield `^?`
#[test]
fn object_unknown_in_struct() {
let enc = Encoding::Struct("S", &[Encoding::Block, Encoding::Object, Encoding::Unknown]);
let s = "{S=@?@?}";
assert_eq!(&enc.to_string(), s);
let parsed = EncodingBox::from_str(s).unwrap();
let expected = EncodingBox::Struct(
"S".to_string(),
vec![EncodingBox::Block, EncodingBox::Block],
);
assert_eq!(parsed, expected);
assert!(!enc.equivalent_to_box(&expected));
}
// Similar to `?`, `` cannot be accurately represented inside pointers
// inside structs, and may be parsed incorrectly.
#[test]
fn none_in_struct() {
let enc = Encoding::Struct("?", &[Encoding::Pointer(&Encoding::None), Encoding::Int]);
let s = "{?=^i}";
assert_eq!(&enc.to_string(), s);
let parsed = EncodingBox::from_str(s).unwrap();
let expected = EncodingBox::Struct(
"?".to_string(),
vec![EncodingBox::Pointer(Box::new(EncodingBox::Int))],
);
assert_eq!(parsed, expected);
assert!(!enc.equivalent_to_box(&expected));
}
}

265
vendor/objc2-encode/src/encoding_box.rs vendored Normal file
View File

@@ -0,0 +1,265 @@
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use core::str::FromStr;
use crate::helper::{compare_encodings, Helper, NestingLevel};
use crate::parse::{ErrorKind, ParseError, Parser};
use crate::Encoding;
/// The boxed version of [`Encoding`].
///
/// This has exactly the same items as `Encoding`, the only difference is in
/// where the contents of the more complex encodings like [`Struct`] are
/// stored.
///
/// In `Encoding`, the data is stored in static memory, while in `EncodingBox`
/// it is stored on the heap. The former allows storing in constants (which is
/// required by the `objc2::encode::Encode` and `objc2::encode::RefEncode`
/// traits), while the latter allows dynamic creation, such as in the case of
/// parsing encodings.
///
/// **This should be considered a _temporary_ restriction**. `Encoding` and
/// `EncodingBox` will become equivalent once heap allocation in constants
/// is possible.
///
/// [`Struct`]: Self::Struct
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive] // Maybe we're missing some encodings?
pub enum EncodingBox {
/// Same as [`Encoding::Char`].
Char,
/// Same as [`Encoding::Short`].
Short,
/// Same as [`Encoding::Int`].
Int,
/// Same as [`Encoding::Long`].
Long,
/// Same as [`Encoding::LongLong`].
LongLong,
/// Same as [`Encoding::UChar`].
UChar,
/// Same as [`Encoding::UShort`].
UShort,
/// Same as [`Encoding::UInt`].
UInt,
/// Same as [`Encoding::ULong`].
ULong,
/// Same as [`Encoding::ULongLong`].
ULongLong,
/// Same as [`Encoding::Float`].
Float,
/// Same as [`Encoding::Double`].
Double,
/// Same as [`Encoding::LongDouble`].
LongDouble,
/// Same as [`Encoding::FloatComplex`].
FloatComplex,
/// Same as [`Encoding::DoubleComplex`].
DoubleComplex,
/// Same as [`Encoding::LongDoubleComplex`].
LongDoubleComplex,
/// Same as [`Encoding::Bool`].
Bool,
/// Same as [`Encoding::Void`].
Void,
/// Same as [`Encoding::String`].
String,
/// Same as [`Encoding::Object`].
Object,
/// Same as [`Encoding::Block`].
Block,
/// Same as [`Encoding::Class`].
Class,
/// Same as [`Encoding::Sel`].
Sel,
/// Same as [`Encoding::Unknown`].
Unknown,
/// Same as [`Encoding::BitField`].
BitField(u8, Option<Box<(u64, Self)>>),
/// Same as [`Encoding::Pointer`].
Pointer(Box<Self>),
/// Same as [`Encoding::Atomic`].
Atomic(Box<Self>),
/// Same as [`Encoding::Array`].
Array(u64, Box<Self>),
/// Same as [`Encoding::Struct`].
Struct(String, Vec<Self>),
/// Same as [`Encoding::Union`].
Union(String, Vec<Self>),
/// Same as [`Encoding::None`].
None,
}
impl EncodingBox {
/// Same as [`Encoding::C_LONG`].
pub const C_LONG: Self = match Encoding::C_LONG {
Encoding::Long => Self::Long,
Encoding::LongLong => Self::LongLong,
_ => unreachable!(),
};
/// Same as [`Encoding::C_ULONG`].
pub const C_ULONG: Self = match Encoding::C_ULONG {
Encoding::ULong => Self::ULong,
Encoding::ULongLong => Self::ULongLong,
_ => unreachable!(),
};
/// Parse and consume an encoding from the start of a string.
///
/// This is can be used to parse concatenated encodings, such as those
/// returned by `method_getTypeEncoding`.
///
/// [`from_str`][Self::from_str] is simpler, use that instead if you can.
///
///
/// # Errors
///
/// Returns an error if the string was an ill-formatted encoding string.
pub fn from_start_of_str(s: &mut &str) -> Result<Self, ParseError> {
let mut parser = Parser::new(s);
parser.strip_leading_qualifiers();
match parser.parse_encoding_or_none() {
Err(ErrorKind::Unknown(b'0'..=b'9')) => {
let remaining = parser.remaining();
*s = remaining;
Ok(EncodingBox::None)
}
Err(err) => Err(ParseError::new(parser, err)),
Ok(encoding) => {
let remaining = parser.remaining();
*s = remaining;
Ok(encoding)
}
}
}
}
/// Same formatting as [`Encoding`]'s `Display` implementation.
impl fmt::Display for EncodingBox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Helper::from_box(self).fmt(f, NestingLevel::new())
}
}
impl PartialEq<Encoding> for EncodingBox {
fn eq(&self, other: &Encoding) -> bool {
compare_encodings(self, other, NestingLevel::new(), true)
}
}
impl PartialEq<EncodingBox> for Encoding {
fn eq(&self, other: &EncodingBox) -> bool {
other.eq(self)
}
}
impl FromStr for EncodingBox {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parser = Parser::new(s);
parser.strip_leading_qualifiers();
parser
.parse_encoding_or_none()
.and_then(|enc| parser.expect_empty().map(|()| enc))
.map_err(|err| ParseError::new(parser, err))
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
#[test]
fn eq_encodings() {
let enc1 = Encoding::Char;
let enc2 = EncodingBox::Char;
let enc3 = EncodingBox::String;
assert_eq!(enc1, enc2);
assert_ne!(enc1, enc3);
}
#[test]
fn eq_complex_encodings() {
let enc1 = Encoding::Atomic(&Encoding::Struct(
"test",
&[Encoding::Array(2, &Encoding::Int)],
));
let enc2 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
"test".to_string(),
vec![EncodingBox::Array(2, Box::new(EncodingBox::Int))],
)));
let enc3 = EncodingBox::Atomic(Box::new(EncodingBox::Struct(
"test".to_string(),
vec![EncodingBox::Array(2, Box::new(EncodingBox::Char))],
)));
assert_eq!(enc1, enc2);
assert_ne!(enc1, enc3);
}
#[test]
fn struct_nested_in_pointer() {
let enc1 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Char]);
let enc2 = EncodingBox::Struct("test".to_string(), vec![EncodingBox::Int]);
const ENC3A: Encoding = Encoding::Struct("test", &[Encoding::Char]);
assert_ne!(enc1, enc2);
assert!(ENC3A.equivalent_to_box(&enc1));
assert!(!ENC3A.equivalent_to_box(&enc2));
let enc1 = EncodingBox::Pointer(Box::new(enc1));
let enc2 = EncodingBox::Pointer(Box::new(enc2));
const ENC3B: Encoding = Encoding::Pointer(&ENC3A);
assert_ne!(enc1, enc2);
assert!(ENC3B.equivalent_to_box(&enc1));
assert!(!ENC3B.equivalent_to_box(&enc2));
let enc1 = EncodingBox::Pointer(Box::new(enc1));
let enc2 = EncodingBox::Pointer(Box::new(enc2));
const ENC3C: Encoding = Encoding::Pointer(&ENC3B);
assert_ne!(enc1, enc2);
assert!(ENC3C.equivalent_to_box(&enc1));
assert!(ENC3C.equivalent_to_box(&enc2), "now they're equivalent");
}
#[test]
fn parse_atomic_struct() {
let expected = EncodingBox::Atomic(Box::new(EncodingBox::Atomic(Box::new(
EncodingBox::Struct("a".into(), vec![]),
))));
let actual = EncodingBox::from_str("AA{a=}").unwrap();
assert_eq!(expected, actual);
assert_eq!(expected.to_string(), "AA{a}");
let actual = EncodingBox::from_str("AA{a}").unwrap();
assert_eq!(expected, actual);
assert_eq!(expected.to_string(), "AA{a}");
}
#[test]
fn parse_part_of_string() {
let mut s = "{a}cb0i16";
let expected = EncodingBox::Struct("a".into(), vec![]);
let actual = EncodingBox::from_start_of_str(&mut s).unwrap();
assert_eq!(expected, actual);
let expected = EncodingBox::Char;
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, "");
}
}

499
vendor/objc2-encode/src/helper.rs vendored Normal file
View File

@@ -0,0 +1,499 @@
use core::ffi;
use core::fmt;
use core::mem;
use core::slice;
use core::write;
use crate::parse::verify_name;
use crate::Encoding;
use crate::EncodingBox;
#[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 {
// This is a bit irrelevant, since bitfields can only contain integral
// types
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_include_fields(self) -> Option<Self> {
match self {
Self::Top | Self::Within => {
// Move top one step down
Some(Self::Within)
}
Self::Bottom => None,
}
}
}
pub(crate) fn compare_encodings<E1: EncodingType, E2: EncodingType>(
enc1: &E1,
enc2: &E2,
level: NestingLevel,
include_all: bool,
) -> bool {
use Helper::*;
// Note: Ideally `Block` and sequence of `Object, Unknown` in struct
// should compare equivalent, but we don't bother since in practice a
// plain `Unknown` will never appear.
let level = if include_all {
NestingLevel::new()
} else {
level
};
match (enc1.helper(), enc2.helper()) {
(Primitive(p1), Primitive(p2)) => p1.equivalents().contains(&p2),
(BitField(size1, Some((offset1, type1))), BitField(size2, Some((offset2, type2)))) => {
size1 == size2
&& offset1 == offset2
&& compare_encodings(type1, type2, level.bitfield(), include_all)
}
(BitField(size1, None), BitField(size2, None)) => 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), Indirection(kind2, t2)) => {
kind1 == kind2 && compare_encodings(t1, t2, level.indirection(kind1), include_all)
}
(Array(len1, item1), Array(len2, item2)) => {
len1 == len2 && compare_encodings(item1, item2, level.array(), include_all)
}
(Container(kind1, name1, items1), Container(kind2, name2, items2)) => {
kind1 == kind2 && name1 == name2 && {
if let Some(level) = level.container_include_fields() {
// If either container is empty, then they are equivalent
if items1.is_empty() || items2.is_empty() {
return true;
}
if items1.len() != items2.len() {
return false;
}
for (item1, item2) in items1.iter().zip(items2.iter()) {
if !compare_encodings(item1, item2, level, include_all) {
return false;
}
}
true
} else {
true
}
}
}
(NoneInvalid, NoneInvalid) => true,
(_, _) => false,
}
}
#[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 => "?",
}
}
/// Classes, blocks and objects can all be used in places where `id` is
/// expected (i.e. where the encoding is for objects).
///
/// So to support those use-cases, we compare them as equivalent.
pub(crate) fn equivalents(&self) -> &[Self] {
match self {
Self::Block | Self::Object | Self::Class => &[Self::Block, Self::Object, Self::Class],
_ => slice::from_ref(self),
}
}
pub(crate) const fn size(self) -> Option<usize> {
match self {
// Under all the considered targets, `_Bool` is sized and aligned
// to a single byte. See:
// https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+BoolWidth&type=code
// Obj-C's `BOOL` is `signed char`, i.e. `c`, so will fall in the
// below case. See: https://developer.apple.com/documentation/objectivec/bool?language=objc
Self::Bool => Some(1),
// Numbers.
Self::Char => Some(mem::size_of::<ffi::c_char>()),
Self::UChar => Some(mem::size_of::<ffi::c_uchar>()),
Self::Short => Some(mem::size_of::<ffi::c_short>()),
Self::UShort => Some(mem::size_of::<ffi::c_ushort>()),
Self::Int => Some(mem::size_of::<ffi::c_int>()),
Self::UInt => Some(mem::size_of::<ffi::c_uint>()),
Self::Long => Some(mem::size_of::<ffi::c_long>()),
Self::ULong => Some(mem::size_of::<ffi::c_ulong>()),
Self::LongLong => Some(mem::size_of::<ffi::c_longlong>()),
Self::ULongLong => Some(mem::size_of::<ffi::c_ulonglong>()),
Self::Float => Some(mem::size_of::<ffi::c_float>()),
Self::Double => Some(mem::size_of::<ffi::c_double>()),
// https://github.com/search?q=repo%3Allvm%2Fllvm-project+path%3Aclang%2Flib%2FBasic%2FTargets+LongDoubleWidth&type=code
#[cfg(any(
target_arch = "x86_64",
all(target_arch = "x86", target_vendor = "apple"),
all(target_arch = "aarch64", not(target_vendor = "apple")),
))]
Self::LongDouble => Some(16),
#[cfg(all(target_arch = "x86", not(target_vendor = "apple")))]
Self::LongDouble => Some(12),
#[cfg(any(
target_arch = "arm",
all(target_arch = "aarch64", target_vendor = "apple"),
))]
Self::LongDouble => Some(8),
Self::FloatComplex => Some(mem::size_of::<ffi::c_float>() * 2),
Self::DoubleComplex => Some(mem::size_of::<ffi::c_double>() * 2),
Self::LongDoubleComplex => match Self::LongDouble.size() {
Some(size) => Some(size * 2),
None => None,
},
// Pointers.
Self::String | Self::Object | Self::Block | Self::Class | Self::Sel => {
Some(mem::size_of::<*const ()>())
}
// Nothing.
Self::Void | Self::Unknown => None,
}
}
}
#[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')',
}
}
}
impl fmt::Display for ContainerKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Struct => write!(f, "struct"),
Self::Union => write!(f, "union"),
}
}
}
pub(crate) trait EncodingType: Sized + fmt::Debug {
fn helper(&self) -> Helper<'_, Self>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub(crate) enum Helper<'a, E = Encoding> {
Primitive(Primitive),
BitField(u8, Option<&'a (u64, E)>),
Indirection(IndirectionKind, &'a E),
Array(u64, &'a E),
Container(ContainerKind, &'a str, &'a [E]),
NoneInvalid,
}
impl<E: EncodingType> Helper<'_, E> {
pub(crate) fn fmt(&self, f: &mut fmt::Formatter<'_>, level: NestingLevel) -> fmt::Result {
match self {
Self::Primitive(primitive) => {
write!(f, "{}", primitive.to_str())?;
}
Self::BitField(size, None) => {
write!(f, "b{size}")?;
}
Self::BitField(size, Some((offset, t))) => {
write!(f, "b{offset}")?;
t.helper().fmt(f, level.bitfield())?;
write!(f, "{size}")?;
}
Self::Indirection(kind, t) => {
write!(f, "{}", kind.prefix())?;
t.helper().fmt(f, level.indirection(*kind))?;
}
Self::Array(len, item) => {
write!(f, "[")?;
write!(f, "{len}")?;
item.helper().fmt(f, level.array())?;
write!(f, "]")?;
}
Self::Container(kind, name, items) => {
write!(f, "{}", kind.start())?;
write!(f, "{name}")?;
if let Some(level) = level.container_include_fields() {
write!(f, "=")?;
for item in *items {
item.helper().fmt(f, level)?;
}
}
write!(f, "{}", kind.end())?;
}
Self::NoneInvalid => {}
}
Ok(())
}
pub(crate) fn size(&self, level: NestingLevel) -> Option<usize> {
// TODO: alignment?
match self {
Self::NoneInvalid => None,
Self::Primitive(prim) => prim.size(),
Self::BitField(size_bits, off_typ) => Some(
(usize::from(*size_bits).next_power_of_two().max(8) / 8).max(
off_typ
.and_then(|(_, typ)| typ.helper().size(level.bitfield()))
.unwrap_or_default(),
),
),
Self::Indirection(kind, typ) => match kind {
IndirectionKind::Pointer => Some(mem::size_of::<*const ()>()),
IndirectionKind::Atomic => typ.helper().size(level.indirection(*kind)),
},
Self::Array(len, typ) => typ
.helper()
.size(level.array())
.map(|typ_size| *len as usize * typ_size),
Self::Container(kind, _, fields) => {
level
.container_include_fields()
.and_then(|level| match kind {
ContainerKind::Struct => {
fields.iter().map(|field| field.helper().size(level)).sum()
}
ContainerKind::Union => fields
.iter()
.map(|field| field.helper().size(level))
.max()
.flatten(),
})
}
}
}
}
impl Helper<'_> {
pub(crate) const fn new(encoding: &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) => {
if !verify_name(name) {
panic!("Struct name was not a valid identifier");
}
Self::Container(ContainerKind::Struct, name, fields)
}
Union(name, members) => {
if !verify_name(name) {
panic!("Union name was not a valid identifier");
}
Self::Container(ContainerKind::Union, name, members)
}
None => Self::NoneInvalid,
}
}
}
impl<'a> Helper<'a, EncodingBox> {
pub(crate) fn from_box(encoding: &'a EncodingBox) -> Self {
use EncodingBox::*;
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.as_deref()),
Pointer(t) => Self::Indirection(IndirectionKind::Pointer, t),
Atomic(t) => Self::Indirection(IndirectionKind::Atomic, t),
Array(len, item) => Self::Array(*len, item),
Struct(name, fields) => {
if !verify_name(name) {
panic!("Struct name was not a valid identifier");
}
Self::Container(ContainerKind::Struct, name, fields)
}
Union(name, members) => {
if !verify_name(name) {
panic!("Union name was not a valid identifier");
}
Self::Container(ContainerKind::Union, name, members)
}
None => Self::NoneInvalid,
}
}
}
impl EncodingType for Encoding {
fn helper(&self) -> Helper<'_, Self> {
Helper::new(self)
}
}
impl EncodingType for EncodingBox {
fn helper(&self) -> Helper<'_, Self> {
Helper::from_box(self)
}
}

64
vendor/objc2-encode/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
//! # Objective-C type-encoding
//!
//! 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 describe and compare these
//! type-encodings, and the [`EncodingBox`] type which does the same, except
//! it can be parsed from an encoding at runtime.
//!
//! The types from this crate is exported under the [`objc2`] crate as
//! `objc2::encode`, so usually you would use it from there.
//!
//! [`objc2`]: https://crates.io/crates/objc2
//!
//!
//! ## Example
//!
//! Parse an encoding from a string and compare it to a known encoding.
//!
//! ```rust
//! use objc2_encode::{Encoding, EncodingBox};
//! let s = "{s=i}";
//! let enc = Encoding::Struct("s", &[Encoding::Int]);
//! let parsed: EncodingBox = s.parse()?;
//! assert!(enc.equivalent_to_box(&parsed));
//! assert_eq!(enc.to_string(), s);
//! # Ok::<(), objc2_encode::ParseError>(())
//! ```
//!
//!
//! ## 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(missing_docs)]
#![warn(missing_debug_implementations)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
// Update in Cargo.toml as well.
#![doc(html_root_url = "https://docs.rs/objc2-encode/4.1.0")]
#[cfg(not(feature = "alloc"))]
compile_error!("the `alloc` feature currently must be enabled");
extern crate alloc;
#[cfg(any(feature = "std", doc))]
extern crate std;
mod encoding;
mod encoding_box;
mod helper;
mod parse;
// Will be used at some point when generic constants are available
#[allow(dead_code)]
mod static_str;
pub use self::encoding::Encoding;
pub use self::encoding_box::EncodingBox;
pub use self::parse::ParseError;

637
vendor/objc2-encode/src/parse.rs vendored Normal file
View File

@@ -0,0 +1,637 @@
//! Parsing encodings from their string representation.
#![deny(unsafe_code)]
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt;
use crate::helper::{ContainerKind, EncodingType, Helper, NestingLevel, Primitive};
use crate::{Encoding, EncodingBox};
/// Check whether a struct or union name is a valid identifier
pub(crate) const fn verify_name(name: &str) -> bool {
let bytes = name.as_bytes();
if let b"?" = bytes {
return true;
}
if bytes.is_empty() {
return false;
}
let mut i = 0;
while i < bytes.len() {
let byte = bytes[i];
if !(byte.is_ascii_alphanumeric() || byte == b'_') {
return false;
}
i += 1;
}
true
}
/// The error that was encountered while parsing an encoding string.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ParseError {
kind: ErrorKind,
data: String,
split_point: usize,
}
impl ParseError {
pub(crate) fn new(parser: Parser<'_>, kind: ErrorKind) -> Self {
Self {
kind,
data: parser.data.to_string(),
split_point: parser.split_point,
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"failed parsing encoding: {} at byte-index {} in {:?}",
self.kind, self.split_point, self.data,
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {}
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) enum ErrorKind {
UnexpectedEnd,
Unknown(u8),
UnknownAfterComplex(u8),
ExpectedInteger,
IntegerTooLarge,
WrongEndArray,
WrongEndContainer(ContainerKind),
InvalidIdentifier(ContainerKind),
NotAllConsumed,
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedEnd => write!(f, "unexpected end"),
Self::Unknown(b) => {
write!(f, "unknown encoding character {}", *b as char)
}
Self::UnknownAfterComplex(b) => {
write!(f, "unknown encoding character {} after complex", *b as char,)
}
Self::ExpectedInteger => write!(f, "expected integer"),
Self::IntegerTooLarge => write!(f, "integer too large"),
Self::WrongEndArray => write!(f, "expected array to be closed"),
Self::WrongEndContainer(kind) => {
write!(f, "expected {kind} to be closed")
}
Self::InvalidIdentifier(kind) => {
write!(f, "got invalid identifier in {kind}")
}
Self::NotAllConsumed => {
write!(f, "remaining contents after parsing")
}
}
}
}
type Result<T, E = ErrorKind> = core::result::Result<T, E>;
enum ParseInner {
Empty,
Encoding(EncodingBox),
ContainerEnd(ContainerKind),
ArrayEnd,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub(crate) struct Parser<'a> {
data: &'a str,
// Always "behind"/"at" the current character
split_point: usize,
}
impl<'a> Parser<'a> {
pub(crate) fn new(data: &'a str) -> Self {
Self {
split_point: 0,
data,
}
}
pub(crate) fn remaining(&self) -> &'a str {
&self.data[self.split_point..]
}
fn peek(&self) -> Result<u8> {
self.try_peek().ok_or(ErrorKind::UnexpectedEnd)
}
fn try_peek(&self) -> Option<u8> {
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;
}
fn rollback(&mut self) {
self.split_point -= 1;
}
fn consume_while(&mut self, mut condition: impl FnMut(u8) -> bool) {
while let Some(b) = self.try_peek() {
if condition(b) {
self.advance();
} else {
break;
}
}
}
pub(crate) fn is_empty(&self) -> bool {
self.try_peek().is_none()
}
pub(crate) fn expect_empty(&self) -> Result<()> {
if self.is_empty() {
Ok(())
} else {
Err(ErrorKind::NotAllConsumed)
}
}
}
impl Parser<'_> {
/// Strip leading qualifiers, if any.
pub(crate) fn strip_leading_qualifiers(&mut self) {
// TODO: Add API for accessing and outputting qualifiers.
#[allow(clippy::byte_char_slices)]
const QUALIFIERS: &[u8] = &[
b'r', // const
b'n', // in
b'N', // inout
b'o', // out
b'O', // bycopy
b'R', // byref
b'V', // oneway
];
// TODO: b'|', // GCINVISIBLE
self.consume_while(|b| QUALIFIERS.contains(&b));
}
/// Chomp until we hit a non-digit.
///
/// + and - prefixes are not supported.
fn chomp_digits(&mut self) -> Result<&str> {
let old_split_point = self.split_point;
// Parse first digit (which must be present).
if !self.peek()?.is_ascii_digit() {
return Err(ErrorKind::ExpectedInteger);
}
// Parse the rest, stopping if we hit a non-digit.
self.consume_while(|b| b.is_ascii_digit());
Ok(&self.data[old_split_point..self.split_point])
}
fn parse_u64(&mut self) -> Result<u64> {
self.chomp_digits()?
.parse()
.map_err(|_| ErrorKind::IntegerTooLarge)
}
fn parse_u8(&mut self) -> Result<u8> {
self.chomp_digits()?
.parse()
.map_err(|_| ErrorKind::IntegerTooLarge)
}
}
/// Check if the data matches an expected value.
///
/// The errors here aren't currently used, so they're hackily set up.
impl Parser<'_> {
fn expect_byte(&mut self, byte: u8) -> Option<()> {
if self.try_peek()? == byte {
self.advance();
Some(())
} else {
None
}
}
fn expect_one_of_str<'a>(&mut self, strings: impl IntoIterator<Item = &'a str>) -> Option<()> {
for s in strings {
if self.remaining().starts_with(s) {
for b in s.as_bytes() {
self.expect_byte(*b).unwrap();
}
return Some(());
}
}
None
}
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
}
}
pub(crate) fn expect_encoding(&mut self, enc: &Encoding, level: NestingLevel) -> Option<()> {
match enc.helper() {
Helper::Primitive(primitive) => {
self.expect_one_of_str(primitive.equivalents().iter().map(|p| p.to_str()))?;
if primitive == Primitive::Object && self.try_peek() == Some(b'"') {
self.advance();
self.consume_while(|b| b != b'"');
self.expect_byte(b'"')?;
}
Some(())
}
Helper::BitField(size, Some((offset, t))) => {
self.expect_byte(b'b')?;
self.expect_u64(*offset)?;
self.expect_encoding(t, level.bitfield())?;
self.expect_u8(size)
}
Helper::BitField(size, None) => {
self.expect_byte(b'b')?;
self.expect_u8(size)
}
Helper::Indirection(kind, t) => {
self.expect_byte(kind.prefix_byte())?;
self.expect_encoding(t, level.indirection(kind))
}
Helper::Array(len, item) => {
self.expect_byte(b'[')?;
self.expect_u64(len)?;
self.expect_encoding(item, level.array())?;
self.expect_byte(b']')
}
Helper::Container(kind, name, items) => {
self.expect_byte(kind.start_byte())?;
self.expect_one_of_str([name])?;
if let Some(level) = level.container_include_fields() {
self.expect_byte(b'=')?;
// Parse as equal if the container is empty
if items.is_empty() {
loop {
match self.parse_inner().ok()? {
ParseInner::Empty => {
// Require the container to have an end
return None;
}
ParseInner::Encoding(_) => {}
ParseInner::ContainerEnd(parsed_kind) => {
if parsed_kind == kind {
return Some(());
} else {
return None;
}
}
ParseInner::ArrayEnd => {
return None;
}
}
}
}
// Parse as equal if the string's container is empty
if self.try_peek() == Some(kind.end_byte()) {
self.advance();
return Some(());
}
for item in items {
self.expect_encoding(item, level)?;
}
}
self.expect_byte(kind.end_byte())
}
Helper::NoneInvalid => Some(()),
}
}
}
impl Parser<'_> {
fn parse_container(&mut self, kind: ContainerKind) -> Result<(&str, Vec<EncodingBox>)> {
let old_split_point = self.split_point;
// Parse name until hits `=` or `}`/`)`
let has_items = loop {
let b = self.try_peek().ok_or(ErrorKind::WrongEndContainer(kind))?;
if b == b'=' {
break true;
} else if b == kind.end_byte() {
break false;
}
self.advance();
};
let s = &self.data[old_split_point..self.split_point];
if !verify_name(s) {
return Err(ErrorKind::InvalidIdentifier(kind));
}
if has_items {
self.advance();
}
let mut items = Vec::new();
// Parse items until hits end
loop {
match self.parse_inner()? {
ParseInner::Empty => {
return Err(ErrorKind::WrongEndContainer(kind));
}
ParseInner::Encoding(enc) => {
items.push(enc);
}
ParseInner::ContainerEnd(parsed_kind) => {
if parsed_kind == kind {
return Ok((s, items));
} else {
return Err(ErrorKind::Unknown(parsed_kind.end_byte()));
}
}
ParseInner::ArrayEnd => {
return Err(ErrorKind::Unknown(b']'));
}
}
}
}
pub(crate) fn parse_encoding_or_none(&mut self) -> Result<EncodingBox> {
match self.parse_inner()? {
ParseInner::Empty => Ok(EncodingBox::None),
ParseInner::Encoding(enc) => Ok(enc),
ParseInner::ContainerEnd(kind) => Err(ErrorKind::Unknown(kind.end_byte())),
ParseInner::ArrayEnd => Err(ErrorKind::Unknown(b']')),
}
}
fn parse_inner(&mut self) -> Result<ParseInner> {
if self.is_empty() {
return Ok(ParseInner::Empty);
}
let b = self.peek()?;
self.advance();
Ok(ParseInner::Encoding(match b {
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'f' => EncodingBox::Float,
b'd' => EncodingBox::Double,
b'D' => EncodingBox::LongDouble,
b'j' => {
let res = match self.peek()? {
b'f' => EncodingBox::FloatComplex,
b'd' => EncodingBox::DoubleComplex,
b'D' => EncodingBox::LongDoubleComplex,
b => return Err(ErrorKind::UnknownAfterComplex(b)),
};
self.advance();
res
}
b'B' => EncodingBox::Bool,
b'v' => EncodingBox::Void,
b'*' => EncodingBox::String,
b'@' => match self.try_peek() {
// Special handling for blocks
Some(b'?') => {
self.advance();
EncodingBox::Block
}
// Parse class name if present
Some(b'"') => {
self.advance();
self.consume_while(|b| b != b'"');
self.expect_byte(b'"').ok_or(ErrorKind::UnexpectedEnd)?;
EncodingBox::Object
}
_ => EncodingBox::Object,
},
b'#' => EncodingBox::Class,
b':' => EncodingBox::Sel,
b'?' => EncodingBox::Unknown,
b'b' => {
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(match self.parse_inner()? {
ParseInner::Empty => EncodingBox::None,
ParseInner::Encoding(enc) => enc,
ParseInner::ContainerEnd(_) | ParseInner::ArrayEnd => {
self.rollback();
EncodingBox::None
}
})),
b'A' => EncodingBox::Atomic(Box::new(match self.parse_inner()? {
ParseInner::Empty => EncodingBox::None,
ParseInner::Encoding(enc) => enc,
ParseInner::ContainerEnd(_) | ParseInner::ArrayEnd => {
self.rollback();
EncodingBox::None
}
})),
b'[' => {
let len = self.parse_u64()?;
match self.parse_inner()? {
ParseInner::Empty => {
return Err(ErrorKind::WrongEndArray);
}
ParseInner::Encoding(item) => {
self.expect_byte(b']').ok_or(ErrorKind::WrongEndArray)?;
EncodingBox::Array(len, Box::new(item))
}
ParseInner::ArrayEnd => EncodingBox::Array(len, Box::new(EncodingBox::None)),
ParseInner::ContainerEnd(kind) => {
return Err(ErrorKind::Unknown(kind.end_byte()))
}
}
}
b']' => {
return Ok(ParseInner::ArrayEnd);
}
b'{' => {
let kind = ContainerKind::Struct;
let (name, items) = self.parse_container(kind)?;
EncodingBox::Struct(name.to_string(), items)
}
b'}' => {
return Ok(ParseInner::ContainerEnd(ContainerKind::Struct));
}
b'(' => {
let kind = ContainerKind::Union;
let (name, items) = self.parse_container(kind)?;
EncodingBox::Union(name.to_string(), items)
}
b')' => {
return Ok(ParseInner::ContainerEnd(ContainerKind::Union));
}
b => return Err(ErrorKind::Unknown(b)),
}))
}
fn try_parse_bitfield_gnustep(&mut self) -> Result<Option<(u8, EncodingBox)>> {
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)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn parse_container() {
const KIND: ContainerKind = ContainerKind::Struct;
#[track_caller]
fn assert_name(enc: &str, expected: Result<(&str, Vec<EncodingBox>)>) {
let mut parser = Parser::new(enc);
assert_eq!(parser.parse_container(KIND), expected);
}
assert_name("abc=}", Ok(("abc", vec![])));
assert_name(
"abc=ii}",
Ok(("abc", vec![EncodingBox::Int, EncodingBox::Int])),
);
assert_name("_=}.a'", Ok(("_", vec![])));
assert_name("abc}def", Ok(("abc", vec![])));
assert_name("=def}", Err(ErrorKind::InvalidIdentifier(KIND)));
assert_name(".=def}", Err(ErrorKind::InvalidIdentifier(KIND)));
assert_name("}xyz", Err(ErrorKind::InvalidIdentifier(KIND)));
assert_name("", Err(ErrorKind::WrongEndContainer(KIND)));
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<EncodingBox>) {
let mut parser = Parser::new(enc);
assert_eq!(
parser
.parse_encoding_or_none()
.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(),
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));
}
#[test]
fn parse_closing() {
let mut parser = Parser::new("]");
assert_eq!(
parser.parse_encoding_or_none(),
Err(ErrorKind::Unknown(b']'))
);
}
}

246
vendor/objc2-encode/src/static_str.rs vendored Normal file
View File

@@ -0,0 +1,246 @@
use crate::helper::{Helper, NestingLevel};
use super::Encoding;
pub(crate) const fn static_int_str_len(mut n: u64) -> 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: u64) -> [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(size, None) => 1 + static_int_str_len(size as u64),
BitField(size, Some((offset, t))) => {
1 + static_int_str_len(*offset)
+ static_encoding_str_len(t, level.bitfield())
+ static_int_str_len(size as u64)
}
Indirection(kind, t) => 1 + static_encoding_str_len(t, level.indirection(kind)),
Array(len, item) => {
1 + static_int_str_len(len) + static_encoding_str_len(item, level.array()) + 1
}
Container(_, name, items) => {
let mut res = 1 + name.len();
if let Some(level) = level.container_include_fields() {
res += 1;
let mut i = 0;
while i < items.len() {
res += static_encoding_str_len(&items[i], level);
i += 1;
}
}
res + 1
}
NoneInvalid => 0,
}
}
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(size, None) => {
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>(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))) => {
let level = level.bitfield();
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::<LEN>(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;
}
}
Indirection(kind, t) => {
let level = level.indirection(kind);
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);
while i < static_encoding_str_len(t, level) {
res[res_i] = arr[i];
res_i += 1;
i += 1;
}
}
Array(len, item) => {
let level = level.array();
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);
while i < static_int_str_len(len) {
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);
while i < static_encoding_str_len(item, level) {
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_include_fields() {
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();
}
NoneInvalid => {}
};
res
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! const_int_str {
($n:expr) => {{
const X: [u8; static_int_str_len($n)] = static_int_str_array($n);
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`
}