diff options
Diffstat (limited to 'third_party/rust/core-foundation/src/string.rs')
-rw-r--r-- | third_party/rust/core-foundation/src/string.rs | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/third_party/rust/core-foundation/src/string.rs b/third_party/rust/core-foundation/src/string.rs new file mode 100644 index 0000000000..3f5994bc5a --- /dev/null +++ b/third_party/rust/core-foundation/src/string.rs @@ -0,0 +1,197 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Immutable strings. + +pub use core_foundation_sys::string::*; + +use base::{CFIndexConvertible, TCFType}; + +use core_foundation_sys::base::{Boolean, CFIndex, CFRange}; +use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull}; +use std::borrow::Cow; +use std::fmt; +use std::str::{self, FromStr}; +use std::ptr; +use std::ffi::CStr; + + +declare_TCFType!{ + /// An immutable string in one of a variety of encodings. + CFString, CFStringRef +} +impl_TCFType!(CFString, CFStringRef, CFStringGetTypeID); + +impl FromStr for CFString { + type Err = (); + + /// See also CFString::new for a variant of this which does not return a Result + #[inline] + fn from_str(string: &str) -> Result<CFString, ()> { + Ok(CFString::new(string)) + } +} + +impl<'a> From<&'a str> for CFString { + #[inline] + fn from(string: &'a str) -> CFString { + CFString::new(string) + } +} + +impl<'a> From<&'a CFString> for Cow<'a, str> { + fn from(cf_str: &'a CFString) -> Cow<'a, str> { + unsafe { + // Do this without allocating if we can get away with it + let c_string = CFStringGetCStringPtr(cf_str.0, kCFStringEncodingUTF8); + if !c_string.is_null() { + let c_str = CStr::from_ptr(c_string); + Cow::Borrowed(str::from_utf8_unchecked(c_str.to_bytes())) + } else { + let char_len = cf_str.char_len(); + + // First, ask how big the buffer ought to be. + let mut bytes_required: CFIndex = 0; + CFStringGetBytes(cf_str.0, + CFRange { location: 0, length: char_len }, + kCFStringEncodingUTF8, + 0, + false as Boolean, + ptr::null_mut(), + 0, + &mut bytes_required); + + // Then, allocate the buffer and actually copy. + let mut buffer = vec![b'\x00'; bytes_required as usize]; + + let mut bytes_used: CFIndex = 0; + let chars_written = CFStringGetBytes(cf_str.0, + CFRange { location: 0, length: char_len }, + kCFStringEncodingUTF8, + 0, + false as Boolean, + buffer.as_mut_ptr(), + buffer.len().to_CFIndex(), + &mut bytes_used); + assert_eq!(chars_written, char_len); + + // This is dangerous; we over-allocate and null-terminate the string (during + // initialization). + assert_eq!(bytes_used, buffer.len().to_CFIndex()); + Cow::Owned(String::from_utf8_unchecked(buffer)) + } + } + } +} + +impl fmt::Display for CFString { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(&Cow::from(self)) + } +} + +impl fmt::Debug for CFString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "\"{}\"", self) + } +} + + +impl CFString { + /// Creates a new `CFString` instance from a Rust string. + #[inline] + pub fn new(string: &str) -> CFString { + unsafe { + let string_ref = CFStringCreateWithBytes(kCFAllocatorDefault, + string.as_ptr(), + string.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean); + CFString::wrap_under_create_rule(string_ref) + } + } + + /// Like `CFString::new`, but references a string that can be used as a backing store + /// by virtue of being statically allocated. + #[inline] + pub fn from_static_string(string: &'static str) -> CFString { + unsafe { + let string_ref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, + string.as_ptr(), + string.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean, + kCFAllocatorNull); + TCFType::wrap_under_create_rule(string_ref) + } + } + + /// Returns the number of characters in the string. + #[inline] + pub fn char_len(&self) -> CFIndex { + unsafe { + CFStringGetLength(self.0) + } + } +} + +impl<'a> PartialEq<&'a str> for CFString { + fn eq(&self, other: &&str) -> bool { + unsafe { + let temp = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, + other.as_ptr(), + other.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean, + kCFAllocatorNull); + self.eq(&CFString::wrap_under_create_rule(temp)) + } + } +} + +impl<'a> PartialEq<CFString> for &'a str { + #[inline] + fn eq(&self, other: &CFString) -> bool { + other.eq(self) + } +} + +impl PartialEq<CFString> for String { + #[inline] + fn eq(&self, other: &CFString) -> bool { + other.eq(&self.as_str()) + } +} + +impl PartialEq<String> for CFString { + #[inline] + fn eq(&self, other: &String) -> bool { + self.eq(&other.as_str()) + } +} + +#[test] +fn str_cmp() { + let cfstr = CFString::new("hello"); + assert_eq!("hello", cfstr); + assert_eq!(cfstr, "hello"); + assert_ne!(cfstr, "wrong"); + assert_ne!("wrong", cfstr); + let hello = String::from("hello"); + assert_eq!(hello, cfstr); + assert_eq!(cfstr, hello); +} + +#[test] +fn string_and_back() { + let original = "The quick brown fox jumped over the slow lazy dog."; + let cfstr = CFString::from_static_string(original); + let converted = cfstr.to_string(); + assert_eq!(converted, original); +} |