Skip to content

Commit

Permalink
Add static encoding strings
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Aug 9, 2022
1 parent fdbe88f commit c5ad3ce
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 7 deletions.
3 changes: 3 additions & 0 deletions objc2-encode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Added `Encode::ENCODING_CSTR` for statically generating an encoding string.

### Fixed
* Fixed the encoding output and comparison of structs behind pointers.

Expand Down
80 changes: 80 additions & 0 deletions objc2-encode/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ use core::num::{
};
use core::ptr::NonNull;
use core::sync::atomic;
#[cfg(feature = "std")] // TODO: Use `core`
use std::ffi::CStr;

use crate::helper::NestingLevel;
use crate::static_str::{static_encoding_str_array, static_encoding_str_len};
use crate::Encoding;

/// Types that have an Objective-C type-encoding.
Expand All @@ -16,6 +20,7 @@ use crate::Encoding;
/// 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)`,
Expand All @@ -33,6 +38,9 @@ use crate::Encoding;
/// passed to Objective-C via. `objc2::msg_send!` their destructor will not be
/// called!
///
/// Finally, you must not override [`ENCODING_CSTR`][Self::ENCODING_CSTR].
///
///
/// # Examples
///
/// Implementing for a struct:
Expand Down Expand Up @@ -69,6 +77,43 @@ use crate::Encoding;
pub unsafe trait Encode {
/// The Objective-C type-encoding for this type.
const ENCODING: Encoding<'static>;

#[doc(hidden)]
const __ENCODING_CSTR_LEN: usize = static_encoding_str_len(Self::ENCODING, NestingLevel::new());

#[doc(hidden)]
const __ENCODING_CSTR_ARRAY: [u8; 128] = {
if Self::__ENCODING_CSTR_LEN >= 127 {
panic!("encoding string was too long! The maximum supported length is 1023.");
}

static_encoding_str_array(Self::ENCODING, NestingLevel::new())
};

/// The encoding as a static [`CStr`].
///
/// This has the same output as `Encoding::to_string`, but it is created
/// at compile-time instead.
///
/// The encoding is guaranteed to be a pure ASCII string.
#[cfg(feature = "std")]
const ENCODING_CSTR: &'static CStr = {
let mut slice: &[u8] = &Self::__ENCODING_CSTR_ARRAY;
// Cut down to desired size (length + 1 for NUL byte)
// Equivalent to:
// slice[0..Self::__ENCODING_CSTR_LEN + 1]
while slice.len() > Self::__ENCODING_CSTR_LEN + 1 {
if let Some(res) = slice.split_last() {
slice = res.1;
} else {
unreachable!();
}
}
// SAFETY: `static_encoding_str_array` is guaranteed to not contain
// any NULL bytes (the only place those could appear would be in a
// struct or union name, and that case is checked).
unsafe { CStr::from_bytes_with_nul_unchecked(slice) }
};
}

/// Types whoose references has an Objective-C type-encoding.
Expand Down Expand Up @@ -736,4 +781,39 @@ mod tests {
assert_eq!(<(i8,)>::ENCODINGS, &[i8::ENCODING]);
assert_eq!(<(i8, u32)>::ENCODINGS, &[i8::ENCODING, u32::ENCODING]);
}

#[test]
#[cfg(feature = "std")]
fn test_cstr_simple() {
assert_eq!(i8::__ENCODING_CSTR_LEN, 1);

let mut array = [0; 1024];
array[0] = b'c';
assert_eq!(i8::__ENCODING_CSTR_ARRAY, array);

let cstr = CStr::from_bytes_with_nul(b"c\0").unwrap();
assert_eq!(i8::ENCODING_CSTR, cstr);
}

#[test]
#[cfg(feature = "std")]
fn test_cstr() {
struct X;

unsafe impl Encode for X {
const ENCODING: Encoding<'static> = Encoding::Struct(
"abc",
&[
Encoding::Union("def", &[Encoding::Char]),
<*const *const i8>::ENCODING,
<AtomicPtr<AtomicI16>>::ENCODING,
<extern "C" fn()>::ENCODING,
],
);
}

let s = b"{abc=(def=c)^*A^As^?}\0";

assert_eq!(X::ENCODING_CSTR.to_bytes_with_nul(), s);
}
}
3 changes: 0 additions & 3 deletions objc2-encode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ 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, RefEncode};
Expand Down
26 changes: 26 additions & 0 deletions test-assembly/crates/test_encoding_cstr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "test_encoding_cstr"
version = "0.1.0"
edition = "2021"
publish = false

[lib]
path = "lib.rs"

[dependencies]
objc2-encode = { path = "../../../objc2-encode", default-features = false }

[features]
default = ["apple", "std"]
std = ["objc2-encode/std"]

# Runtime
apple = []
gnustep-1-7 = []
gnustep-1-8 = ["gnustep-1-7"]
gnustep-1-9 = ["gnustep-1-8"]
gnustep-2-0 = ["gnustep-1-9"]
gnustep-2-1 = ["gnustep-2-0"]

# Hack
assembly-features = ["std"]
13 changes: 13 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-aarch64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.section __TEXT,__text,regular,pure_instructions
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 3
_ENC:
.quad l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000\000\000\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-armv7.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.syntax unified
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-armv7s.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.syntax unified
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-x86.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.intel_syntax noprefix
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 2
_ENC:
.long l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"

.subsections_via_symbols
14 changes: 14 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/apple-x86_64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.section __TEXT,__text,regular,pure_instructions
.intel_syntax noprefix
.section __TEXT,__const
l_anon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"

.section __DATA,__const
.globl _ENC
.p2align 3
_ENC:
.quad l_anon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000\000\000\000\000"

.subsections_via_symbols
18 changes: 18 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/gnustep-x86.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.text
.intel_syntax noprefix
.type .Lanon.a88231c846af3b75605317c1ca346ede.0,@object
.section .rodata..Lanon.a88231c846af3b75605317c1ca346ede.0,"a",@progbits
.Lanon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
.size .Lanon.a88231c846af3b75605317c1ca346ede.0, 128

.type ENC,@object
.section .data.rel.ro.ENC,"aw",@progbits
.globl ENC
.p2align 2
ENC:
.long .Lanon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000"
.size ENC, 8

.section ".note.GNU-stack","",@progbits
18 changes: 18 additions & 0 deletions test-assembly/crates/test_encoding_cstr/expected/gnustep-x86_64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.text
.intel_syntax noprefix
.type .Lanon.a88231c846af3b75605317c1ca346ede.0,@object
.section .rodata..Lanon.a88231c846af3b75605317c1ca346ede.0,"a",@progbits
.Lanon.a88231c846af3b75605317c1ca346ede.0:
.asciz "c\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
.size .Lanon.a88231c846af3b75605317c1ca346ede.0, 128

.type ENC,@object
.section .data.rel.ro.ENC,"aw",@progbits
.globl ENC
.p2align 3
ENC:
.quad .Lanon.a88231c846af3b75605317c1ca346ede.0
.asciz "\002\000\000\000\000\000\000"
.size ENC, 16

.section ".note.GNU-stack","",@progbits
7 changes: 7 additions & 0 deletions test-assembly/crates/test_encoding_cstr/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Test that the encoding string that we output is not full length.
use std::ffi::CStr;

use objc2_encode::Encode;

#[no_mangle]
static ENC: &CStr = i8::ENCODING_CSTR;
6 changes: 6 additions & 0 deletions test-ui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
#[cfg(feature = "run")]
fn main() {
let t = trybuild::TestCases::new();

let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("ui")
.join("*.rs");
t.compile_fail(path);

let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("ui-ignore")
.join("*.rs");
t.pass(path);
}

#[cfg(not(feature = "run"))]
Expand Down
15 changes: 15 additions & 0 deletions test-ui/ui-ignore/encode_cstr_not_ident.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Test that compilation fails when the struct name is invalid.
//!
//! Ideally, this should be tested by `trybuild`, but it doesn't work at the
//! moment (`cargo check` doesn't catch the error).
use objc2::{Encode, Encoding};

struct X;

unsafe impl Encode for X {
const ENCODING: Encoding<'static> = Encoding::Struct("-", &[]);
}

fn main() {
let _ = X::ENCODING_CSTR;
}
17 changes: 17 additions & 0 deletions test-ui/ui-ignore/encode_cstr_too_long.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Test that compilation fails when the encoding is too long.
//!
//! Ideally, this should be tested by `trybuild`, but it doesn't work at the
//! moment (`cargo check` doesn't catch the error).
use objc2::{Encode, Encoding};

struct X;

const S: &str = unsafe { std::str::from_utf8_unchecked(&[b'a'; 1020]) };

unsafe impl Encode for X {
const ENCODING: Encoding<'static> = Encoding::Struct(S, &[]);
}

fn main() {
let _ = X::ENCODING_CSTR;
}
24 changes: 20 additions & 4 deletions tests/src/test_encode_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ unsafe fn assert_encoding(s: *const c_char, e: Encoding) {
assert_eq!(e.to_string(), s.trim_start_matches('r'));
}

unsafe fn assert_ty<T: Encode>(s: *const c_char) {
assert_encoding(s, T::ENCODING);
// To ensure ENCODING_CSTR is implemented correctly
assert_eq!(T::ENCODING_CSTR.to_str().unwrap(), T::ENCODING.to_string());
}

#[allow(unused)]
unsafe fn assert_str<T: Display>(s: *const c_char, expected: T) {
let s = CStr::from_ptr(s).to_str().unwrap();
Expand All @@ -39,6 +45,16 @@ macro_rules! assert_inner {
unsafe { assert_encoding($stat, $expected) };
}
};
(ty $(#[$m:meta])* $stat:ident => $expected:ty) => {
$(#[$m])*
#[test]
fn $stat() {
extern "C" {
static $stat: *const c_char;
}
unsafe { assert_ty::<$expected>($stat) };
}
};
(str $(#[$m:meta])* $stat:ident => $expected:expr) => {
$(#[$m])*
#[test]
Expand Down Expand Up @@ -78,10 +94,10 @@ macro_rules! assert_types {
$stat:ident $($should_atomic:ident)? => $type:ident
) => {
paste! {
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat>] => <$type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER>] => <*const $type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER>] => <*const *const $type>::ENCODING);
assert_inner!(enc $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER_POINTER>] => <*const *const *const $type>::ENCODING);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat>] => $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER>] => *const $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER>] => *const *const $type);
assert_inner!(ty $(#[$m])* [<ENCODING_ $stat _POINTER_POINTER_POINTER>] => *const *const *const $type);
$(assert_types!(#$should_atomic);)?
assert_inner!(enc $(#[$m])* $(#[cfg($should_atomic)])? [<ENCODING_ $stat _ATOMIC>] => Encoding::Atomic(&<$type>::ENCODING));
assert_inner!(enc $(#[$m])* $(#[cfg($should_atomic)])? [<ENCODING_ $stat _ATOMIC_POINTER>] => Encoding::Pointer(&Encoding::Atomic(&<$type>::ENCODING)));
Expand Down

0 comments on commit c5ad3ce

Please sign in to comment.