1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10 //! Immutable strings.
11
12 pub use core_foundation_sys::string::*;
13
14 use base::{CFIndexConvertible, TCFType};
15
16 use core_foundation_sys::base::{Boolean, CFIndex, CFRange};
17 use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull};
18 use std::borrow::Cow;
19 use std::fmt;
20 use std::str::{self, FromStr};
21 use std::ptr;
22 use std::ffi::CStr;
23
24
25 declare_TCFType!{
26 /// An immutable string in one of a variety of encodings.
27 CFString, CFStringRef
28 }
29 impl_TCFType!(CFString, CFStringRef, CFStringGetTypeID);
30
31 impl FromStr for CFString {
32 type Err = ();
33
34 /// See also CFString::new for a variant of this which does not return a Result
35 #[inline]
from_str(string: &str) -> Result<CFString, ()>36 fn from_str(string: &str) -> Result<CFString, ()> {
37 Ok(CFString::new(string))
38 }
39 }
40
41 impl<'a> From<&'a str> for CFString {
42 #[inline]
from(string: &'a str) -> CFString43 fn from(string: &'a str) -> CFString {
44 CFString::new(string)
45 }
46 }
47
48 impl<'a> From<&'a CFString> for Cow<'a, str> {
from(cf_str: &'a CFString) -> Cow<'a, str>49 fn from(cf_str: &'a CFString) -> Cow<'a, str> {
50 unsafe {
51 // Do this without allocating if we can get away with it
52 let c_string = CFStringGetCStringPtr(cf_str.0, kCFStringEncodingUTF8);
53 if !c_string.is_null() {
54 let c_str = CStr::from_ptr(c_string);
55 Cow::Borrowed(str::from_utf8_unchecked(c_str.to_bytes()))
56 } else {
57 let char_len = cf_str.char_len();
58
59 // First, ask how big the buffer ought to be.
60 let mut bytes_required: CFIndex = 0;
61 CFStringGetBytes(cf_str.0,
62 CFRange { location: 0, length: char_len },
63 kCFStringEncodingUTF8,
64 0,
65 false as Boolean,
66 ptr::null_mut(),
67 0,
68 &mut bytes_required);
69
70 // Then, allocate the buffer and actually copy.
71 let mut buffer = vec![b'\x00'; bytes_required as usize];
72
73 let mut bytes_used: CFIndex = 0;
74 let chars_written = CFStringGetBytes(cf_str.0,
75 CFRange { location: 0, length: char_len },
76 kCFStringEncodingUTF8,
77 0,
78 false as Boolean,
79 buffer.as_mut_ptr(),
80 buffer.len().to_CFIndex(),
81 &mut bytes_used);
82 assert_eq!(chars_written, char_len);
83
84 // This is dangerous; we over-allocate and null-terminate the string (during
85 // initialization).
86 assert_eq!(bytes_used, buffer.len().to_CFIndex());
87 Cow::Owned(String::from_utf8_unchecked(buffer))
88 }
89 }
90 }
91 }
92
93 impl fmt::Display for CFString {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result94 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
95 fmt.write_str(&Cow::from(self))
96 }
97 }
98
99 impl fmt::Debug for CFString {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 write!(f, "\"{}\"", self)
102 }
103 }
104
105
106 impl CFString {
107 /// Creates a new `CFString` instance from a Rust string.
108 #[inline]
new(string: &str) -> CFString109 pub fn new(string: &str) -> CFString {
110 unsafe {
111 let string_ref = CFStringCreateWithBytes(kCFAllocatorDefault,
112 string.as_ptr(),
113 string.len().to_CFIndex(),
114 kCFStringEncodingUTF8,
115 false as Boolean);
116 CFString::wrap_under_create_rule(string_ref)
117 }
118 }
119
120 /// Like `CFString::new`, but references a string that can be used as a backing store
121 /// by virtue of being statically allocated.
122 #[inline]
from_static_string(string: &'static str) -> CFString123 pub fn from_static_string(string: &'static str) -> CFString {
124 unsafe {
125 let string_ref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
126 string.as_ptr(),
127 string.len().to_CFIndex(),
128 kCFStringEncodingUTF8,
129 false as Boolean,
130 kCFAllocatorNull);
131 TCFType::wrap_under_create_rule(string_ref)
132 }
133 }
134
135 /// Returns the number of characters in the string.
136 #[inline]
char_len(&self) -> CFIndex137 pub fn char_len(&self) -> CFIndex {
138 unsafe {
139 CFStringGetLength(self.0)
140 }
141 }
142 }
143
144 impl<'a> PartialEq<&'a str> for CFString {
eq(&self, other: &&str) -> bool145 fn eq(&self, other: &&str) -> bool {
146 unsafe {
147 let temp = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
148 other.as_ptr(),
149 other.len().to_CFIndex(),
150 kCFStringEncodingUTF8,
151 false as Boolean,
152 kCFAllocatorNull);
153 self.eq(&CFString::wrap_under_create_rule(temp))
154 }
155 }
156 }
157
158 impl<'a> PartialEq<CFString> for &'a str {
159 #[inline]
eq(&self, other: &CFString) -> bool160 fn eq(&self, other: &CFString) -> bool {
161 other.eq(self)
162 }
163 }
164
165 impl PartialEq<CFString> for String {
166 #[inline]
eq(&self, other: &CFString) -> bool167 fn eq(&self, other: &CFString) -> bool {
168 other.eq(&self.as_str())
169 }
170 }
171
172 impl PartialEq<String> for CFString {
173 #[inline]
eq(&self, other: &String) -> bool174 fn eq(&self, other: &String) -> bool {
175 self.eq(&other.as_str())
176 }
177 }
178
179 #[test]
str_cmp()180 fn str_cmp() {
181 let cfstr = CFString::new("hello");
182 assert_eq!("hello", cfstr);
183 assert_eq!(cfstr, "hello");
184 assert_ne!(cfstr, "wrong");
185 assert_ne!("wrong", cfstr);
186 let hello = String::from("hello");
187 assert_eq!(hello, cfstr);
188 assert_eq!(cfstr, hello);
189 }
190
191 #[test]
string_and_back()192 fn string_and_back() {
193 let original = "The quick brown fox jumped over the slow lazy dog.";
194 let cfstr = CFString::from_static_string(original);
195 let converted = cfstr.to_string();
196 assert_eq!(converted, original);
197 }
198