Source code

Revision control

Copy as Markdown

Other Tools

/// Create a [`NSString`] from a static [`str`].
///
/// Equivalent to the [boxed C-strings] `@"string"` syntax in Objective-C.
///
/// [`NSString`]: crate::NSString
///
///
/// # Specification
///
/// The macro takes any expression that evaluates to a `const` `&str`, and
/// produces a `&'static NSString`.
///
/// The returned string's encoding is not guaranteed to be UTF-8.
///
/// Strings containing ASCII NUL is allowed, the NUL is preserved as one would
/// expect.
///
///
/// # Cargo features
///
/// If the experimental `"unstable-static-nsstring"` feature is enabled, this
/// will emit statics placed in special sections that will be replaced by dyld
/// when the program starts up - which will in turn will cause the runtime
/// cost of this macro to be completely non-existent!
///
/// However, it is known to not be completely reliable yet, see [#258] for
/// details.
///
///
///
/// # Examples
///
/// Creating a static `NSString`.
///
/// ```
/// use objc2_foundation::{ns_string, NSString};
///
/// let hello: &'static NSString = ns_string!("hello");
/// assert_eq!(hello.to_string(), "hello");
/// ```
///
/// Creating a `NSString` from a `const` `&str`.
///
/// ```
/// # use objc2_foundation::ns_string;
/// const EXAMPLE: &str = "example";
/// assert_eq!(ns_string!(EXAMPLE).to_string(), EXAMPLE);
/// ```
///
/// Creating unicode strings.
///
/// ```
/// # use objc2_foundation::ns_string;
/// let hello_ru = ns_string!("Привет");
/// assert_eq!(hello_ru.to_string(), "Привет");
/// ```
///
/// Creating a string containing a NUL byte:
///
/// ```
/// # use objc2_foundation::ns_string;
/// assert_eq!(ns_string!("example\0").to_string(), "example\0");
/// assert_eq!(ns_string!("exa\0mple").to_string(), "exa\0mple");
/// ```
// For auto_doc_cfg
#[cfg(feature = "NSString")]
#[macro_export]
macro_rules! ns_string {
($s:expr) => {{
// Immediately place in constant for better UI
const INPUT: &str = $s;
$crate::__ns_string_inner!(INPUT)
}};
}
#[doc(hidden)]
#[cfg(all(target_vendor = "apple", feature = "unstable-static-nsstring"))]
#[macro_export]
macro_rules! __ns_string_inner {
($inp:ident) => {{
const X: &[u8] = $inp.as_bytes();
$crate::__ns_string_static!(X);
// Return &'static NSString
CFSTRING.as_nsstring()
}};
}
#[doc(hidden)]
#[cfg(all(target_vendor = "apple", feature = "unstable-static-nsstring"))]
#[macro_export]
macro_rules! __ns_string_static {
($inp:ident) => {
// Note: We create both the ASCII + NUL and the UTF-16 + NUL versions
// of the string, since we can't conditionally create a static.
//
// Since we don't add the `#[used]` attribute, Rust can fairly
// reliably figure out that one of the variants are never used, and
// exclude it.
// Convert the input slice to a C-style string with a NUL byte.
//
// The section is the same as what clang sets, see:
#[link_section = "__TEXT,__cstring,cstring_literals"]
static ASCII: [u8; $inp.len() + 1] = {
// Zero-fill with $inp.len() + 1
let mut res: [u8; $inp.len() + 1] = [0; $inp.len() + 1];
let mut i = 0;
// Fill with data from $inp
while i < $inp.len() {
res[i] = $inp[i];
i += 1;
}
// Now contains $inp + '\0'
res
};
// The full UTF-16 contents along with the written length.
const UTF16_FULL: (&[u16; $inp.len()], usize) = {
let mut out = [0u16; $inp.len()];
let mut iter = $crate::__ns_macro_helpers::EncodeUtf16Iter::new($inp);
let mut written = 0;
while let Some((state, chars)) = iter.next() {
iter = state;
out[written] = chars.repr[0];
written += 1;
if chars.len > 1 {
out[written] = chars.repr[1];
written += 1;
}
}
(&{ out }, written)
};
// Convert the slice to an UTF-16 array + a final NUL byte.
//
// The section is the same as what clang sets, see:
#[link_section = "__TEXT,__ustring"]
static UTF16: [u16; UTF16_FULL.1 + 1] = {
// Zero-fill with UTF16_FULL.1 + 1
let mut res: [u16; UTF16_FULL.1 + 1] = [0; UTF16_FULL.1 + 1];
let mut i = 0;
// Fill with data from UTF16_FULL.0 up until UTF16_FULL.1
while i < UTF16_FULL.1 {
res[i] = UTF16_FULL.0[i];
i += 1;
}
// Now contains UTF16_FULL.1 + NUL
res
};
// Create the constant string structure, and store it in a static
// within a special section.
//
// The section is the same as what clang sets, see:
#[link_section = "__DATA,__cfstring"]
static CFSTRING: $crate::__ns_macro_helpers::CFConstString = unsafe {
if $crate::__ns_macro_helpers::is_ascii_no_nul($inp) {
// This is technically an optimization (UTF-16 strings are
// always valid), but it's a fairly important one!
$crate::__ns_macro_helpers::CFConstString::new_ascii(
&$crate::__ns_macro_helpers::__CFConstantStringClassReference,
&ASCII,
)
} else {
$crate::__ns_macro_helpers::CFConstString::new_utf16(
&$crate::__ns_macro_helpers::__CFConstantStringClassReference,
&UTF16,
)
}
};
};
}
#[doc(hidden)]
#[cfg(not(all(target_vendor = "apple", feature = "unstable-static-nsstring")))]
#[macro_export]
macro_rules! __ns_string_inner {
($inp:ident) => {{
static CACHED_NSSTRING: $crate::__ns_macro_helpers::CachedRetained<$crate::NSString> =
$crate::__ns_macro_helpers::CachedRetained::new();
CACHED_NSSTRING.get(|| $crate::NSString::from_str($inp))
}};
}