1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
/// Create a [`NSString`] from a static [`str`].
///
/// Equivalent to the [boxed C-strings] `@"string"` syntax in Objective-C.
///
/// [`NSString`]: crate::Foundation::NSString
/// [boxed C-strings]: https://clang.llvm.org/docs/ObjectiveCLiterals.html#boxed-c-strings
///
///
/// # 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-existant!
///
/// However, it is known to not be completely reliable yet, see [#258] for
/// details.
///
/// [#258]: https://github.com/madsmtm/objc2/issues/258
///
///
/// # 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:
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5192
#[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::__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:
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5193
#[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:
// https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CodeGenModule.cpp#L5243
#[link_section = "__DATA,__cfstring"]
static CFSTRING: $crate::__macro_helpers::CFConstString = unsafe {
if $crate::__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::__macro_helpers::CFConstString::new_ascii(
&$crate::__macro_helpers::__CFConstantStringClassReference,
&ASCII,
)
} else {
$crate::__macro_helpers::CFConstString::new_utf16(
&$crate::__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::__macro_helpers::CachedId<$crate::NSString> =
$crate::__macro_helpers::CachedId::new();
CACHED_NSSTRING.get(|| $crate::NSString::from_str($inp))
}};
}