4707 lines
142 KiB
Rust
4707 lines
142 KiB
Rust
// Copyright 2012-2014 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 <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.
|
|
|
|
/*!
|
|
|
|
Unicode string manipulation (`str` type)
|
|
|
|
# Basic Usage
|
|
|
|
Rust's string type is one of the core primitive types of the language. While
|
|
represented by the name `str`, the name `str` is not actually a valid type in
|
|
Rust. Each string must also be decorated with its ownership. This means that
|
|
there are two common kinds of strings in rust:
|
|
|
|
* `~str` - This is an owned string. This type obeys all of the normal semantics
|
|
of the `~T` types, meaning that it has one, and only one, owner. This
|
|
type cannot be implicitly copied, and is moved out of when passed to
|
|
other functions.
|
|
|
|
* `&str` - This is the borrowed string type. This type of string can only be
|
|
created from the other kind of string. As the name "borrowed"
|
|
implies, this type of string is owned elsewhere, and this string
|
|
cannot be moved out of.
|
|
|
|
As an example, here's a few different kinds of strings.
|
|
|
|
```rust
|
|
fn main() {
|
|
let owned_string = ~"I am an owned string";
|
|
let borrowed_string1 = "This string is borrowed with the 'static lifetime";
|
|
let borrowed_string2: &str = owned_string; // owned strings can be borrowed
|
|
}
|
|
```
|
|
|
|
From the example above, you can see that rust has 2 different kinds of string
|
|
literals. The owned literals correspond to the owned string types, but the
|
|
"borrowed literal" is actually more akin to C's concept of a static string.
|
|
|
|
When a string is declared without a `~` sigil, then the string is allocated
|
|
statically in the rodata of the executable/library. The string then has the
|
|
type `&'static str` meaning that the string is valid for the `'static`
|
|
lifetime, otherwise known as the lifetime of the entire program. As can be
|
|
inferred from the type, these static strings are not mutable.
|
|
|
|
# Mutability
|
|
|
|
Many languages have immutable strings by default, and rust has a particular
|
|
flavor on this idea. As with the rest of Rust types, strings are immutable by
|
|
default. If a string is declared as `mut`, however, it may be mutated. This
|
|
works the same way as the rest of Rust's type system in the sense that if
|
|
there's a mutable reference to a string, there may only be one mutable reference
|
|
to that string. With these guarantees, strings can easily transition between
|
|
being mutable/immutable with the same benefits of having mutable strings in
|
|
other languages.
|
|
|
|
```rust
|
|
let mut buf = ~"testing";
|
|
buf.push_char(' ');
|
|
buf.push_str("123");
|
|
assert_eq!(buf, ~"testing 123");
|
|
```
|
|
|
|
# Representation
|
|
|
|
Rust's string type, `str`, is a sequence of unicode codepoints encoded as a
|
|
stream of UTF-8 bytes. All safely-created strings are guaranteed to be validly
|
|
encoded UTF-8 sequences. Additionally, strings are not null-terminated
|
|
and can contain null codepoints.
|
|
|
|
The actual representation of strings have direct mappings to vectors:
|
|
|
|
* `~str` is the same as `~[u8]`
|
|
* `&str` is the same as `&[u8]`
|
|
|
|
*/
|
|
|
|
use cast;
|
|
use cast::transmute;
|
|
use char;
|
|
use char::Char;
|
|
use clone::{Clone, DeepClone};
|
|
use cmp::{Eq, TotalEq, Ord, TotalOrd, Equiv, Ordering};
|
|
use container::{Container, Mutable};
|
|
use fmt;
|
|
use iter::{Iterator, FromIterator, Extendable, range};
|
|
use iter::{Filter, AdditiveIterator, Map};
|
|
use iter::{Rev, DoubleEndedIterator, ExactSize};
|
|
use libc;
|
|
use num::Saturating;
|
|
use option::{None, Option, Some};
|
|
use ptr;
|
|
use ptr::RawPtr;
|
|
use to_str::ToStr;
|
|
use from_str::FromStr;
|
|
use vec;
|
|
use vec::{OwnedVector, OwnedCloneableVector, ImmutableVector, MutableVector};
|
|
use default::Default;
|
|
use to_bytes::{IterBytes, Cb};
|
|
use unstable::raw::Repr;
|
|
|
|
/*
|
|
Section: Creating a string
|
|
*/
|
|
|
|
/// Consumes a vector of bytes to create a new utf-8 string.
|
|
/// Returns None if the vector contains invalid UTF-8.
|
|
pub fn from_utf8_owned(vv: ~[u8]) -> Option<~str> {
|
|
if is_utf8(vv) {
|
|
Some(unsafe { raw::from_utf8_owned(vv) })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Converts a vector to a string slice without performing any allocations.
|
|
///
|
|
/// Once the slice has been validated as utf-8, it is transmuted in-place and
|
|
/// returned as a '&str' instead of a '&[u8]'
|
|
///
|
|
/// Returns None if the slice is not utf-8.
|
|
pub fn from_utf8<'a>(v: &'a [u8]) -> Option<&'a str> {
|
|
if is_utf8(v) {
|
|
Some(unsafe { raw::from_utf8(v) })
|
|
} else { None }
|
|
}
|
|
|
|
impl ToStr for ~str {
|
|
#[inline]
|
|
fn to_str(&self) -> ~str { self.to_owned() }
|
|
}
|
|
|
|
impl FromStr for ~str {
|
|
#[inline]
|
|
fn from_str(s: &str) -> Option<~str> { Some(s.to_owned()) }
|
|
}
|
|
|
|
impl<'a> ToStr for &'a str {
|
|
#[inline]
|
|
fn to_str(&self) -> ~str { self.to_owned() }
|
|
}
|
|
|
|
/// Convert a byte to a UTF-8 string
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// Fails if invalid UTF-8
|
|
pub fn from_byte(b: u8) -> ~str {
|
|
assert!(b < 128u8);
|
|
unsafe { ::cast::transmute(~[b]) }
|
|
}
|
|
|
|
/// Convert a char to a string
|
|
pub fn from_char(ch: char) -> ~str {
|
|
let mut buf = ~"";
|
|
buf.push_char(ch);
|
|
buf
|
|
}
|
|
|
|
/// Convert a vector of chars to a string
|
|
pub fn from_chars(chs: &[char]) -> ~str {
|
|
chs.iter().map(|c| *c).collect()
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub fn push_str(lhs: &mut ~str, rhs: &str) {
|
|
lhs.push_str(rhs)
|
|
}
|
|
|
|
/// Methods for vectors of strings
|
|
pub trait StrVector {
|
|
/// Concatenate a vector of strings.
|
|
fn concat(&self) -> ~str;
|
|
|
|
/// Concatenate a vector of strings, placing a given separator between each.
|
|
fn connect(&self, sep: &str) -> ~str;
|
|
}
|
|
|
|
impl<'a, S: Str> StrVector for &'a [S] {
|
|
fn concat(&self) -> ~str {
|
|
if self.is_empty() { return ~""; }
|
|
|
|
// `len` calculation may overflow but push_str but will check boundaries
|
|
let len = self.iter().map(|s| s.as_slice().len()).sum();
|
|
|
|
let mut result = with_capacity(len);
|
|
|
|
for s in self.iter() {
|
|
result.push_str(s.as_slice())
|
|
}
|
|
result
|
|
}
|
|
|
|
fn connect(&self, sep: &str) -> ~str {
|
|
if self.is_empty() { return ~""; }
|
|
|
|
// concat is faster
|
|
if sep.is_empty() { return self.concat(); }
|
|
|
|
// this is wrong without the guarantee that `self` is non-empty
|
|
// `len` calculation may overflow but push_str but will check boundaries
|
|
let len = sep.len() * (self.len() - 1)
|
|
+ self.iter().map(|s| s.as_slice().len()).sum();
|
|
let mut result = with_capacity(len);
|
|
let mut first = true;
|
|
|
|
for s in self.iter() {
|
|
if first {
|
|
first = false;
|
|
} else {
|
|
result.push_str(sep);
|
|
}
|
|
result.push_str(s.as_slice());
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// Something that can be used to compare against a character
|
|
pub trait CharEq {
|
|
/// Determine if the splitter should split at the given character
|
|
fn matches(&self, char) -> bool;
|
|
/// Indicate if this is only concerned about ASCII characters,
|
|
/// which can allow for a faster implementation.
|
|
fn only_ascii(&self) -> bool;
|
|
}
|
|
|
|
impl CharEq for char {
|
|
#[inline]
|
|
fn matches(&self, c: char) -> bool { *self == c }
|
|
|
|
fn only_ascii(&self) -> bool { (*self as uint) < 128 }
|
|
}
|
|
|
|
impl<'a> CharEq for 'a |char| -> bool {
|
|
#[inline]
|
|
fn matches(&self, c: char) -> bool { (*self)(c) }
|
|
|
|
fn only_ascii(&self) -> bool { false }
|
|
}
|
|
|
|
impl CharEq for extern "Rust" fn(char) -> bool {
|
|
#[inline]
|
|
fn matches(&self, c: char) -> bool { (*self)(c) }
|
|
|
|
fn only_ascii(&self) -> bool { false }
|
|
}
|
|
|
|
impl<'a, C: CharEq> CharEq for &'a [C] {
|
|
#[inline]
|
|
fn matches(&self, c: char) -> bool {
|
|
self.iter().any(|m| m.matches(c))
|
|
}
|
|
|
|
fn only_ascii(&self) -> bool {
|
|
self.iter().all(|m| m.only_ascii())
|
|
}
|
|
}
|
|
|
|
/*
|
|
Section: Iterators
|
|
*/
|
|
|
|
/// External iterator for a string's characters.
|
|
/// Use with the `std::iter` module.
|
|
#[deriving(Clone)]
|
|
pub struct Chars<'a> {
|
|
/// The slice remaining to be iterated
|
|
priv string: &'a str,
|
|
}
|
|
|
|
impl<'a> Iterator<char> for Chars<'a> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<char> {
|
|
// Decode the next codepoint, then update
|
|
// the slice to be just the remaining part
|
|
if self.string.len() != 0 {
|
|
let CharRange {ch, next} = self.string.char_range_at(0);
|
|
unsafe {
|
|
self.string = raw::slice_unchecked(self.string, next, self.string.len());
|
|
}
|
|
Some(ch)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
(self.string.len().saturating_add(3)/4, Some(self.string.len()))
|
|
}
|
|
}
|
|
|
|
impl<'a> DoubleEndedIterator<char> for Chars<'a> {
|
|
#[inline]
|
|
fn next_back(&mut self) -> Option<char> {
|
|
if self.string.len() != 0 {
|
|
let CharRange {ch, next} = self.string.char_range_at_reverse(self.string.len());
|
|
unsafe {
|
|
self.string = raw::slice_unchecked(self.string, 0, next);
|
|
}
|
|
Some(ch)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// External iterator for a string's characters and their byte offsets.
|
|
/// Use with the `std::iter` module.
|
|
#[deriving(Clone)]
|
|
pub struct CharOffsets<'a> {
|
|
/// The original string to be iterated
|
|
priv string: &'a str,
|
|
priv iter: Chars<'a>,
|
|
}
|
|
|
|
impl<'a> Iterator<(uint, char)> for CharOffsets<'a> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<(uint, char)> {
|
|
// Compute the byte offset by using the pointer offset between
|
|
// the original string slice and the iterator's remaining part
|
|
let offset = self.iter.string.as_ptr() as uint - self.string.as_ptr() as uint;
|
|
self.iter.next().map(|ch| (offset, ch))
|
|
}
|
|
|
|
#[inline]
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
self.iter.size_hint()
|
|
}
|
|
}
|
|
|
|
impl<'a> DoubleEndedIterator<(uint, char)> for CharOffsets<'a> {
|
|
#[inline]
|
|
fn next_back(&mut self) -> Option<(uint, char)> {
|
|
self.iter.next_back().map(|ch| {
|
|
let offset = self.iter.string.len() +
|
|
self.iter.string.as_ptr() as uint - self.string.as_ptr() as uint;
|
|
(offset, ch)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// External iterator for a string's characters in reverse order.
|
|
/// Use with the `std::iter` module.
|
|
pub type RevChars<'a> = Rev<Chars<'a>>;
|
|
|
|
/// External iterator for a string's characters and their byte offsets in reverse order.
|
|
/// Use with the `std::iter` module.
|
|
pub type RevCharOffsets<'a> = Rev<CharOffsets<'a>>;
|
|
|
|
/// External iterator for a string's bytes.
|
|
/// Use with the `std::iter` module.
|
|
pub type Bytes<'a> =
|
|
Map<'a, &'a u8, u8, vec::Items<'a, u8>>;
|
|
|
|
/// External iterator for a string's bytes in reverse order.
|
|
/// Use with the `std::iter` module.
|
|
pub type RevBytes<'a> = Rev<Bytes<'a>>;
|
|
|
|
/// An iterator over the substrings of a string, separated by `sep`.
|
|
#[deriving(Clone)]
|
|
pub struct CharSplits<'a, Sep> {
|
|
/// The slice remaining to be iterated
|
|
priv string: &'a str,
|
|
priv sep: Sep,
|
|
/// Whether an empty string at the end is allowed
|
|
priv allow_trailing_empty: bool,
|
|
priv only_ascii: bool,
|
|
priv finished: bool,
|
|
}
|
|
|
|
/// An iterator over the substrings of a string, separated by `sep`,
|
|
/// starting from the back of the string.
|
|
pub type RevCharSplits<'a, Sep> = Rev<CharSplits<'a, Sep>>;
|
|
|
|
/// An iterator over the substrings of a string, separated by `sep`,
|
|
/// splitting at most `count` times.
|
|
#[deriving(Clone)]
|
|
pub struct CharSplitsN<'a, Sep> {
|
|
priv iter: CharSplits<'a, Sep>,
|
|
/// The number of splits remaining
|
|
priv count: uint,
|
|
priv invert: bool,
|
|
}
|
|
|
|
/// An iterator over the words of a string, separated by a sequence of whitespace
|
|
pub type Words<'a> =
|
|
Filter<'a, &'a str, CharSplits<'a, extern "Rust" fn(char) -> bool>>;
|
|
|
|
/// An iterator over the lines of a string, separated by either `\n` or (`\r\n`).
|
|
pub type AnyLines<'a> =
|
|
Map<'a, &'a str, &'a str, CharSplits<'a, char>>;
|
|
|
|
impl<'a, Sep> CharSplits<'a, Sep> {
|
|
#[inline]
|
|
fn get_end(&mut self) -> Option<&'a str> {
|
|
if !self.finished && (self.allow_trailing_empty || self.string.len() > 0) {
|
|
self.finished = true;
|
|
Some(self.string)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, Sep: CharEq> Iterator<&'a str> for CharSplits<'a, Sep> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<&'a str> {
|
|
if self.finished { return None }
|
|
|
|
let mut next_split = None;
|
|
if self.only_ascii {
|
|
for (idx, byte) in self.string.bytes().enumerate() {
|
|
if self.sep.matches(byte as char) && byte < 128u8 {
|
|
next_split = Some((idx, idx + 1));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (idx, ch) in self.string.char_indices() {
|
|
if self.sep.matches(ch) {
|
|
next_split = Some((idx, self.string.char_range_at(idx).next));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
match next_split {
|
|
Some((a, b)) => unsafe {
|
|
let elt = raw::slice_unchecked(self.string, 0, a);
|
|
self.string = raw::slice_unchecked(self.string, b, self.string.len());
|
|
Some(elt)
|
|
},
|
|
None => self.get_end(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, Sep: CharEq> DoubleEndedIterator<&'a str>
|
|
for CharSplits<'a, Sep> {
|
|
#[inline]
|
|
fn next_back(&mut self) -> Option<&'a str> {
|
|
if self.finished { return None }
|
|
|
|
if !self.allow_trailing_empty {
|
|
self.allow_trailing_empty = true;
|
|
match self.next_back() {
|
|
Some(elt) if !elt.is_empty() => return Some(elt),
|
|
_ => if self.finished { return None }
|
|
}
|
|
}
|
|
let len = self.string.len();
|
|
let mut next_split = None;
|
|
|
|
if self.only_ascii {
|
|
for (idx, byte) in self.string.bytes().enumerate().rev() {
|
|
if self.sep.matches(byte as char) && byte < 128u8 {
|
|
next_split = Some((idx, idx + 1));
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (idx, ch) in self.string.char_indices_rev() {
|
|
if self.sep.matches(ch) {
|
|
next_split = Some((idx, self.string.char_range_at(idx).next));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
match next_split {
|
|
Some((a, b)) => unsafe {
|
|
let elt = raw::slice_unchecked(self.string, b, len);
|
|
self.string = raw::slice_unchecked(self.string, 0, a);
|
|
Some(elt)
|
|
},
|
|
None => { self.finished = true; Some(self.string) }
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, Sep: CharEq> Iterator<&'a str> for CharSplitsN<'a, Sep> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<&'a str> {
|
|
if self.count != 0 {
|
|
self.count -= 1;
|
|
if self.invert { self.iter.next_back() } else { self.iter.next() }
|
|
} else {
|
|
self.iter.get_end()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An iterator over the start and end indices of the matches of a
|
|
/// substring within a larger string
|
|
#[deriving(Clone)]
|
|
pub struct MatchIndices<'a> {
|
|
priv haystack: &'a str,
|
|
priv needle: &'a str,
|
|
priv position: uint,
|
|
}
|
|
|
|
/// An iterator over the substrings of a string separated by a given
|
|
/// search string
|
|
#[deriving(Clone)]
|
|
pub struct StrSplits<'a> {
|
|
priv it: MatchIndices<'a>,
|
|
priv last_end: uint,
|
|
priv finished: bool
|
|
}
|
|
|
|
impl<'a> Iterator<(uint, uint)> for MatchIndices<'a> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<(uint, uint)> {
|
|
// See Issue #1932 for why this is a naive search
|
|
let (h_len, n_len) = (self.haystack.len(), self.needle.len());
|
|
let mut match_start = 0;
|
|
let mut match_i = 0;
|
|
|
|
while self.position < h_len {
|
|
if self.haystack[self.position] == self.needle[match_i] {
|
|
if match_i == 0 { match_start = self.position; }
|
|
match_i += 1;
|
|
self.position += 1;
|
|
|
|
if match_i == n_len {
|
|
// found a match!
|
|
return Some((match_start, self.position));
|
|
}
|
|
} else {
|
|
// failed match, backtrack
|
|
if match_i > 0 {
|
|
match_i = 0;
|
|
self.position = match_start;
|
|
}
|
|
self.position += 1;
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator<&'a str> for StrSplits<'a> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<&'a str> {
|
|
if self.finished { return None; }
|
|
|
|
match self.it.next() {
|
|
Some((from, to)) => {
|
|
let ret = Some(self.it.haystack.slice(self.last_end, from));
|
|
self.last_end = to;
|
|
ret
|
|
}
|
|
None => {
|
|
self.finished = true;
|
|
Some(self.it.haystack.slice(self.last_end, self.it.haystack.len()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper functions used for Unicode normalization
|
|
fn canonical_sort(comb: &mut [(char, u8)]) {
|
|
use iter::range;
|
|
use tuple::Tuple2;
|
|
|
|
let len = comb.len();
|
|
for i in range(0, len) {
|
|
let mut swapped = false;
|
|
for j in range(1, len-i) {
|
|
let classA = *comb[j-1].ref1();
|
|
let classB = *comb[j].ref1();
|
|
if classA != 0 && classB != 0 && classA > classB {
|
|
comb.swap(j-1, j);
|
|
swapped = true;
|
|
}
|
|
}
|
|
if !swapped { break; }
|
|
}
|
|
}
|
|
|
|
#[deriving(Clone)]
|
|
enum NormalizationForm {
|
|
NFD,
|
|
NFKD
|
|
}
|
|
|
|
/// External iterator for a string's normalization's characters.
|
|
/// Use with the `std::iter` module.
|
|
#[deriving(Clone)]
|
|
pub struct Normalizations<'a> {
|
|
priv kind: NormalizationForm,
|
|
priv iter: Chars<'a>,
|
|
priv buffer: ~[(char, u8)],
|
|
priv sorted: bool
|
|
}
|
|
|
|
impl<'a> Iterator<char> for Normalizations<'a> {
|
|
#[inline]
|
|
fn next(&mut self) -> Option<char> {
|
|
use unicode::decompose::canonical_combining_class;
|
|
|
|
match self.buffer.head() {
|
|
Some(&(c, 0)) => {
|
|
self.sorted = false;
|
|
self.buffer.shift();
|
|
return Some(c);
|
|
}
|
|
Some(&(c, _)) if self.sorted => {
|
|
self.buffer.shift();
|
|
return Some(c);
|
|
}
|
|
_ => self.sorted = false
|
|
}
|
|
|
|
let decomposer = match self.kind {
|
|
NFD => char::decompose_canonical,
|
|
NFKD => char::decompose_compatible
|
|
};
|
|
|
|
if !self.sorted {
|
|
for ch in self.iter {
|
|
let buffer = &mut self.buffer;
|
|
let sorted = &mut self.sorted;
|
|
decomposer(ch, |d| {
|
|
let class = canonical_combining_class(d);
|
|
if class == 0 && !*sorted {
|
|
canonical_sort(*buffer);
|
|
*sorted = true;
|
|
}
|
|
buffer.push((d, class));
|
|
});
|
|
if *sorted { break }
|
|
}
|
|
}
|
|
|
|
if !self.sorted {
|
|
canonical_sort(self.buffer);
|
|
self.sorted = true;
|
|
}
|
|
|
|
match self.buffer.shift() {
|
|
Some((c, 0)) => {
|
|
self.sorted = false;
|
|
Some(c)
|
|
}
|
|
Some((c, _)) => Some(c),
|
|
None => None
|
|
}
|
|
}
|
|
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
let (lower, _) = self.iter.size_hint();
|
|
(lower, None)
|
|
}
|
|
}
|
|
|
|
/// Replace all occurrences of one string with another
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * s - The string containing substrings to replace
|
|
/// * from - The string to replace
|
|
/// * to - The replacement string
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// The original string with all occurances of `from` replaced with `to`
|
|
pub fn replace(s: &str, from: &str, to: &str) -> ~str {
|
|
let mut result = ~"";
|
|
let mut last_end = 0;
|
|
for (start, end) in s.match_indices(from) {
|
|
result.push_str(unsafe{raw::slice_bytes(s, last_end, start)});
|
|
result.push_str(to);
|
|
last_end = end;
|
|
}
|
|
result.push_str(unsafe{raw::slice_bytes(s, last_end, s.len())});
|
|
result
|
|
}
|
|
|
|
/*
|
|
Section: Comparing strings
|
|
*/
|
|
|
|
// share the implementation of the lang-item vs. non-lang-item
|
|
// eq_slice.
|
|
#[inline]
|
|
fn eq_slice_(a: &str, b: &str) -> bool {
|
|
a.len() == b.len() && unsafe {
|
|
libc::memcmp(a.as_ptr() as *libc::c_void,
|
|
b.as_ptr() as *libc::c_void,
|
|
a.len() as libc::size_t) == 0
|
|
}
|
|
}
|
|
|
|
/// Bytewise slice equality
|
|
#[cfg(not(test))]
|
|
#[lang="str_eq"]
|
|
#[inline]
|
|
pub fn eq_slice(a: &str, b: &str) -> bool {
|
|
eq_slice_(a, b)
|
|
}
|
|
|
|
/// Bytewise slice equality
|
|
#[cfg(test)]
|
|
#[inline]
|
|
pub fn eq_slice(a: &str, b: &str) -> bool {
|
|
eq_slice_(a, b)
|
|
}
|
|
|
|
/// Bytewise string equality
|
|
#[cfg(not(test))]
|
|
#[lang="uniq_str_eq"]
|
|
#[inline]
|
|
pub fn eq(a: &~str, b: &~str) -> bool {
|
|
eq_slice(*a, *b)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[inline]
|
|
pub fn eq(a: &~str, b: &~str) -> bool {
|
|
eq_slice(*a, *b)
|
|
}
|
|
|
|
/*
|
|
Section: Misc
|
|
*/
|
|
|
|
/// Walk through `iter` checking that it's a valid UTF-8 sequence,
|
|
/// returning `true` in that case, or, if it is invalid, `false` with
|
|
/// `iter` reset such that it is pointing at the first byte in the
|
|
/// invalid sequence.
|
|
#[inline(always)]
|
|
fn run_utf8_validation_iterator(iter: &mut vec::Items<u8>) -> bool {
|
|
loop {
|
|
// save the current thing we're pointing at.
|
|
let old = *iter;
|
|
|
|
// restore the iterator we had at the start of this codepoint.
|
|
macro_rules! err ( () => { {*iter = old; return false} });
|
|
macro_rules! next ( () => {
|
|
match iter.next() {
|
|
Some(a) => *a,
|
|
// we needed data, but there was none: error!
|
|
None => err!()
|
|
}
|
|
});
|
|
|
|
let first = match iter.next() {
|
|
Some(&b) => b,
|
|
// we're at the end of the iterator and a codepoint
|
|
// boundary at the same time, so this string is valid.
|
|
None => return true
|
|
};
|
|
|
|
// ASCII characters are always valid, so only large
|
|
// bytes need more examination.
|
|
if first >= 128 {
|
|
let w = utf8_char_width(first);
|
|
let second = next!();
|
|
// 2-byte encoding is for codepoints \u0080 to \u07ff
|
|
// first C2 80 last DF BF
|
|
// 3-byte encoding is for codepoints \u0800 to \uffff
|
|
// first E0 A0 80 last EF BF BF
|
|
// excluding surrogates codepoints \ud800 to \udfff
|
|
// ED A0 80 to ED BF BF
|
|
// 4-byte encoding is for codepoints \u10000 to \u10ffff
|
|
// first F0 90 80 80 last F4 8F BF BF
|
|
//
|
|
// Use the UTF-8 syntax from the RFC
|
|
//
|
|
// https://tools.ietf.org/html/rfc3629
|
|
// UTF8-1 = %x00-7F
|
|
// UTF8-2 = %xC2-DF UTF8-tail
|
|
// UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
|
// %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
|
// UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
|
// %xF4 %x80-8F 2( UTF8-tail )
|
|
match w {
|
|
2 => if second & 192 != TAG_CONT_U8 {err!()},
|
|
3 => {
|
|
match (first, second, next!() & 192) {
|
|
(0xE0 , 0xA0 .. 0xBF, TAG_CONT_U8) |
|
|
(0xE1 .. 0xEC, 0x80 .. 0xBF, TAG_CONT_U8) |
|
|
(0xED , 0x80 .. 0x9F, TAG_CONT_U8) |
|
|
(0xEE .. 0xEF, 0x80 .. 0xBF, TAG_CONT_U8) => {}
|
|
_ => err!()
|
|
}
|
|
}
|
|
4 => {
|
|
match (first, second, next!() & 192, next!() & 192) {
|
|
(0xF0 , 0x90 .. 0xBF, TAG_CONT_U8, TAG_CONT_U8) |
|
|
(0xF1 .. 0xF3, 0x80 .. 0xBF, TAG_CONT_U8, TAG_CONT_U8) |
|
|
(0xF4 , 0x80 .. 0x8F, TAG_CONT_U8, TAG_CONT_U8) => {}
|
|
_ => err!()
|
|
}
|
|
}
|
|
_ => err!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Determines if a vector of bytes contains valid UTF-8.
|
|
pub fn is_utf8(v: &[u8]) -> bool {
|
|
run_utf8_validation_iterator(&mut v.iter())
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn first_non_utf8_index(v: &[u8]) -> Option<uint> {
|
|
let mut it = v.iter();
|
|
|
|
let ok = run_utf8_validation_iterator(&mut it);
|
|
if ok {
|
|
None
|
|
} else {
|
|
// work out how many valid bytes we've consumed
|
|
// (run_utf8_validation_iterator resets the iterator to just
|
|
// after the last good byte), which we can do because the
|
|
// vector iterator size_hint is exact.
|
|
let (remaining, _) = it.size_hint();
|
|
Some(v.len() - remaining)
|
|
}
|
|
}
|
|
|
|
/// Determines if a vector of `u16` contains valid UTF-16
|
|
pub fn is_utf16(v: &[u16]) -> bool {
|
|
let mut it = v.iter();
|
|
macro_rules! next ( ($ret:expr) => {
|
|
match it.next() { Some(u) => *u, None => return $ret }
|
|
}
|
|
)
|
|
loop {
|
|
let u = next!(true);
|
|
|
|
match char::from_u32(u as u32) {
|
|
Some(_) => {}
|
|
None => {
|
|
let u2 = next!(false);
|
|
if u < 0xD7FF || u > 0xDBFF ||
|
|
u2 < 0xDC00 || u2 > 0xDFFF { return false; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An iterator that decodes UTF-16 encoded codepoints from a vector
|
|
/// of `u16`s.
|
|
#[deriving(Clone)]
|
|
pub struct UTF16Items<'a> {
|
|
priv iter: vec::Items<'a, u16>
|
|
}
|
|
/// The possibilities for values decoded from a `u16` stream.
|
|
#[deriving(Eq, TotalEq, Clone)]
|
|
pub enum UTF16Item {
|
|
/// A valid codepoint.
|
|
ScalarValue(char),
|
|
/// An invalid surrogate without its pair.
|
|
LoneSurrogate(u16)
|
|
}
|
|
|
|
impl UTF16Item {
|
|
/// Convert `self` to a `char`, taking `LoneSurrogate`s to the
|
|
/// replacement character (U+FFFD).
|
|
#[inline]
|
|
pub fn to_char_lossy(&self) -> char {
|
|
match *self {
|
|
ScalarValue(c) => c,
|
|
LoneSurrogate(_) => '\uFFFD'
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator<UTF16Item> for UTF16Items<'a> {
|
|
fn next(&mut self) -> Option<UTF16Item> {
|
|
let u = match self.iter.next() {
|
|
Some(u) => *u,
|
|
None => return None
|
|
};
|
|
|
|
if u < 0xD800 || 0xDFFF < u {
|
|
// not a surrogate
|
|
Some(ScalarValue(unsafe {cast::transmute(u as u32)}))
|
|
} else if u >= 0xDC00 {
|
|
// a trailing surrogate
|
|
Some(LoneSurrogate(u))
|
|
} else {
|
|
// preserve state for rewinding.
|
|
let old = self.iter;
|
|
|
|
let u2 = match self.iter.next() {
|
|
Some(u2) => *u2,
|
|
// eof
|
|
None => return Some(LoneSurrogate(u))
|
|
};
|
|
if u2 < 0xDC00 || u2 > 0xDFFF {
|
|
// not a trailing surrogate so we're not a valid
|
|
// surrogate pair, so rewind to redecode u2 next time.
|
|
self.iter = old;
|
|
return Some(LoneSurrogate(u))
|
|
}
|
|
|
|
// all ok, so lets decode it.
|
|
let c = ((u - 0xD800) as u32 << 10 | (u2 - 0xDC00) as u32) + 0x1_0000;
|
|
Some(ScalarValue(unsafe {cast::transmute(c)}))
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
|
let (low, high) = self.iter.size_hint();
|
|
// we could be entirely valid surrogates (2 elements per
|
|
// char), or entirely non-surrogates (1 element per char)
|
|
(low / 2, high)
|
|
}
|
|
}
|
|
|
|
/// Create an iterator over the UTF-16 encoded codepoints in `v`,
|
|
/// returning invalid surrogates as `LoneSurrogate`s.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use std::str;
|
|
/// use std::str::{ScalarValue, LoneSurrogate};
|
|
///
|
|
/// // 𝄞mus<invalid>ic<invalid>
|
|
/// let v = [0xD834, 0xDD1E, 0x006d, 0x0075,
|
|
/// 0x0073, 0xDD1E, 0x0069, 0x0063,
|
|
/// 0xD834];
|
|
///
|
|
/// assert_eq!(str::utf16_items(v).to_owned_vec(),
|
|
/// ~[ScalarValue('𝄞'),
|
|
/// ScalarValue('m'), ScalarValue('u'), ScalarValue('s'),
|
|
/// LoneSurrogate(0xDD1E),
|
|
/// ScalarValue('i'), ScalarValue('c'),
|
|
/// LoneSurrogate(0xD834)]);
|
|
/// ```
|
|
pub fn utf16_items<'a>(v: &'a [u16]) -> UTF16Items<'a> {
|
|
UTF16Items { iter : v.iter() }
|
|
}
|
|
|
|
/// Return a slice of `v` ending at (and not including) the first NUL
|
|
/// (0).
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use std::str;
|
|
///
|
|
/// // "abcd"
|
|
/// let mut v = ['a' as u16, 'b' as u16, 'c' as u16, 'd' as u16];
|
|
/// // no NULs so no change
|
|
/// assert_eq!(str::truncate_utf16_at_nul(v), v.as_slice());
|
|
///
|
|
/// // "ab\0d"
|
|
/// v[2] = 0;
|
|
/// assert_eq!(str::truncate_utf16_at_nul(v),
|
|
/// &['a' as u16, 'b' as u16]);
|
|
/// ```
|
|
pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
|
|
match v.iter().position(|c| *c == 0) {
|
|
// don't include the 0
|
|
Some(i) => v.slice_to(i),
|
|
None => v
|
|
}
|
|
}
|
|
|
|
/// Decode a UTF-16 encoded vector `v` into a string, returning `None`
|
|
/// if `v` contains any invalid data.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use std::str;
|
|
///
|
|
/// // 𝄞music
|
|
/// let mut v = [0xD834, 0xDD1E, 0x006d, 0x0075,
|
|
/// 0x0073, 0x0069, 0x0063];
|
|
/// assert_eq!(str::from_utf16(v), Some(~"𝄞music"));
|
|
///
|
|
/// // 𝄞mu<invalid>ic
|
|
/// v[4] = 0xD800;
|
|
/// assert_eq!(str::from_utf16(v), None);
|
|
/// ```
|
|
pub fn from_utf16(v: &[u16]) -> Option<~str> {
|
|
let mut s = with_capacity(v.len() / 2);
|
|
for c in utf16_items(v) {
|
|
match c {
|
|
ScalarValue(c) => s.push_char(c),
|
|
LoneSurrogate(_) => return None
|
|
}
|
|
}
|
|
Some(s)
|
|
}
|
|
|
|
/// Decode a UTF-16 encoded vector `v` into a string, replacing
|
|
/// invalid data with the replacement character (U+FFFD).
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// use std::str;
|
|
///
|
|
/// // 𝄞mus<invalid>ic<invalid>
|
|
/// let v = [0xD834, 0xDD1E, 0x006d, 0x0075,
|
|
/// 0x0073, 0xDD1E, 0x0069, 0x0063,
|
|
/// 0xD834];
|
|
///
|
|
/// assert_eq!(str::from_utf16_lossy(v),
|
|
/// ~"𝄞mus\uFFFDic\uFFFD");
|
|
/// ```
|
|
pub fn from_utf16_lossy(v: &[u16]) -> ~str {
|
|
utf16_items(v).map(|c| c.to_char_lossy()).collect()
|
|
}
|
|
|
|
/// Allocates a new string with the specified capacity. The string returned is
|
|
/// the empty string, but has capacity for much more.
|
|
#[inline]
|
|
pub fn with_capacity(capacity: uint) -> ~str {
|
|
unsafe {
|
|
cast::transmute(vec::with_capacity::<~[u8]>(capacity))
|
|
}
|
|
}
|
|
|
|
// https://tools.ietf.org/html/rfc3629
|
|
static UTF8_CHAR_WIDTH: [u8, ..256] = [
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF
|
|
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF
|
|
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF
|
|
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
|
|
];
|
|
|
|
/// Given a first byte, determine how many bytes are in this UTF-8 character
|
|
pub fn utf8_char_width(b: u8) -> uint {
|
|
return UTF8_CHAR_WIDTH[b] as uint;
|
|
}
|
|
|
|
/// Struct that contains a `char` and the index of the first byte of
|
|
/// the next `char` in a string. This can be used as a data structure
|
|
/// for iterating over the UTF-8 bytes of a string.
|
|
pub struct CharRange {
|
|
/// Current `char`
|
|
ch: char,
|
|
/// Index of the first byte of the next `char`
|
|
next: uint
|
|
}
|
|
|
|
// Return the initial codepoint accumulator for the first byte.
|
|
// The first byte is special, only want bottom 5 bits for width 2, 4 bits
|
|
// for width 3, and 3 bits for width 4
|
|
macro_rules! utf8_first_byte(
|
|
($byte:expr, $width:expr) => (($byte & (0x7F >> $width)) as u32)
|
|
)
|
|
|
|
// return the value of $ch updated with continuation byte $byte
|
|
macro_rules! utf8_acc_cont_byte(
|
|
($ch:expr, $byte:expr) => (($ch << 6) | ($byte & 63u8) as u32)
|
|
)
|
|
|
|
static TAG_CONT_U8: u8 = 128u8;
|
|
|
|
/// Converts a vector of bytes to a new utf-8 string.
|
|
/// Any invalid utf-8 sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let input = bytes!("Hello ", 0xF0, 0x90, 0x80, "World");
|
|
/// let output = std::str::from_utf8_lossy(input);
|
|
/// assert_eq!(output.as_slice(), "Hello \uFFFDWorld");
|
|
/// ```
|
|
pub fn from_utf8_lossy<'a>(v: &'a [u8]) -> MaybeOwned<'a> {
|
|
let firstbad = match first_non_utf8_index(v) {
|
|
None => return Slice(unsafe { cast::transmute(v) }),
|
|
Some(i) => i
|
|
};
|
|
|
|
static REPLACEMENT: &'static [u8] = bytes!(0xEF, 0xBF, 0xBD); // U+FFFD in UTF-8
|
|
let mut i = firstbad;
|
|
let total = v.len();
|
|
fn unsafe_get(xs: &[u8], i: uint) -> u8 {
|
|
unsafe { *xs.unsafe_ref(i) }
|
|
}
|
|
fn safe_get(xs: &[u8], i: uint, total: uint) -> u8 {
|
|
if i >= total {
|
|
0
|
|
} else {
|
|
unsafe_get(xs, i)
|
|
}
|
|
}
|
|
let mut res = with_capacity(total);
|
|
|
|
if i > 0 {
|
|
unsafe { raw::push_bytes(&mut res, v.slice_to(i)) };
|
|
}
|
|
|
|
// subseqidx is the index of the first byte of the subsequence we're looking at.
|
|
// It's used to copy a bunch of contiguous good codepoints at once instead of copying
|
|
// them one by one.
|
|
let mut subseqidx = firstbad;
|
|
|
|
while i < total {
|
|
let i_ = i;
|
|
let byte = unsafe_get(v, i);
|
|
i += 1;
|
|
|
|
macro_rules! error(() => ({
|
|
unsafe {
|
|
if subseqidx != i_ {
|
|
raw::push_bytes(&mut res, v.slice(subseqidx, i_));
|
|
}
|
|
subseqidx = i;
|
|
raw::push_bytes(&mut res, REPLACEMENT);
|
|
}
|
|
}))
|
|
|
|
if byte < 128u8 {
|
|
// subseqidx handles this
|
|
} else {
|
|
let w = utf8_char_width(byte);
|
|
|
|
match w {
|
|
2 => {
|
|
if safe_get(v, i, total) & 192u8 != TAG_CONT_U8 {
|
|
error!();
|
|
continue;
|
|
}
|
|
i += 1;
|
|
}
|
|
3 => {
|
|
match (byte, safe_get(v, i, total)) {
|
|
(0xE0 , 0xA0 .. 0xBF) => (),
|
|
(0xE1 .. 0xEC, 0x80 .. 0xBF) => (),
|
|
(0xED , 0x80 .. 0x9F) => (),
|
|
(0xEE .. 0xEF, 0x80 .. 0xBF) => (),
|
|
_ => {
|
|
error!();
|
|
continue;
|
|
}
|
|
}
|
|
i += 1;
|
|
if safe_get(v, i, total) & 192u8 != TAG_CONT_U8 {
|
|
error!();
|
|
continue;
|
|
}
|
|
i += 1;
|
|
}
|
|
4 => {
|
|
match (byte, safe_get(v, i, total)) {
|
|
(0xF0 , 0x90 .. 0xBF) => (),
|
|
(0xF1 .. 0xF3, 0x80 .. 0xBF) => (),
|
|
(0xF4 , 0x80 .. 0x8F) => (),
|
|
_ => {
|
|
error!();
|
|
continue;
|
|
}
|
|
}
|
|
i += 1;
|
|
if safe_get(v, i, total) & 192u8 != TAG_CONT_U8 {
|
|
error!();
|
|
continue;
|
|
}
|
|
i += 1;
|
|
if safe_get(v, i, total) & 192u8 != TAG_CONT_U8 {
|
|
error!();
|
|
continue;
|
|
}
|
|
i += 1;
|
|
}
|
|
_ => {
|
|
error!();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if subseqidx < total {
|
|
unsafe { raw::push_bytes(&mut res, v.slice(subseqidx, total)) };
|
|
}
|
|
Owned(res)
|
|
}
|
|
|
|
/*
|
|
Section: MaybeOwned
|
|
*/
|
|
|
|
/// A MaybeOwned is a string that can hold either a ~str or a &str.
|
|
/// This can be useful as an optimization when an allocation is sometimes
|
|
/// needed but not always.
|
|
pub enum MaybeOwned<'a> {
|
|
/// A borrowed string
|
|
Slice(&'a str),
|
|
/// An owned string
|
|
Owned(~str)
|
|
}
|
|
|
|
/// SendStr is a specialization of `MaybeOwned` to be sendable
|
|
pub type SendStr = MaybeOwned<'static>;
|
|
|
|
impl<'a> MaybeOwned<'a> {
|
|
/// Returns `true` if this `MaybeOwned` wraps an owned string
|
|
#[inline]
|
|
pub fn is_owned(&self) -> bool {
|
|
match *self {
|
|
Slice(_) => false,
|
|
Owned(_) => true
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if this `MaybeOwned` wraps a borrowed string
|
|
#[inline]
|
|
pub fn is_slice(&self) -> bool {
|
|
match *self {
|
|
Slice(_) => true,
|
|
Owned(_) => false
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Trait for moving into a `MaybeOwned`
|
|
pub trait IntoMaybeOwned<'a> {
|
|
/// Moves self into a `MaybeOwned`
|
|
fn into_maybe_owned(self) -> MaybeOwned<'a>;
|
|
}
|
|
|
|
impl<'a> IntoMaybeOwned<'a> for ~str {
|
|
#[inline]
|
|
fn into_maybe_owned(self) -> MaybeOwned<'a> { Owned(self) }
|
|
}
|
|
|
|
impl<'a> IntoMaybeOwned<'a> for &'a str {
|
|
#[inline]
|
|
fn into_maybe_owned(self) -> MaybeOwned<'a> { Slice(self) }
|
|
}
|
|
|
|
impl<'a> IntoMaybeOwned<'a> for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn into_maybe_owned(self) -> MaybeOwned<'a> { self }
|
|
}
|
|
|
|
impl<'a> ToStr for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn to_str(&self) -> ~str { self.as_slice().to_owned() }
|
|
}
|
|
|
|
impl<'a> Eq for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn eq(&self, other: &MaybeOwned) -> bool {
|
|
self.as_slice().equals(&other.as_slice())
|
|
}
|
|
}
|
|
|
|
impl<'a> TotalEq for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn equals(&self, other: &MaybeOwned) -> bool {
|
|
self.as_slice().equals(&other.as_slice())
|
|
}
|
|
}
|
|
|
|
impl<'a> Ord for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn lt(&self, other: &MaybeOwned) -> bool {
|
|
self.as_slice().lt(&other.as_slice())
|
|
}
|
|
}
|
|
|
|
impl<'a> TotalOrd for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn cmp(&self, other: &MaybeOwned) -> Ordering {
|
|
self.as_slice().cmp(&other.as_slice())
|
|
}
|
|
}
|
|
|
|
impl<'a, S: Str> Equiv<S> for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn equiv(&self, other: &S) -> bool {
|
|
self.as_slice().equals(&other.as_slice())
|
|
}
|
|
}
|
|
|
|
impl<'a> Str for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn as_slice<'b>(&'b self) -> &'b str {
|
|
match *self {
|
|
Slice(s) => s,
|
|
Owned(ref s) => s.as_slice()
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn into_owned(self) -> ~str {
|
|
match self {
|
|
Slice(s) => s.to_owned(),
|
|
Owned(s) => s
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Container for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn len(&self) -> uint { self.as_slice().len() }
|
|
}
|
|
|
|
impl<'a> Clone for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn clone(&self) -> MaybeOwned<'a> {
|
|
match *self {
|
|
Slice(s) => Slice(s),
|
|
Owned(ref s) => Owned(s.to_owned())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> DeepClone for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn deep_clone(&self) -> MaybeOwned<'a> {
|
|
match *self {
|
|
Slice(s) => Slice(s),
|
|
Owned(ref s) => Owned(s.to_owned())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Default for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn default() -> MaybeOwned<'a> { Slice("") }
|
|
}
|
|
|
|
impl<'a> IterBytes for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
|
|
match *self {
|
|
Slice(s) => s.iter_bytes(lsb0, f),
|
|
Owned(ref s) => s.iter_bytes(lsb0, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> fmt::Show for MaybeOwned<'a> {
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match *self {
|
|
Slice(ref s) => s.fmt(f),
|
|
Owned(ref s) => s.fmt(f)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Unsafe operations
|
|
pub mod raw {
|
|
use cast;
|
|
use container::Container;
|
|
use libc;
|
|
use ptr;
|
|
use ptr::RawPtr;
|
|
use str::{is_utf8, OwnedStr, StrSlice};
|
|
use vec;
|
|
use vec::{MutableVector, ImmutableVector, OwnedVector};
|
|
use unstable::raw::Slice;
|
|
|
|
/// Create a Rust string from a *u8 buffer of the given length
|
|
pub unsafe fn from_buf_len(buf: *u8, len: uint) -> ~str {
|
|
let mut v: ~[u8] = vec::with_capacity(len);
|
|
ptr::copy_memory(v.as_mut_ptr(), buf, len);
|
|
v.set_len(len);
|
|
|
|
assert!(is_utf8(v));
|
|
::cast::transmute(v)
|
|
}
|
|
|
|
#[lang="strdup_uniq"]
|
|
#[cfg(not(test))]
|
|
#[allow(missing_doc)]
|
|
#[inline]
|
|
pub unsafe fn strdup_uniq(ptr: *u8, len: uint) -> ~str {
|
|
from_buf_len(ptr, len)
|
|
}
|
|
|
|
/// Create a Rust string from a null-terminated C string
|
|
pub unsafe fn from_c_str(buf: *libc::c_char) -> ~str {
|
|
let mut curr = buf;
|
|
let mut i = 0;
|
|
while *curr != 0 {
|
|
i += 1;
|
|
curr = buf.offset(i);
|
|
}
|
|
from_buf_len(buf as *u8, i as uint)
|
|
}
|
|
|
|
/// Converts a slice of bytes to a string slice without checking
|
|
/// that the string contains valid UTF-8.
|
|
pub unsafe fn from_utf8<'a>(v: &'a [u8]) -> &'a str {
|
|
cast::transmute(v)
|
|
}
|
|
|
|
/// Converts an owned vector of bytes to a new owned string. This assumes
|
|
/// that the utf-8-ness of the vector has already been validated
|
|
#[inline]
|
|
pub unsafe fn from_utf8_owned(v: ~[u8]) -> ~str {
|
|
cast::transmute(v)
|
|
}
|
|
|
|
/// Converts a byte to a string.
|
|
pub unsafe fn from_byte(u: u8) -> ~str { from_utf8_owned(~[u]) }
|
|
|
|
/// Form a slice from a C string. Unsafe because the caller must ensure the
|
|
/// C string has the static lifetime, or else the return value may be
|
|
/// invalidated later.
|
|
pub unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str {
|
|
let s = s as *u8;
|
|
let mut curr = s;
|
|
let mut len = 0u;
|
|
while *curr != 0u8 {
|
|
len += 1u;
|
|
curr = s.offset(len as int);
|
|
}
|
|
let v = Slice { data: s, len: len };
|
|
assert!(is_utf8(::cast::transmute(v)));
|
|
::cast::transmute(v)
|
|
}
|
|
|
|
/// Takes a bytewise (not UTF-8) slice from a string.
|
|
///
|
|
/// Returns the substring from [`begin`..`end`).
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// If begin is greater than end.
|
|
/// If end is greater than the length of the string.
|
|
#[inline]
|
|
pub unsafe fn slice_bytes<'a>(s: &'a str, begin: uint, end: uint) -> &'a str {
|
|
assert!(begin <= end);
|
|
assert!(end <= s.len());
|
|
slice_unchecked(s, begin, end)
|
|
}
|
|
|
|
/// Takes a bytewise (not UTF-8) slice from a string.
|
|
///
|
|
/// Returns the substring from [`begin`..`end`).
|
|
///
|
|
/// Caller must check slice boundaries!
|
|
#[inline]
|
|
pub unsafe fn slice_unchecked<'a>(s: &'a str, begin: uint, end: uint) -> &'a str {
|
|
cast::transmute(Slice {
|
|
data: s.as_ptr().offset(begin as int),
|
|
len: end - begin,
|
|
})
|
|
}
|
|
|
|
/// Appends a byte to a string.
|
|
/// The caller must preserve the valid UTF-8 property.
|
|
#[inline]
|
|
pub unsafe fn push_byte(s: &mut ~str, b: u8) {
|
|
as_owned_vec(s).push(b)
|
|
}
|
|
|
|
/// Appends a vector of bytes to a string.
|
|
/// The caller must preserve the valid UTF-8 property.
|
|
#[inline]
|
|
pub unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) {
|
|
vec::bytes::push_bytes(as_owned_vec(s), bytes);
|
|
}
|
|
|
|
/// Removes the last byte from a string and returns it.
|
|
/// The caller must preserve the valid UTF-8 property.
|
|
pub unsafe fn pop_byte(s: &mut ~str) -> u8 {
|
|
let len = s.len();
|
|
assert!((len > 0u));
|
|
let b = s[len - 1u];
|
|
s.set_len(len - 1);
|
|
return b;
|
|
}
|
|
|
|
/// Removes the first byte from a string and returns it.
|
|
/// The caller must preserve the valid UTF-8 property.
|
|
pub unsafe fn shift_byte(s: &mut ~str) -> u8 {
|
|
let len = s.len();
|
|
assert!((len > 0u));
|
|
let b = s[0];
|
|
*s = s.slice(1, len).to_owned();
|
|
return b;
|
|
}
|
|
|
|
/// Access the str in its vector representation.
|
|
/// The caller must preserve the valid UTF-8 property when modifying.
|
|
#[inline]
|
|
pub unsafe fn as_owned_vec<'a>(s: &'a mut ~str) -> &'a mut ~[u8] {
|
|
cast::transmute(s)
|
|
}
|
|
|
|
/// Sets the length of a string
|
|
///
|
|
/// This will explicitly set the size of the string, without actually
|
|
/// modifing its buffers, so it is up to the caller to ensure that
|
|
/// the string is actually the specified size.
|
|
#[test]
|
|
fn test_from_buf_len() {
|
|
unsafe {
|
|
let a = ~[65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8];
|
|
let b = a.as_ptr();
|
|
let c = from_buf_len(b, 3u);
|
|
assert_eq!(c, ~"AAA");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Section: Trait implementations
|
|
*/
|
|
|
|
#[cfg(not(test))]
|
|
#[allow(missing_doc)]
|
|
pub mod traits {
|
|
use container::Container;
|
|
use cmp::{TotalOrd, Ordering, Less, Equal, Greater, Eq, Ord, Equiv, TotalEq};
|
|
use iter::Iterator;
|
|
use ops::Add;
|
|
use option::{Some, None};
|
|
use str::{Str, StrSlice, OwnedStr, eq_slice};
|
|
|
|
impl<'a> Add<&'a str,~str> for &'a str {
|
|
#[inline]
|
|
fn add(&self, rhs: & &'a str) -> ~str {
|
|
let mut ret = self.to_owned();
|
|
ret.push_str(*rhs);
|
|
ret
|
|
}
|
|
}
|
|
|
|
impl<'a> TotalOrd for &'a str {
|
|
#[inline]
|
|
fn cmp(&self, other: & &'a str) -> Ordering {
|
|
for (s_b, o_b) in self.bytes().zip(other.bytes()) {
|
|
match s_b.cmp(&o_b) {
|
|
Greater => return Greater,
|
|
Less => return Less,
|
|
Equal => ()
|
|
}
|
|
}
|
|
|
|
self.len().cmp(&other.len())
|
|
}
|
|
}
|
|
|
|
impl TotalOrd for ~str {
|
|
#[inline]
|
|
fn cmp(&self, other: &~str) -> Ordering { self.as_slice().cmp(&other.as_slice()) }
|
|
}
|
|
|
|
impl<'a> Eq for &'a str {
|
|
#[inline]
|
|
fn eq(&self, other: & &'a str) -> bool {
|
|
eq_slice((*self), (*other))
|
|
}
|
|
#[inline]
|
|
fn ne(&self, other: & &'a str) -> bool { !(*self).eq(other) }
|
|
}
|
|
|
|
impl Eq for ~str {
|
|
#[inline]
|
|
fn eq(&self, other: &~str) -> bool {
|
|
eq_slice((*self), (*other))
|
|
}
|
|
}
|
|
|
|
impl<'a> TotalEq for &'a str {
|
|
#[inline]
|
|
fn equals(&self, other: & &'a str) -> bool {
|
|
eq_slice((*self), (*other))
|
|
}
|
|
}
|
|
|
|
impl TotalEq for ~str {
|
|
#[inline]
|
|
fn equals(&self, other: &~str) -> bool {
|
|
eq_slice((*self), (*other))
|
|
}
|
|
}
|
|
|
|
impl<'a> Ord for &'a str {
|
|
#[inline]
|
|
fn lt(&self, other: & &'a str) -> bool { self.cmp(other) == Less }
|
|
}
|
|
|
|
impl Ord for ~str {
|
|
#[inline]
|
|
fn lt(&self, other: &~str) -> bool { self.cmp(other) == Less }
|
|
}
|
|
|
|
impl<'a, S: Str> Equiv<S> for &'a str {
|
|
#[inline]
|
|
fn equiv(&self, other: &S) -> bool { eq_slice(*self, other.as_slice()) }
|
|
}
|
|
|
|
impl<'a, S: Str> Equiv<S> for ~str {
|
|
#[inline]
|
|
fn equiv(&self, other: &S) -> bool { eq_slice(*self, other.as_slice()) }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod traits {}
|
|
|
|
/// Any string that can be represented as a slice
|
|
pub trait Str {
|
|
/// Work with `self` as a slice.
|
|
fn as_slice<'a>(&'a self) -> &'a str;
|
|
|
|
/// Convert `self` into a ~str, not making a copy if possible
|
|
fn into_owned(self) -> ~str;
|
|
}
|
|
|
|
impl<'a> Str for &'a str {
|
|
#[inline]
|
|
fn as_slice<'a>(&'a self) -> &'a str { *self }
|
|
|
|
#[inline]
|
|
fn into_owned(self) -> ~str { self.to_owned() }
|
|
}
|
|
|
|
impl<'a> Str for ~str {
|
|
#[inline]
|
|
fn as_slice<'a>(&'a self) -> &'a str {
|
|
let s: &'a str = *self; s
|
|
}
|
|
|
|
#[inline]
|
|
fn into_owned(self) -> ~str { self }
|
|
}
|
|
|
|
impl<'a> Container for &'a str {
|
|
#[inline]
|
|
fn len(&self) -> uint {
|
|
self.repr().len
|
|
}
|
|
}
|
|
|
|
impl Container for ~str {
|
|
#[inline]
|
|
fn len(&self) -> uint { self.as_slice().len() }
|
|
}
|
|
|
|
impl Mutable for ~str {
|
|
/// Remove all content, make the string empty
|
|
#[inline]
|
|
fn clear(&mut self) {
|
|
unsafe {
|
|
self.set_len(0)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Methods for string slices
|
|
pub trait StrSlice<'a> {
|
|
/// Returns true if one string contains another
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// - needle - The string to look for
|
|
fn contains<'a>(&self, needle: &'a str) -> bool;
|
|
|
|
/// Returns true if a string contains a char.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// - needle - The char to look for
|
|
fn contains_char(&self, needle: char) -> bool;
|
|
|
|
/// An iterator over the characters of `self`. Note, this iterates
|
|
/// over unicode code-points, not unicode graphemes.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[char] = "abc åäö".chars().collect();
|
|
/// assert_eq!(v, ~['a', 'b', 'c', ' ', 'å', 'ä', 'ö']);
|
|
/// ```
|
|
fn chars(&self) -> Chars<'a>;
|
|
|
|
/// An iterator over the characters of `self`, in reverse order.
|
|
fn chars_rev(&self) -> RevChars<'a>;
|
|
|
|
/// An iterator over the bytes of `self`
|
|
fn bytes(&self) -> Bytes<'a>;
|
|
|
|
/// An iterator over the bytes of `self`, in reverse order
|
|
fn bytes_rev(&self) -> RevBytes<'a>;
|
|
|
|
/// An iterator over the characters of `self` and their byte offsets.
|
|
fn char_indices(&self) -> CharOffsets<'a>;
|
|
|
|
/// An iterator over the characters of `self` and their byte offsets,
|
|
/// in reverse order.
|
|
fn char_indices_rev(&self) -> RevCharOffsets<'a>;
|
|
|
|
/// An iterator over substrings of `self`, separated by characters
|
|
/// matched by `sep`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "Mary had a little lamb".split(' ').collect();
|
|
/// assert_eq!(v, ~["Mary", "had", "a", "little", "lamb"]);
|
|
///
|
|
/// let v: ~[&str] = "abc1def2ghi".split(|c: char| c.is_digit()).collect();
|
|
/// assert_eq!(v, ~["abc", "def", "ghi"]);
|
|
///
|
|
/// let v: ~[&str] = "lionXXtigerXleopard".split('X').collect();
|
|
/// assert_eq!(v, ~["lion", "", "tiger", "leopard"]);
|
|
/// ```
|
|
fn split<Sep: CharEq>(&self, sep: Sep) -> CharSplits<'a, Sep>;
|
|
|
|
/// An iterator over substrings of `self`, separated by characters
|
|
/// matched by `sep`, restricted to splitting at most `count`
|
|
/// times.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "Mary had a little lambda".splitn(' ', 2).collect();
|
|
/// assert_eq!(v, ~["Mary", "had", "a little lambda"]);
|
|
///
|
|
/// let v: ~[&str] = "abc1def2ghi".splitn(|c: char| c.is_digit(), 1).collect();
|
|
/// assert_eq!(v, ~["abc", "def2ghi"]);
|
|
///
|
|
/// let v: ~[&str] = "lionXXtigerXleopard".splitn('X', 2).collect();
|
|
/// assert_eq!(v, ~["lion", "", "tigerXleopard"]);
|
|
/// ```
|
|
fn splitn<Sep: CharEq>(&self, sep: Sep, count: uint) -> CharSplitsN<'a, Sep>;
|
|
|
|
/// An iterator over substrings of `self`, separated by characters
|
|
/// matched by `sep`.
|
|
///
|
|
/// Equivalent to `split`, except that the trailing substring
|
|
/// is skipped if empty (terminator semantics).
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "A.B.".split_terminator('.').collect();
|
|
/// assert_eq!(v, ~["A", "B"]);
|
|
///
|
|
/// let v: ~[&str] = "A..B..".split_terminator('.').collect();
|
|
/// assert_eq!(v, ~["A", "", "B", ""]);
|
|
/// ```
|
|
fn split_terminator<Sep: CharEq>(&self, sep: Sep) -> CharSplits<'a, Sep>;
|
|
|
|
/// An iterator over substrings of `self`, separated by characters
|
|
/// matched by `sep`, in reverse order.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "Mary had a little lamb".rsplit(' ').collect();
|
|
/// assert_eq!(v, ~["lamb", "little", "a", "had", "Mary"]);
|
|
///
|
|
/// let v: ~[&str] = "abc1def2ghi".rsplit(|c: char| c.is_digit()).collect();
|
|
/// assert_eq!(v, ~["ghi", "def", "abc"]);
|
|
///
|
|
/// let v: ~[&str] = "lionXXtigerXleopard".rsplit('X').collect();
|
|
/// assert_eq!(v, ~["leopard", "tiger", "", "lion"]);
|
|
/// ```
|
|
fn rsplit<Sep: CharEq>(&self, sep: Sep) -> RevCharSplits<'a, Sep>;
|
|
|
|
/// An iterator over substrings of `self`, separated by characters
|
|
/// matched by `sep`, starting from the end of the string.
|
|
/// Restricted to splitting at most `count` times.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "Mary had a little lamb".rsplitn(' ', 2).collect();
|
|
/// assert_eq!(v, ~["lamb", "little", "Mary had a"]);
|
|
///
|
|
/// let v: ~[&str] = "abc1def2ghi".rsplitn(|c: char| c.is_digit(), 1).collect();
|
|
/// assert_eq!(v, ~["ghi", "abc1def"]);
|
|
///
|
|
/// let v: ~[&str] = "lionXXtigerXleopard".rsplitn('X', 2).collect();
|
|
/// assert_eq!(v, ~["leopard", "tiger", "lionX"]);
|
|
/// ```
|
|
fn rsplitn<Sep: CharEq>(&self, sep: Sep, count: uint) -> CharSplitsN<'a, Sep>;
|
|
|
|
/// An iterator over the start and end indices of the disjoint
|
|
/// matches of `sep` within `self`.
|
|
///
|
|
/// That is, each returned value `(start, end)` satisfies
|
|
/// `self.slice(start, end) == sep`. For matches of `sep` within
|
|
/// `self` that overlap, only the indicies corresponding to the
|
|
/// first match are returned.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[(uint, uint)] = "abcXXXabcYYYabc".match_indices("abc").collect();
|
|
/// assert_eq!(v, ~[(0,3), (6,9), (12,15)]);
|
|
///
|
|
/// let v: ~[(uint, uint)] = "1abcabc2".match_indices("abc").collect();
|
|
/// assert_eq!(v, ~[(1,4), (4,7)]);
|
|
///
|
|
/// let v: ~[(uint, uint)] = "ababa".match_indices("aba").collect();
|
|
/// assert_eq!(v, ~[(0, 3)]); // only the first `aba`
|
|
/// ```
|
|
fn match_indices(&self, sep: &'a str) -> MatchIndices<'a>;
|
|
|
|
/// An iterator over the substrings of `self` separated by `sep`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let v: ~[&str] = "abcXXXabcYYYabc".split_str("abc").collect();
|
|
/// assert_eq!(v, ~["", "XXX", "YYY", ""]);
|
|
///
|
|
/// let v: ~[&str] = "1abcabc2".split_str("abc").collect();
|
|
/// assert_eq!(v, ~["1", "", "2"]);
|
|
/// ```
|
|
fn split_str(&self, &'a str) -> StrSplits<'a>;
|
|
|
|
/// An iterator over the lines of a string (subsequences separated
|
|
/// by `\n`). This does not include the empty string after a
|
|
/// trailing `\n`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let four_lines = "foo\nbar\n\nbaz\n";
|
|
/// let v: ~[&str] = four_lines.lines().collect();
|
|
/// assert_eq!(v, ~["foo", "bar", "", "baz"]);
|
|
/// ```
|
|
fn lines(&self) -> CharSplits<'a, char>;
|
|
|
|
/// An iterator over the lines of a string, separated by either
|
|
/// `\n` or `\r\n`. As with `.lines()`, this does not include an
|
|
/// empty trailing line.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let four_lines = "foo\r\nbar\n\r\nbaz\n";
|
|
/// let v: ~[&str] = four_lines.lines_any().collect();
|
|
/// assert_eq!(v, ~["foo", "bar", "", "baz"]);
|
|
/// ```
|
|
fn lines_any(&self) -> AnyLines<'a>;
|
|
|
|
/// An iterator over the words of a string (subsequences separated
|
|
/// by any sequence of whitespace). Sequences of whitespace are
|
|
/// collapsed, so empty "words" are not included.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let some_words = " Mary had\ta little \n\t lamb";
|
|
/// let v: ~[&str] = some_words.words().collect();
|
|
/// assert_eq!(v, ~["Mary", "had", "a", "little", "lamb"]);
|
|
/// ```
|
|
fn words(&self) -> Words<'a>;
|
|
|
|
/// An Iterator over the string in Unicode Normalization Form D
|
|
/// (canonical decomposition).
|
|
fn nfd_chars(&self) -> Normalizations<'a>;
|
|
|
|
/// An Iterator over the string in Unicode Normalization Form KD
|
|
/// (compatibility decomposition).
|
|
fn nfkd_chars(&self) -> Normalizations<'a>;
|
|
|
|
/// Returns true if the string contains only whitespace.
|
|
///
|
|
/// Whitespace characters are determined by `char::is_whitespace`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// assert!(" \t\n".is_whitespace());
|
|
/// assert!("".is_whitespace());
|
|
///
|
|
/// assert!( !"abc".is_whitespace());
|
|
/// ```
|
|
fn is_whitespace(&self) -> bool;
|
|
|
|
/// Returns true if the string contains only alphanumeric code
|
|
/// points.
|
|
///
|
|
/// Alphanumeric characters are determined by `char::is_alphanumeric`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// assert!("Löwe老虎Léopard123".is_alphanumeric());
|
|
/// assert!("".is_alphanumeric());
|
|
///
|
|
/// assert!( !" &*~".is_alphanumeric());
|
|
/// ```
|
|
fn is_alphanumeric(&self) -> bool;
|
|
|
|
/// Returns the number of Unicode code points (`char`) that a
|
|
/// string holds.
|
|
///
|
|
/// This does not perform any normalization, and is `O(n)`, since
|
|
/// UTF-8 is a variable width encoding of code points.
|
|
///
|
|
/// *Warning*: The number of code points in a string does not directly
|
|
/// correspond to the number of visible characters or width of the
|
|
/// visible text due to composing characters, and double- and
|
|
/// zero-width ones.
|
|
///
|
|
/// See also `.len()` for the byte length.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// // composed forms of `ö` and `é`
|
|
/// let c = "Löwe 老虎 Léopard"; // German, Simplified Chinese, French
|
|
/// // decomposed forms of `ö` and `é`
|
|
/// let d = "Lo\u0308we 老虎 Le\u0301opard";
|
|
///
|
|
/// assert_eq!(c.char_len(), 15);
|
|
/// assert_eq!(d.char_len(), 17);
|
|
///
|
|
/// assert_eq!(c.len(), 21);
|
|
/// assert_eq!(d.len(), 23);
|
|
///
|
|
/// // the two strings *look* the same
|
|
/// println!("{}", c);
|
|
/// println!("{}", d);
|
|
/// ```
|
|
fn char_len(&self) -> uint;
|
|
|
|
/// Returns a slice of the given string from the byte range
|
|
/// [`begin`..`end`).
|
|
///
|
|
/// This operation is `O(1)`.
|
|
///
|
|
/// Fails when `begin` and `end` do not point to valid characters
|
|
/// or point beyond the last character of the string.
|
|
///
|
|
/// See also `slice_to` and `slice_from` for slicing prefixes and
|
|
/// suffixes of strings, and `slice_chars` for slicing based on
|
|
/// code point counts.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
/// assert_eq!(s.slice(0, 1), "L");
|
|
///
|
|
/// assert_eq!(s.slice(1, 9), "öwe 老");
|
|
///
|
|
/// // these will fail:
|
|
/// // byte 2 lies within `ö`:
|
|
/// // s.slice(2, 3);
|
|
///
|
|
/// // byte 8 lies within `老`
|
|
/// // s.slice(1, 8);
|
|
///
|
|
/// // byte 100 is outside the string
|
|
/// // s.slice(3, 100);
|
|
/// ```
|
|
fn slice(&self, begin: uint, end: uint) -> &'a str;
|
|
|
|
/// Returns a slice of the string from `begin` to its end.
|
|
///
|
|
/// Equivalent to `self.slice(begin, self.len())`.
|
|
///
|
|
/// Fails when `begin` does not point to a valid character, or is
|
|
/// out of bounds.
|
|
///
|
|
/// See also `slice`, `slice_to` and `slice_chars`.
|
|
fn slice_from(&self, begin: uint) -> &'a str;
|
|
|
|
/// Returns a slice of the string from the beginning to byte
|
|
/// `end`.
|
|
///
|
|
/// Equivalent to `self.slice(0, end)`.
|
|
///
|
|
/// Fails when `end` does not point to a valid character, or is
|
|
/// out of bounds.
|
|
///
|
|
/// See also `slice`, `slice_from` and `slice_chars`.
|
|
fn slice_to(&self, end: uint) -> &'a str;
|
|
|
|
/// Returns a slice of the string from the character range
|
|
/// [`begin`..`end`).
|
|
///
|
|
/// That is, start at the `begin`-th code point of the string and
|
|
/// continue to the `end`-th code point. This does not detect or
|
|
/// handle edge cases such as leaving a combining character as the
|
|
/// first code point of the string.
|
|
///
|
|
/// Due to the design of UTF-8, this operation is `O(end -
|
|
/// begin)`. See `slice`, `slice_to` and `slice_from` for `O(1)`
|
|
/// variants that use byte indices rather than code point
|
|
/// indices.
|
|
///
|
|
/// Fails if `begin` > `end` or the either `begin` or `end` are
|
|
/// beyond the last character of the string.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
/// assert_eq!(s.slice_chars(0, 4), "Löwe");
|
|
/// assert_eq!(s.slice_chars(5, 7), "老虎");
|
|
/// ```
|
|
fn slice_chars(&self, begin: uint, end: uint) -> &'a str;
|
|
|
|
/// Returns true if `needle` is a prefix of the string.
|
|
fn starts_with(&self, needle: &str) -> bool;
|
|
|
|
/// Returns true if `needle` is a suffix of the string.
|
|
fn ends_with(&self, needle: &str) -> bool;
|
|
|
|
/// Escape each char in `s` with `char::escape_default`.
|
|
fn escape_default(&self) -> ~str;
|
|
|
|
/// Escape each char in `s` with `char::escape_unicode`.
|
|
fn escape_unicode(&self) -> ~str;
|
|
|
|
/// Returns a string with leading and trailing whitespace removed.
|
|
fn trim(&self) -> &'a str;
|
|
|
|
/// Returns a string with leading whitespace removed.
|
|
fn trim_left(&self) -> &'a str;
|
|
|
|
/// Returns a string with trailing whitespace removed.
|
|
fn trim_right(&self) -> &'a str;
|
|
|
|
/// Returns a string with characters that match `to_trim` removed.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * to_trim - a character matcher
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// assert_eq!("11foo1bar11".trim_chars(&'1'), "foo1bar")
|
|
/// assert_eq!("12foo1bar12".trim_chars(& &['1', '2']), "foo1bar")
|
|
/// assert_eq!("123foo1bar123".trim_chars(&|c: char| c.is_digit()), "foo1bar")
|
|
/// ```
|
|
fn trim_chars<C: CharEq>(&self, to_trim: &C) -> &'a str;
|
|
|
|
/// Returns a string with leading `chars_to_trim` removed.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * to_trim - a character matcher
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// assert_eq!("11foo1bar11".trim_left_chars(&'1'), "foo1bar11")
|
|
/// assert_eq!("12foo1bar12".trim_left_chars(& &['1', '2']), "foo1bar12")
|
|
/// assert_eq!("123foo1bar123".trim_left_chars(&|c: char| c.is_digit()), "foo1bar123")
|
|
/// ```
|
|
fn trim_left_chars<C: CharEq>(&self, to_trim: &C) -> &'a str;
|
|
|
|
/// Returns a string with trailing `chars_to_trim` removed.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * to_trim - a character matcher
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// assert_eq!("11foo1bar11".trim_right_chars(&'1'), "11foo1bar")
|
|
/// assert_eq!("12foo1bar12".trim_right_chars(& &['1', '2']), "12foo1bar")
|
|
/// assert_eq!("123foo1bar123".trim_right_chars(&|c: char| c.is_digit()), "123foo1bar")
|
|
/// ```
|
|
fn trim_right_chars<C: CharEq>(&self, to_trim: &C) -> &'a str;
|
|
|
|
/// Replace all occurrences of one string with another.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `from` - The string to replace
|
|
/// * `to` - The replacement string
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// The original string with all occurances of `from` replaced with `to`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = ~"Do you know the muffin man,
|
|
/// The muffin man, the muffin man, ...";
|
|
///
|
|
/// assert_eq!(s.replace("muffin man", "little lamb"),
|
|
/// ~"Do you know the little lamb,
|
|
/// The little lamb, the little lamb, ...");
|
|
///
|
|
/// // not found, so no change.
|
|
/// assert_eq!(s.replace("cookie monster", "little lamb"), s);
|
|
/// ```
|
|
fn replace(&self, from: &str, to: &str) -> ~str;
|
|
|
|
/// Copy a slice into a new owned str.
|
|
fn to_owned(&self) -> ~str;
|
|
|
|
/// Converts to a vector of `u16` encoded as UTF-16.
|
|
fn to_utf16(&self) -> ~[u16];
|
|
|
|
/// Check that `index`-th byte lies at the start and/or end of a
|
|
/// UTF-8 code point sequence.
|
|
///
|
|
/// The start and end of the string (when `index == self.len()`)
|
|
/// are considered to be boundaries.
|
|
///
|
|
/// Fails if `index` is greater than `self.len()`.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
/// assert!(s.is_char_boundary(0));
|
|
/// // start of `老`
|
|
/// assert!(s.is_char_boundary(6));
|
|
/// assert!(s.is_char_boundary(s.len()));
|
|
///
|
|
/// // second byte of `ö`
|
|
/// assert!(!s.is_char_boundary(2));
|
|
///
|
|
/// // third byte of `老`
|
|
/// assert!(!s.is_char_boundary(8));
|
|
/// ```
|
|
fn is_char_boundary(&self, index: uint) -> bool;
|
|
|
|
/// Pluck a character out of a string and return the index of the next
|
|
/// character.
|
|
///
|
|
/// This function can be used to iterate over the unicode characters of a
|
|
/// string.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// This example manually iterate through the characters of a
|
|
/// string; this should normally by done by `.chars()` or
|
|
/// `.char_indices`.
|
|
///
|
|
/// ```rust
|
|
/// use std::str::CharRange;
|
|
///
|
|
/// let s = "中华Việt Nam";
|
|
/// let mut i = 0u;
|
|
/// while i < s.len() {
|
|
/// let CharRange {ch, next} = s.char_range_at(i);
|
|
/// println!("{}: {}", i, ch);
|
|
/// i = next;
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// ## Output
|
|
///
|
|
/// ```ignore
|
|
/// 0: 中
|
|
/// 3: 华
|
|
/// 6: V
|
|
/// 7: i
|
|
/// 8: ệ
|
|
/// 11: t
|
|
/// 12:
|
|
/// 13: N
|
|
/// 14: a
|
|
/// 15: m
|
|
/// ```
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * s - The string
|
|
/// * i - The byte offset of the char to extract
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// A record {ch: char, next: uint} containing the char value and the byte
|
|
/// index of the next unicode character.
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// If `i` is greater than or equal to the length of the string.
|
|
/// If `i` is not the index of the beginning of a valid UTF-8 character.
|
|
fn char_range_at(&self, start: uint) -> CharRange;
|
|
|
|
/// Given a byte position and a str, return the previous char and its position.
|
|
///
|
|
/// This function can be used to iterate over a unicode string in reverse.
|
|
///
|
|
/// Returns 0 for next index if called on start index 0.
|
|
fn char_range_at_reverse(&self, start: uint) -> CharRange;
|
|
|
|
/// Plucks the character starting at the `i`th byte of a string
|
|
fn char_at(&self, i: uint) -> char;
|
|
|
|
/// Plucks the character ending at the `i`th byte of a string
|
|
fn char_at_reverse(&self, i: uint) -> char;
|
|
|
|
/// Work with the byte buffer of a string as a byte slice.
|
|
fn as_bytes(&self) -> &'a [u8];
|
|
|
|
/// Returns the byte index of the first character of `self` that
|
|
/// matches `search`.
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// `Some` containing the byte index of the last matching character
|
|
/// or `None` if there is no match
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
///
|
|
/// assert_eq!(s.find('L'), Some(0));
|
|
/// assert_eq!(s.find('é'), Some(14));
|
|
///
|
|
/// // the first space
|
|
/// assert_eq!(s.find(|c: char| c.is_whitespace()), Some(5));
|
|
///
|
|
/// // neither are found
|
|
/// assert_eq!(s.find(&['1', '2']), None);
|
|
/// ```
|
|
fn find<C: CharEq>(&self, search: C) -> Option<uint>;
|
|
|
|
/// Returns the byte index of the last character of `self` that
|
|
/// matches `search`.
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// `Some` containing the byte index of the last matching character
|
|
/// or `None` if there is no match.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
///
|
|
/// assert_eq!(s.rfind('L'), Some(13));
|
|
/// assert_eq!(s.rfind('é'), Some(14));
|
|
///
|
|
/// // the second space
|
|
/// assert_eq!(s.rfind(|c: char| c.is_whitespace()), Some(12));
|
|
///
|
|
/// // searches for an occurrence of either `1` or `2`, but neither are found
|
|
/// assert_eq!(s.rfind(&['1', '2']), None);
|
|
/// ```
|
|
fn rfind<C: CharEq>(&self, search: C) -> Option<uint>;
|
|
|
|
/// Returns the byte index of the first matching substring
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `needle` - The string to search for
|
|
///
|
|
/// # Return value
|
|
///
|
|
/// `Some` containing the byte index of the first matching substring
|
|
/// or `None` if there is no match.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
///
|
|
/// assert_eq!(s.find_str("老虎 L"), Some(6));
|
|
/// assert_eq!(s.find_str("muffin man"), None);
|
|
/// ```
|
|
fn find_str(&self, &str) -> Option<uint>;
|
|
|
|
/// Given a string, make a new string with repeated copies of it.
|
|
fn repeat(&self, nn: uint) -> ~str;
|
|
|
|
/// Retrieves the first character from a string slice and returns
|
|
/// it. This does not allocate a new string; instead, it returns a
|
|
/// slice that point one character beyond the character that was
|
|
/// shifted.
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// If the string does not contain any characters.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let s = "Löwe 老虎 Léopard";
|
|
/// let (c, s1) = s.slice_shift_char();
|
|
/// assert_eq!(c, 'L');
|
|
/// assert_eq!(s1, "öwe 老虎 Léopard");
|
|
///
|
|
/// let (c, s2) = s1.slice_shift_char();
|
|
/// assert_eq!(c, 'ö');
|
|
/// assert_eq!(s2, "we 老虎 Léopard");
|
|
/// ```
|
|
fn slice_shift_char(&self) -> (char, &'a str);
|
|
|
|
/// Levenshtein Distance between two strings.
|
|
fn lev_distance(&self, t: &str) -> uint;
|
|
|
|
/// Returns the byte offset of an inner slice relative to an enclosing outer slice.
|
|
///
|
|
/// Fails if `inner` is not a direct slice contained within self.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// let string = "a\nb\nc";
|
|
/// let lines: ~[&str] = string.lines().collect();
|
|
///
|
|
/// assert!(string.subslice_offset(lines[0]) == 0); // &"a"
|
|
/// assert!(string.subslice_offset(lines[1]) == 2); // &"b"
|
|
/// assert!(string.subslice_offset(lines[2]) == 4); // &"c"
|
|
/// ```
|
|
fn subslice_offset(&self, inner: &str) -> uint;
|
|
|
|
/// Return an unsafe pointer to the strings buffer.
|
|
///
|
|
/// The caller must ensure that the string outlives this pointer,
|
|
/// and that it is not reallocated (e.g. by pushing to the
|
|
/// string).
|
|
fn as_ptr(&self) -> *u8;
|
|
}
|
|
|
|
impl<'a> StrSlice<'a> for &'a str {
|
|
#[inline]
|
|
fn contains<'a>(&self, needle: &'a str) -> bool {
|
|
self.find_str(needle).is_some()
|
|
}
|
|
|
|
#[inline]
|
|
fn contains_char(&self, needle: char) -> bool {
|
|
self.find(needle).is_some()
|
|
}
|
|
|
|
#[inline]
|
|
fn chars(&self) -> Chars<'a> {
|
|
Chars{string: *self}
|
|
}
|
|
|
|
#[inline]
|
|
fn chars_rev(&self) -> RevChars<'a> {
|
|
self.chars().rev()
|
|
}
|
|
|
|
#[inline]
|
|
fn bytes(&self) -> Bytes<'a> {
|
|
self.as_bytes().iter().map(|&b| b)
|
|
}
|
|
|
|
#[inline]
|
|
fn bytes_rev(&self) -> RevBytes<'a> {
|
|
self.bytes().rev()
|
|
}
|
|
|
|
#[inline]
|
|
fn char_indices(&self) -> CharOffsets<'a> {
|
|
CharOffsets{string: *self, iter: self.chars()}
|
|
}
|
|
|
|
#[inline]
|
|
fn char_indices_rev(&self) -> RevCharOffsets<'a> {
|
|
self.char_indices().rev()
|
|
}
|
|
|
|
#[inline]
|
|
fn split<Sep: CharEq>(&self, sep: Sep) -> CharSplits<'a, Sep> {
|
|
CharSplits {
|
|
string: *self,
|
|
only_ascii: sep.only_ascii(),
|
|
sep: sep,
|
|
allow_trailing_empty: true,
|
|
finished: false,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn splitn<Sep: CharEq>(&self, sep: Sep, count: uint)
|
|
-> CharSplitsN<'a, Sep> {
|
|
CharSplitsN {
|
|
iter: self.split(sep),
|
|
count: count,
|
|
invert: false,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn split_terminator<Sep: CharEq>(&self, sep: Sep)
|
|
-> CharSplits<'a, Sep> {
|
|
CharSplits {
|
|
allow_trailing_empty: false,
|
|
..self.split(sep)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn rsplit<Sep: CharEq>(&self, sep: Sep) -> RevCharSplits<'a, Sep> {
|
|
self.split(sep).rev()
|
|
}
|
|
|
|
#[inline]
|
|
fn rsplitn<Sep: CharEq>(&self, sep: Sep, count: uint)
|
|
-> CharSplitsN<'a, Sep> {
|
|
CharSplitsN {
|
|
iter: self.split(sep),
|
|
count: count,
|
|
invert: true,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn match_indices(&self, sep: &'a str) -> MatchIndices<'a> {
|
|
assert!(!sep.is_empty())
|
|
MatchIndices {
|
|
haystack: *self,
|
|
needle: sep,
|
|
position: 0
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn split_str(&self, sep: &'a str) -> StrSplits<'a> {
|
|
StrSplits {
|
|
it: self.match_indices(sep),
|
|
last_end: 0,
|
|
finished: false
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn lines(&self) -> CharSplits<'a, char> {
|
|
self.split_terminator('\n')
|
|
}
|
|
|
|
fn lines_any(&self) -> AnyLines<'a> {
|
|
self.lines().map(|line| {
|
|
let l = line.len();
|
|
if l > 0 && line[l - 1] == '\r' as u8 { line.slice(0, l - 1) }
|
|
else { line }
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn words(&self) -> Words<'a> {
|
|
self.split(char::is_whitespace).filter(|s| !s.is_empty())
|
|
}
|
|
|
|
#[inline]
|
|
fn nfd_chars(&self) -> Normalizations<'a> {
|
|
Normalizations {
|
|
iter: self.chars(),
|
|
buffer: ~[],
|
|
sorted: false,
|
|
kind: NFD
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn nfkd_chars(&self) -> Normalizations<'a> {
|
|
Normalizations {
|
|
iter: self.chars(),
|
|
buffer: ~[],
|
|
sorted: false,
|
|
kind: NFKD
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn is_whitespace(&self) -> bool { self.chars().all(char::is_whitespace) }
|
|
|
|
#[inline]
|
|
fn is_alphanumeric(&self) -> bool { self.chars().all(char::is_alphanumeric) }
|
|
|
|
#[inline]
|
|
fn char_len(&self) -> uint { self.chars().len() }
|
|
|
|
#[inline]
|
|
fn slice(&self, begin: uint, end: uint) -> &'a str {
|
|
assert!(self.is_char_boundary(begin) && self.is_char_boundary(end));
|
|
unsafe { raw::slice_bytes(*self, begin, end) }
|
|
}
|
|
|
|
#[inline]
|
|
fn slice_from(&self, begin: uint) -> &'a str {
|
|
self.slice(begin, self.len())
|
|
}
|
|
|
|
#[inline]
|
|
fn slice_to(&self, end: uint) -> &'a str {
|
|
assert!(self.is_char_boundary(end));
|
|
unsafe { raw::slice_bytes(*self, 0, end) }
|
|
}
|
|
|
|
fn slice_chars(&self, begin: uint, end: uint) -> &'a str {
|
|
assert!(begin <= end);
|
|
let mut count = 0;
|
|
let mut begin_byte = None;
|
|
let mut end_byte = None;
|
|
|
|
// This could be even more efficient by not decoding,
|
|
// only finding the char boundaries
|
|
for (idx, _) in self.char_indices() {
|
|
if count == begin { begin_byte = Some(idx); }
|
|
if count == end { end_byte = Some(idx); break; }
|
|
count += 1;
|
|
}
|
|
if begin_byte.is_none() && count == begin { begin_byte = Some(self.len()) }
|
|
if end_byte.is_none() && count == end { end_byte = Some(self.len()) }
|
|
|
|
match (begin_byte, end_byte) {
|
|
(None, _) => fail!("slice_chars: `begin` is beyond end of string"),
|
|
(_, None) => fail!("slice_chars: `end` is beyond end of string"),
|
|
(Some(a), Some(b)) => unsafe { raw::slice_bytes(*self, a, b) }
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn starts_with<'a>(&self, needle: &'a str) -> bool {
|
|
let n = needle.len();
|
|
self.len() >= n && needle.as_bytes() == self.as_bytes().slice_to(n)
|
|
}
|
|
|
|
#[inline]
|
|
fn ends_with(&self, needle: &str) -> bool {
|
|
let (m, n) = (self.len(), needle.len());
|
|
m >= n && needle.as_bytes() == self.as_bytes().slice_from(m - n)
|
|
}
|
|
|
|
fn escape_default(&self) -> ~str {
|
|
let mut out = with_capacity(self.len());
|
|
for c in self.chars() {
|
|
c.escape_default(|c| out.push_char(c));
|
|
}
|
|
out
|
|
}
|
|
|
|
fn escape_unicode(&self) -> ~str {
|
|
let mut out = with_capacity(self.len());
|
|
for c in self.chars() {
|
|
c.escape_unicode(|c| out.push_char(c));
|
|
}
|
|
out
|
|
}
|
|
|
|
#[inline]
|
|
fn trim(&self) -> &'a str {
|
|
self.trim_left().trim_right()
|
|
}
|
|
|
|
#[inline]
|
|
fn trim_left(&self) -> &'a str {
|
|
self.trim_left_chars(&char::is_whitespace)
|
|
}
|
|
|
|
#[inline]
|
|
fn trim_right(&self) -> &'a str {
|
|
self.trim_right_chars(&char::is_whitespace)
|
|
}
|
|
|
|
#[inline]
|
|
fn trim_chars<C: CharEq>(&self, to_trim: &C) -> &'a str {
|
|
self.trim_left_chars(to_trim).trim_right_chars(to_trim)
|
|
}
|
|
|
|
#[inline]
|
|
fn trim_left_chars<C: CharEq>(&self, to_trim: &C) -> &'a str {
|
|
match self.find(|c: char| !to_trim.matches(c)) {
|
|
None => "",
|
|
Some(first) => unsafe { raw::slice_bytes(*self, first, self.len()) }
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn trim_right_chars<C: CharEq>(&self, to_trim: &C) -> &'a str {
|
|
match self.rfind(|c: char| !to_trim.matches(c)) {
|
|
None => "",
|
|
Some(last) => {
|
|
let next = self.char_range_at(last).next;
|
|
unsafe { raw::slice_bytes(*self, 0u, next) }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn replace(&self, from: &str, to: &str) -> ~str {
|
|
let mut result = ~"";
|
|
let mut last_end = 0;
|
|
for (start, end) in self.match_indices(from) {
|
|
result.push_str(unsafe{raw::slice_bytes(*self, last_end, start)});
|
|
result.push_str(to);
|
|
last_end = end;
|
|
}
|
|
result.push_str(unsafe{raw::slice_bytes(*self, last_end, self.len())});
|
|
result
|
|
}
|
|
|
|
#[inline]
|
|
fn to_owned(&self) -> ~str {
|
|
let len = self.len();
|
|
unsafe {
|
|
let mut v = vec::with_capacity(len);
|
|
|
|
ptr::copy_memory(v.as_mut_ptr(), self.as_ptr(), len);
|
|
v.set_len(len);
|
|
::cast::transmute(v)
|
|
}
|
|
}
|
|
|
|
fn to_utf16(&self) -> ~[u16] {
|
|
let mut u = ~[];
|
|
for ch in self.chars() {
|
|
// Arithmetic with u32 literals is easier on the eyes than chars.
|
|
let mut ch = ch as u32;
|
|
|
|
if (ch & 0xFFFF_u32) == ch {
|
|
// The BMP falls through (assuming non-surrogate, as it
|
|
// should)
|
|
assert!(ch <= 0xD7FF_u32 || ch >= 0xE000_u32);
|
|
u.push(ch as u16)
|
|
} else {
|
|
// Supplementary planes break into surrogates.
|
|
assert!(ch >= 0x1_0000_u32 && ch <= 0x10_FFFF_u32);
|
|
ch -= 0x1_0000_u32;
|
|
let w1 = 0xD800_u16 | ((ch >> 10) as u16);
|
|
let w2 = 0xDC00_u16 | ((ch as u16) & 0x3FF_u16);
|
|
u.push_all([w1, w2])
|
|
}
|
|
}
|
|
u
|
|
}
|
|
|
|
#[inline]
|
|
fn is_char_boundary(&self, index: uint) -> bool {
|
|
if index == self.len() { return true; }
|
|
let b = self[index];
|
|
return b < 128u8 || b >= 192u8;
|
|
}
|
|
|
|
#[inline]
|
|
fn char_range_at(&self, i: uint) -> CharRange {
|
|
if self[i] < 128u8 {
|
|
return CharRange {ch: self[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[i] as u32;
|
|
let w = UTF8_CHAR_WIDTH[val] as uint;
|
|
assert!((w != 0));
|
|
|
|
val = utf8_first_byte!(val, w);
|
|
val = utf8_acc_cont_byte!(val, s[i + 1]);
|
|
if w > 2 { val = utf8_acc_cont_byte!(val, s[i + 2]); }
|
|
if w > 3 { val = utf8_acc_cont_byte!(val, s[i + 3]); }
|
|
|
|
return CharRange {ch: unsafe { transmute(val) }, next: i + w};
|
|
}
|
|
|
|
return multibyte_char_range_at(*self, i);
|
|
}
|
|
|
|
#[inline]
|
|
fn char_at(&self, i: uint) -> char { self.char_range_at(i).ch }
|
|
|
|
#[inline]
|
|
fn char_range_at_reverse(&self, start: uint) -> CharRange {
|
|
let mut prev = start;
|
|
|
|
prev = prev.saturating_sub(1);
|
|
if self[prev] < 128 { return CharRange{ch: self[prev] as char, next: prev} }
|
|
|
|
// Multibyte case is a fn to allow char_range_at_reverse to inline cleanly
|
|
fn multibyte_char_range_at_reverse(s: &str, mut i: uint) -> CharRange {
|
|
// while there is a previous byte == 10......
|
|
while i > 0 && s[i] & 192u8 == TAG_CONT_U8 {
|
|
i -= 1u;
|
|
}
|
|
|
|
let mut val = s[i] as u32;
|
|
let w = UTF8_CHAR_WIDTH[val] as uint;
|
|
assert!((w != 0));
|
|
|
|
val = utf8_first_byte!(val, w);
|
|
val = utf8_acc_cont_byte!(val, s[i + 1]);
|
|
if w > 2 { val = utf8_acc_cont_byte!(val, s[i + 2]); }
|
|
if w > 3 { val = utf8_acc_cont_byte!(val, s[i + 3]); }
|
|
|
|
return CharRange {ch: unsafe { transmute(val) }, next: i};
|
|
}
|
|
|
|
return multibyte_char_range_at_reverse(*self, prev);
|
|
}
|
|
|
|
#[inline]
|
|
fn char_at(&self, i: uint) -> char {
|
|
self.char_range_at(i).ch
|
|
}
|
|
|
|
#[inline]
|
|
fn char_at_reverse(&self, i: uint) -> char {
|
|
self.char_range_at_reverse(i).ch
|
|
}
|
|
|
|
#[inline]
|
|
fn as_bytes(&self) -> &'a [u8] {
|
|
unsafe { cast::transmute(*self) }
|
|
}
|
|
|
|
fn find<C: CharEq>(&self, search: C) -> Option<uint> {
|
|
if search.only_ascii() {
|
|
self.bytes().position(|b| search.matches(b as char))
|
|
} else {
|
|
for (index, c) in self.char_indices() {
|
|
if search.matches(c) { return Some(index); }
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
fn rfind<C: CharEq>(&self, search: C) -> Option<uint> {
|
|
if search.only_ascii() {
|
|
self.bytes().rposition(|b| search.matches(b as char))
|
|
} else {
|
|
for (index, c) in self.char_indices_rev() {
|
|
if search.matches(c) { return Some(index); }
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
fn find_str(&self, needle: &str) -> Option<uint> {
|
|
if needle.is_empty() {
|
|
Some(0)
|
|
} else {
|
|
self.match_indices(needle)
|
|
.next()
|
|
.map(|(start, _end)| start)
|
|
}
|
|
}
|
|
|
|
fn repeat(&self, nn: uint) -> ~str {
|
|
let mut ret = with_capacity(nn * self.len());
|
|
for _ in range(0, nn) {
|
|
ret.push_str(*self);
|
|
}
|
|
ret
|
|
}
|
|
|
|
#[inline]
|
|
fn slice_shift_char(&self) -> (char, &'a str) {
|
|
let CharRange {ch, next} = self.char_range_at(0u);
|
|
let next_s = unsafe { raw::slice_bytes(*self, next, self.len()) };
|
|
return (ch, next_s);
|
|
}
|
|
|
|
fn lev_distance(&self, t: &str) -> uint {
|
|
let slen = self.len();
|
|
let tlen = t.len();
|
|
|
|
if slen == 0 { return tlen; }
|
|
if tlen == 0 { return slen; }
|
|
|
|
let mut dcol = vec::from_fn(tlen + 1, |x| x);
|
|
|
|
for (i, sc) in self.chars().enumerate() {
|
|
|
|
let mut current = i;
|
|
dcol[0] = current + 1;
|
|
|
|
for (j, tc) in t.chars().enumerate() {
|
|
|
|
let next = dcol[j + 1];
|
|
|
|
if sc == tc {
|
|
dcol[j + 1] = current;
|
|
} else {
|
|
dcol[j + 1] = ::cmp::min(current, next);
|
|
dcol[j + 1] = ::cmp::min(dcol[j + 1], dcol[j]) + 1;
|
|
}
|
|
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
return dcol[tlen];
|
|
}
|
|
|
|
fn subslice_offset(&self, inner: &str) -> uint {
|
|
let a_start = self.as_ptr() as uint;
|
|
let a_end = a_start + self.len();
|
|
let b_start = inner.as_ptr() as uint;
|
|
let b_end = b_start + inner.len();
|
|
|
|
assert!(a_start <= b_start);
|
|
assert!(b_end <= a_end);
|
|
b_start - a_start
|
|
}
|
|
|
|
#[inline]
|
|
fn as_ptr(&self) -> *u8 {
|
|
self.repr().data
|
|
}
|
|
}
|
|
|
|
/// Methods for owned strings
|
|
pub trait OwnedStr {
|
|
/// Appends a string slice to the back of a string, without overallocating.
|
|
fn push_str_no_overallocate(&mut self, rhs: &str);
|
|
|
|
/// Appends a string slice to the back of a string
|
|
fn push_str(&mut self, rhs: &str);
|
|
|
|
/// Appends a character to the back of a string
|
|
fn push_char(&mut self, c: char);
|
|
|
|
/// Remove the final character from a string and return it
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// If the string does not contain any characters
|
|
fn pop_char(&mut self) -> char;
|
|
|
|
/// Remove the first character from a string and return it
|
|
///
|
|
/// # Failure
|
|
///
|
|
/// If the string does not contain any characters
|
|
fn shift_char(&mut self) -> char;
|
|
|
|
/// Prepend a char to a string
|
|
fn unshift_char(&mut self, ch: char);
|
|
|
|
/// Insert a new sub-string at the given position in a string, in O(n + m) time
|
|
/// (with n and m the lengths of the string and the substring.)
|
|
/// This fails if `position` is not at a character boundary.
|
|
fn insert(&mut self, position: uint, substring: &str);
|
|
|
|
/// Insert a char at the given position in a string, in O(n + m) time
|
|
/// (with n and m the lengths of the string and the substring.)
|
|
/// This fails if `position` is not at a character boundary.
|
|
fn insert_char(&mut self, position: uint, ch: char);
|
|
|
|
/// Concatenate two strings together.
|
|
fn append(self, rhs: &str) -> ~str;
|
|
|
|
/// Reserves capacity for exactly `n` bytes in the given string.
|
|
///
|
|
/// Assuming single-byte characters, the resulting string will be large
|
|
/// enough to hold a string of length `n`.
|
|
///
|
|
/// If the capacity for `s` is already equal to or greater than the requested
|
|
/// capacity, then no action is taken.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * s - A string
|
|
/// * n - The number of bytes to reserve space for
|
|
fn reserve_exact(&mut self, n: uint);
|
|
|
|
/// Reserves capacity for at least `n` bytes in the given string.
|
|
///
|
|
/// Assuming single-byte characters, the resulting string will be large
|
|
/// enough to hold a string of length `n`.
|
|
///
|
|
/// This function will over-allocate in order to amortize the allocation costs
|
|
/// in scenarios where the caller may need to repeatedly reserve additional
|
|
/// space.
|
|
///
|
|
/// If the capacity for `s` is already equal to or greater than the requested
|
|
/// capacity, then no action is taken.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * s - A string
|
|
/// * n - The number of bytes to reserve space for
|
|
fn reserve(&mut self, n: uint);
|
|
|
|
/// Returns the number of single-byte characters the string can hold without
|
|
/// reallocating
|
|
fn capacity(&self) -> uint;
|
|
|
|
/// Shorten a string to the specified length (which must be <= the current length)
|
|
fn truncate(&mut self, len: uint);
|
|
|
|
/// Consumes the string, returning the underlying byte buffer.
|
|
///
|
|
/// The buffer does not have a null terminator.
|
|
fn into_bytes(self) -> ~[u8];
|
|
|
|
/// Sets the length of a string
|
|
///
|
|
/// This will explicitly set the size of the string, without actually
|
|
/// modifying its buffers, so it is up to the caller to ensure that
|
|
/// the string is actually the specified size.
|
|
unsafe fn set_len(&mut self, new_len: uint);
|
|
}
|
|
|
|
impl OwnedStr for ~str {
|
|
#[inline]
|
|
fn push_str_no_overallocate(&mut self, rhs: &str) {
|
|
let new_cap = self.len() + rhs.len();
|
|
self.reserve_exact(new_cap);
|
|
self.push_str(rhs);
|
|
}
|
|
|
|
#[inline]
|
|
fn push_str(&mut self, rhs: &str) {
|
|
unsafe {
|
|
raw::push_bytes(self, rhs.as_bytes());
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn push_char(&mut self, c: char) {
|
|
let cur_len = self.len();
|
|
// may use up to 4 bytes.
|
|
unsafe {
|
|
let v = raw::as_owned_vec(self);
|
|
v.reserve_additional(4);
|
|
|
|
// Attempt to not use an intermediate buffer by just pushing bytes
|
|
// directly onto this string.
|
|
let write_ptr = v.as_mut_ptr().offset(cur_len as int);
|
|
let used = vec::raw::mut_buf_as_slice(write_ptr, 4, |slc| c.encode_utf8(slc));
|
|
|
|
v.set_len(cur_len + used);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn pop_char(&mut self) -> char {
|
|
let end = self.len();
|
|
assert!(end > 0u);
|
|
let CharRange {ch, next} = self.char_range_at_reverse(end);
|
|
unsafe { self.set_len(next); }
|
|
return ch;
|
|
}
|
|
|
|
#[inline]
|
|
fn shift_char(&mut self) -> char {
|
|
let CharRange {ch, next} = self.char_range_at(0u);
|
|
*self = self.slice(next, self.len()).to_owned();
|
|
return ch;
|
|
}
|
|
|
|
#[inline]
|
|
fn unshift_char(&mut self, ch: char) {
|
|
// This could be more efficient.
|
|
let mut new_str = ~"";
|
|
new_str.push_char(ch);
|
|
new_str.push_str(*self);
|
|
*self = new_str;
|
|
}
|
|
|
|
#[inline]
|
|
fn insert(&mut self, position: uint, substring: &str) {
|
|
// This could be more efficient.
|
|
let mut new_str = self.slice_to(position).to_owned();
|
|
new_str.push_str(substring);
|
|
new_str.push_str(self.slice_from(position));
|
|
*self = new_str;
|
|
}
|
|
|
|
#[inline]
|
|
fn insert_char(&mut self, position: uint, ch: char) {
|
|
// This could be more efficient.
|
|
let mut new_str = self.slice_to(position).to_owned();
|
|
new_str.push_char(ch);
|
|
new_str.push_str(self.slice_from(position));
|
|
*self = new_str;
|
|
}
|
|
|
|
#[inline]
|
|
fn append(self, rhs: &str) -> ~str {
|
|
let mut new_str = self;
|
|
new_str.push_str_no_overallocate(rhs);
|
|
new_str
|
|
}
|
|
|
|
#[inline]
|
|
fn reserve_exact(&mut self, n: uint) {
|
|
unsafe {
|
|
raw::as_owned_vec(self).reserve_exact(n)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn reserve(&mut self, n: uint) {
|
|
unsafe {
|
|
raw::as_owned_vec(self).reserve(n)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn capacity(&self) -> uint {
|
|
unsafe {
|
|
let buf: &~[u8] = cast::transmute(self);
|
|
buf.capacity()
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn truncate(&mut self, len: uint) {
|
|
assert!(len <= self.len());
|
|
assert!(self.is_char_boundary(len));
|
|
unsafe { self.set_len(len); }
|
|
}
|
|
|
|
#[inline]
|
|
fn into_bytes(self) -> ~[u8] {
|
|
unsafe { cast::transmute(self) }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_len(&mut self, new_len: uint) {
|
|
raw::as_owned_vec(self).set_len(new_len)
|
|
}
|
|
}
|
|
|
|
impl Clone for ~str {
|
|
#[inline]
|
|
fn clone(&self) -> ~str {
|
|
self.to_owned()
|
|
}
|
|
}
|
|
|
|
impl DeepClone for ~str {
|
|
#[inline]
|
|
fn deep_clone(&self) -> ~str {
|
|
self.to_owned()
|
|
}
|
|
}
|
|
|
|
impl FromIterator<char> for ~str {
|
|
#[inline]
|
|
fn from_iterator<T: Iterator<char>>(iterator: &mut T) -> ~str {
|
|
let (lower, _) = iterator.size_hint();
|
|
let mut buf = with_capacity(lower);
|
|
buf.extend(iterator);
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl Extendable<char> for ~str {
|
|
#[inline]
|
|
fn extend<T: Iterator<char>>(&mut self, iterator: &mut T) {
|
|
let (lower, _) = iterator.size_hint();
|
|
let reserve = lower + self.len();
|
|
self.reserve(reserve);
|
|
for ch in *iterator {
|
|
self.push_char(ch)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This works because every lifetime is a sub-lifetime of 'static
|
|
impl<'a> Default for &'a str {
|
|
fn default() -> &'a str { "" }
|
|
}
|
|
|
|
impl Default for ~str {
|
|
fn default() -> ~str { ~"" }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use iter::AdditiveIterator;
|
|
use prelude::*;
|
|
use str::*;
|
|
|
|
#[test]
|
|
fn test_eq() {
|
|
assert!((eq(&~"", &~"")));
|
|
assert!((eq(&~"foo", &~"foo")));
|
|
assert!((!eq(&~"foo", &~"bar")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_eq_slice() {
|
|
assert!((eq_slice("foobar".slice(0, 3), "foo")));
|
|
assert!((eq_slice("barfoo".slice(3, 6), "foo")));
|
|
assert!((!eq_slice("foo1", "foo2")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_le() {
|
|
assert!("" <= "");
|
|
assert!("" <= "foo");
|
|
assert!("foo" <= "foo");
|
|
assert!("foo" != "bar");
|
|
}
|
|
|
|
#[test]
|
|
fn test_len() {
|
|
assert_eq!("".len(), 0u);
|
|
assert_eq!("hello world".len(), 11u);
|
|
assert_eq!("\x63".len(), 1u);
|
|
assert_eq!("\xa2".len(), 2u);
|
|
assert_eq!("\u03c0".len(), 2u);
|
|
assert_eq!("\u2620".len(), 3u);
|
|
assert_eq!("\U0001d11e".len(), 4u);
|
|
|
|
assert_eq!("".char_len(), 0u);
|
|
assert_eq!("hello world".char_len(), 11u);
|
|
assert_eq!("\x63".char_len(), 1u);
|
|
assert_eq!("\xa2".char_len(), 1u);
|
|
assert_eq!("\u03c0".char_len(), 1u);
|
|
assert_eq!("\u2620".char_len(), 1u);
|
|
assert_eq!("\U0001d11e".char_len(), 1u);
|
|
assert_eq!("ประเทศไทย中华Việt Nam".char_len(), 19u);
|
|
}
|
|
|
|
#[test]
|
|
fn test_find() {
|
|
assert_eq!("hello".find('l'), Some(2u));
|
|
assert_eq!("hello".find(|c:char| c == 'o'), Some(4u));
|
|
assert!("hello".find('x').is_none());
|
|
assert!("hello".find(|c:char| c == 'x').is_none());
|
|
assert_eq!("ประเทศไทย中华Việt Nam".find('华'), Some(30u));
|
|
assert_eq!("ประเทศไทย中华Việt Nam".find(|c: char| c == '华'), Some(30u));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rfind() {
|
|
assert_eq!("hello".rfind('l'), Some(3u));
|
|
assert_eq!("hello".rfind(|c:char| c == 'o'), Some(4u));
|
|
assert!("hello".rfind('x').is_none());
|
|
assert!("hello".rfind(|c:char| c == 'x').is_none());
|
|
assert_eq!("ประเทศไทย中华Việt Nam".rfind('华'), Some(30u));
|
|
assert_eq!("ประเทศไทย中华Việt Nam".rfind(|c: char| c == '华'), Some(30u));
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_str() {
|
|
let mut s = ~"";
|
|
s.push_str("");
|
|
assert_eq!(s.slice_from(0), "");
|
|
s.push_str("abc");
|
|
assert_eq!(s.slice_from(0), "abc");
|
|
s.push_str("ประเทศไทย中华Việt Nam");
|
|
assert_eq!(s.slice_from(0), "abcประเทศไทย中华Việt Nam");
|
|
}
|
|
|
|
#[test]
|
|
fn test_append() {
|
|
let mut s = ~"";
|
|
s = s.append("");
|
|
assert_eq!(s.slice_from(0), "");
|
|
s = s.append("abc");
|
|
assert_eq!(s.slice_from(0), "abc");
|
|
s = s.append("ประเทศไทย中华Việt Nam");
|
|
assert_eq!(s.slice_from(0), "abcประเทศไทย中华Việt Nam");
|
|
}
|
|
|
|
#[test]
|
|
fn test_pop_char() {
|
|
let mut data = ~"ประเทศไทย中华";
|
|
let cc = data.pop_char();
|
|
assert_eq!(~"ประเทศไทย中", data);
|
|
assert_eq!('华', cc);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pop_char_2() {
|
|
let mut data2 = ~"华";
|
|
let cc2 = data2.pop_char();
|
|
assert_eq!(~"", data2);
|
|
assert_eq!('华', cc2);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_pop_char_fail() {
|
|
let mut data = ~"";
|
|
let _cc3 = data.pop_char();
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_char() {
|
|
let mut data = ~"ประเทศไทย中";
|
|
data.push_char('华');
|
|
data.push_char('b'); // 1 byte
|
|
data.push_char('¢'); // 2 byte
|
|
data.push_char('€'); // 3 byte
|
|
data.push_char('𤭢'); // 4 byte
|
|
assert_eq!(~"ประเทศไทย中华b¢€𤭢", data);
|
|
}
|
|
|
|
#[test]
|
|
fn test_shift_char() {
|
|
let mut data = ~"ประเทศไทย中";
|
|
let cc = data.shift_char();
|
|
assert_eq!(~"ระเทศไทย中", data);
|
|
assert_eq!('ป', cc);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unshift_char() {
|
|
let mut data = ~"ประเทศไทย中";
|
|
data.unshift_char('华');
|
|
assert_eq!(~"华ประเทศไทย中", data);
|
|
}
|
|
|
|
#[test]
|
|
fn test_insert_char() {
|
|
let mut data = ~"ประเทศไทย中";
|
|
data.insert_char(15, '华');
|
|
assert_eq!(~"ประเท华ศไทย中", data);
|
|
}
|
|
|
|
#[test]
|
|
fn test_insert() {
|
|
let mut data = ~"ประเทศไทย中";
|
|
data.insert(15, "华中");
|
|
assert_eq!(~"ประเท华中ศไทย中", data);
|
|
}
|
|
|
|
#[test]
|
|
fn test_collect() {
|
|
let empty = ~"";
|
|
let s: ~str = empty.chars().collect();
|
|
assert_eq!(empty, s);
|
|
let data = ~"ประเทศไทย中";
|
|
let s: ~str = data.chars().collect();
|
|
assert_eq!(data, s);
|
|
}
|
|
|
|
#[test]
|
|
fn test_extend() {
|
|
let data = ~"ประเทศไทย中";
|
|
let mut cpy = data.clone();
|
|
let other = "abc";
|
|
let mut it = other.chars();
|
|
cpy.extend(&mut it);
|
|
assert_eq!(cpy, data + other);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clear() {
|
|
let mut empty = ~"";
|
|
empty.clear();
|
|
assert_eq!("", empty.as_slice());
|
|
let mut data = ~"ประเทศไทย中";
|
|
data.clear();
|
|
assert_eq!("", data.as_slice());
|
|
data.push_char('华');
|
|
assert_eq!("华", data.as_slice());
|
|
}
|
|
|
|
#[test]
|
|
fn test_into_bytes() {
|
|
let data = ~"asdf";
|
|
let buf = data.into_bytes();
|
|
assert_eq!(bytes!("asdf"), buf.as_slice());
|
|
}
|
|
|
|
#[test]
|
|
fn test_find_str() {
|
|
// byte positions
|
|
assert_eq!("".find_str(""), Some(0u));
|
|
assert!("banana".find_str("apple pie").is_none());
|
|
|
|
let data = "abcabc";
|
|
assert_eq!(data.slice(0u, 6u).find_str("ab"), Some(0u));
|
|
assert_eq!(data.slice(2u, 6u).find_str("ab"), Some(3u - 2u));
|
|
assert!(data.slice(2u, 4u).find_str("ab").is_none());
|
|
|
|
let mut data = ~"ประเทศไทย中华Việt Nam";
|
|
data = data + data;
|
|
assert!(data.find_str("ไท华").is_none());
|
|
assert_eq!(data.slice(0u, 43u).find_str(""), Some(0u));
|
|
assert_eq!(data.slice(6u, 43u).find_str(""), Some(6u - 6u));
|
|
|
|
assert_eq!(data.slice(0u, 43u).find_str("ประ"), Some( 0u));
|
|
assert_eq!(data.slice(0u, 43u).find_str("ทศไ"), Some(12u));
|
|
assert_eq!(data.slice(0u, 43u).find_str("ย中"), Some(24u));
|
|
assert_eq!(data.slice(0u, 43u).find_str("iệt"), Some(34u));
|
|
assert_eq!(data.slice(0u, 43u).find_str("Nam"), Some(40u));
|
|
|
|
assert_eq!(data.slice(43u, 86u).find_str("ประ"), Some(43u - 43u));
|
|
assert_eq!(data.slice(43u, 86u).find_str("ทศไ"), Some(55u - 43u));
|
|
assert_eq!(data.slice(43u, 86u).find_str("ย中"), Some(67u - 43u));
|
|
assert_eq!(data.slice(43u, 86u).find_str("iệt"), Some(77u - 43u));
|
|
assert_eq!(data.slice(43u, 86u).find_str("Nam"), Some(83u - 43u));
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice_chars() {
|
|
fn t(a: &str, b: &str, start: uint) {
|
|
assert_eq!(a.slice_chars(start, start + b.char_len()), b);
|
|
}
|
|
t("", "", 0);
|
|
t("hello", "llo", 2);
|
|
t("hello", "el", 1);
|
|
t("αβλ", "β", 1);
|
|
t("αβλ", "", 3);
|
|
assert_eq!("ะเทศไท", "ประเทศไทย中华Việt Nam".slice_chars(2, 8));
|
|
}
|
|
|
|
#[test]
|
|
fn test_concat() {
|
|
fn t(v: &[~str], s: &str) {
|
|
assert_eq!(v.concat(), s.to_str());
|
|
}
|
|
t([~"you", ~"know", ~"I'm", ~"no", ~"good"], "youknowI'mnogood");
|
|
let v: &[~str] = [];
|
|
t(v, "");
|
|
t([~"hi"], "hi");
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect() {
|
|
fn t(v: &[~str], sep: &str, s: &str) {
|
|
assert_eq!(v.connect(sep), s.to_str());
|
|
}
|
|
t([~"you", ~"know", ~"I'm", ~"no", ~"good"],
|
|
" ", "you know I'm no good");
|
|
let v: &[~str] = [];
|
|
t(v, " ", "");
|
|
t([~"hi"], " ", "hi");
|
|
}
|
|
|
|
#[test]
|
|
fn test_concat_slices() {
|
|
fn t(v: &[&str], s: &str) {
|
|
assert_eq!(v.concat(), s.to_str());
|
|
}
|
|
t(["you", "know", "I'm", "no", "good"], "youknowI'mnogood");
|
|
let v: &[&str] = [];
|
|
t(v, "");
|
|
t(["hi"], "hi");
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect_slices() {
|
|
fn t(v: &[&str], sep: &str, s: &str) {
|
|
assert_eq!(v.connect(sep), s.to_str());
|
|
}
|
|
t(["you", "know", "I'm", "no", "good"],
|
|
" ", "you know I'm no good");
|
|
t([], " ", "");
|
|
t(["hi"], " ", "hi");
|
|
}
|
|
|
|
#[test]
|
|
fn test_repeat() {
|
|
assert_eq!("x".repeat(4), ~"xxxx");
|
|
assert_eq!("hi".repeat(4), ~"hihihihi");
|
|
assert_eq!("ไท华".repeat(3), ~"ไท华ไท华ไท华");
|
|
assert_eq!("".repeat(4), ~"");
|
|
assert_eq!("hi".repeat(0), ~"");
|
|
}
|
|
|
|
#[test]
|
|
fn test_unsafe_slice() {
|
|
assert_eq!("ab", unsafe {raw::slice_bytes("abc", 0, 2)});
|
|
assert_eq!("bc", unsafe {raw::slice_bytes("abc", 1, 3)});
|
|
assert_eq!("", unsafe {raw::slice_bytes("abc", 1, 1)});
|
|
fn a_million_letter_a() -> ~str {
|
|
let mut i = 0;
|
|
let mut rs = ~"";
|
|
while i < 100000 { rs.push_str("aaaaaaaaaa"); i += 1; }
|
|
rs
|
|
}
|
|
fn half_a_million_letter_a() -> ~str {
|
|
let mut i = 0;
|
|
let mut rs = ~"";
|
|
while i < 100000 { rs.push_str("aaaaa"); i += 1; }
|
|
rs
|
|
}
|
|
let letters = a_million_letter_a();
|
|
assert!(half_a_million_letter_a() ==
|
|
unsafe {raw::slice_bytes(letters, 0u, 500000)}.to_owned());
|
|
}
|
|
|
|
#[test]
|
|
fn test_starts_with() {
|
|
assert!(("".starts_with("")));
|
|
assert!(("abc".starts_with("")));
|
|
assert!(("abc".starts_with("a")));
|
|
assert!((!"a".starts_with("abc")));
|
|
assert!((!"".starts_with("abc")));
|
|
assert!((!"ödd".starts_with("-")));
|
|
assert!(("ödd".starts_with("öd")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_ends_with() {
|
|
assert!(("".ends_with("")));
|
|
assert!(("abc".ends_with("")));
|
|
assert!(("abc".ends_with("c")));
|
|
assert!((!"a".ends_with("abc")));
|
|
assert!((!"".ends_with("abc")));
|
|
assert!((!"ddö".ends_with("-")));
|
|
assert!(("ddö".ends_with("dö")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_empty() {
|
|
assert!("".is_empty());
|
|
assert!(!"a".is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace() {
|
|
let a = "a";
|
|
assert_eq!("".replace(a, "b"), ~"");
|
|
assert_eq!("a".replace(a, "b"), ~"b");
|
|
assert_eq!("ab".replace(a, "b"), ~"bb");
|
|
let test = "test";
|
|
assert!(" test test ".replace(test, "toast") ==
|
|
~" toast toast ");
|
|
assert_eq!(" test test ".replace(test, ""), ~" ");
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace_2a() {
|
|
let data = ~"ประเทศไทย中华";
|
|
let repl = ~"دولة الكويت";
|
|
|
|
let a = ~"ประเ";
|
|
let A = ~"دولة الكويتทศไทย中华";
|
|
assert_eq!(data.replace(a, repl), A);
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace_2b() {
|
|
let data = ~"ประเทศไทย中华";
|
|
let repl = ~"دولة الكويت";
|
|
|
|
let b = ~"ะเ";
|
|
let B = ~"ปรدولة الكويتทศไทย中华";
|
|
assert_eq!(data.replace(b, repl), B);
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace_2c() {
|
|
let data = ~"ประเทศไทย中华";
|
|
let repl = ~"دولة الكويت";
|
|
|
|
let c = ~"中华";
|
|
let C = ~"ประเทศไทยدولة الكويت";
|
|
assert_eq!(data.replace(c, repl), C);
|
|
}
|
|
|
|
#[test]
|
|
fn test_replace_2d() {
|
|
let data = ~"ประเทศไทย中华";
|
|
let repl = ~"دولة الكويت";
|
|
|
|
let d = ~"ไท华";
|
|
assert_eq!(data.replace(d, repl), data);
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice() {
|
|
assert_eq!("ab", "abc".slice(0, 2));
|
|
assert_eq!("bc", "abc".slice(1, 3));
|
|
assert_eq!("", "abc".slice(1, 1));
|
|
assert_eq!("\u65e5", "\u65e5\u672c".slice(0, 3));
|
|
|
|
let data = "ประเทศไทย中华";
|
|
assert_eq!("ป", data.slice(0, 3));
|
|
assert_eq!("ร", data.slice(3, 6));
|
|
assert_eq!("", data.slice(3, 3));
|
|
assert_eq!("华", data.slice(30, 33));
|
|
|
|
fn a_million_letter_X() -> ~str {
|
|
let mut i = 0;
|
|
let mut rs = ~"";
|
|
while i < 100000 {
|
|
push_str(&mut rs, "华华华华华华华华华华");
|
|
i += 1;
|
|
}
|
|
rs
|
|
}
|
|
fn half_a_million_letter_X() -> ~str {
|
|
let mut i = 0;
|
|
let mut rs = ~"";
|
|
while i < 100000 { push_str(&mut rs, "华华华华华"); i += 1; }
|
|
rs
|
|
}
|
|
let letters = a_million_letter_X();
|
|
assert!(half_a_million_letter_X() ==
|
|
letters.slice(0u, 3u * 500000u).to_owned());
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice_2() {
|
|
let ss = "中华Việt Nam";
|
|
|
|
assert_eq!("华", ss.slice(3u, 6u));
|
|
assert_eq!("Việt Nam", ss.slice(6u, 16u));
|
|
|
|
assert_eq!("ab", "abc".slice(0u, 2u));
|
|
assert_eq!("bc", "abc".slice(1u, 3u));
|
|
assert_eq!("", "abc".slice(1u, 1u));
|
|
|
|
assert_eq!("中", ss.slice(0u, 3u));
|
|
assert_eq!("华V", ss.slice(3u, 7u));
|
|
assert_eq!("", ss.slice(3u, 3u));
|
|
/*0: 中
|
|
3: 华
|
|
6: V
|
|
7: i
|
|
8: ệ
|
|
11: t
|
|
12:
|
|
13: N
|
|
14: a
|
|
15: m */
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_slice_fail() {
|
|
"中华Việt Nam".slice(0u, 2u);
|
|
}
|
|
|
|
#[test]
|
|
fn test_slice_from() {
|
|
assert_eq!("abcd".slice_from(0), "abcd");
|
|
assert_eq!("abcd".slice_from(2), "cd");
|
|
assert_eq!("abcd".slice_from(4), "");
|
|
}
|
|
#[test]
|
|
fn test_slice_to() {
|
|
assert_eq!("abcd".slice_to(0), "");
|
|
assert_eq!("abcd".slice_to(2), "ab");
|
|
assert_eq!("abcd".slice_to(4), "abcd");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_left_chars() {
|
|
let v: &[char] = &[];
|
|
assert_eq!(" *** foo *** ".trim_left_chars(&v), " *** foo *** ");
|
|
assert_eq!(" *** foo *** ".trim_left_chars(& &['*', ' ']), "foo *** ");
|
|
assert_eq!(" *** *** ".trim_left_chars(& &['*', ' ']), "");
|
|
assert_eq!("foo *** ".trim_left_chars(& &['*', ' ']), "foo *** ");
|
|
|
|
assert_eq!("11foo1bar11".trim_left_chars(&'1'), "foo1bar11");
|
|
assert_eq!("12foo1bar12".trim_left_chars(& &['1', '2']), "foo1bar12");
|
|
assert_eq!("123foo1bar123".trim_left_chars(&|c: char| c.is_digit()), "foo1bar123");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_right_chars() {
|
|
let v: &[char] = &[];
|
|
assert_eq!(" *** foo *** ".trim_right_chars(&v), " *** foo *** ");
|
|
assert_eq!(" *** foo *** ".trim_right_chars(& &['*', ' ']), " *** foo");
|
|
assert_eq!(" *** *** ".trim_right_chars(& &['*', ' ']), "");
|
|
assert_eq!(" *** foo".trim_right_chars(& &['*', ' ']), " *** foo");
|
|
|
|
assert_eq!("11foo1bar11".trim_right_chars(&'1'), "11foo1bar");
|
|
assert_eq!("12foo1bar12".trim_right_chars(& &['1', '2']), "12foo1bar");
|
|
assert_eq!("123foo1bar123".trim_right_chars(&|c: char| c.is_digit()), "123foo1bar");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_chars() {
|
|
let v: &[char] = &[];
|
|
assert_eq!(" *** foo *** ".trim_chars(&v), " *** foo *** ");
|
|
assert_eq!(" *** foo *** ".trim_chars(& &['*', ' ']), "foo");
|
|
assert_eq!(" *** *** ".trim_chars(& &['*', ' ']), "");
|
|
assert_eq!("foo".trim_chars(& &['*', ' ']), "foo");
|
|
|
|
assert_eq!("11foo1bar11".trim_chars(&'1'), "foo1bar");
|
|
assert_eq!("12foo1bar12".trim_chars(& &['1', '2']), "foo1bar");
|
|
assert_eq!("123foo1bar123".trim_chars(&|c: char| c.is_digit()), "foo1bar");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_left() {
|
|
assert_eq!("".trim_left(), "");
|
|
assert_eq!("a".trim_left(), "a");
|
|
assert_eq!(" ".trim_left(), "");
|
|
assert_eq!(" blah".trim_left(), "blah");
|
|
assert_eq!(" \u3000 wut".trim_left(), "wut");
|
|
assert_eq!("hey ".trim_left(), "hey ");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim_right() {
|
|
assert_eq!("".trim_right(), "");
|
|
assert_eq!("a".trim_right(), "a");
|
|
assert_eq!(" ".trim_right(), "");
|
|
assert_eq!("blah ".trim_right(), "blah");
|
|
assert_eq!("wut \u3000 ".trim_right(), "wut");
|
|
assert_eq!(" hey".trim_right(), " hey");
|
|
}
|
|
|
|
#[test]
|
|
fn test_trim() {
|
|
assert_eq!("".trim(), "");
|
|
assert_eq!("a".trim(), "a");
|
|
assert_eq!(" ".trim(), "");
|
|
assert_eq!(" blah ".trim(), "blah");
|
|
assert_eq!("\nwut \u3000 ".trim(), "wut");
|
|
assert_eq!(" hey dude ".trim(), "hey dude");
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_whitespace() {
|
|
assert!("".is_whitespace());
|
|
assert!(" ".is_whitespace());
|
|
assert!("\u2009".is_whitespace()); // Thin space
|
|
assert!(" \n\t ".is_whitespace());
|
|
assert!(!" _ ".is_whitespace());
|
|
}
|
|
|
|
#[test]
|
|
fn test_push_byte() {
|
|
let mut s = ~"ABC";
|
|
unsafe{raw::push_byte(&mut s, 'D' as u8)};
|
|
assert_eq!(s, ~"ABCD");
|
|
}
|
|
|
|
#[test]
|
|
fn test_shift_byte() {
|
|
let mut s = ~"ABC";
|
|
let b = unsafe{raw::shift_byte(&mut s)};
|
|
assert_eq!(s, ~"BC");
|
|
assert_eq!(b, 65u8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_pop_byte() {
|
|
let mut s = ~"ABC";
|
|
let b = unsafe{raw::pop_byte(&mut s)};
|
|
assert_eq!(s, ~"AB");
|
|
assert_eq!(b, 67u8);
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_utf8() {
|
|
// deny overlong encodings
|
|
assert!(!is_utf8([0xc0, 0x80]));
|
|
assert!(!is_utf8([0xc0, 0xae]));
|
|
assert!(!is_utf8([0xe0, 0x80, 0x80]));
|
|
assert!(!is_utf8([0xe0, 0x80, 0xaf]));
|
|
assert!(!is_utf8([0xe0, 0x81, 0x81]));
|
|
assert!(!is_utf8([0xf0, 0x82, 0x82, 0xac]));
|
|
assert!(!is_utf8([0xf4, 0x90, 0x80, 0x80]));
|
|
|
|
// deny surrogates
|
|
assert!(!is_utf8([0xED, 0xA0, 0x80]));
|
|
assert!(!is_utf8([0xED, 0xBF, 0xBF]));
|
|
|
|
assert!(is_utf8([0xC2, 0x80]));
|
|
assert!(is_utf8([0xDF, 0xBF]));
|
|
assert!(is_utf8([0xE0, 0xA0, 0x80]));
|
|
assert!(is_utf8([0xED, 0x9F, 0xBF]));
|
|
assert!(is_utf8([0xEE, 0x80, 0x80]));
|
|
assert!(is_utf8([0xEF, 0xBF, 0xBF]));
|
|
assert!(is_utf8([0xF0, 0x90, 0x80, 0x80]));
|
|
assert!(is_utf8([0xF4, 0x8F, 0xBF, 0xBF]));
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_utf16() {
|
|
macro_rules! pos ( ($($e:expr),*) => { { $(assert!(is_utf16($e));)* } });
|
|
|
|
// non-surrogates
|
|
pos!([0x0000],
|
|
[0x0001, 0x0002],
|
|
[0xD7FF],
|
|
[0xE000]);
|
|
|
|
// surrogate pairs (randomly generated with Python 3's
|
|
// .encode('utf-16be'))
|
|
pos!([0xdb54, 0xdf16, 0xd880, 0xdee0, 0xdb6a, 0xdd45],
|
|
[0xd91f, 0xdeb1, 0xdb31, 0xdd84, 0xd8e2, 0xde14],
|
|
[0xdb9f, 0xdc26, 0xdb6f, 0xde58, 0xd850, 0xdfae]);
|
|
|
|
// mixtures (also random)
|
|
pos!([0xd921, 0xdcc2, 0x002d, 0x004d, 0xdb32, 0xdf65],
|
|
[0xdb45, 0xdd2d, 0x006a, 0xdacd, 0xddfe, 0x0006],
|
|
[0x0067, 0xd8ff, 0xddb7, 0x000f, 0xd900, 0xdc80]);
|
|
|
|
// negative tests
|
|
macro_rules! neg ( ($($e:expr),*) => { { $(assert!(!is_utf16($e));)* } });
|
|
|
|
neg!(
|
|
// surrogate + regular unit
|
|
[0xdb45, 0x0000],
|
|
// surrogate + lead surrogate
|
|
[0xd900, 0xd900],
|
|
// unterminated surrogate
|
|
[0xd8ff],
|
|
// trail surrogate without a lead
|
|
[0xddb7]);
|
|
|
|
// random byte sequences that Python 3's .decode('utf-16be')
|
|
// failed on
|
|
neg!([0x5b3d, 0x0141, 0xde9e, 0x8fdc, 0xc6e7],
|
|
[0xdf5a, 0x82a5, 0x62b9, 0xb447, 0x92f3],
|
|
[0xda4e, 0x42bc, 0x4462, 0xee98, 0xc2ca],
|
|
[0xbe00, 0xb04a, 0x6ecb, 0xdd89, 0xe278],
|
|
[0x0465, 0xab56, 0xdbb6, 0xa893, 0x665e],
|
|
[0x6b7f, 0x0a19, 0x40f4, 0xa657, 0xdcc5],
|
|
[0x9b50, 0xda5e, 0x24ec, 0x03ad, 0x6dee],
|
|
[0x8d17, 0xcaa7, 0xf4ae, 0xdf6e, 0xbed7],
|
|
[0xdaee, 0x2584, 0x7d30, 0xa626, 0x121a],
|
|
[0xd956, 0x4b43, 0x7570, 0xccd6, 0x4f4a],
|
|
[0x9dcf, 0x1b49, 0x4ba5, 0xfce9, 0xdffe],
|
|
[0x6572, 0xce53, 0xb05a, 0xf6af, 0xdacf],
|
|
[0x1b90, 0x728c, 0x9906, 0xdb68, 0xf46e],
|
|
[0x1606, 0xbeca, 0xbe76, 0x860f, 0xdfa5],
|
|
[0x8b4f, 0xde7a, 0xd220, 0x9fac, 0x2b6f],
|
|
[0xb8fe, 0xebbe, 0xda32, 0x1a5f, 0x8b8b],
|
|
[0x934b, 0x8956, 0xc434, 0x1881, 0xddf7],
|
|
[0x5a95, 0x13fc, 0xf116, 0xd89b, 0x93f9],
|
|
[0xd640, 0x71f1, 0xdd7d, 0x77eb, 0x1cd8],
|
|
[0x348b, 0xaef0, 0xdb2c, 0xebf1, 0x1282],
|
|
[0x50d7, 0xd824, 0x5010, 0xb369, 0x22ea]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_raw_from_c_str() {
|
|
unsafe {
|
|
let a = ~[65, 65, 65, 65, 65, 65, 65, 0];
|
|
let b = a.as_ptr();
|
|
let c = raw::from_c_str(b);
|
|
assert_eq!(c, ~"AAAAAAA");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_as_bytes() {
|
|
// no null
|
|
let v = [
|
|
224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228,
|
|
184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97,
|
|
109
|
|
];
|
|
assert_eq!("".as_bytes(), &[]);
|
|
assert_eq!("abc".as_bytes(), &['a' as u8, 'b' as u8, 'c' as u8]);
|
|
assert_eq!("ศไทย中华Việt Nam".as_bytes(), v);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_as_bytes_fail() {
|
|
// Don't double free. (I'm not sure if this exercises the
|
|
// original problem code path anymore.)
|
|
let s = ~"";
|
|
let _bytes = s.as_bytes();
|
|
fail!();
|
|
}
|
|
|
|
#[test]
|
|
fn test_as_ptr() {
|
|
let buf = "hello".as_ptr();
|
|
unsafe {
|
|
assert_eq!(*buf.offset(0), 'h' as u8);
|
|
assert_eq!(*buf.offset(1), 'e' as u8);
|
|
assert_eq!(*buf.offset(2), 'l' as u8);
|
|
assert_eq!(*buf.offset(3), 'l' as u8);
|
|
assert_eq!(*buf.offset(4), 'o' as u8);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_subslice_offset() {
|
|
let a = "kernelsprite";
|
|
let b = a.slice(7, a.len());
|
|
let c = a.slice(0, a.len() - 6);
|
|
assert_eq!(a.subslice_offset(b), 7);
|
|
assert_eq!(a.subslice_offset(c), 0);
|
|
|
|
let string = "a\nb\nc";
|
|
let mut lines = ~[];
|
|
for line in string.lines() { lines.push(line) }
|
|
assert_eq!(string.subslice_offset(lines[0]), 0);
|
|
assert_eq!(string.subslice_offset(lines[1]), 2);
|
|
assert_eq!(string.subslice_offset(lines[2]), 4);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_subslice_offset_2() {
|
|
let a = "alchemiter";
|
|
let b = "cruxtruder";
|
|
a.subslice_offset(b);
|
|
}
|
|
|
|
#[test]
|
|
fn vec_str_conversions() {
|
|
let s1: ~str = ~"All mimsy were the borogoves";
|
|
|
|
let v: ~[u8] = s1.as_bytes().to_owned();
|
|
let s2: ~str = from_utf8(v).unwrap().to_owned();
|
|
let mut i: uint = 0u;
|
|
let n1: uint = s1.len();
|
|
let n2: uint = v.len();
|
|
assert_eq!(n1, n2);
|
|
while i < n1 {
|
|
let a: u8 = s1[i];
|
|
let b: u8 = s2[i];
|
|
debug!("{}", a);
|
|
debug!("{}", b);
|
|
assert_eq!(a, b);
|
|
i += 1u;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_contains() {
|
|
assert!("abcde".contains("bcd"));
|
|
assert!("abcde".contains("abcd"));
|
|
assert!("abcde".contains("bcde"));
|
|
assert!("abcde".contains(""));
|
|
assert!("".contains(""));
|
|
assert!(!"abcde".contains("def"));
|
|
assert!(!"".contains("a"));
|
|
|
|
let data = ~"ประเทศไทย中华Việt Nam";
|
|
assert!(data.contains("ประเ"));
|
|
assert!(data.contains("ะเ"));
|
|
assert!(data.contains("中华"));
|
|
assert!(!data.contains("ไท华"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_contains_char() {
|
|
assert!("abc".contains_char('b'));
|
|
assert!("a".contains_char('a'));
|
|
assert!(!"abc".contains_char('d'));
|
|
assert!(!"".contains_char('a'));
|
|
}
|
|
|
|
#[test]
|
|
fn test_utf16() {
|
|
let pairs =
|
|
[(~"𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n",
|
|
~[0xd800_u16, 0xdf45_u16, 0xd800_u16, 0xdf3f_u16,
|
|
0xd800_u16, 0xdf3b_u16, 0xd800_u16, 0xdf46_u16,
|
|
0xd800_u16, 0xdf39_u16, 0xd800_u16, 0xdf3b_u16,
|
|
0xd800_u16, 0xdf30_u16, 0x000a_u16]),
|
|
|
|
(~"𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n",
|
|
~[0xd801_u16, 0xdc12_u16, 0xd801_u16,
|
|
0xdc49_u16, 0xd801_u16, 0xdc2e_u16, 0xd801_u16,
|
|
0xdc40_u16, 0xd801_u16, 0xdc32_u16, 0xd801_u16,
|
|
0xdc4b_u16, 0x0020_u16, 0xd801_u16, 0xdc0f_u16,
|
|
0xd801_u16, 0xdc32_u16, 0xd801_u16, 0xdc4d_u16,
|
|
0x000a_u16]),
|
|
|
|
(~"𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n",
|
|
~[0xd800_u16, 0xdf00_u16, 0xd800_u16, 0xdf16_u16,
|
|
0xd800_u16, 0xdf0b_u16, 0xd800_u16, 0xdf04_u16,
|
|
0xd800_u16, 0xdf11_u16, 0xd800_u16, 0xdf09_u16,
|
|
0x00b7_u16, 0xd800_u16, 0xdf0c_u16, 0xd800_u16,
|
|
0xdf04_u16, 0xd800_u16, 0xdf15_u16, 0xd800_u16,
|
|
0xdf04_u16, 0xd800_u16, 0xdf0b_u16, 0xd800_u16,
|
|
0xdf09_u16, 0xd800_u16, 0xdf11_u16, 0x000a_u16 ]),
|
|
|
|
(~"𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n",
|
|
~[0xd801_u16, 0xdc8b_u16, 0xd801_u16, 0xdc98_u16,
|
|
0xd801_u16, 0xdc88_u16, 0xd801_u16, 0xdc91_u16,
|
|
0xd801_u16, 0xdc9b_u16, 0xd801_u16, 0xdc92_u16,
|
|
0x0020_u16, 0xd801_u16, 0xdc95_u16, 0xd801_u16,
|
|
0xdc93_u16, 0x0020_u16, 0xd801_u16, 0xdc88_u16,
|
|
0xd801_u16, 0xdc9a_u16, 0xd801_u16, 0xdc8d_u16,
|
|
0x0020_u16, 0xd801_u16, 0xdc8f_u16, 0xd801_u16,
|
|
0xdc9c_u16, 0xd801_u16, 0xdc92_u16, 0xd801_u16,
|
|
0xdc96_u16, 0xd801_u16, 0xdc86_u16, 0x0020_u16,
|
|
0xd801_u16, 0xdc95_u16, 0xd801_u16, 0xdc86_u16,
|
|
0x000a_u16 ]),
|
|
// Issue #12318, even-numbered non-BMP planes
|
|
(~"\U00020000",
|
|
~[0xD840, 0xDC00])];
|
|
|
|
for p in pairs.iter() {
|
|
let (s, u) = (*p).clone();
|
|
assert!(is_utf16(u));
|
|
assert_eq!(s.to_utf16(), u);
|
|
|
|
assert_eq!(from_utf16(u).unwrap(), s);
|
|
assert_eq!(from_utf16_lossy(u), s);
|
|
|
|
assert_eq!(from_utf16(s.to_utf16()).unwrap(), s);
|
|
assert_eq!(from_utf16(u).unwrap().to_utf16(), u);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_utf16_invalid() {
|
|
// completely positive cases tested above.
|
|
// lead + eof
|
|
assert_eq!(from_utf16([0xD800]), None);
|
|
// lead + lead
|
|
assert_eq!(from_utf16([0xD800, 0xD800]), None);
|
|
|
|
// isolated trail
|
|
assert_eq!(from_utf16([0x0061, 0xDC00]), None);
|
|
|
|
// general
|
|
assert_eq!(from_utf16([0xD800, 0xd801, 0xdc8b, 0xD800]), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_utf16_lossy() {
|
|
// completely positive cases tested above.
|
|
// lead + eof
|
|
assert_eq!(from_utf16_lossy([0xD800]), ~"\uFFFD");
|
|
// lead + lead
|
|
assert_eq!(from_utf16_lossy([0xD800, 0xD800]), ~"\uFFFD\uFFFD");
|
|
|
|
// isolated trail
|
|
assert_eq!(from_utf16_lossy([0x0061, 0xDC00]), ~"a\uFFFD");
|
|
|
|
// general
|
|
assert_eq!(from_utf16_lossy([0xD800, 0xd801, 0xdc8b, 0xD800]), ~"\uFFFD𐒋\uFFFD");
|
|
}
|
|
|
|
#[test]
|
|
fn test_truncate_utf16_at_nul() {
|
|
let v = [];
|
|
assert_eq!(truncate_utf16_at_nul(v), &[]);
|
|
|
|
let v = [0, 2, 3];
|
|
assert_eq!(truncate_utf16_at_nul(v), &[]);
|
|
|
|
let v = [1, 0, 3];
|
|
assert_eq!(truncate_utf16_at_nul(v), &[1]);
|
|
|
|
let v = [1, 2, 0];
|
|
assert_eq!(truncate_utf16_at_nul(v), &[1, 2]);
|
|
|
|
let v = [1, 2, 3];
|
|
assert_eq!(truncate_utf16_at_nul(v), &[1, 2, 3]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_at() {
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
|
|
let mut pos = 0;
|
|
for ch in v.iter() {
|
|
assert!(s.char_at(pos) == *ch);
|
|
pos += from_char(*ch).len();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_at_reverse() {
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
|
|
let mut pos = s.len();
|
|
for ch in v.rev_iter() {
|
|
assert!(s.char_at_reverse(pos) == *ch);
|
|
pos -= from_char(*ch).len();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_escape_unicode() {
|
|
assert_eq!("abc".escape_unicode(), ~"\\x61\\x62\\x63");
|
|
assert_eq!("a c".escape_unicode(), ~"\\x61\\x20\\x63");
|
|
assert_eq!("\r\n\t".escape_unicode(), ~"\\x0d\\x0a\\x09");
|
|
assert_eq!("'\"\\".escape_unicode(), ~"\\x27\\x22\\x5c");
|
|
assert_eq!("\x00\x01\xfe\xff".escape_unicode(), ~"\\x00\\x01\\xfe\\xff");
|
|
assert_eq!("\u0100\uffff".escape_unicode(), ~"\\u0100\\uffff");
|
|
assert_eq!("\U00010000\U0010ffff".escape_unicode(), ~"\\U00010000\\U0010ffff");
|
|
assert_eq!("ab\ufb00".escape_unicode(), ~"\\x61\\x62\\ufb00");
|
|
assert_eq!("\U0001d4ea\r".escape_unicode(), ~"\\U0001d4ea\\x0d");
|
|
}
|
|
|
|
#[test]
|
|
fn test_escape_default() {
|
|
assert_eq!("abc".escape_default(), ~"abc");
|
|
assert_eq!("a c".escape_default(), ~"a c");
|
|
assert_eq!("\r\n\t".escape_default(), ~"\\r\\n\\t");
|
|
assert_eq!("'\"\\".escape_default(), ~"\\'\\\"\\\\");
|
|
assert_eq!("\u0100\uffff".escape_default(), ~"\\u0100\\uffff");
|
|
assert_eq!("\U00010000\U0010ffff".escape_default(), ~"\\U00010000\\U0010ffff");
|
|
assert_eq!("ab\ufb00".escape_default(), ~"ab\\ufb00");
|
|
assert_eq!("\U0001d4ea\r".escape_default(), ~"\\U0001d4ea\\r");
|
|
}
|
|
|
|
#[test]
|
|
fn test_total_ord() {
|
|
"1234".cmp(& &"123") == Greater;
|
|
"123".cmp(& &"1234") == Less;
|
|
"1234".cmp(& &"1234") == Equal;
|
|
"12345555".cmp(& &"123456") == Less;
|
|
"22".cmp(& &"1234") == Greater;
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_range_at() {
|
|
let data = ~"b¢€𤭢𤭢€¢b";
|
|
assert_eq!('b', data.char_range_at(0).ch);
|
|
assert_eq!('¢', data.char_range_at(1).ch);
|
|
assert_eq!('€', data.char_range_at(3).ch);
|
|
assert_eq!('𤭢', data.char_range_at(6).ch);
|
|
assert_eq!('𤭢', data.char_range_at(10).ch);
|
|
assert_eq!('€', data.char_range_at(14).ch);
|
|
assert_eq!('¢', data.char_range_at(17).ch);
|
|
assert_eq!('b', data.char_range_at(19).ch);
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_range_at_reverse_underflow() {
|
|
assert_eq!("abc".char_range_at_reverse(0).next, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_add() {
|
|
#[allow(unnecessary_allocation)];
|
|
macro_rules! t (
|
|
($s1:expr, $s2:expr, $e:expr) => { {
|
|
let s1 = $s1;
|
|
let s2 = $s2;
|
|
let e = $e;
|
|
assert_eq!(s1 + s2, e.to_owned());
|
|
assert_eq!(s1.to_owned() + s2, e.to_owned());
|
|
} }
|
|
);
|
|
|
|
t!("foo", "bar", "foobar");
|
|
t!("foo", ~"bar", "foobar");
|
|
t!("ศไทย中", "华Việt Nam", "ศไทย中华Việt Nam");
|
|
t!("ศไทย中", ~"华Việt Nam", "ศไทย中华Việt Nam");
|
|
}
|
|
|
|
#[test]
|
|
fn test_iterator() {
|
|
use iter::*;
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = ~['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
|
|
|
|
let mut pos = 0;
|
|
let mut it = s.chars();
|
|
|
|
for c in it {
|
|
assert_eq!(c, v[pos]);
|
|
pos += 1;
|
|
}
|
|
assert_eq!(pos, v.len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_rev_iterator() {
|
|
use iter::*;
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = ~['m', 'a', 'N', ' ', 't', 'ệ','i','V','华','中','ย','ท','ไ','ศ'];
|
|
|
|
let mut pos = 0;
|
|
let mut it = s.chars_rev();
|
|
|
|
for c in it {
|
|
assert_eq!(c, v[pos]);
|
|
pos += 1;
|
|
}
|
|
assert_eq!(pos, v.len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_iterator_clone() {
|
|
let s = "ศไทย中华Việt Nam";
|
|
let mut it = s.chars();
|
|
it.next();
|
|
assert!(it.zip(it.clone()).all(|(x,y)| x == y));
|
|
}
|
|
|
|
#[test]
|
|
fn test_bytesator() {
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = [
|
|
224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228,
|
|
184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97,
|
|
109
|
|
];
|
|
let mut pos = 0;
|
|
|
|
for b in s.bytes() {
|
|
assert_eq!(b, v[pos]);
|
|
pos += 1;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_bytes_revator() {
|
|
let s = ~"ศไทย中华Việt Nam";
|
|
let v = [
|
|
224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228,
|
|
184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97,
|
|
109
|
|
];
|
|
let mut pos = v.len();
|
|
|
|
for b in s.bytes_rev() {
|
|
pos -= 1;
|
|
assert_eq!(b, v[pos]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_indicesator() {
|
|
use iter::*;
|
|
let s = "ศไทย中华Việt Nam";
|
|
let p = [0, 3, 6, 9, 12, 15, 18, 19, 20, 23, 24, 25, 26, 27];
|
|
let v = ['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
|
|
|
|
let mut pos = 0;
|
|
let mut it = s.char_indices();
|
|
|
|
for c in it {
|
|
assert_eq!(c, (p[pos], v[pos]));
|
|
pos += 1;
|
|
}
|
|
assert_eq!(pos, v.len());
|
|
assert_eq!(pos, p.len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_char_indices_revator() {
|
|
use iter::*;
|
|
let s = "ศไทย中华Việt Nam";
|
|
let p = [27, 26, 25, 24, 23, 20, 19, 18, 15, 12, 9, 6, 3, 0];
|
|
let v = ['m', 'a', 'N', ' ', 't', 'ệ','i','V','华','中','ย','ท','ไ','ศ'];
|
|
|
|
let mut pos = 0;
|
|
let mut it = s.char_indices_rev();
|
|
|
|
for c in it {
|
|
assert_eq!(c, (p[pos], v[pos]));
|
|
pos += 1;
|
|
}
|
|
assert_eq!(pos, v.len());
|
|
assert_eq!(pos, p.len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_char_iterator() {
|
|
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
|
|
|
|
let split: ~[&str] = data.split(' ').collect();
|
|
assert_eq!( split, ~["\nMäry", "häd", "ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
let mut rsplit: ~[&str] = data.rsplit(' ').collect();
|
|
rsplit.reverse();
|
|
assert_eq!(rsplit, ~["\nMäry", "häd", "ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
let split: ~[&str] = data.split(|c: char| c == ' ').collect();
|
|
assert_eq!( split, ~["\nMäry", "häd", "ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
let mut rsplit: ~[&str] = data.rsplit(|c: char| c == ' ').collect();
|
|
rsplit.reverse();
|
|
assert_eq!(rsplit, ~["\nMäry", "häd", "ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
// Unicode
|
|
let split: ~[&str] = data.split('ä').collect();
|
|
assert_eq!( split, ~["\nM", "ry h", "d ", " little l", "mb\nLittle l", "mb\n"]);
|
|
|
|
let mut rsplit: ~[&str] = data.rsplit('ä').collect();
|
|
rsplit.reverse();
|
|
assert_eq!(rsplit, ~["\nM", "ry h", "d ", " little l", "mb\nLittle l", "mb\n"]);
|
|
|
|
let split: ~[&str] = data.split(|c: char| c == 'ä').collect();
|
|
assert_eq!( split, ~["\nM", "ry h", "d ", " little l", "mb\nLittle l", "mb\n"]);
|
|
|
|
let mut rsplit: ~[&str] = data.rsplit(|c: char| c == 'ä').collect();
|
|
rsplit.reverse();
|
|
assert_eq!(rsplit, ~["\nM", "ry h", "d ", " little l", "mb\nLittle l", "mb\n"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_splitn_char_iterator() {
|
|
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
|
|
|
|
let split: ~[&str] = data.splitn(' ', 3).collect();
|
|
assert_eq!(split, ~["\nMäry", "häd", "ä", "little lämb\nLittle lämb\n"]);
|
|
|
|
let split: ~[&str] = data.splitn(|c: char| c == ' ', 3).collect();
|
|
assert_eq!(split, ~["\nMäry", "häd", "ä", "little lämb\nLittle lämb\n"]);
|
|
|
|
// Unicode
|
|
let split: ~[&str] = data.splitn('ä', 3).collect();
|
|
assert_eq!(split, ~["\nM", "ry h", "d ", " little lämb\nLittle lämb\n"]);
|
|
|
|
let split: ~[&str] = data.splitn(|c: char| c == 'ä', 3).collect();
|
|
assert_eq!(split, ~["\nM", "ry h", "d ", " little lämb\nLittle lämb\n"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rsplitn_char_iterator() {
|
|
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
|
|
|
|
let mut split: ~[&str] = data.rsplitn(' ', 3).collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["\nMäry häd ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
let mut split: ~[&str] = data.rsplitn(|c: char| c == ' ', 3).collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["\nMäry häd ä", "little", "lämb\nLittle", "lämb\n"]);
|
|
|
|
// Unicode
|
|
let mut split: ~[&str] = data.rsplitn('ä', 3).collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["\nMäry häd ", " little l", "mb\nLittle l", "mb\n"]);
|
|
|
|
let mut split: ~[&str] = data.rsplitn(|c: char| c == 'ä', 3).collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["\nMäry häd ", " little l", "mb\nLittle l", "mb\n"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_char_iterator_no_trailing() {
|
|
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
|
|
|
|
let split: ~[&str] = data.split('\n').collect();
|
|
assert_eq!(split, ~["", "Märy häd ä little lämb", "Little lämb", ""]);
|
|
|
|
let split: ~[&str] = data.split_terminator('\n').collect();
|
|
assert_eq!(split, ~["", "Märy häd ä little lämb", "Little lämb"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rev_split_char_iterator_no_trailing() {
|
|
let data = "\nMäry häd ä little lämb\nLittle lämb\n";
|
|
|
|
let mut split: ~[&str] = data.split('\n').rev().collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["", "Märy häd ä little lämb", "Little lämb", ""]);
|
|
|
|
let mut split: ~[&str] = data.split_terminator('\n').rev().collect();
|
|
split.reverse();
|
|
assert_eq!(split, ~["", "Märy häd ä little lämb", "Little lämb"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_words() {
|
|
let data = "\n \tMäry häd\tä little lämb\nLittle lämb\n";
|
|
let words: ~[&str] = data.words().collect();
|
|
assert_eq!(words, ~["Märy", "häd", "ä", "little", "lämb", "Little", "lämb"])
|
|
}
|
|
|
|
#[test]
|
|
fn test_nfd_chars() {
|
|
assert_eq!("abc".nfd_chars().collect::<~str>(), ~"abc");
|
|
assert_eq!("\u1e0b\u01c4".nfd_chars().collect::<~str>(), ~"d\u0307\u01c4");
|
|
assert_eq!("\u2026".nfd_chars().collect::<~str>(), ~"\u2026");
|
|
assert_eq!("\u2126".nfd_chars().collect::<~str>(), ~"\u03a9");
|
|
assert_eq!("\u1e0b\u0323".nfd_chars().collect::<~str>(), ~"d\u0323\u0307");
|
|
assert_eq!("\u1e0d\u0307".nfd_chars().collect::<~str>(), ~"d\u0323\u0307");
|
|
assert_eq!("a\u0301".nfd_chars().collect::<~str>(), ~"a\u0301");
|
|
assert_eq!("\u0301a".nfd_chars().collect::<~str>(), ~"\u0301a");
|
|
assert_eq!("\ud4db".nfd_chars().collect::<~str>(), ~"\u1111\u1171\u11b6");
|
|
assert_eq!("\uac1c".nfd_chars().collect::<~str>(), ~"\u1100\u1162");
|
|
}
|
|
|
|
#[test]
|
|
fn test_nfkd_chars() {
|
|
assert_eq!("abc".nfkd_chars().collect::<~str>(), ~"abc");
|
|
assert_eq!("\u1e0b\u01c4".nfkd_chars().collect::<~str>(), ~"d\u0307DZ\u030c");
|
|
assert_eq!("\u2026".nfkd_chars().collect::<~str>(), ~"...");
|
|
assert_eq!("\u2126".nfkd_chars().collect::<~str>(), ~"\u03a9");
|
|
assert_eq!("\u1e0b\u0323".nfkd_chars().collect::<~str>(), ~"d\u0323\u0307");
|
|
assert_eq!("\u1e0d\u0307".nfkd_chars().collect::<~str>(), ~"d\u0323\u0307");
|
|
assert_eq!("a\u0301".nfkd_chars().collect::<~str>(), ~"a\u0301");
|
|
assert_eq!("\u0301a".nfkd_chars().collect::<~str>(), ~"\u0301a");
|
|
assert_eq!("\ud4db".nfkd_chars().collect::<~str>(), ~"\u1111\u1171\u11b6");
|
|
assert_eq!("\uac1c".nfkd_chars().collect::<~str>(), ~"\u1100\u1162");
|
|
}
|
|
|
|
#[test]
|
|
fn test_lines() {
|
|
let data = "\nMäry häd ä little lämb\n\nLittle lämb\n";
|
|
let lines: ~[&str] = data.lines().collect();
|
|
assert_eq!(lines, ~["", "Märy häd ä little lämb", "", "Little lämb"]);
|
|
|
|
let data = "\nMäry häd ä little lämb\n\nLittle lämb"; // no trailing \n
|
|
let lines: ~[&str] = data.lines().collect();
|
|
assert_eq!(lines, ~["", "Märy häd ä little lämb", "", "Little lämb"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_split_strator() {
|
|
fn t<'a>(s: &str, sep: &'a str, u: ~[&str]) {
|
|
let v: ~[&str] = s.split_str(sep).collect();
|
|
assert_eq!(v, u);
|
|
}
|
|
t("--1233345--", "12345", ~["--1233345--"]);
|
|
t("abc::hello::there", "::", ~["abc", "hello", "there"]);
|
|
t("::hello::there", "::", ~["", "hello", "there"]);
|
|
t("hello::there::", "::", ~["hello", "there", ""]);
|
|
t("::hello::there::", "::", ~["", "hello", "there", ""]);
|
|
t("ประเทศไทย中华Việt Nam", "中华", ~["ประเทศไทย", "Việt Nam"]);
|
|
t("zzXXXzzYYYzz", "zz", ~["", "XXX", "YYY", ""]);
|
|
t("zzXXXzYYYz", "XXX", ~["zz", "zYYYz"]);
|
|
t(".XXX.YYY.", ".", ~["", "XXX", "YYY", ""]);
|
|
t("", ".", ~[""]);
|
|
t("zz", "zz", ~["",""]);
|
|
t("ok", "z", ~["ok"]);
|
|
t("zzz", "zz", ~["","z"]);
|
|
t("zzzzz", "zz", ~["","","z"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_default() {
|
|
use default::Default;
|
|
fn t<S: Default + Str>() {
|
|
let s: S = Default::default();
|
|
assert_eq!(s.as_slice(), "");
|
|
}
|
|
|
|
t::<&str>();
|
|
t::<~str>();
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_container() {
|
|
fn sum_len<S: Container>(v: &[S]) -> uint {
|
|
v.iter().map(|x| x.len()).sum()
|
|
}
|
|
|
|
let s = ~"01234";
|
|
assert_eq!(5, sum_len(["012", "", "34"]));
|
|
assert_eq!(5, sum_len([~"01", ~"2", ~"34", ~""]));
|
|
assert_eq!(5, sum_len([s.as_slice()]));
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_truncate() {
|
|
let mut s = ~"12345";
|
|
s.truncate(5);
|
|
assert_eq!(s.as_slice(), "12345");
|
|
s.truncate(3);
|
|
assert_eq!(s.as_slice(), "123");
|
|
s.truncate(0);
|
|
assert_eq!(s.as_slice(), "");
|
|
|
|
let mut s = ~"12345";
|
|
let p = s.as_ptr();
|
|
s.truncate(3);
|
|
s.push_str("6");
|
|
let p_ = s.as_ptr();
|
|
assert_eq!(p_, p);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_str_truncate_invalid_len() {
|
|
let mut s = ~"12345";
|
|
s.truncate(6);
|
|
}
|
|
|
|
#[test]
|
|
#[should_fail]
|
|
fn test_str_truncate_split_codepoint() {
|
|
let mut s = ~"\u00FC"; // ü
|
|
s.truncate(1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_from_utf8() {
|
|
let xs = bytes!("hello");
|
|
assert_eq!(from_utf8(xs), Some("hello"));
|
|
|
|
let xs = bytes!("ศไทย中华Việt Nam");
|
|
assert_eq!(from_utf8(xs), Some("ศไทย中华Việt Nam"));
|
|
|
|
let xs = bytes!("hello", 0xff);
|
|
assert_eq!(from_utf8(xs), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_from_utf8_owned() {
|
|
let xs = bytes!("hello").to_owned();
|
|
assert_eq!(from_utf8_owned(xs), Some(~"hello"));
|
|
|
|
let xs = bytes!("ศไทย中华Việt Nam").to_owned();
|
|
assert_eq!(from_utf8_owned(xs), Some(~"ศไทย中华Việt Nam"));
|
|
|
|
let xs = bytes!("hello", 0xff).to_owned();
|
|
assert_eq!(from_utf8_owned(xs), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_str_from_utf8_lossy() {
|
|
let xs = bytes!("hello");
|
|
assert_eq!(from_utf8_lossy(xs), Slice("hello"));
|
|
|
|
let xs = bytes!("ศไทย中华Việt Nam");
|
|
assert_eq!(from_utf8_lossy(xs), Slice("ศไทย中华Việt Nam"));
|
|
|
|
let xs = bytes!("Hello", 0xC2, " There", 0xFF, " Goodbye");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"Hello\uFFFD There\uFFFD Goodbye"));
|
|
|
|
let xs = bytes!("Hello", 0xC0, 0x80, " There", 0xE6, 0x83, " Goodbye");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"Hello\uFFFD\uFFFD There\uFFFD Goodbye"));
|
|
|
|
let xs = bytes!(0xF5, "foo", 0xF5, 0x80, "bar");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFDfoo\uFFFD\uFFFDbar"));
|
|
|
|
let xs = bytes!(0xF1, "foo", 0xF1, 0x80, "bar", 0xF1, 0x80, 0x80, "baz");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFDfoo\uFFFDbar\uFFFDbaz"));
|
|
|
|
let xs = bytes!(0xF4, "foo", 0xF4, 0x80, "bar", 0xF4, 0xBF, "baz");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFDfoo\uFFFDbar\uFFFD\uFFFDbaz"));
|
|
|
|
let xs = bytes!(0xF0, 0x80, 0x80, 0x80, "foo", 0xF0, 0x90, 0x80, 0x80, "bar");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFD\uFFFD\uFFFD\uFFFDfoo\U00010000bar"));
|
|
|
|
// surrogates
|
|
let xs = bytes!(0xED, 0xA0, 0x80, "foo", 0xED, 0xBF, 0xBF, "bar");
|
|
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFD\uFFFD\uFFFDfoo\uFFFD\uFFFD\uFFFDbar"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_from_str() {
|
|
let owned: Option<~str> = from_str(&"string");
|
|
assert_eq!(owned, Some(~"string"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_maybe_owned_traits() {
|
|
let s = Slice("abcde");
|
|
assert_eq!(s.len(), 5);
|
|
assert_eq!(s.as_slice(), "abcde");
|
|
assert_eq!(s.to_str(), ~"abcde");
|
|
assert_eq!(format!("{}", s), ~"abcde");
|
|
assert!(s.lt(&Owned(~"bcdef")));
|
|
assert_eq!(Slice(""), Default::default());
|
|
|
|
let o = Owned(~"abcde");
|
|
assert_eq!(o.len(), 5);
|
|
assert_eq!(o.as_slice(), "abcde");
|
|
assert_eq!(o.to_str(), ~"abcde");
|
|
assert_eq!(format!("{}", o), ~"abcde");
|
|
assert!(o.lt(&Slice("bcdef")));
|
|
assert_eq!(Owned(~""), Default::default());
|
|
|
|
assert_eq!(s.cmp(&o), Equal);
|
|
assert!(s.equals(&o));
|
|
assert!(s.equiv(&o));
|
|
|
|
assert_eq!(o.cmp(&s), Equal);
|
|
assert!(o.equals(&s));
|
|
assert!(o.equiv(&s));
|
|
}
|
|
|
|
#[test]
|
|
fn test_maybe_owned_methods() {
|
|
let s = Slice("abcde");
|
|
assert!(s.is_slice());
|
|
assert!(!s.is_owned());
|
|
|
|
let o = Owned(~"abcde");
|
|
assert!(!o.is_slice());
|
|
assert!(o.is_owned());
|
|
}
|
|
|
|
#[test]
|
|
fn test_maybe_owned_clone() {
|
|
assert_eq!(Owned(~"abcde"), Slice("abcde").clone());
|
|
assert_eq!(Owned(~"abcde"), Slice("abcde").deep_clone());
|
|
|
|
assert_eq!(Owned(~"abcde"), Owned(~"abcde").clone());
|
|
assert_eq!(Owned(~"abcde"), Owned(~"abcde").deep_clone());
|
|
|
|
assert_eq!(Slice("abcde"), Slice("abcde").clone());
|
|
assert_eq!(Slice("abcde"), Slice("abcde").deep_clone());
|
|
|
|
assert_eq!(Slice("abcde"), Owned(~"abcde").clone());
|
|
assert_eq!(Slice("abcde"), Owned(~"abcde").deep_clone());
|
|
}
|
|
|
|
#[test]
|
|
fn test_maybe_owned_into_owned() {
|
|
assert_eq!(Slice("abcde").into_owned(), ~"abcde");
|
|
assert_eq!(Owned(~"abcde").into_owned(), ~"abcde");
|
|
}
|
|
|
|
#[test]
|
|
fn test_into_maybe_owned() {
|
|
assert_eq!("abcde".into_maybe_owned(), Slice("abcde"));
|
|
assert_eq!((~"abcde").into_maybe_owned(), Slice("abcde"));
|
|
assert_eq!("abcde".into_maybe_owned(), Owned(~"abcde"));
|
|
assert_eq!((~"abcde").into_maybe_owned(), Owned(~"abcde"));
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod bench {
|
|
extern crate test;
|
|
use self::test::BenchHarness;
|
|
use super::*;
|
|
use prelude::*;
|
|
|
|
#[bench]
|
|
fn char_iterator(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
let len = s.char_len();
|
|
|
|
bh.iter(|| assert_eq!(s.chars().len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn char_iterator_ascii(bh: &mut BenchHarness) {
|
|
let s = "Mary had a little lamb, Little lamb
|
|
Mary had a little lamb, Little lamb
|
|
Mary had a little lamb, Little lamb
|
|
Mary had a little lamb, Little lamb
|
|
Mary had a little lamb, Little lamb
|
|
Mary had a little lamb, Little lamb";
|
|
let len = s.char_len();
|
|
|
|
bh.iter(|| assert_eq!(s.chars().len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn char_iterator_rev(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
let len = s.char_len();
|
|
|
|
bh.iter(|| assert_eq!(s.chars_rev().len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn char_indicesator(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
let len = s.char_len();
|
|
|
|
bh.iter(|| assert_eq!(s.char_indices().len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn char_indicesator_rev(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
let len = s.char_len();
|
|
|
|
bh.iter(|| assert_eq!(s.char_indices_rev().len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_unicode_ascii(bh: &mut BenchHarness) {
|
|
let s = "ประเทศไทย中华Việt Namประเทศไทย中华Việt Nam";
|
|
|
|
bh.iter(|| assert_eq!(s.split('V').len(), 3));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_unicode_not_ascii(bh: &mut BenchHarness) {
|
|
struct NotAscii(char);
|
|
impl CharEq for NotAscii {
|
|
fn matches(&self, c: char) -> bool {
|
|
let NotAscii(cc) = *self;
|
|
cc == c
|
|
}
|
|
fn only_ascii(&self) -> bool { false }
|
|
}
|
|
let s = "ประเทศไทย中华Việt Namประเทศไทย中华Việt Nam";
|
|
|
|
bh.iter(|| assert_eq!(s.split(NotAscii('V')).len(), 3));
|
|
}
|
|
|
|
|
|
#[bench]
|
|
fn split_ascii(bh: &mut BenchHarness) {
|
|
let s = "Mary had a little lamb, Little lamb, little-lamb.";
|
|
let len = s.split(' ').len();
|
|
|
|
bh.iter(|| assert_eq!(s.split(' ').len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_not_ascii(bh: &mut BenchHarness) {
|
|
struct NotAscii(char);
|
|
impl CharEq for NotAscii {
|
|
#[inline]
|
|
fn matches(&self, c: char) -> bool {
|
|
let NotAscii(cc) = *self;
|
|
cc == c
|
|
}
|
|
fn only_ascii(&self) -> bool { false }
|
|
}
|
|
let s = "Mary had a little lamb, Little lamb, little-lamb.";
|
|
let len = s.split(' ').len();
|
|
|
|
bh.iter(|| assert_eq!(s.split(NotAscii(' ')).len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_extern_fn(bh: &mut BenchHarness) {
|
|
let s = "Mary had a little lamb, Little lamb, little-lamb.";
|
|
let len = s.split(' ').len();
|
|
fn pred(c: char) -> bool { c == ' ' }
|
|
|
|
bh.iter(|| assert_eq!(s.split(pred).len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_closure(bh: &mut BenchHarness) {
|
|
let s = "Mary had a little lamb, Little lamb, little-lamb.";
|
|
let len = s.split(' ').len();
|
|
|
|
bh.iter(|| assert_eq!(s.split(|c: char| c == ' ').len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn split_slice(bh: &mut BenchHarness) {
|
|
let s = "Mary had a little lamb, Little lamb, little-lamb.";
|
|
let len = s.split(' ').len();
|
|
|
|
bh.iter(|| assert_eq!(s.split(&[' ']).len(), len));
|
|
}
|
|
|
|
#[bench]
|
|
fn is_utf8_100_ascii(bh: &mut BenchHarness) {
|
|
|
|
let s = bytes!("Hello there, the quick brown fox jumped over the lazy dog! \
|
|
Lorem ipsum dolor sit amet, consectetur. ");
|
|
|
|
assert_eq!(100, s.len());
|
|
bh.iter(|| {
|
|
is_utf8(s)
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn is_utf8_100_multibyte(bh: &mut BenchHarness) {
|
|
let s = bytes!("𐌀𐌖𐌋𐌄𐌑𐌉ปรدولة الكويتทศไทย中华𐍅𐌿𐌻𐍆𐌹𐌻𐌰");
|
|
assert_eq!(100, s.len());
|
|
bh.iter(|| {
|
|
is_utf8(s)
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn from_utf8_lossy_100_ascii(bh: &mut BenchHarness) {
|
|
let s = bytes!("Hello there, the quick brown fox jumped over the lazy dog! \
|
|
Lorem ipsum dolor sit amet, consectetur. ");
|
|
|
|
assert_eq!(100, s.len());
|
|
bh.iter(|| {
|
|
let _ = from_utf8_lossy(s);
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn from_utf8_lossy_100_multibyte(bh: &mut BenchHarness) {
|
|
let s = bytes!("𐌀𐌖𐌋𐌄𐌑𐌉ปรدولة الكويتทศไทย中华𐍅𐌿𐌻𐍆𐌹𐌻𐌰");
|
|
assert_eq!(100, s.len());
|
|
bh.iter(|| {
|
|
let _ = from_utf8_lossy(s);
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn from_utf8_lossy_invalid(bh: &mut BenchHarness) {
|
|
let s = bytes!("Hello", 0xC0, 0x80, " There", 0xE6, 0x83, " Goodbye");
|
|
bh.iter(|| {
|
|
let _ = from_utf8_lossy(s);
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn from_utf8_lossy_100_invalid(bh: &mut BenchHarness) {
|
|
let s = ::vec::from_elem(100, 0xF5u8);
|
|
bh.iter(|| {
|
|
let _ = from_utf8_lossy(s);
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn bench_with_capacity(bh: &mut BenchHarness) {
|
|
bh.iter(|| {
|
|
with_capacity(100)
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn bench_push_str(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
bh.iter(|| {
|
|
let mut r = ~"";
|
|
r.push_str(s);
|
|
});
|
|
}
|
|
|
|
#[bench]
|
|
fn bench_connect(bh: &mut BenchHarness) {
|
|
let s = "ศไทย中华Việt Nam; Mary had a little lamb, Little lamb";
|
|
let sep = "→";
|
|
let v = [s, s, s, s, s, s, s, s, s, s];
|
|
bh.iter(|| {
|
|
assert_eq!(v.connect(sep).len(), s.len() * 10 + sep.len() * 9);
|
|
})
|
|
}
|
|
}
|