auto merge of #12098 : kballard/rust/from_utf8_lossy_tweak, r=huonw

MaybeOwned allows from_utf8_lossy to avoid allocation if there are no
invalid bytes in the input.

Before:
```
test str::bench::from_utf8_lossy_100_ascii                      ... bench:       183 ns/iter (+/- 5)
test str::bench::from_utf8_lossy_100_invalid                    ... bench:       341 ns/iter (+/- 15)
test str::bench::from_utf8_lossy_100_multibyte                  ... bench:       227 ns/iter (+/- 13)
test str::bench::from_utf8_lossy_invalid                        ... bench:       102 ns/iter (+/- 4)
test str::bench::is_utf8_100_ascii                              ... bench:         2 ns/iter (+/- 0)
test str::bench::is_utf8_100_multibyte                          ... bench:         2 ns/iter (+/- 0)
```

Now:
```
test str::bench::from_utf8_lossy_100_ascii                      ... bench:        96 ns/iter (+/- 4)
test str::bench::from_utf8_lossy_100_invalid                    ... bench:       318 ns/iter (+/- 10)
test str::bench::from_utf8_lossy_100_multibyte                  ... bench:       105 ns/iter (+/- 2)
test str::bench::from_utf8_lossy_invalid                        ... bench:       105 ns/iter (+/- 2)
test str::bench::is_utf8_100_ascii                              ... bench:         2 ns/iter (+/- 0)
test str::bench::is_utf8_100_multibyte                          ... bench:         2 ns/iter (+/- 0)
```
This commit is contained in:
bors 2014-02-08 05:01:30 -08:00
commit 5acc998ed9
20 changed files with 398 additions and 421 deletions

View File

@ -442,9 +442,7 @@ fn check_error_patterns(props: &TestProps,
testfile: &Path,
ProcRes: &ProcRes) {
if props.error_patterns.is_empty() {
testfile.display().with_str(|s| {
fatal(~"no error pattern specified in " + s);
})
fatal(~"no error pattern specified in " + testfile.display().as_maybe_owned().as_slice());
}
if ProcRes.status.success() {

View File

@ -87,8 +87,7 @@ syn keyword rustTrait Primitive Int Float ToStrRadix ToPrimitive FromPrimitive
syn keyword rustTrait GenericPath Path PosixPath WindowsPath
syn keyword rustTrait RawPtr
syn keyword rustTrait Buffer Writer Reader Seek
syn keyword rustTrait SendStr SendStrOwned SendStrStatic IntoSendStr
syn keyword rustTrait Str StrVector StrSlice OwnedStr
syn keyword rustTrait Str StrVector StrSlice OwnedStr IntoMaybeOwned
syn keyword rustTrait IterBytes
syn keyword rustTrait ToStr IntoStr
syn keyword rustTrait CloneableTuple ImmutableTuple

View File

@ -893,8 +893,8 @@ pub fn run_test(force_ignore: bool,
spawn(proc() {
let mut task = task::task();
task.name(match desc.name {
DynTestName(ref name) => SendStrOwned(name.clone()),
StaticTestName(name) => SendStrStatic(name),
DynTestName(ref name) => name.to_owned().into_maybe_owned(),
StaticTestName(name) => name.into_maybe_owned()
});
let result_future = task.future_result();
task.spawn(testfn);

View File

@ -257,7 +257,7 @@ pub fn run(main: proc()) -> int {
let (port, chan) = Chan::new();
let mut opts = TaskOpts::new();
opts.notify_chan = Some(chan);
opts.name = Some(SendStrStatic("<main>"));
opts.name = Some("<main>".into_maybe_owned());
pool.spawn(opts, main);
// Wait for the main task to return, and set the process error code

View File

@ -510,7 +510,7 @@ mod tests {
#[test]
fn smoke_opts() {
let mut opts = TaskOpts::new();
opts.name = Some(SendStrStatic("test"));
opts.name = Some("test".into_maybe_owned());
opts.stack_size = Some(20 * 4096);
let (p, c) = Chan::new();
opts.notify_chan = Some(c);

View File

@ -294,7 +294,7 @@ mod tests {
#[test]
fn smoke_opts() {
let mut opts = TaskOpts::new();
opts.name = Some(SendStrStatic("test"));
opts.name = Some("test".into_maybe_owned());
opts.stack_size = Some(20 * 4096);
let (p, c) = Chan::new();
opts.notify_chan = Some(c);

View File

@ -436,7 +436,7 @@ impl<'a> SourceCollector<'a> {
cur.push(p.filename().expect("source has no filename") + bytes!(".html"));
let mut w = BufferedWriter::new(if_ok!(File::create(&cur)));
let title = cur.filename_display().with_str(|s| format!("{} -- source", s));
let title = format!("{} -- source", cur.filename_display());
let page = layout::Page {
title: title,
ty: "source",

View File

@ -117,7 +117,6 @@ pub mod vec_ng;
pub mod str;
pub mod ascii;
pub mod send_str;
pub mod ptr;
pub mod owned;

View File

@ -70,7 +70,7 @@ use fmt;
use iter::Iterator;
use option::{Option, None, Some};
use str;
use str::{OwnedStr, Str, StrSlice};
use str::{MaybeOwned, OwnedStr, Str, StrSlice, from_utf8_lossy};
use to_str::ToStr;
use vec;
use vec::{CloneableVector, OwnedCloneableVector, OwnedVector, Vector};
@ -495,7 +495,7 @@ pub struct Display<'a, P> {
impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.with_str(|s| f.pad(s))
self.as_maybe_owned().as_slice().fmt(f)
}
}
@ -505,33 +505,25 @@ impl<'a, P: GenericPath> ToStr for Display<'a, P> {
/// If the path is not UTF-8, invalid sequences with be replaced with the
/// unicode replacement char. This involves allocation.
fn to_str(&self) -> ~str {
if self.filename {
match self.path.filename() {
None => ~"",
Some(v) => str::from_utf8_lossy(v)
}
} else {
str::from_utf8_lossy(self.path.as_vec())
}
self.as_maybe_owned().into_owned()
}
}
impl<'a, P: GenericPath> Display<'a, P> {
/// Provides the path as a string to a closure
/// Returns the path as a possibly-owned string.
///
/// If the path is not UTF-8, invalid sequences will be replaced with the
/// unicode replacement char. This involves allocation.
#[inline]
pub fn with_str<T>(&self, f: |&str| -> T) -> T {
let opt = if self.filename { self.path.filename_str() }
else { self.path.as_str() };
match opt {
Some(s) => f(s),
None => {
let s = self.to_str();
f(s.as_slice())
}
pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
from_utf8_lossy(if self.filename {
match self.path.filename() {
None => &[],
Some(v) => v
}
} else {
self.path.as_vec()
})
}
}
@ -591,6 +583,23 @@ impl BytesContainer for CString {
}
}
impl<'a> BytesContainer for str::MaybeOwned<'a> {
#[inline]
fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
self.as_slice().as_bytes()
}
#[inline]
fn container_into_owned_bytes(self) -> ~[u8] {
self.into_owned().into_bytes()
}
#[inline]
fn container_as_str<'b>(&'b self) -> Option<&'b str> {
Some(self.as_slice())
}
#[inline]
fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
}
#[inline(always)]
fn contains_nul(v: &[u8]) -> bool {
v.iter().any(|&x| x == 0)

View File

@ -564,24 +564,16 @@ mod tests {
macro_rules! t(
($path:expr, $exp:expr) => (
{
let mut called = false;
let path = Path::new($path);
path.display().with_str(|s| {
assert_eq!(s, $exp);
called = true;
});
assert!(called);
let mo = path.display().as_maybe_owned();
assert_eq!(mo.as_slice(), $exp);
}
);
($path:expr, $exp:expr, filename) => (
{
let mut called = false;
let path = Path::new($path);
path.filename_display().with_str(|s| {
assert_eq!(s, $exp);
called = true;
});
assert!(called);
let mo = path.filename_display().as_maybe_owned();
assert_eq!(mo.as_slice(), $exp);
}
)
)

View File

@ -1278,20 +1278,12 @@ mod tests {
let path = Path::new(b!("\\"));
assert_eq!(path.filename_display().to_str(), ~"");
let mut called = false;
let path = Path::new("foo");
path.display().with_str(|s| {
assert_eq!(s, "foo");
called = true;
});
assert!(called);
called = false;
let mo = path.display().as_maybe_owned();
assert_eq!(mo.as_slice(), "foo");
let path = Path::new(b!("\\"));
path.filename_display().with_str(|s| {
assert_eq!(s, "");
called = true;
});
assert!(called);
let mo = path.filename_display().as_maybe_owned();
assert_eq!(mo.as_slice(), "");
}
#[test]

View File

@ -63,8 +63,7 @@ pub use num::{Primitive, Int, Float, ToStrRadix, ToPrimitive, FromPrimitive};
pub use path::{GenericPath, Path, PosixPath, WindowsPath};
pub use ptr::RawPtr;
pub use io::{Buffer, Writer, Reader, Seek};
pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr};
pub use str::{Str, StrVector, StrSlice, OwnedStr};
pub use str::{Str, StrVector, StrSlice, OwnedStr, IntoMaybeOwned};
pub use to_bytes::IterBytes;
pub use to_str::{ToStr, IntoStr};
pub use tuple::{CloneableTuple, ImmutableTuple};

View File

@ -30,7 +30,7 @@ use rt::local::Local;
use rt::local_heap::LocalHeap;
use rt::rtio::LocalIo;
use rt::unwind::Unwinder;
use send_str::SendStr;
use str::SendStr;
use sync::arc::UnsafeArc;
use sync::atomics::{AtomicUint, SeqCst};
use task::{TaskResult, TaskOpts};

View File

@ -1,246 +0,0 @@
// Copyright 2013-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.
//! The `SendStr` trait for optionally static strings
use clone::{Clone, DeepClone};
use cmp::{Eq, TotalEq, Ord, TotalOrd, Equiv};
use cmp::Ordering;
use container::Container;
use default::Default;
use str::{Str, StrSlice};
use to_str::ToStr;
use to_bytes::{IterBytes, Cb};
/// A SendStr is a string that can hold either a ~str or a &'static str.
/// This can be useful as an optimization when an allocation is sometimes
/// needed but the common case is statically known.
#[allow(missing_doc)]
pub enum SendStr {
SendStrOwned(~str),
SendStrStatic(&'static str)
}
impl SendStr {
/// Returns `true` if this `SendStr` wraps an owned string
#[inline]
pub fn is_owned(&self) -> bool {
match *self {
SendStrOwned(_) => true,
SendStrStatic(_) => false
}
}
/// Returns `true` if this `SendStr` wraps a static string
#[inline]
pub fn is_static(&self) -> bool {
match *self {
SendStrOwned(_) => false,
SendStrStatic(_) => true
}
}
}
/// Trait for moving into an `SendStr`
pub trait IntoSendStr {
/// Moves self into an `SendStr`
fn into_send_str(self) -> SendStr;
}
impl IntoSendStr for ~str {
#[inline]
fn into_send_str(self) -> SendStr { SendStrOwned(self) }
}
impl IntoSendStr for &'static str {
#[inline]
fn into_send_str(self) -> SendStr { SendStrStatic(self) }
}
impl IntoSendStr for SendStr {
#[inline]
fn into_send_str(self) -> SendStr { self }
}
/*
Section: String trait impls.
`SendStr` should behave like a normal string, so we don't derive.
*/
impl ToStr for SendStr {
#[inline]
fn to_str(&self) -> ~str { self.as_slice().to_owned() }
}
impl Eq for SendStr {
#[inline]
fn eq(&self, other: &SendStr) -> bool {
self.as_slice().equals(&other.as_slice())
}
}
impl TotalEq for SendStr {
#[inline]
fn equals(&self, other: &SendStr) -> bool {
self.as_slice().equals(&other.as_slice())
}
}
impl Ord for SendStr {
#[inline]
fn lt(&self, other: &SendStr) -> bool {
self.as_slice().lt(&other.as_slice())
}
}
impl TotalOrd for SendStr {
#[inline]
fn cmp(&self, other: &SendStr) -> Ordering {
self.as_slice().cmp(&other.as_slice())
}
}
impl<'a, S: Str> Equiv<S> for SendStr {
#[inline]
fn equiv(&self, other: &S) -> bool {
self.as_slice().equals(&other.as_slice())
}
}
impl Str for SendStr {
#[inline]
fn as_slice<'r>(&'r self) -> &'r str {
match *self {
SendStrOwned(ref s) => s.as_slice(),
// FIXME: Borrowchecker doesn't recognize lifetime as static unless prompted
// SendStrStatic(s) => s.as_slice()
SendStrStatic(s) => {let tmp: &'static str = s; tmp}
}
}
#[inline]
fn into_owned(self) -> ~str {
match self {
SendStrOwned(s) => s,
SendStrStatic(s) => s.to_owned()
}
}
}
impl Container for SendStr {
#[inline]
fn len(&self) -> uint { self.as_slice().len() }
}
impl Clone for SendStr {
#[inline]
fn clone(&self) -> SendStr {
match *self {
SendStrOwned(ref s) => SendStrOwned(s.to_owned()),
SendStrStatic(s) => SendStrStatic(s)
}
}
}
impl DeepClone for SendStr {
#[inline]
fn deep_clone(&self) -> SendStr {
match *self {
SendStrOwned(ref s) => SendStrOwned(s.to_owned()),
SendStrStatic(s) => SendStrStatic(s)
}
}
}
impl Default for SendStr {
#[inline]
fn default() -> SendStr { SendStrStatic("") }
}
impl IterBytes for SendStr {
#[inline]
fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
match *self {
SendStrOwned(ref s) => s.iter_bytes(lsb0, f),
SendStrStatic(s) => s.iter_bytes(lsb0, f)
}
}
}
#[cfg(test)]
mod tests {
use prelude::*;
use send_str::{SendStrOwned, SendStrStatic};
#[test]
fn test_send_str_traits() {
let s = SendStrStatic("abcde");
assert_eq!(s.len(), 5);
assert_eq!(s.as_slice(), "abcde");
assert_eq!(s.to_str(), ~"abcde");
assert!(s.lt(&SendStrOwned(~"bcdef")));
assert_eq!(SendStrStatic(""), Default::default());
let o = SendStrOwned(~"abcde");
assert_eq!(o.len(), 5);
assert_eq!(o.as_slice(), "abcde");
assert_eq!(o.to_str(), ~"abcde");
assert!(o.lt(&SendStrStatic("bcdef")));
assert_eq!(SendStrOwned(~""), 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_send_str_methods() {
let s = SendStrStatic("abcde");
assert!(s.is_static());
assert!(!s.is_owned());
let o = SendStrOwned(~"abcde");
assert!(!o.is_static());
assert!(o.is_owned());
}
#[test]
fn test_send_str_clone() {
assert_eq!(SendStrOwned(~"abcde"), SendStrStatic("abcde").clone());
assert_eq!(SendStrOwned(~"abcde"), SendStrStatic("abcde").deep_clone());
assert_eq!(SendStrOwned(~"abcde"), SendStrOwned(~"abcde").clone());
assert_eq!(SendStrOwned(~"abcde"), SendStrOwned(~"abcde").deep_clone());
assert_eq!(SendStrStatic("abcde"), SendStrStatic("abcde").clone());
assert_eq!(SendStrStatic("abcde"), SendStrStatic("abcde").deep_clone());
assert_eq!(SendStrStatic("abcde"), SendStrOwned(~"abcde").clone());
assert_eq!(SendStrStatic("abcde"), SendStrOwned(~"abcde").deep_clone());
}
#[test]
fn test_send_str_into_owned() {
assert_eq!(SendStrStatic("abcde").into_owned(), ~"abcde");
assert_eq!(SendStrOwned(~"abcde").into_owned(), ~"abcde");
}
#[test]
fn test_into_send_str() {
assert_eq!("abcde".into_send_str(), SendStrStatic("abcde"));
assert_eq!((~"abcde").into_send_str(), SendStrStatic("abcde"));
assert_eq!("abcde".into_send_str(), SendStrOwned(~"abcde"));
assert_eq!((~"abcde").into_send_str(), SendStrOwned(~"abcde"));
}
}

View File

@ -86,7 +86,9 @@ 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};
@ -100,7 +102,7 @@ use from_str::FromStr;
use vec;
use vec::{OwnedVector, OwnedCloneableVector, ImmutableVector, MutableVector};
use default::Default;
use send_str::{SendStr, SendStrOwned};
use to_bytes::{IterBytes, Cb};
use unstable::raw::Repr;
/*
@ -729,6 +731,11 @@ Section: Misc
/// Determines if a vector of bytes contains valid UTF-8
pub fn is_utf8(v: &[u8]) -> bool {
first_non_utf8_index(v).is_none()
}
#[inline(always)]
fn first_non_utf8_index(v: &[u8]) -> Option<uint> {
let mut i = 0u;
let total = v.len();
fn unsafe_get(xs: &[u8], i: uint) -> u8 {
@ -740,10 +747,10 @@ pub fn is_utf8(v: &[u8]) -> bool {
i += 1u;
} else {
let w = utf8_char_width(v_i);
if w == 0u { return false; }
if w == 0u { return Some(i); }
let nexti = i + w;
if nexti > total { return false; }
if nexti > total { return Some(i); }
// 2-byte encoding is for codepoints \u0080 to \u07ff
// first C2 80 last DF BF
@ -766,7 +773,7 @@ pub fn is_utf8(v: &[u8]) -> bool {
// UTF8-tail = %x80-BF
match w {
2 => if unsafe_get(v, i + 1) & 192u8 != TAG_CONT_U8 {
return false
return Some(i)
},
3 => match (v_i,
unsafe_get(v, i + 1),
@ -775,7 +782,7 @@ pub fn is_utf8(v: &[u8]) -> bool {
(0xE1 .. 0xEC, 0x80 .. 0xBF, TAG_CONT_U8) => (),
(0xED , 0x80 .. 0x9F, TAG_CONT_U8) => (),
(0xEE .. 0xEF, 0x80 .. 0xBF, TAG_CONT_U8) => (),
_ => return false,
_ => return Some(i),
},
_ => match (v_i,
unsafe_get(v, i + 1),
@ -784,14 +791,14 @@ pub fn is_utf8(v: &[u8]) -> bool {
(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) => (),
_ => return false,
_ => return Some(i)
},
}
i = nexti;
}
}
true
None
}
/// Determines if a vector of `u16` contains valid UTF-16
@ -918,12 +925,16 @@ static TAG_CONT_U8: u8 = 128u8;
/// ```rust
/// let input = bytes!("Hello ", 0xF0, 0x90, 0x80, "World");
/// let output = std::str::from_utf8_lossy(input);
/// assert_eq!(output, ~"Hello \uFFFDWorld");
/// assert_eq!(output.as_slice(), "Hello \uFFFDWorld");
/// ```
pub fn from_utf8_lossy(v: &[u8]) -> ~str {
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 = 0u;
let mut lastgood = 0u;
let mut i = firstbad;
let total = v.len();
fn unsafe_get(xs: &[u8], i: uint) -> u8 {
unsafe { *xs.unsafe_ref(i) }
@ -937,23 +948,32 @@ pub fn from_utf8_lossy(v: &[u8]) -> ~str {
}
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(() => {
macro_rules! error(() => ({
unsafe {
if lastgood != i_ {
raw::push_bytes(&mut res, v.slice(lastgood, i_));
if subseqidx != i_ {
raw::push_bytes(&mut res, v.slice(subseqidx, i_));
}
lastgood = i;
subseqidx = i;
raw::push_bytes(&mut res, REPLACEMENT);
}
})
}))
if byte < 128u8 {
// lastgood handles this
// subseqidx handles this
} else {
let w = utf8_char_width(byte);
@ -1012,8 +1032,176 @@ pub fn from_utf8_lossy(v: &[u8]) -> ~str {
}
}
}
unsafe { raw::push_bytes(&mut res, v.slice(lastgood, total)) };
res
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
@ -1770,9 +1958,6 @@ pub trait StrSlice<'a> {
/// Converts to a vector of `u16` encoded as UTF-16.
fn to_utf16(&self) -> ~[u16];
/// Copy a slice into a new `SendStr`.
fn to_send_str(&self) -> SendStr;
/// Check that `index`-th byte lies at the start and/or end of a
/// UTF-8 code point sequence.
///
@ -2297,11 +2482,6 @@ impl<'a> StrSlice<'a> for &'a str {
u
}
#[inline]
fn to_send_str(&self) -> SendStr {
SendStrOwned(self.to_owned())
}
#[inline]
fn is_char_boundary(&self, index: uint) -> bool {
if index == self.len() { return true; }
@ -2741,7 +2921,6 @@ mod tests {
use prelude::*;
use ptr;
use str::*;
use send_str::{SendStrOwned, SendStrStatic};
#[test]
fn test_eq() {
@ -3943,38 +4122,32 @@ mod tests {
#[test]
fn test_str_from_utf8_lossy() {
let xs = bytes!("hello");
assert_eq!(from_utf8_lossy(xs), ~"hello");
assert_eq!(from_utf8_lossy(xs), Slice("hello"));
let xs = bytes!("ศไทย中华Việt Nam");
assert_eq!(from_utf8_lossy(xs), ~"ศไทย中华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), ~"Hello\uFFFD There\uFFFD 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), ~"Hello\uFFFD\uFFFD There\uFFFD 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), ~"\uFFFDfoo\uFFFD\uFFFDbar");
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), ~"\uFFFDfoo\uFFFDbar\uFFFDbaz");
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), ~"\uFFFDfoo\uFFFDbar\uFFFD\uFFFDbaz");
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), ~"\uFFFD\uFFFD\uFFFD\uFFFDfoo\U00010000bar");
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), ~"\uFFFD\uFFFD\uFFFDfoo\uFFFD\uFFFD\uFFFDbar");
}
#[test]
fn test_to_send_str() {
assert_eq!("abcde".to_send_str(), SendStrStatic("abcde"));
assert_eq!("abcde".to_send_str(), SendStrOwned(~"abcde"));
assert_eq!(from_utf8_lossy(xs), Owned(~"\uFFFD\uFFFD\uFFFDfoo\uFFFD\uFFFD\uFFFDbar"));
}
#[test]
@ -3982,6 +4155,71 @@ mod tests {
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!(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!(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)]

View File

@ -62,8 +62,7 @@ use option::{None, Some, Option};
use result::{Result, Ok, Err};
use rt::local::Local;
use rt::task::Task;
use send_str::{SendStr, IntoSendStr};
use str::Str;
use str::{Str, SendStr, IntoMaybeOwned};
#[cfg(test)] use any::{AnyOwnExt, AnyRefExt};
#[cfg(test)] use comm::SharedChan;
@ -190,8 +189,8 @@ impl TaskBuilder {
/// Name the task-to-be. Currently the name is used for identification
/// only in failure messages.
pub fn name<S: IntoSendStr>(&mut self, name: S) {
self.opts.name = Some(name.into_send_str());
pub fn name<S: IntoMaybeOwned<'static>>(&mut self, name: S) {
self.opts.name = Some(name.into_maybe_owned());
}
/**
@ -396,7 +395,7 @@ fn test_static_named_task() {
#[test]
fn test_send_named_task() {
let mut t = task();
t.name("ada lovelace".into_send_str());
t.name("ada lovelace".into_maybe_owned());
t.spawn(proc() {
with_task_name(|name| {
assert!(name.unwrap() == "ada lovelace");

View File

@ -4200,10 +4200,10 @@ impl Parser {
let mut err = ~"circular modules: ";
let len = included_mod_stack.get().len();
for p in included_mod_stack.get().slice(i, len).iter() {
p.display().with_str(|s| err.push_str(s));
err.push_str(p.display().as_maybe_owned().as_slice());
err.push_str(" -> ");
}
path.display().with_str(|s| err.push_str(s));
err.push_str(path.display().as_maybe_owned().as_slice());
self.span_fatal(id_sp, err);
}
None => ()

View File

@ -12,7 +12,7 @@
fn main() {
let mut t = ::std::task::task();
t.name("send name".to_send_str());
t.name("send name".into_maybe_owned());
t.try(proc() {
fail!("test");
3

View File

@ -13,45 +13,44 @@ use std::cmp::{TotalEq, Ord, TotalOrd, Equiv};
use std::cmp::Equal;
use std::container::{Container, Map, MutableMap};
use std::default::Default;
use std::send_str::{SendStr, SendStrOwned, SendStrStatic};
use std::str::Str;
use std::str::{Str, SendStr, Owned, Slice};
use std::to_str::ToStr;
use std::hashmap::HashMap;
use std::option::Some;
pub fn main() {
let mut map: HashMap<SendStr, uint> = HashMap::new();
assert!(map.insert(SendStrStatic("foo"), 42));
assert!(!map.insert(SendStrOwned(~"foo"), 42));
assert!(!map.insert(SendStrStatic("foo"), 42));
assert!(!map.insert(SendStrOwned(~"foo"), 42));
assert!(map.insert(Slice("foo"), 42));
assert!(!map.insert(Owned(~"foo"), 42));
assert!(!map.insert(Slice("foo"), 42));
assert!(!map.insert(Owned(~"foo"), 42));
assert!(!map.insert(SendStrStatic("foo"), 43));
assert!(!map.insert(SendStrOwned(~"foo"), 44));
assert!(!map.insert(SendStrStatic("foo"), 45));
assert!(!map.insert(SendStrOwned(~"foo"), 46));
assert!(!map.insert(Slice("foo"), 43));
assert!(!map.insert(Owned(~"foo"), 44));
assert!(!map.insert(Slice("foo"), 45));
assert!(!map.insert(Owned(~"foo"), 46));
let v = 46;
assert_eq!(map.find(&SendStrOwned(~"foo")), Some(&v));
assert_eq!(map.find(&SendStrStatic("foo")), Some(&v));
assert_eq!(map.find(&Owned(~"foo")), Some(&v));
assert_eq!(map.find(&Slice("foo")), Some(&v));
let (a, b, c, d) = (50, 51, 52, 53);
assert!(map.insert(SendStrStatic("abc"), a));
assert!(map.insert(SendStrOwned(~"bcd"), b));
assert!(map.insert(SendStrStatic("cde"), c));
assert!(map.insert(SendStrOwned(~"def"), d));
assert!(map.insert(Slice("abc"), a));
assert!(map.insert(Owned(~"bcd"), b));
assert!(map.insert(Slice("cde"), c));
assert!(map.insert(Owned(~"def"), d));
assert!(!map.insert(SendStrStatic("abc"), a));
assert!(!map.insert(SendStrOwned(~"bcd"), b));
assert!(!map.insert(SendStrStatic("cde"), c));
assert!(!map.insert(SendStrOwned(~"def"), d));
assert!(!map.insert(Slice("abc"), a));
assert!(!map.insert(Owned(~"bcd"), b));
assert!(!map.insert(Slice("cde"), c));
assert!(!map.insert(Owned(~"def"), d));
assert!(!map.insert(SendStrOwned(~"abc"), a));
assert!(!map.insert(SendStrStatic("bcd"), b));
assert!(!map.insert(SendStrOwned(~"cde"), c));
assert!(!map.insert(SendStrStatic("def"), d));
assert!(!map.insert(Owned(~"abc"), a));
assert!(!map.insert(Slice("bcd"), b));
assert!(!map.insert(Owned(~"cde"), c));
assert!(!map.insert(Slice("def"), d));
assert_eq!(map.find_equiv(&("abc")), Some(&a));
assert_eq!(map.find_equiv(&("bcd")), Some(&b));
@ -63,13 +62,13 @@ pub fn main() {
assert_eq!(map.find_equiv(&(~"cde")), Some(&c));
assert_eq!(map.find_equiv(&(~"def")), Some(&d));
assert_eq!(map.find_equiv(&SendStrStatic("abc")), Some(&a));
assert_eq!(map.find_equiv(&SendStrStatic("bcd")), Some(&b));
assert_eq!(map.find_equiv(&SendStrStatic("cde")), Some(&c));
assert_eq!(map.find_equiv(&SendStrStatic("def")), Some(&d));
assert_eq!(map.find_equiv(&Slice("abc")), Some(&a));
assert_eq!(map.find_equiv(&Slice("bcd")), Some(&b));
assert_eq!(map.find_equiv(&Slice("cde")), Some(&c));
assert_eq!(map.find_equiv(&Slice("def")), Some(&d));
assert_eq!(map.find_equiv(&SendStrOwned(~"abc")), Some(&a));
assert_eq!(map.find_equiv(&SendStrOwned(~"bcd")), Some(&b));
assert_eq!(map.find_equiv(&SendStrOwned(~"cde")), Some(&c));
assert_eq!(map.find_equiv(&SendStrOwned(~"def")), Some(&d));
assert_eq!(map.find_equiv(&Owned(~"abc")), Some(&a));
assert_eq!(map.find_equiv(&Owned(~"bcd")), Some(&b));
assert_eq!(map.find_equiv(&Owned(~"cde")), Some(&c));
assert_eq!(map.find_equiv(&Owned(~"def")), Some(&d));
}

View File

@ -15,57 +15,56 @@ use std::cmp::{TotalEq, Ord, TotalOrd, Equiv};
use std::cmp::Equal;
use std::container::{Container, Map, MutableMap};
use std::default::Default;
use std::send_str::{SendStr, SendStrOwned, SendStrStatic};
use std::str::Str;
use std::str::{Str, SendStr, Owned, Slice};
use std::to_str::ToStr;
use self::collections::TreeMap;
use std::option::Some;
pub fn main() {
let mut map: TreeMap<SendStr, uint> = TreeMap::new();
assert!(map.insert(SendStrStatic("foo"), 42));
assert!(!map.insert(SendStrOwned(~"foo"), 42));
assert!(!map.insert(SendStrStatic("foo"), 42));
assert!(!map.insert(SendStrOwned(~"foo"), 42));
assert!(map.insert(Slice("foo"), 42));
assert!(!map.insert(Owned(~"foo"), 42));
assert!(!map.insert(Slice("foo"), 42));
assert!(!map.insert(Owned(~"foo"), 42));
assert!(!map.insert(SendStrStatic("foo"), 43));
assert!(!map.insert(SendStrOwned(~"foo"), 44));
assert!(!map.insert(SendStrStatic("foo"), 45));
assert!(!map.insert(SendStrOwned(~"foo"), 46));
assert!(!map.insert(Slice("foo"), 43));
assert!(!map.insert(Owned(~"foo"), 44));
assert!(!map.insert(Slice("foo"), 45));
assert!(!map.insert(Owned(~"foo"), 46));
let v = 46;
assert_eq!(map.find(&SendStrOwned(~"foo")), Some(&v));
assert_eq!(map.find(&SendStrStatic("foo")), Some(&v));
assert_eq!(map.find(&Owned(~"foo")), Some(&v));
assert_eq!(map.find(&Slice("foo")), Some(&v));
let (a, b, c, d) = (50, 51, 52, 53);
assert!(map.insert(SendStrStatic("abc"), a));
assert!(map.insert(SendStrOwned(~"bcd"), b));
assert!(map.insert(SendStrStatic("cde"), c));
assert!(map.insert(SendStrOwned(~"def"), d));
assert!(map.insert(Slice("abc"), a));
assert!(map.insert(Owned(~"bcd"), b));
assert!(map.insert(Slice("cde"), c));
assert!(map.insert(Owned(~"def"), d));
assert!(!map.insert(SendStrStatic("abc"), a));
assert!(!map.insert(SendStrOwned(~"bcd"), b));
assert!(!map.insert(SendStrStatic("cde"), c));
assert!(!map.insert(SendStrOwned(~"def"), d));
assert!(!map.insert(Slice("abc"), a));
assert!(!map.insert(Owned(~"bcd"), b));
assert!(!map.insert(Slice("cde"), c));
assert!(!map.insert(Owned(~"def"), d));
assert!(!map.insert(SendStrOwned(~"abc"), a));
assert!(!map.insert(SendStrStatic("bcd"), b));
assert!(!map.insert(SendStrOwned(~"cde"), c));
assert!(!map.insert(SendStrStatic("def"), d));
assert!(!map.insert(Owned(~"abc"), a));
assert!(!map.insert(Slice("bcd"), b));
assert!(!map.insert(Owned(~"cde"), c));
assert!(!map.insert(Slice("def"), d));
assert_eq!(map.find(&SendStrStatic("abc")), Some(&a));
assert_eq!(map.find(&SendStrStatic("bcd")), Some(&b));
assert_eq!(map.find(&SendStrStatic("cde")), Some(&c));
assert_eq!(map.find(&SendStrStatic("def")), Some(&d));
assert_eq!(map.find(&Slice("abc")), Some(&a));
assert_eq!(map.find(&Slice("bcd")), Some(&b));
assert_eq!(map.find(&Slice("cde")), Some(&c));
assert_eq!(map.find(&Slice("def")), Some(&d));
assert_eq!(map.find(&SendStrOwned(~"abc")), Some(&a));
assert_eq!(map.find(&SendStrOwned(~"bcd")), Some(&b));
assert_eq!(map.find(&SendStrOwned(~"cde")), Some(&c));
assert_eq!(map.find(&SendStrOwned(~"def")), Some(&d));
assert_eq!(map.find(&Owned(~"abc")), Some(&a));
assert_eq!(map.find(&Owned(~"bcd")), Some(&b));
assert_eq!(map.find(&Owned(~"cde")), Some(&c));
assert_eq!(map.find(&Owned(~"def")), Some(&d));
assert!(map.pop(&SendStrStatic("foo")).is_some());
assert!(map.pop(&Slice("foo")).is_some());
assert_eq!(map.move_iter().map(|(k, v)| k.to_str() + v.to_str())
.to_owned_vec()
.concat(),