Skip to content

Commit 0ed3c8e

Browse files
committed
Add CFString::as_str_unchecked
1 parent 480cad9 commit 0ed3c8e

File tree

2 files changed

+14
-11
lines changed

2 files changed

+14
-11
lines changed

crates/objc2/src/topics/about_generated/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
9999
- Added getters.
100100
- Added mutation methods.
101101
* Added simple `#define`ed constants.
102+
* Added `CFString::as_str_unchecked`.
102103

103104
### Changed
104105
* Made `CFArray`, `CFBag`, `CFBinaryHeap`, `CFDictionary`, `CFSet` and their

framework-crates/objc2-core-foundation/src/string.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,10 @@ impl CFString {
8686
///
8787
/// Warning: This is very difficult to ensure in generic contexts, e.g. it
8888
/// cannot even be used inside `Debug::fmt`, since `Formatter` uses `dyn`
89-
/// internally, and can thus mutate the string inside there.
89+
/// internally, and could thus mutate the string inside there.
9090
#[doc(alias = "CFStringGetCStringPtr")]
91-
// NOTE: This is NOT public, since it's completely broken for differently
92-
// encoded strings, see the `as_str_broken` test below. See also:
93-
// <https://github.com/swiftlang/swift-corelibs-foundation/issues/5164>.
94-
#[allow(dead_code)]
95-
unsafe fn as_str_unchecked(&self) -> Option<&str> {
91+
pub unsafe fn as_str_unchecked(&self) -> Option<&str> {
92+
// NOTE: The encoding is an 8-bit encoding.
9693
let bytes = CFStringGetCStringPtr(self, CFStringBuiltInEncodings::EncodingUTF8.0);
9794
NonNull::new(bytes as *mut c_char).map(|bytes| {
9895
// SAFETY: The pointer is valid for as long as the CFString is not
@@ -102,8 +99,6 @@ impl CFString {
10299
// We won't accidentally truncate the string here, since
103100
// `CFStringGetCStringPtr` makes sure that there are no internal
104101
// NUL bytes in the string.
105-
//
106-
// TODO: Verify this claim with a test.
107102
let cstr = unsafe { CStr::from_ptr(bytes.as_ptr()) };
108103
// SAFETY: `CFStringGetCStringPtr` is (very likely) implemented
109104
// correctly, and won't return non-UTF8 strings.
@@ -255,6 +250,8 @@ mod tests {
255250
let s = CFString::from_str("a\0b\0c\0d");
256251
// Works with `CFStringGetBytes`.
257252
assert_eq!(s.to_string(), "a\0b\0c\0d");
253+
// Expectedly does not work `CFStringGetCStringPtr`.
254+
assert_eq!(unsafe { s.as_str_unchecked() }, None);
258255

259256
// Test `CFStringGetCString`.
260257
let mut buf = [0u8; 10];
@@ -287,9 +284,15 @@ mod tests {
287284
}
288285

289286
#[test]
290-
fn as_str_broken() {
287+
fn create_with_cstring_broken_on_non_8_bit() {
291288
// A CFString that is supposed to contain a "♥" (the UTF-8 encoding of
292289
// that is the vastly different b"\xE2\x99\xA5").
290+
//
291+
// This line is wrong, because `CFStringCreateWithCString` expects an
292+
// 8-bit encoding.
293+
//
294+
// See also:
295+
// https://github.com/swiftlang/swift-corelibs-foundation/issues/5164
293296
let s = unsafe {
294297
CFStringCreateWithCString(
295298
None,
@@ -315,8 +318,7 @@ mod tests {
315318
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
316319
assert_eq!(cstr.to_bytes(), "♥".as_bytes());
317320

318-
// But `CFStringGetCStringPtr` completely ignores the UTF-8 conversion
319-
// we asked it to do, i.e. a huge correctness footgun!
321+
// `CFStringGetCStringPtr` completely ignores the UTF-8 conversion.
320322
assert_eq!(unsafe { s.as_str_unchecked() }, Some("e&"));
321323
}
322324

0 commit comments

Comments
 (0)