diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs index db5edc2009e51..b39b3102a343a 100644 --- a/src/libstd/logging.rs +++ b/src/libstd/logging.rs @@ -13,7 +13,8 @@ use option::*; use os; use rt; -use rt::logging::{Logger, StdErrLogger, OwnedString}; +use rt::logging::{Logger, StdErrLogger}; +use send_str::SendStrOwned; /// Turns on logging to stdout globally pub fn console_on() { @@ -56,12 +57,12 @@ fn newsched_log_str(msg: ~str) { match optional_task { Some(local) => { // Use the available logger - (*local).logger.log(OwnedString(msg)); + (*local).logger.log(SendStrOwned(msg)); } None => { // There is no logger anywhere, just write to stderr let mut logger = StdErrLogger; - logger.log(OwnedString(msg)); + logger.log(SendStrOwned(msg)); } } } diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 818b0562955bd..1672f0a902e71 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -66,6 +66,7 @@ pub use path::PosixPath; pub use path::WindowsPath; pub use ptr::RawPtr; pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume}; +pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr}; pub use str::{Str, StrVector, StrSlice, OwnedStr}; pub use from_str::FromStr; pub use to_bytes::IterBytes; diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs index 54084bb14c08a..fbe05267cf4f9 100644 --- a/src/libstd/rt/logging.rs +++ b/src/libstd/rt/logging.rs @@ -17,6 +17,7 @@ use str::raw::from_c_str; use u32; use vec::ImmutableVector; use cast::transmute; +use send_str::{SendStr, SendStrOwned, SendStrStatic}; struct LogDirective { name: Option<~str>, @@ -168,20 +169,14 @@ fn update_log_settings(crate_map: *u8, settings: ~str) { } } -/// Represent a string with `Send` bound. -pub enum SendableString { - OwnedString(~str), - StaticString(&'static str) -} - pub trait Logger { - fn log(&mut self, msg: SendableString); + fn log(&mut self, msg: SendStr); } pub struct StdErrLogger; impl Logger for StdErrLogger { - fn log(&mut self, msg: SendableString) { + fn log(&mut self, msg: SendStr) { use io::{Writer, WriterUtil}; if !should_log_console() { @@ -189,11 +184,11 @@ impl Logger for StdErrLogger { } let s: &str = match msg { - OwnedString(ref s) => { + SendStrOwned(ref s) => { let slc: &str = *s; slc }, - StaticString(s) => s, + SendStrStatic(s) => s, }; // Truncate the string diff --git a/src/libstd/send_str.rs b/src/libstd/send_str.rs new file mode 100644 index 0000000000000..d674b4980dd13 --- /dev/null +++ b/src/libstd/send_str.rs @@ -0,0 +1,248 @@ +// Copyright 2013 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. + +//! `SendStr` definition and trait implementations + +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. +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 an 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) } +} + +/* +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<'self, 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(), + // XXX: 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 clone::{Clone, DeepClone}; + use cmp::{TotalEq, Ord, TotalOrd, Equiv}; + use cmp::Equal; + use container::Container; + use default::Default; + use send_str::{SendStrOwned, SendStrStatic}; + use str::Str; + use to_str::ToStr; + + #[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.equiv(&@"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.equiv(&@"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/std.rs b/src/libstd/std.rs index d78ea9f00938d..05433c4705983 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -121,6 +121,7 @@ pub mod str; #[path = "str/ascii.rs"] pub mod ascii; +pub mod send_str; pub mod ptr; pub mod owned; diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 0f125280c2da8..ccb39c605eb06 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -37,6 +37,7 @@ use unstable::raw::{Repr, Slice}; use vec; use vec::{OwnedVector, OwnedCopyableVector, ImmutableVector, MutableVector}; use default::Default; +use send_str::{SendStr, SendStrOwned}; /* Section: Conditions @@ -130,10 +131,12 @@ impl ToStr for ~str { #[inline] fn to_str(&self) -> ~str { self.to_owned() } } + impl<'self> ToStr for &'self str { #[inline] fn to_str(&self) -> ~str { self.to_owned() } } + impl ToStr for @str { #[inline] fn to_str(&self) -> ~str { self.to_owned() } @@ -330,7 +333,6 @@ impl<'self> DoubleEndedIterator for CharIterator<'self> { } } - /// External iterator for a string's characters and their byte offsets. /// Use with the `std::iterator` module. #[deriving(Clone)] @@ -1355,6 +1357,7 @@ pub trait StrSlice<'self> { fn to_owned(&self) -> ~str; fn to_managed(&self) -> @str; fn to_utf16(&self) -> ~[u16]; + fn to_send_str(&self) -> SendStr; fn is_char_boundary(&self, index: uint) -> bool; fn char_range_at(&self, start: uint) -> CharRange; fn char_at(&self, i: uint) -> char; @@ -1869,6 +1872,11 @@ impl<'self> StrSlice<'self> for &'self str { u } + #[inline] + fn to_send_str(&self) -> SendStr { + SendStrOwned(self.to_owned()) + } + /// Returns false if the index points into the middle of a multi-byte /// character sequence. #[inline] @@ -2428,6 +2436,7 @@ mod tests { use vec; use vec::{Vector, ImmutableVector, CopyableVector}; use cmp::{TotalOrd, Less, Equal, Greater}; + use send_str::{SendStrOwned, SendStrStatic}; #[test] fn test_eq() { @@ -3724,6 +3733,12 @@ mod tests { let xs = bytes!("hello", 0xff).to_owned(); assert_eq!(from_utf8_owned_opt(xs), None); } + + #[test] + fn test_to_send_str() { + assert_eq!("abcde".to_send_str(), SendStrStatic("abcde")); + assert_eq!("abcde".to_send_str(), SendStrOwned(~"abcde")); + } } #[cfg(test)] diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs index f7f7fef6fa093..3d35de0f89815 100644 --- a/src/libstd/sys.rs +++ b/src/libstd/sys.rs @@ -140,7 +140,8 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { use rt::in_green_task_context; use rt::task::Task; use rt::local::Local; - use rt::logging::{Logger, OwnedString}; + use rt::logging::Logger; + use send_str::SendStrOwned; use str::Str; unsafe { @@ -163,7 +164,7 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { msg, file, line as int) }; - task.logger.log(OwnedString(msg)); + task.logger.log(SendStrOwned(msg)); } } else { rterrln!("failed in non-task context at '%s', %s:%i", diff --git a/src/test/run-pass/send_str_hashmap.rs b/src/test/run-pass/send_str_hashmap.rs new file mode 100644 index 0000000000000..a33cb99682bb0 --- /dev/null +++ b/src/test/run-pass/send_str_hashmap.rs @@ -0,0 +1,80 @@ +// Copyright 2013 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. + +use std::clone::{Clone, DeepClone}; +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::to_str::ToStr; +use std::hashmap::HashMap; +use std::option::Some; + +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(SendStrStatic("foo"), 43)); + assert!(!map.insert(SendStrOwned(~"foo"), 44)); + assert!(!map.insert(SendStrStatic("foo"), 45)); + assert!(!map.insert(SendStrOwned(~"foo"), 46)); + + let v = 46; + + assert_eq!(map.find(&SendStrOwned(~"foo")), Some(&v)); + assert_eq!(map.find(&SendStrStatic("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(SendStrStatic("abc"), a)); + assert!(!map.insert(SendStrOwned(~"bcd"), b)); + assert!(!map.insert(SendStrStatic("cde"), c)); + assert!(!map.insert(SendStrOwned(~"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_eq!(map.find_equiv(&("abc")), Some(&a)); + assert_eq!(map.find_equiv(&("bcd")), Some(&b)); + assert_eq!(map.find_equiv(&("cde")), Some(&c)); + assert_eq!(map.find_equiv(&("def")), Some(&d)); + + assert_eq!(map.find_equiv(&(~"abc")), Some(&a)); + assert_eq!(map.find_equiv(&(~"bcd")), Some(&b)); + assert_eq!(map.find_equiv(&(~"cde")), Some(&c)); + assert_eq!(map.find_equiv(&(~"def")), Some(&d)); + + assert_eq!(map.find_equiv(&(@"abc")), Some(&a)); + assert_eq!(map.find_equiv(&(@"bcd")), Some(&b)); + 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(&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)); +} diff --git a/src/test/run-pass/send_str_treemap.rs b/src/test/run-pass/send_str_treemap.rs new file mode 100644 index 0000000000000..7094dca7c4dc6 --- /dev/null +++ b/src/test/run-pass/send_str_treemap.rs @@ -0,0 +1,73 @@ +// Copyright 2013 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. + +extern mod extra; + +use std::clone::{Clone, DeepClone}; +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::to_str::ToStr; +use self::extra::treemap::TreeMap; +use std::option::Some; + +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(SendStrStatic("foo"), 43)); + assert!(!map.insert(SendStrOwned(~"foo"), 44)); + assert!(!map.insert(SendStrStatic("foo"), 45)); + assert!(!map.insert(SendStrOwned(~"foo"), 46)); + + let v = 46; + + assert_eq!(map.find(&SendStrOwned(~"foo")), Some(&v)); + assert_eq!(map.find(&SendStrStatic("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(SendStrStatic("abc"), a)); + assert!(!map.insert(SendStrOwned(~"bcd"), b)); + assert!(!map.insert(SendStrStatic("cde"), c)); + assert!(!map.insert(SendStrOwned(~"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_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(&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!(map.pop(&SendStrStatic("foo")).is_some()); + assert_eq!(map.move_iter().map(|(k, v)| k.to_str() + v.to_str()) + .to_owned_vec() + .concat(), + ~"abc50bcd51cde52def53"); +}