about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstd/logging.rs7
-rw-r--r--src/libstd/prelude.rs1
-rw-r--r--src/libstd/rt/logging.rs15
-rw-r--r--src/libstd/send_str.rs223
-rw-r--r--src/libstd/std.rs1
-rw-r--r--src/libstd/str.rs17
-rw-r--r--src/libstd/sys.rs5
-rw-r--r--src/test/run-pass/send_str_hashmap.rs80
-rw-r--r--src/test/run-pass/send_str_treemap.rs73
9 files changed, 406 insertions, 16 deletions
diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs
index db5edc2009e..b39b3102a34 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 818b0562955..1672f0a902e 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 54084bb14c0..fbe05267cf4 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 00000000000..1b7d0a40e1c
--- /dev/null
+++ b/src/libstd/send_str.rs
@@ -0,0 +1,223 @@
+// 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 <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.
+
+//! `SendStr` definition and trait implementations
+
+use clone::{Clone, DeepClone};
+use cmp::{Eq, TotalEq, Ord, TotalOrd, Equiv};
+use cmp::{Ordering, Less};
+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.cmp(other) == Less }
+}
+
+impl TotalOrd for SendStr {
+    #[inline]
+    fn cmp(&self, other: &SendStr) -> Ordering {
+        self.as_slice().cmp(&other.as_slice())
+    }
+}
+
+impl<'self, 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(),
+            // 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() {
+        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());
+        assert!(s.is_static());
+        assert!(!s.is_owned());
+
+        assert_eq!(s.clone(), s.clone());
+        assert_eq!(s.clone().into_owned(), ~"abcde");
+        assert_eq!(s.clone().deep_clone(), s.clone());
+
+        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!(!o.is_static());
+        assert!(o.is_owned());
+
+        assert_eq!(o.clone(), o.clone());
+        assert_eq!(o.clone().into_owned(), ~"abcde");
+        assert_eq!(o.clone().deep_clone(), o.clone());
+
+        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));
+
+        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 d78ea9f0093..05433c47059 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 0f125280c2d..ccb39c605eb 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<char> 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 f7f7fef6fa0..3d35de0f898 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 00000000000..a33cb99682b
--- /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 <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.
+
+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<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(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 00000000000..d3af1360b4b
--- /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 <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.
+
+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 extra::treemap::TreeMap;
+use std::option::Some;
+
+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(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");
+}