diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index adb81803ab095..8edd923390af4 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -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() { diff --git a/src/etc/vim/syntax/rust.vim b/src/etc/vim/syntax/rust.vim index 0d248354b737b..9aa76cad83e27 100644 --- a/src/etc/vim/syntax/rust.vim +++ b/src/etc/vim/syntax/rust.vim @@ -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 diff --git a/src/libextra/test.rs b/src/libextra/test.rs index d1fffd9e515c1..2d52a59c8365a 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -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); diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 30b786e4f19ee..c5b221d22724b 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -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("
")); + opts.name = Some("
".into_maybe_owned()); pool.spawn(opts, main); // Wait for the main task to return, and set the process error code diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs index 4fb61f156809f..e492acb4468fd 100644 --- a/src/libgreen/task.rs +++ b/src/libgreen/task.rs @@ -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); diff --git a/src/libnative/task.rs b/src/libnative/task.rs index c08e326d90331..d0ca8364aa7d9 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -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); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 5bd970834a64d..606cb445262e9 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -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", diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 0398af9c1c1b4..7b574d68e7800 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -117,7 +117,6 @@ pub mod vec_ng; pub mod str; pub mod ascii; -pub mod send_str; pub mod ptr; pub mod owned; diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 18f28994cba96..ed0ce20175086 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -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(&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) -> bool { true } +} + #[inline(always)] fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 6970ebfb15d70..f8e9d0ae34404 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -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); } ) ) diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 90154adb7fef7..972b7d178a111 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -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] diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index b08d792822bfb..f113b2f17ebe6 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -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}; diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index fbe82531f6977..a7648dd2d19df 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -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}; diff --git a/src/libstd/send_str.rs b/src/libstd/send_str.rs deleted file mode 100644 index b075b75b70acc..0000000000000 --- a/src/libstd/send_str.rs +++ /dev/null @@ -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 or the MIT license -// , 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 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")); - } -} diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 25e15fc16018e..3225bb3a6789c 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -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 { 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 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)] diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 078933be78fdd..921d0feaa8b19 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -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(&mut self, name: S) { - self.opts.name = Some(name.into_send_str()); + pub fn name>(&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"); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index aeeae94238bd5..93574f4d85850 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 => () diff --git a/src/test/run-fail/fail-task-name-send-str.rs b/src/test/run-fail/fail-task-name-send-str.rs index ca2472cfa55f1..ea530fe9f69d2 100644 --- a/src/test/run-fail/fail-task-name-send-str.rs +++ b/src/test/run-fail/fail-task-name-send-str.rs @@ -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 diff --git a/src/test/run-pass/send_str_hashmap.rs b/src/test/run-pass/send_str_hashmap.rs index dc7e51c3c2364..8599f9d383605 100644 --- a/src/test/run-pass/send_str_hashmap.rs +++ b/src/test/run-pass/send_str_hashmap.rs @@ -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 = 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)); } diff --git a/src/test/run-pass/send_str_treemap.rs b/src/test/run-pass/send_str_treemap.rs index 6332d779e5e6d..1b7d7bf0b4b57 100644 --- a/src/test/run-pass/send_str_treemap.rs +++ b/src/test/run-pass/send_str_treemap.rs @@ -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 = 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(),