diff --git a/src/libcore/char.rs b/src/libcore/char.rs index caac894c0daa3..0e6b634bd11ef 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -258,49 +258,69 @@ impl CharExt for char { #[inline] #[unstable = "pending decision about Iterator/Writer/Reader"] fn encode_utf8(self, dst: &mut [u8]) -> Option { - // Marked #[inline] to allow llvm optimizing it away - let code = self as u32; - if code < MAX_ONE_B && dst.len() >= 1 { - dst[0] = code as u8; - Some(1) - } else if code < MAX_TWO_B && dst.len() >= 2 { - dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B; - dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(2) - } else if code < MAX_THREE_B && dst.len() >= 3 { - dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B; - dst[1] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; - dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(3) - } else if dst.len() >= 4 { - dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B; - dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT; - dst[2] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; - dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT; - Some(4) - } else { - None - } + encode_utf8_raw(self as u32, dst) } #[inline] #[unstable = "pending decision about Iterator/Writer/Reader"] fn encode_utf16(self, dst: &mut [u16]) -> Option { - // Marked #[inline] to allow llvm optimizing it away - let mut ch = self as u32; - if (ch & 0xFFFF_u32) == ch && dst.len() >= 1 { - // The BMP falls through (assuming non-surrogate, as it should) - dst[0] = ch as u16; - Some(1) - } else if dst.len() >= 2 { - // Supplementary planes break into surrogates. - ch -= 0x1_0000_u32; - dst[0] = 0xD800_u16 | ((ch >> 10) as u16); - dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); - Some(2) - } else { - None - } + encode_utf16_raw(self as u32, dst) + } +} + +/// Encodes a raw u32 value as UTF-8 into the provided byte buffer, +/// and then returns the number of bytes written. +/// +/// If the buffer is not large enough, nothing will be written into it +/// and a `None` will be returned. +#[inline] +#[unstable] +pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> Option { + // Marked #[inline] to allow llvm optimizing it away + if code < MAX_ONE_B && dst.len() >= 1 { + dst[0] = code as u8; + Some(1) + } else if code < MAX_TWO_B && dst.len() >= 2 { + dst[0] = (code >> 6u & 0x1F_u32) as u8 | TAG_TWO_B; + dst[1] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(2) + } else if code < MAX_THREE_B && dst.len() >= 3 { + dst[0] = (code >> 12u & 0x0F_u32) as u8 | TAG_THREE_B; + dst[1] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(3) + } else if dst.len() >= 4 { + dst[0] = (code >> 18u & 0x07_u32) as u8 | TAG_FOUR_B; + dst[1] = (code >> 12u & 0x3F_u32) as u8 | TAG_CONT; + dst[2] = (code >> 6u & 0x3F_u32) as u8 | TAG_CONT; + dst[3] = (code & 0x3F_u32) as u8 | TAG_CONT; + Some(4) + } else { + None + } +} + +/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, +/// and then returns the number of `u16`s written. +/// +/// If the buffer is not large enough, nothing will be written into it +/// and a `None` will be returned. +#[inline] +#[unstable] +pub fn encode_utf16_raw(mut ch: u32, dst: &mut [u16]) -> Option { + // Marked #[inline] to allow llvm optimizing it away + if (ch & 0xFFFF_u32) == ch && dst.len() >= 1 { + // The BMP falls through (assuming non-surrogate, as it should) + dst[0] = ch as u16; + Some(1) + } else if dst.len() >= 2 { + // Supplementary planes break into surrogates. + ch -= 0x1_0000_u32; + dst[0] = 0xD800_u16 | ((ch >> 10) as u16); + dst[1] = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16); + Some(2) + } else { + None } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index bdac686cb66bd..1e01da4e41d27 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -305,43 +305,52 @@ fn unwrap_or_0(opt: Option<&u8>) -> u8 { } } +/// Reads the next code point out of a byte iterator (assuming a +/// UTF-8-like encoding). +#[unstable] +pub fn next_code_point(bytes: &mut slice::Iter) -> Option { + // Decode UTF-8 + let x = match bytes.next() { + None => return None, + Some(&next_byte) if next_byte < 128 => return Some(next_byte as u32), + Some(&next_byte) => next_byte, + }; + + // Multibyte case follows + // Decode from a byte combination out of: [[[x y] z] w] + // NOTE: Performance is sensitive to the exact formulation here + let init = utf8_first_byte!(x, 2); + let y = unwrap_or_0(bytes.next()); + let mut ch = utf8_acc_cont_byte!(init, y); + if x >= 0xE0 { + // [[x y z] w] case + // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid + let z = unwrap_or_0(bytes.next()); + let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z); + ch = init << 12 | y_z; + if x >= 0xF0 { + // [x y z w] case + // use only the lower 3 bits of `init` + let w = unwrap_or_0(bytes.next()); + ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w); + } + } + + Some(ch) +} + #[stable] impl<'a> Iterator for Chars<'a> { type Item = char; #[inline] fn next(&mut self) -> Option { - // Decode UTF-8, using the valid UTF-8 invariant - let x = match self.iter.next() { - None => return None, - Some(&next_byte) if next_byte < 128 => return Some(next_byte as char), - Some(&next_byte) => next_byte, - }; - - // Multibyte case follows - // Decode from a byte combination out of: [[[x y] z] w] - // NOTE: Performance is sensitive to the exact formulation here - let init = utf8_first_byte!(x, 2); - let y = unwrap_or_0(self.iter.next()); - let mut ch = utf8_acc_cont_byte!(init, y); - if x >= 0xE0 { - // [[x y z] w] case - // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid - let z = unwrap_or_0(self.iter.next()); - let y_z = utf8_acc_cont_byte!((y & CONT_MASK) as u32, z); - ch = init << 12 | y_z; - if x >= 0xF0 { - // [x y z w] case - // use only the lower 3 bits of `init` - let w = unwrap_or_0(self.iter.next()); - ch = (init & 7) << 18 | utf8_acc_cont_byte!(y_z, w); + next_code_point(&mut self.iter).map(|ch| { + // str invariant says `ch` is a valid Unicode Scalar Value + unsafe { + mem::transmute(ch) } - } - - // str invariant says `ch` is a valid Unicode Scalar Value - unsafe { - Some(mem::transmute(ch)) - } + }) } #[inline] @@ -1517,25 +1526,8 @@ impl StrExt for str { #[inline] fn char_range_at(&self, i: uint) -> CharRange { - if self.as_bytes()[i] < 128u8 { - return CharRange {ch: self.as_bytes()[i] as char, next: i + 1 }; - } - - // Multibyte case is a fn to allow char_range_at to inline cleanly - fn multibyte_char_range_at(s: &str, i: uint) -> CharRange { - let mut val = s.as_bytes()[i] as u32; - let w = UTF8_CHAR_WIDTH[val as uint] as uint; - assert!((w != 0)); - - val = utf8_first_byte!(val, w); - val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 1]); - if w > 2 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 2]); } - if w > 3 { val = utf8_acc_cont_byte!(val, s.as_bytes()[i + 3]); } - - return CharRange {ch: unsafe { mem::transmute(val) }, next: i + w}; - } - - return multibyte_char_range_at(self, i); + let (c, n) = char_range_at_raw(self.as_bytes(), i); + CharRange { ch: unsafe { mem::transmute(c) }, next: n } } #[inline] @@ -1653,6 +1645,32 @@ impl StrExt for str { fn parse(&self) -> Option { FromStr::from_str(self) } } +/// Pluck a code point out of a UTF-8-like byte slice and return the +/// index of the next code point. +#[inline] +#[unstable] +pub fn char_range_at_raw(bytes: &[u8], i: uint) -> (u32, usize) { + if bytes[i] < 128u8 { + return (bytes[i] as u32, i + 1); + } + + // Multibyte case is a fn to allow char_range_at to inline cleanly + fn multibyte_char_range_at(bytes: &[u8], i: uint) -> (u32, usize) { + let mut val = bytes[i] as u32; + let w = UTF8_CHAR_WIDTH[val as uint] as uint; + assert!((w != 0)); + + val = utf8_first_byte!(val, w); + val = utf8_acc_cont_byte!(val, bytes[i + 1]); + if w > 2 { val = utf8_acc_cont_byte!(val, bytes[i + 2]); } + if w > 3 { val = utf8_acc_cont_byte!(val, bytes[i + 3]); } + + return (val, i + w); + } + + multibyte_char_range_at(bytes, i) +} + #[stable] impl<'a> Default for &'a str { #[stable] diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index cc86f804e3eb1..95ad6178babe2 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -17,4 +17,9 @@ pub use self::c_str::CString; pub use self::c_str::c_str_to_bytes; pub use self::c_str::c_str_to_bytes_with_nul; +pub use self::os_str::OsString; +pub use self::os_str::OsStr; +pub use self::os_str::AsOsStr; + mod c_str; +mod os_str; diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs new file mode 100644 index 0000000000000..9c5cf62f8534c --- /dev/null +++ b/src/libstd/ffi/os_str.rs @@ -0,0 +1,259 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A type that can represent all platform-native strings, but is cheaply +//! interconvertable with Rust strings. +//! +//! The need for this type arises from the fact that: +//! +//! * On Unix systems, strings are often arbitrary sequences of non-zero +//! bytes, in many cases interpreted as UTF-8. +//! +//! * On Windows, strings are often arbitrary sequences of non-zero 16-bit +//! values, interpreted as UTF-16 when it is valid to do so. +//! +//! * In Rust, strings are always valid UTF-8, but may contain zeros. +//! +//! The types in this module bridge this gap by simultaneously representing Rust +//! and platform-native string values, and in particular allowing a Rust string +//! to be converted into an "OS" string with no cost. +//! +//! **Note**: At the moment, these types are extremely bare-bones, usable only +//! for conversion to/from various other string types. Eventually these types +//! will offer a full-fledged string API. + +#![unstable = "recently added as part of path/io reform"] + +use core::prelude::*; + +use core::borrow::{BorrowFrom, ToOwned}; +use fmt::{self, Debug}; +use mem; +use string::{String, CowString}; +use ops; +use cmp; +use hash::{Hash, Hasher, Writer}; + +use sys::os_str::{Buf, Slice}; +use sys_common::{AsInner, IntoInner, FromInner}; + +/// Owned, mutable OS strings. +#[derive(Clone)] +pub struct OsString { + inner: Buf +} + +/// Slices into OS strings. +pub struct OsStr { + inner: Slice +} + +impl OsString { + /// Constructs an `OsString` at no cost by consuming a `String`. + pub fn from_string(s: String) -> OsString { + OsString { inner: Buf::from_string(s) } + } + + /// Constructs an `OsString` by copying from a `&str` slice. + /// + /// Equivalent to: `OsString::from_string(String::from_str(s))`. + pub fn from_str(s: &str) -> OsString { + OsString { inner: Buf::from_str(s) } + } + + /// Convert the `OsString` into a `String` if it contains valid Unicode data. + /// + /// On failure, ownership of the original `OsString` is returned. + pub fn into_string(self) -> Result { + self.inner.into_string().map_err(|buf| OsString { inner: buf} ) + } + + /// Extend the string with the given `&OsStr` slice. + pub fn push_os_str(&mut self, s: &OsStr) { + self.inner.push_slice(&s.inner) + } +} + +impl ops::Index for OsString { + type Output = OsStr; + + #[inline] + fn index(&self, _index: &ops::FullRange) -> &OsStr { + unsafe { mem::transmute(self.inner.as_slice()) } + } +} + +impl ops::Deref for OsString { + type Target = OsStr; + + #[inline] + fn deref(&self) -> &OsStr { + &self[] + } +} + +impl Debug for OsString { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt::Debug::fmt(&**self, formatter) + } +} + +impl OsStr { + /// Coerce directly from a `&str` slice to a `&OsStr` slice. + pub fn from_str(s: &str) -> &OsStr { + unsafe { mem::transmute(Slice::from_str(s)) } + } + + /// Yield a `&str` slice if the `OsStr` is valid unicode. + /// + /// This conversion may entail doing a check for UTF-8 validity. + pub fn to_str(&self) -> Option<&str> { + self.inner.to_str() + } + + /// Convert an `OsStr` to a `CowString`. + /// + /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER. + pub fn to_string_lossy(&self) -> CowString { + self.inner.to_string_lossy() + } + + /// Copy the slice into an onwed `OsString`. + pub fn to_os_string(&self) -> OsString { + OsString { inner: self.inner.to_owned() } + } + + /// Get the underlying byte representation. + /// + /// Note: it is *crucial* that this API is private, to avoid + /// revealing the internal, platform-specific encodings. + fn bytes(&self) -> &[u8] { + unsafe { mem::transmute(&self.inner) } + } +} + +impl PartialEq for OsStr { + fn eq(&self, other: &OsStr) -> bool { + self.bytes().eq(other.bytes()) + } +} + +impl PartialEq for OsStr { + fn eq(&self, other: &str) -> bool { + *self == *OsStr::from_str(other) + } +} + +impl PartialEq for str { + fn eq(&self, other: &OsStr) -> bool { + *other == *OsStr::from_str(self) + } +} + +impl Eq for OsStr {} + +impl PartialOrd for OsStr { + #[inline] + fn partial_cmp(&self, other: &OsStr) -> Option { + self.bytes().partial_cmp(other.bytes()) + } + #[inline] + fn lt(&self, other: &OsStr) -> bool { self.bytes().lt(other.bytes()) } + #[inline] + fn le(&self, other: &OsStr) -> bool { self.bytes().le(other.bytes()) } + #[inline] + fn gt(&self, other: &OsStr) -> bool { self.bytes().gt(other.bytes()) } + #[inline] + fn ge(&self, other: &OsStr) -> bool { self.bytes().ge(other.bytes()) } +} + +impl PartialOrd for OsStr { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + self.partial_cmp(OsStr::from_str(other)) + } +} + +// FIXME (#19470): cannot provide PartialOrd for str until we +// have more flexible coherence rules. + +impl Ord for OsStr { + #[inline] + fn cmp(&self, other: &OsStr) -> cmp::Ordering { self.bytes().cmp(other.bytes()) } +} + +impl<'a, S: Hasher + Writer> Hash for OsStr { + #[inline] + fn hash(&self, state: &mut S) { + self.bytes().hash(state) + } +} + +impl Debug for OsStr { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.inner.fmt(formatter) + } +} + +impl BorrowFrom for OsStr { + fn borrow_from(owned: &OsString) -> &OsStr { &owned[] } +} + +impl ToOwned for OsStr { + fn to_owned(&self) -> OsString { self.to_os_string() } +} + +/// Freely convertible to an `&OsStr` slice. +pub trait AsOsStr { + /// Convert to an `&OsStr` slice. + fn as_os_str(&self) -> &OsStr; +} + +impl AsOsStr for OsStr { + fn as_os_str(&self) -> &OsStr { + self + } +} + +impl AsOsStr for OsString { + fn as_os_str(&self) -> &OsStr { + &self[] + } +} + +impl AsOsStr for str { + fn as_os_str(&self) -> &OsStr { + OsStr::from_str(self) + } +} + +impl AsOsStr for String { + fn as_os_str(&self) -> &OsStr { + OsStr::from_str(&self[]) + } +} + +impl FromInner for OsString { + fn from_inner(buf: Buf) -> OsString { + OsString { inner: buf } + } +} + +impl IntoInner for OsString { + fn into_inner(self) -> Buf { + self.inner + } +} + +impl AsInner for OsStr { + fn as_inner(&self) -> &Slice { + &self.inner + } +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index a31dcc9884f46..272cf9bd0c07a 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -29,6 +29,7 @@ pub mod stack; pub mod thread; pub mod thread_info; pub mod thread_local; +pub mod wtf8; // common error constructors @@ -93,11 +94,21 @@ pub fn keep_going(data: &[u8], mut f: F) -> i64 where return (origamt - amt) as i64; } -// A trait for extracting representations from std::io types -pub trait AsInner { +/// A trait for viewing representations from std types +pub trait AsInner { fn as_inner(&self) -> &Inner; } +/// A trait for extracting representations from std types +pub trait IntoInner { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations +pub trait FromInner { + fn from_inner(inner: Inner) -> Self; +} + pub trait ProcessConfig { fn program(&self) -> &CString; fn args(&self) -> &[CString]; diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs new file mode 100644 index 0000000000000..bc0721550634c --- /dev/null +++ b/src/libstd/sys/common/wtf8.rs @@ -0,0 +1,1212 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). +//! +//! This library uses Rust’s type system to maintain +//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), +//! like the `String` and `&str` types do for UTF-8. +//! +//! Since [WTF-8 must not be used +//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), +//! this library deliberately does not provide access to the underlying bytes +//! of WTF-8 strings, +//! nor can it decode WTF-8 from arbitrary bytes. +//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points. + +use core::prelude::*; + +use core::char::{encode_utf8_raw, encode_utf16_raw}; +use core::str::{char_range_at_raw, next_code_point}; +use core::raw::Slice as RawSlice; + +use borrow::Cow; +use cmp; +use fmt; +use hash::{Hash, Writer, Hasher}; +use iter::FromIterator; +use mem; +use num::Int; +use ops; +use slice; +use str; +use string::{String, CowString}; +use unicode::str::{Utf16Item, utf16_items}; +use vec::Vec; + +static UTF8_REPLACEMENT_CHARACTER: &'static [u8] = b"\xEF\xBF\xBD"; + +/// A Unicode code point: from U+0000 to U+10FFFF. +/// +/// Compare with the `char` type, +/// which represents a Unicode scalar value: +/// a code point that is not a surrogate (U+D800 to U+DFFF). +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub struct CodePoint { + value: u32 +} + +/// Format the code point as `U+` followed by four to six hexadecimal digits. +/// Example: `U+1F4A9` +impl fmt::Debug for CodePoint { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(formatter, "U+{:04X}", self.value) + } +} + +impl CodePoint { + /// Unsafely create a new `CodePoint` without checking the value. + /// + /// Only use when `value` is known to be less than or equal to 0x10FFFF. + #[inline] + pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint { + CodePoint { value: value } + } + + /// Create a new `CodePoint` if the value is a valid code point. + /// + /// Return `None` if `value` is above 0x10FFFF. + #[inline] + pub fn from_u32(value: u32) -> Option { + match value { + 0 ... 0x10FFFF => Some(CodePoint { value: value }), + _ => None + } + } + + /// Create a new `CodePoint` from a `char`. + /// + /// Since all Unicode scalar values are code points, this always succeds. + #[inline] + pub fn from_char(value: char) -> CodePoint { + CodePoint { value: value as u32 } + } + + /// Return the numeric value of the code point. + #[inline] + pub fn to_u32(&self) -> u32 { + self.value + } + + /// Optionally return a Unicode scalar value for the code point. + /// + /// Return `None` if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char(&self) -> Option { + match self.value { + 0xD800 ... 0xDFFF => None, + _ => Some(unsafe { mem::transmute(self.value) }) + } + } + + /// Return a Unicode scalar value for the code point. + /// + /// Return `'\u{FFFD}'` (the replacement character “�”) + /// if the code point is a surrogate (from U+D800 to U+DFFF). + #[inline] + pub fn to_char_lossy(&self) -> char { + self.to_char().unwrap_or('\u{FFFD}') + } +} + +/// An owned, growable string of well-formed WTF-8 data. +/// +/// Similar to `String`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)] +pub struct Wtf8Buf { + bytes: Vec +} + +impl ops::Deref for Wtf8Buf { + type Target = Wtf8; + + fn deref(&self) -> &Wtf8 { + self.as_slice() + } +} + +/// Format the string with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8Buf { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl Wtf8Buf { + /// Create an new, empty WTF-8 string. + #[inline] + pub fn new() -> Wtf8Buf { + Wtf8Buf { bytes: Vec::new() } + } + + /// Create an new, empty WTF-8 string with pre-allocated capacity for `n` bytes. + #[inline] + pub fn with_capacity(n: uint) -> Wtf8Buf { + Wtf8Buf { bytes: Vec::with_capacity(n) } + } + + /// Create a WTF-8 string from an UTF-8 `String`. + /// + /// This takes ownership of the `String` and does not copy. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_string(string: String) -> Wtf8Buf { + Wtf8Buf { bytes: string.into_bytes() } + } + + /// Create a WTF-8 string from an UTF-8 `&str` slice. + /// + /// This copies the content of the slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(str: &str) -> Wtf8Buf { + Wtf8Buf { bytes: slice::SliceExt::to_vec(str.as_bytes()) } + } + + /// Create a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + pub fn from_wide(v: &[u16]) -> Wtf8Buf { + let mut string = Wtf8Buf::with_capacity(v.len()); + for item in utf16_items(v) { + match item { + Utf16Item::ScalarValue(c) => string.push_char(c), + Utf16Item::LoneSurrogate(s) => { + // Surrogates are known to be in the code point range. + let code_point = unsafe { CodePoint::from_u32_unchecked(s as u32) }; + // Skip the WTF-8 concatenation check, + // surrogate pairs are already decoded by utf16_items + string.push_code_point_unchecked(code_point) + } + } + } + string + } + + /// Copied from String::push + /// This does **not** include the WTF-8 concatenation check. + fn push_code_point_unchecked(&mut self, code_point: CodePoint) { + let cur_len = self.len(); + // This may use up to 4 bytes. + self.reserve(4); + + unsafe { + // Attempt to not use an intermediate buffer by just pushing bytes + // directly onto this string. + let slice = RawSlice { + data: self.bytes.as_ptr().offset(cur_len as int), + len: 4, + }; + let used = encode_utf8_raw(code_point.value, mem::transmute(slice)) + .unwrap_or(0); + self.bytes.set_len(cur_len + used); + } + } + + #[inline] + pub fn as_slice(&self) -> &Wtf8 { + unsafe { mem::transmute(self.bytes.as_slice()) } + } + + /// Reserves capacity for at least `additional` more bytes to be inserted + /// in the given `Wtf8Buf`. + /// The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `uint`. + #[inline] + pub fn reserve(&mut self, additional: uint) { + self.bytes.reserve(additional) + } + + /// Returns the number of bytes that this string buffer can hold without reallocating. + #[inline] + pub fn capacity(&self) -> uint { + self.bytes.capacity() + } + + /// Append an UTF-8 slice at the end of the string. + #[inline] + pub fn push_str(&mut self, other: &str) { + self.bytes.push_all(other.as_bytes()) + } + + /// Append a WTF-8 slice at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push_wtf8(&mut self, other: &Wtf8) { + match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { + // Replace newly paired surrogates by a supplementary code point. + (Some(lead), Some(trail)) => { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + let other_without_trail_surrogate = &other.bytes[3..]; + // 4 bytes for the supplementary code point + self.bytes.reserve(4 + other_without_trail_surrogate.len()); + self.push_char(decode_surrogate_pair(lead, trail)); + self.bytes.push_all(other_without_trail_surrogate); + } + _ => self.bytes.push_all(&other.bytes) + } + } + + /// Append a Unicode scalar value at the end of the string. + #[inline] + pub fn push_char(&mut self, c: char) { + self.push_code_point_unchecked(CodePoint::from_char(c)) + } + + /// Append a code point at the end of the string. + /// + /// This replaces newly paired surrogates at the boundary + /// with a supplementary code point, + /// like concatenating ill-formed UTF-16 strings effectively would. + #[inline] + pub fn push(&mut self, code_point: CodePoint) { + match code_point.to_u32() { + trail @ 0xDC00...0xDFFF => { + match (&*self).final_lead_surrogate() { + Some(lead) => { + let len_without_lead_surrogate = self.len() - 3; + self.bytes.truncate(len_without_lead_surrogate); + self.push_char(decode_surrogate_pair(lead, trail as u16)); + return + } + _ => {} + } + } + _ => {} + } + + // No newly paired surrogates at the boundary. + self.push_code_point_unchecked(code_point) + } + + /// Shortens a string to the specified length. + /// + /// # Panics + /// + /// Panics if `new_len` > current length, + /// or if `new_len` is not a code point boundary. + #[inline] + pub fn truncate(&mut self, new_len: uint) { + assert!(is_code_point_boundary(self.as_slice(), new_len)); + self.bytes.truncate(new_len) + } + + /// Consume the WTF-8 string and try to convert it to UTF-8. + /// + /// This does not copy the data. + /// + /// If the contents are not well-formed UTF-8 + /// (that is, if the string contains surrogates), + /// the original WTF-8 string is returned instead. + pub fn into_string(self) -> Result { + match self.next_surrogate(0) { + None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }), + Some(_) => Err(self), + } + } + + /// Consume the WTF-8 string and convert it lossily to UTF-8. + /// + /// This does not copy the data (but may overwrite parts of it in place). + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) + pub fn into_string_lossy(mut self) -> String { + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + pos = surrogate_pos + 3; + slice::bytes::copy_memory( + &mut self.bytes[surrogate_pos .. pos], + UTF8_REPLACEMENT_CHARACTER + ); + }, + None => return unsafe { String::from_utf8_unchecked(self.bytes) } + } + } + } +} + +/// Create a new WTF-8 string from an iterator of code points. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl FromIterator for Wtf8Buf { + fn from_iter>(iterator: T) -> Wtf8Buf { + let mut string = Wtf8Buf::new(); + string.extend(iterator); + string + } +} + +/// Append code points from an iterator to the string. +/// +/// This replaces surrogate code point pairs with supplementary code points, +/// like concatenating ill-formed UTF-16 strings effectively would. +impl Extend for Wtf8Buf { + fn extend>(&mut self, mut iterator: T) { + let (low, _high) = iterator.size_hint(); + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(low); + for code_point in iterator { + self.push(code_point); + } + } +} + +/// A borrowed slice of well-formed WTF-8 data. +/// +/// Similar to `&str`, but can additionally contain surrogate code points +/// if they’re not in a surrogate pair. +pub struct Wtf8 { + bytes: [u8] +} + +// FIXME: https://github.com/rust-lang/rust/issues/18805 +impl PartialEq for Wtf8 { + fn eq(&self, other: &Wtf8) -> bool { self.bytes.eq(&other.bytes) } +} + +// FIXME: https://github.com/rust-lang/rust/issues/18805 +impl Eq for Wtf8 {} + +// FIXME: https://github.com/rust-lang/rust/issues/18738 +impl PartialOrd for Wtf8 { + #[inline] + fn partial_cmp(&self, other: &Wtf8) -> Option { + self.bytes.partial_cmp(&other.bytes) + } + #[inline] + fn lt(&self, other: &Wtf8) -> bool { self.bytes.lt(&other.bytes) } + #[inline] + fn le(&self, other: &Wtf8) -> bool { self.bytes.le(&other.bytes) } + #[inline] + fn gt(&self, other: &Wtf8) -> bool { self.bytes.gt(&other.bytes) } + #[inline] + fn ge(&self, other: &Wtf8) -> bool { self.bytes.ge(&other.bytes) } +} + +// FIXME: https://github.com/rust-lang/rust/issues/18738 +impl Ord for Wtf8 { + #[inline] + fn cmp(&self, other: &Wtf8) -> cmp::Ordering { self.bytes.cmp(&other.bytes) } +} + +/// Format the slice with double quotes, +/// and surrogates as `\u` followed by four hexadecimal digits. +/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800] +impl fmt::Debug for Wtf8 { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + try!(formatter.write_str("\"")); + let mut pos = 0; + loop { + match self.next_surrogate(pos) { + None => break, + Some((surrogate_pos, surrogate)) => { + try!(formatter.write_str(unsafe { + // the data in this slice is valid UTF-8, transmute to &str + mem::transmute(&self.bytes[pos .. surrogate_pos]) + })); + try!(write!(formatter, "\\u{{{:X}}}", surrogate)); + pos = surrogate_pos + 3; + } + } + } + try!(formatter.write_str(unsafe { + // the data in this slice is valid UTF-8, transmute to &str + mem::transmute(&self.bytes[pos..]) + })); + formatter.write_str("\"") + } +} + +impl Wtf8 { + /// Create a WTF-8 slice from a UTF-8 `&str` slice. + /// + /// Since WTF-8 is a superset of UTF-8, this always succeeds. + #[inline] + pub fn from_str(value: &str) -> &Wtf8 { + unsafe { mem::transmute(value.as_bytes()) } + } + + /// Return the length, in WTF-8 bytes. + #[inline] + pub fn len(&self) -> uint { + self.bytes.len() + } + + /// Return the code point at `position` if it is in the ASCII range, + /// or `b'\xFF' otherwise. + /// + /// # Panics + /// + /// Panics if `position` is beyond the end of the string. + #[inline] + pub fn ascii_byte_at(&self, position: uint) -> u8 { + match self.bytes[position] { + ascii_byte @ 0x00 ... 0x7F => ascii_byte, + _ => 0xFF + } + } + + /// Return the code point at `position`. + /// + /// # Panics + /// + /// Panics if `position` is not at a code point boundary, + /// or is beyond the end of the string. + #[inline] + pub fn code_point_at(&self, position: uint) -> CodePoint { + let (code_point, _) = self.code_point_range_at(position); + code_point + } + + /// Return the code point at `position` + /// and the position of the next code point. + /// + /// # Panics + /// + /// Panics if `position` is not at a code point boundary, + /// or is beyond the end of the string. + #[inline] + pub fn code_point_range_at(&self, position: uint) -> (CodePoint, uint) { + let (c, n) = char_range_at_raw(&self.bytes, position); + (CodePoint { value: c }, n) + } + + /// Return an iterator for the string’s code points. + #[inline] + pub fn code_points(&self) -> Wtf8CodePoints { + Wtf8CodePoints { bytes: self.bytes.iter() } + } + + /// Try to convert the string to UTF-8 and return a `&str` slice. + /// + /// Return `None` if the string contains surrogates. + /// + /// This does not copy the data. + #[inline] + pub fn as_str(&self) -> Option<&str> { + // Well-formed WTF-8 is also well-formed UTF-8 + // if and only if it contains no surrogate. + match self.next_surrogate(0) { + None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some(_) => None, + } + } + + /// Lossily convert the string to UTF-8. + /// Return an UTF-8 `&str` slice if the contents are well-formed in UTF-8. + /// + /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). + /// + /// This only copies the data if necessary (if it contains any surrogate). + pub fn to_string_lossy(&self) -> CowString { + let surrogate_pos = match self.next_surrogate(0) { + None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }), + Some((pos, _)) => pos, + }; + let wtf8_bytes = &self.bytes; + let mut utf8_bytes = Vec::with_capacity(self.len()); + utf8_bytes.push_all(&wtf8_bytes[..surrogate_pos]); + utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER); + let mut pos = surrogate_pos + 3; + loop { + match self.next_surrogate(pos) { + Some((surrogate_pos, _)) => { + utf8_bytes.push_all(&wtf8_bytes[pos .. surrogate_pos]); + utf8_bytes.push_all(UTF8_REPLACEMENT_CHARACTER); + pos = surrogate_pos + 3; + }, + None => { + utf8_bytes.push_all(&wtf8_bytes[pos..]); + return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }) + } + } + } + } + + /// Convert the WTF-8 string to potentially ill-formed UTF-16 + /// and return an iterator of 16-bit code units. + /// + /// This is lossless: + /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units + /// would always return the original WTF-8 string. + #[inline] + pub fn encode_wide(&self) -> EncodeWide { + EncodeWide { code_points: self.code_points(), extra: 0 } + } + + #[inline] + fn next_surrogate(&self, mut pos: uint) -> Option<(uint, u16)> { + let mut iter = self.bytes[pos..].iter(); + loop { + let b = match iter.next() { + None => return None, + Some(&b) => b, + }; + if b < 0x80 { + pos += 1; + } else if b < 0xE0 { + iter.next(); + pos += 2; + } else if b == 0xED { + match (iter.next(), iter.next()) { + (Some(&b2), Some(&b3)) if b2 >= 0xA0 => { + return Some((pos, decode_surrogate(b2, b3))) + } + _ => pos += 3 + } + } else if b < 0xF0 { + iter.next(); + iter.next(); + pos += 3; + } else { + iter.next(); + iter.next(); + iter.next(); + pos += 4; + } + } + } + + #[inline] + fn final_lead_surrogate(&self) -> Option { + let len = self.len(); + if len < 3 { + return None + } + match &self.bytes[(len - 3)..] { + [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)), + _ => None + } + } + + #[inline] + fn initial_trail_surrogate(&self) -> Option { + let len = self.len(); + if len < 3 { + return None + } + match &self.bytes[..3] { + [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)), + _ => None + } + } +} + + +/// Return a slice of the given string for the byte range [`begin`..`end`). +/// +/// # Panics +/// +/// Panics when `begin` and `end` do not point to code point boundaries, +/// or point beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::Range) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if range.start <= range.end && + is_code_point_boundary(self, range.start) && + is_code_point_boundary(self, range.end) { + unsafe { slice_unchecked(self, range.start, range.end) } + } else { + slice_error_fail(self, range.start, range.end) + } + } +} + +/// Return a slice of the given string from byte `begin` to its end. +/// +/// # Panics +/// +/// Panics when `begin` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::RangeFrom) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.start) { + unsafe { slice_unchecked(self, range.start, self.len()) } + } else { + slice_error_fail(self, range.start, self.len()) + } + } +} + +/// Return a slice of the given string from its beginning to byte `end`. +/// +/// # Panics +/// +/// Panics when `end` is not at a code point boundary, +/// or is beyond the end of the string. +impl ops::Index> for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, range: &ops::RangeTo) -> &Wtf8 { + // is_code_point_boundary checks that the index is in [0, .len()] + if is_code_point_boundary(self, range.end) { + unsafe { slice_unchecked(self, 0, range.end) } + } else { + slice_error_fail(self, 0, range.end) + } + } +} + +impl ops::Index for Wtf8 { + type Output = Wtf8; + + #[inline] + fn index(&self, _range: &ops::FullRange) -> &Wtf8 { + self + } +} + +#[inline] +fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 { + // The first byte is assumed to be 0xED + 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F +} + +#[inline] +fn decode_surrogate_pair(lead: u16, trail: u16) -> char { + let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); + unsafe { mem::transmute(code_point) } +} + +/// Copied from core::str::StrPrelude::is_char_boundary +#[inline] +pub fn is_code_point_boundary(slice: &Wtf8, index: uint) -> bool { + if index == slice.len() { return true; } + match slice.bytes.get(index) { + None => false, + Some(&b) => b < 128u8 || b >= 192u8, + } +} + +/// Copied from core::str::raw::slice_unchecked +#[inline] +pub unsafe fn slice_unchecked(s: &Wtf8, begin: uint, end: uint) -> &Wtf8 { + mem::transmute(RawSlice { + data: s.bytes.as_ptr().offset(begin as int), + len: end - begin, + }) +} + +/// Copied from core::str::raw::slice_error_fail +#[inline(never)] +pub fn slice_error_fail(s: &Wtf8, begin: uint, end: uint) -> ! { + assert!(begin <= end); + panic!("index {} and/or {} in `{:?}` do not lie on character boundary", + begin, end, s); +} + +/// Iterator for the code points of a WTF-8 string. +/// +/// Created with the method `.code_points()`. +#[derive(Clone)] +pub struct Wtf8CodePoints<'a> { + bytes: slice::Iter<'a, u8> +} + +impl<'a> Iterator for Wtf8CodePoints<'a> { + type Item = CodePoint; + + #[inline] + fn next(&mut self) -> Option { + next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) + } + + #[inline] + fn size_hint(&self) -> (uint, Option) { + let (len, _) = self.bytes.size_hint(); + (len.saturating_add(3) / 4, Some(len)) + } +} + +#[derive(Clone)] +pub struct EncodeWide<'a> { + code_points: Wtf8CodePoints<'a>, + extra: u16 +} + +// Copied from libunicode/u_str.rs +impl<'a> Iterator for EncodeWide<'a> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0u16; 2]; + self.code_points.next().map(|code_point| { + let n = encode_utf16_raw(code_point.value, buf.as_mut_slice()) + .unwrap_or(0); + if n == 2 { self.extra = buf[1]; } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (uint, Option) { + let (low, high) = self.code_points.size_hint(); + // every code point gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +impl Hash for CodePoint { + #[inline] + fn hash(&self, state: &mut S) { + self.value.hash(state) + } +} + +impl Hash for Wtf8Buf { + #[inline] + fn hash(&self, state: &mut S) { + state.write(self.bytes.as_slice()); + 0xfeu8.hash(state) + } +} + +impl<'a, S: Writer + Hasher> Hash for Wtf8 { + #[inline] + fn hash(&self, state: &mut S) { + state.write(&self.bytes); + 0xfeu8.hash(state) + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use borrow::Cow; + use super::*; + use mem::transmute; + use string::CowString; + + #[test] + fn code_point_from_u32() { + assert!(CodePoint::from_u32(0).is_some()); + assert!(CodePoint::from_u32(0xD800).is_some()); + assert!(CodePoint::from_u32(0x10FFFF).is_some()); + assert!(CodePoint::from_u32(0x110000).is_none()); + } + + #[test] + fn code_point_to_u32() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0).to_u32(), 0); + assert_eq!(c(0xD800).to_u32(), 0xD800); + assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF); + } + + #[test] + fn code_point_from_char() { + assert_eq!(CodePoint::from_char('a').to_u32(), 0x61); + assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9); + } + + #[test] + fn code_point_to_string() { + assert_eq!(format!("{:?}", CodePoint::from_char('a')).as_slice(), "U+0061"); + assert_eq!(format!("{:?}", CodePoint::from_char('💩')).as_slice(), "U+1F4A9"); + } + + #[test] + fn code_point_to_char() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0x61).to_char(), Some('a')); + assert_eq!(c(0x1F4A9).to_char(), Some('💩')); + assert_eq!(c(0xD800).to_char(), None); + } + + #[test] + fn code_point_to_char_lossy() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + assert_eq!(c(0x61).to_char_lossy(), 'a'); + assert_eq!(c(0x1F4A9).to_char_lossy(), '💩'); + assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}'); + } + + #[test] + fn wtf8buf_new() { + assert_eq!(Wtf8Buf::new().bytes.as_slice(), b""); + } + + #[test] + fn wtf8buf_from_str() { + assert_eq!(Wtf8Buf::from_str("").bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_str("aé 💩").bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_from_string() { + assert_eq!(Wtf8Buf::from_string(String::from_str("")).bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_string(String::from_str("aé 💩")).bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_from_wide() { + assert_eq!(Wtf8Buf::from_wide(&[]).bytes.as_slice(), b""); + assert_eq!(Wtf8Buf::from_wide( + &[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes.as_slice(), + b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push_str() { + let mut string = Wtf8Buf::new(); + assert_eq!(string.bytes.as_slice(), b""); + string.push_str("aé 💩"); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push_char() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 "); + string.push_char('💩'); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8buf_push() { + let mut string = Wtf8Buf::from_str("aé "); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 "); + string.push(CodePoint::from_char('💩')); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push(c(0xD83D)); // lead + string.push(c(0x20)); // not surrogate + string.push(c(0xDCA9)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xDBFF)); // lead + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD800)); // lead + string.push(c(0xE000)); // not surrogate + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xD7FF)); // not surrogate + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0x61)); // not surrogate, < 3 bytes + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push(c(0xDC00)); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_push_wtf8() { + let mut string = Wtf8Buf::from_str("aé"); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9"); + string.push_wtf8(Wtf8::from_str(" 💩")); + assert_eq!(string.bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + fn w(value: &[u8]) -> &Wtf8 { unsafe { transmute(value) } } + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\xBD")); // lead + string.push_wtf8(w(b" ")); // not surrogate + string.push_wtf8(w(b"\xED\xB2\xA9")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xED\xAF\xBF")); // lead + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xA0\x80")); // lead + string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate + assert_eq!(string.bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\x61\xED\xB0\x80"); + + let mut string = Wtf8Buf::new(); + string.push_wtf8(w(b"\xED\xB0\x80")); // trail + assert_eq!(string.bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_truncate() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(1); + assert_eq!(string.bytes.as_slice(), b"a"); + } + + #[test] + #[should_fail] + fn wtf8buf_truncate_fail_code_point_boundary() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(2); + } + + #[test] + #[should_fail] + fn wtf8buf_truncate_fail_longer() { + let mut string = Wtf8Buf::from_str("aé"); + string.truncate(4); + } + + #[test] + fn wtf8buf_into_string() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string(), Ok(String::from_str("aé 💩"))); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string(), Err(string)); + } + + #[test] + fn wtf8buf_into_string_lossy() { + let mut string = Wtf8Buf::from_str("aé 💩"); + assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩")); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.clone().into_string_lossy(), String::from_str("aé 💩�")); + } + + #[test] + fn wtf8buf_from_iterator() { + fn f(values: &[u32]) -> Wtf8Buf { + values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::() + }; + assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes.as_slice(), b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(f(&[0xD83D, 0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(f(&[0xD800, 0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(f(&[0xD800, 0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(f(&[0xD7FF, 0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(f(&[0x61, 0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80"); + assert_eq!(f(&[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_extend() { + fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf { + fn c(value: &u32) -> CodePoint { CodePoint::from_u32(*value).unwrap() } + let mut string = initial.iter().map(c).collect::(); + string.extend(extended.iter().map(c)); + string + }; + + assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes.as_slice(), + b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + + assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes.as_slice(), b"\xF0\x9F\x92\xA9"); // Magic! + assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes.as_slice(), b"\xED\xA0\xBD \xED\xB2\xA9"); + assert_eq!(e(&[0xD800], &[0xDBFF]).bytes.as_slice(), b"\xED\xA0\x80\xED\xAF\xBF"); + assert_eq!(e(&[0xD800], &[0xE000]).bytes.as_slice(), b"\xED\xA0\x80\xEE\x80\x80"); + assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes.as_slice(), b"\xED\x9F\xBF\xED\xB0\x80"); + assert_eq!(e(&[0x61], &[0xDC00]).bytes.as_slice(), b"\x61\xED\xB0\x80"); + assert_eq!(e(&[], &[0xDC00]).bytes.as_slice(), b"\xED\xB0\x80"); + } + + #[test] + fn wtf8buf_show() { + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(format!("{:?}", string).as_slice(), r#""aé 💩\u{D800}""#); + } + + #[test] + fn wtf8buf_as_slice() { + assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé")); + } + + #[test] + fn wtf8_show() { + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(format!("{:?}", string.as_slice()).as_slice(), r#""aé 💩\u{D800}""#); + } + + #[test] + fn wtf8_from_str() { + assert_eq!(&Wtf8::from_str("").bytes, b""); + assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + fn wtf8_len() { + assert_eq!(Wtf8::from_str("").len(), 0); + assert_eq!(Wtf8::from_str("aé 💩").len(), 8); + } + + #[test] + fn wtf8_slice() { + assert_eq!(&Wtf8::from_str("aé 💩")[1.. 4].bytes, b"\xC3\xA9 "); + } + + #[test] + #[should_fail] + fn wtf8_slice_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2.. 4]; + } + + #[test] + fn wtf8_slice_from() { + assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9"); + } + + #[test] + #[should_fail] + fn wtf8_slice_from_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[2..]; + } + + #[test] + fn wtf8_slice_to() { + assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 "); + } + + #[test] + #[should_fail] + fn wtf8_slice_to_not_code_point_boundary() { + &Wtf8::from_str("aé 💩")[5..]; + } + + #[test] + fn wtf8_ascii_byte_at() { + let slice = Wtf8::from_str("aé 💩"); + assert_eq!(slice.ascii_byte_at(0), b'a'); + assert_eq!(slice.ascii_byte_at(1), b'\xFF'); + assert_eq!(slice.ascii_byte_at(2), b'\xFF'); + assert_eq!(slice.ascii_byte_at(3), b' '); + assert_eq!(slice.ascii_byte_at(4), b'\xFF'); + } + + #[test] + fn wtf8_code_point_at() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.code_point_at(0), CodePoint::from_char('a')); + assert_eq!(string.code_point_at(1), CodePoint::from_char('é')); + assert_eq!(string.code_point_at(3), CodePoint::from_char(' ')); + assert_eq!(string.code_point_at(4), CodePoint::from_u32(0xD83D).unwrap()); + assert_eq!(string.code_point_at(7), CodePoint::from_char('💩')); + } + + #[test] + fn wtf8_code_point_range_at() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.code_point_range_at(0), (CodePoint::from_char('a'), 1)); + assert_eq!(string.code_point_range_at(1), (CodePoint::from_char('é'), 3)); + assert_eq!(string.code_point_range_at(3), (CodePoint::from_char(' '), 4)); + assert_eq!(string.code_point_range_at(4), (CodePoint::from_u32(0xD83D).unwrap(), 7)); + assert_eq!(string.code_point_range_at(7), (CodePoint::from_char('💩'), 11)); + } + + #[test] + fn wtf8_code_points() { + fn c(value: u32) -> CodePoint { CodePoint::from_u32(value).unwrap() } + fn cp(string: &Wtf8Buf) -> Vec> { + string.code_points().map(|c| c.to_char()).collect::>() + } + let mut string = Wtf8Buf::from_str("é "); + assert_eq!(cp(&string), vec![Some('é'), Some(' ')]); + string.push(c(0xD83D)); + assert_eq!(cp(&string), vec![Some('é'), Some(' '), None]); + string.push(c(0xDCA9)); + assert_eq!(cp(&string), vec![Some('é'), Some(' '), Some('💩')]); + } + + #[test] + fn wtf8_as_str() { + assert_eq!(Wtf8::from_str("").as_str(), Some("")); + assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); + let mut string = Wtf8Buf::new(); + string.push(CodePoint::from_u32(0xD800).unwrap()); + assert_eq!(string.as_str(), None); + } + + #[test] + fn wtf8_to_string_lossy() { + assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed("")); + assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); + let mut string = Wtf8Buf::from_str("aé 💩"); + string.push(CodePoint::from_u32(0xD800).unwrap()); + let expected: CowString = Cow::Owned(String::from_str("aé 💩�")); + assert_eq!(string.to_string_lossy(), expected); + } + + #[test] + fn wtf8_encode_wide() { + let mut string = Wtf8Buf::from_str("aé "); + string.push(CodePoint::from_u32(0xD83D).unwrap()); + string.push_char('💩'); + assert_eq!(string.encode_wide().collect::>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]); + } +} diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs index 0e4a9d1b307f7..9c659b56d9380 100644 --- a/src/libstd/sys/unix/ext.rs +++ b/src/libstd/sys/unix/ext.rs @@ -31,7 +31,10 @@ #![unstable] -use sys_common::AsInner; +use vec::Vec; +use sys::os_str::Buf; +use sys_common::{AsInner, IntoInner, FromInner}; +use ffi::{OsStr, OsString}; use libc; use io; @@ -99,6 +102,36 @@ impl AsRawFd for io::net::udp::UdpSocket { } } +// Unix-specific extensions to `OsString`. +pub trait OsStringExt { + /// Create an `OsString` from a byte vector. + fn from_vec(vec: Vec) -> Self; + + /// Yield the underlying byte vector of this `OsString`. + fn into_vec(self) -> Vec; +} + +impl OsStringExt for OsString { + fn from_vec(vec: Vec) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + + fn into_vec(self) -> Vec { + self.into_inner().inner + } +} + +// Unix-specific extensions to `OsStr`. +pub trait OsStrExt { + fn as_byte_slice(&self) -> &[u8]; +} + +impl OsStrExt for OsStr { + fn as_byte_slice(&self) -> &[u8] { + &self.as_inner().inner + } +} + /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index bb98d1e052a1c..5493bc20a87ef 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -44,6 +44,7 @@ pub mod fs; pub mod helper_signal; pub mod mutex; pub mod os; +pub mod os_str; pub mod pipe; pub mod process; pub mod rwlock; diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs new file mode 100644 index 0000000000000..3dd89ad07593a --- /dev/null +++ b/src/libstd/sys/unix/os_str.rs @@ -0,0 +1,86 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec`/`[u8]`. + +use core::prelude::*; + +use fmt::{self, Debug}; +use vec::Vec; +use slice::SliceExt as StdSliceExt; +use str; +use string::{String, CowString}; +use mem; + +#[derive(Clone)] +pub struct Buf { + pub inner: Vec +} + +pub struct Slice { + pub inner: [u8] +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.to_string_lossy().fmt(formatter) + } +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + pub fn from_str(s: &str) -> Buf { + Buf { inner: s.as_bytes().to_vec() } + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn into_string(self) -> Result { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_all(&s.inner) + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(s.as_bytes()) } + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> CowString { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } +} diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs index 87ff31ab73cda..de37c4282884c 100644 --- a/src/libstd/sys/windows/ext.rs +++ b/src/libstd/sys/windows/ext.rs @@ -16,7 +16,11 @@ #![unstable] -use sys_common::AsInner; +pub use sys_common::wtf8::{Wtf8Buf, EncodeWide}; + +use sys::os_str::Buf; +use sys_common::{AsInner, FromInner}; +use ffi::{OsStr, OsString}; use libc; use io; @@ -92,9 +96,35 @@ impl AsRawSocket for io::net::udp::UdpSocket { } } +// Windows-specific extensions to `OsString`. +pub trait OsStringExt { + /// Create an `OsString` from a potentially ill-formed UTF-16 slice of 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + fn from_wide(wide: &[u16]) -> Self; +} + +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +// Windows-specific extensions to `OsStr`. +pub trait OsStrExt { + fn encode_wide(&self) -> EncodeWide; +} + +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide { + self.as_inner().inner.encode_wide() + } +} + /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. pub mod prelude { - pub use super::{Socket, Handle, AsRawSocket, AsRawHandle}; + pub use super::{Socket, Handle, AsRawSocket, AsRawHandle, OsStrExt, OsStringExt}; } diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 72fc2f8700d6f..876159623ac99 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -44,6 +44,7 @@ pub mod fs; pub mod helper_signal; pub mod mutex; pub mod os; +pub mod os_str; pub mod pipe; pub mod process; pub mod rwlock; diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs new file mode 100644 index 0000000000000..aab2406cef92b --- /dev/null +++ b/src/libstd/sys/windows/os_str.rs @@ -0,0 +1,82 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Windows is a +/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. + +use fmt::{self, Debug}; +use sys_common::wtf8::{Wtf8, Wtf8Buf}; +use string::{String, CowString}; +use result::Result; +use option::Option; +use mem; + +#[derive(Clone)] +pub struct Buf { + pub inner: Wtf8Buf +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +pub struct Slice { + pub inner: Wtf8 +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.inner.fmt(formatter) + } +} + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: Wtf8Buf::from_string(s) } + } + + pub fn from_str(s: &str) -> Buf { + Buf { inner: Wtf8Buf::from_str(s) } + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(self.inner.as_slice()) } + } + + pub fn into_string(self) -> Result { + self.inner.into_string().map_err(|buf| Buf { inner: buf }) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_wtf8(&s.inner) + } +} + +impl Slice { + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(Wtf8::from_str(s)) } + } + + pub fn to_str(&self) -> Option<&str> { + self.inner.as_str() + } + + pub fn to_string_lossy(&self) -> CowString { + self.inner.to_string_lossy() + } + + pub fn to_owned(&self) -> Buf { + let mut buf = Wtf8Buf::with_capacity(self.inner.len()); + buf.push_wtf8(&self.inner); + Buf { inner: buf } + } +}