2021-08-07 06:51:58 -05:00
|
|
|
|
use std::assert_matches::assert_matches;
|
2019-02-03 01:27:44 -06:00
|
|
|
|
use std::borrow::Cow;
|
2021-01-18 21:14:38 -06:00
|
|
|
|
use std::cell::Cell;
|
2021-07-23 10:40:43 -05:00
|
|
|
|
use std::collections::TryReserveErrorKind::*;
|
2021-01-18 21:14:38 -06:00
|
|
|
|
use std::ops::Bound;
|
2020-09-03 19:00:00 -05:00
|
|
|
|
use std::ops::Bound::*;
|
2021-01-18 21:14:38 -06:00
|
|
|
|
use std::ops::RangeBounds;
|
2020-10-29 05:48:56 -05:00
|
|
|
|
use std::panic;
|
2021-01-18 21:14:38 -06:00
|
|
|
|
use std::str;
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
2019-12-22 16:42:04 -06:00
|
|
|
|
pub trait IntoCow<'a, B: ?Sized>
|
|
|
|
|
where
|
|
|
|
|
B: ToOwned,
|
|
|
|
|
{
|
2016-01-30 18:19:37 -06:00
|
|
|
|
fn into_cow(self) -> Cow<'a, B>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> IntoCow<'a, str> for String {
|
|
|
|
|
fn into_cow(self) -> Cow<'a, str> {
|
|
|
|
|
Cow::Owned(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> IntoCow<'a, str> for &'a str {
|
|
|
|
|
fn into_cow(self) -> Cow<'a, str> {
|
|
|
|
|
Cow::Borrowed(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 23:58:16 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_str() {
|
2019-02-02 03:34:36 -06:00
|
|
|
|
let owned: Option<std::string::String> = "string".parse().ok();
|
2016-05-22 13:27:13 -05:00
|
|
|
|
assert_eq!(owned.as_ref().map(|s| &**s), Some("string"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
Implement `From<Cow<str>> for String` and `From<Cow<[T]>> for Vec<T>`.
Motivation: the `selectors` crate is generic over a string type,
in order to support all of `String`, `string_cache::Atom`, and
`gecko_string_cache::Atom`. Multiple trait bounds are used
for the various operations done with these strings.
One of these operations is creating a string (as efficiently as possible,
re-using an existing memory allocation if possible) from `Cow<str>`.
The `std::convert::From` trait seems natural for this, but
the relevant implementation was missing before this PR.
To work around this I’ve added a `FromCowStr` trait in `selectors`,
but with trait coherence that means one of `selectors` or `string_cache`
needs to depend on the other to implement this trait.
Using a trait from `std` would solve this.
The `Vec<T>` implementation is just added for consistency.
I also tried a more general
`impl<'a, O, B: ?Sized + ToOwned<Owned=O>> From<Cow<'a, B>> for O`,
but (the compiler thinks?) it conflicts with `From<T> for T` the impl
(after moving all of `collections::borrow` into `core::borrow`
to work around trait coherence).
2016-10-21 09:51:59 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_cow_str() {
|
|
|
|
|
assert_eq!(String::from(Cow::Borrowed("string")), "string");
|
|
|
|
|
assert_eq!(String::from(Cow::Owned(String::from("string"))), "string");
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 23:58:16 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_unsized_to_string() {
|
|
|
|
|
let s: &str = "abc";
|
|
|
|
|
let _: String = (*s).to_string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_utf8() {
|
|
|
|
|
let xs = b"hello".to_vec();
|
2016-05-22 13:27:13 -05:00
|
|
|
|
assert_eq!(String::from_utf8(xs).unwrap(), String::from("hello"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = "ศไทย中华Việt Nam".as_bytes().to_vec();
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(String::from_utf8(xs).unwrap(), String::from("ศไทย中华Việt Nam"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"hello\xFF".to_vec();
|
2016-05-06 18:32:18 -05:00
|
|
|
|
let err = String::from_utf8(xs).unwrap_err();
|
2020-02-01 12:29:28 -06:00
|
|
|
|
assert_eq!(err.as_bytes(), b"hello\xff");
|
|
|
|
|
let err_clone = err.clone();
|
|
|
|
|
assert_eq!(err, err_clone);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(err.into_bytes(), b"hello\xff".to_vec());
|
2020-02-01 12:29:28 -06:00
|
|
|
|
assert_eq!(err_clone.utf8_error().valid_up_to(), 5);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_utf8_lossy() {
|
|
|
|
|
let xs = b"hello";
|
2019-04-20 09:05:25 -05:00
|
|
|
|
let ys: Cow<'_, str> = "hello".into_cow();
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(String::from_utf8_lossy(xs), ys);
|
|
|
|
|
|
|
|
|
|
let xs = "ศไทย中华Việt Nam".as_bytes();
|
2019-04-20 09:05:25 -05:00
|
|
|
|
let ys: Cow<'_, str> = "ศไทย中华Việt Nam".into_cow();
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(String::from_utf8_lossy(xs), ys);
|
|
|
|
|
|
|
|
|
|
let xs = b"Hello\xC2 There\xFF Goodbye";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("Hello\u{FFFD} There\u{FFFD} Goodbye").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"Hello\xC0\x80 There\xE6\x83 Goodbye";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"\xF5foo\xF5\x80bar";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("\u{FFFD}foo\u{FFFD}\u{FFFD}bar").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"\xF1foo\xF1\x80bar\xF1\x80\x80baz";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}baz").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"\xF4foo\xF4\x80bar\xF4\xBFbaz";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}\u{FFFD}baz").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
let xs = b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}foo\u{10000}bar").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
// surrogates
|
|
|
|
|
let xs = b"\xED\xA0\x80foo\xED\xBF\xBFbar";
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf8_lossy(xs),
|
|
|
|
|
String::from("\u{FFFD}\u{FFFD}\u{FFFD}foo\u{FFFD}\u{FFFD}\u{FFFD}bar").into_cow()
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_utf16() {
|
2019-12-22 16:42:04 -06:00
|
|
|
|
let pairs = [
|
|
|
|
|
(
|
|
|
|
|
String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"),
|
|
|
|
|
vec![
|
|
|
|
|
0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39,
|
|
|
|
|
0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"),
|
|
|
|
|
vec![
|
|
|
|
|
0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32,
|
|
|
|
|
0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"),
|
|
|
|
|
vec![
|
|
|
|
|
0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11,
|
|
|
|
|
0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800,
|
|
|
|
|
0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"),
|
|
|
|
|
vec![
|
|
|
|
|
0xd801, 0xdc8b, 0xd801, 0xdc98, 0xd801, 0xdc88, 0xd801, 0xdc91, 0xd801, 0xdc9b,
|
|
|
|
|
0xd801, 0xdc92, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc93, 0x0020, 0xd801, 0xdc88,
|
|
|
|
|
0xd801, 0xdc9a, 0xd801, 0xdc8d, 0x0020, 0xd801, 0xdc8f, 0xd801, 0xdc9c, 0xd801,
|
|
|
|
|
0xdc92, 0xd801, 0xdc96, 0xd801, 0xdc86, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc86,
|
|
|
|
|
0x000a,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
// Issue #12318, even-numbered non-BMP planes
|
|
|
|
|
(String::from("\u{20000}"), vec![0xD840, 0xDC00]),
|
|
|
|
|
];
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
for p in &pairs {
|
|
|
|
|
let (s, u) = (*p).clone();
|
std: Stabilize APIs for the 1.8 release
This commit is the result of the FCPs ending for the 1.8 release cycle for both
the libs and the lang suteams. The full list of changes are:
Stabilized
* `braced_empty_structs`
* `augmented_assignments`
* `str::encode_utf16` - renamed from `utf16_units`
* `str::EncodeUtf16` - renamed from `Utf16Units`
* `Ref::map`
* `RefMut::map`
* `ptr::drop_in_place`
* `time::Instant`
* `time::SystemTime`
* `{Instant,SystemTime}::now`
* `{Instant,SystemTime}::duration_since` - renamed from `duration_from_earlier`
* `{Instant,SystemTime}::elapsed`
* Various `Add`/`Sub` impls for `Time` and `SystemTime`
* `SystemTimeError`
* `SystemTimeError::duration`
* Various impls for `SystemTimeError`
* `UNIX_EPOCH`
* `ops::{Add,Sub,Mul,Div,Rem,BitAnd,BitOr,BitXor,Shl,Shr}Assign`
Deprecated
* Scoped TLS (the `scoped_thread_local!` macro)
* `Ref::filter_map`
* `RefMut::filter_map`
* `RwLockReadGuard::map`
* `RwLockWriteGuard::map`
* `Condvar::wait_timeout_with`
Closes #27714
Closes #27715
Closes #27746
Closes #27748
Closes #27908
Closes #29866
2016-02-25 17:52:29 -06:00
|
|
|
|
let s_as_utf16 = s.encode_utf16().collect::<Vec<u16>>();
|
2015-03-10 23:58:16 -05:00
|
|
|
|
let u_as_string = String::from_utf16(&u).unwrap();
|
|
|
|
|
|
2019-02-02 03:34:36 -06:00
|
|
|
|
assert!(core::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok()));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(s_as_utf16, u);
|
|
|
|
|
|
|
|
|
|
assert_eq!(u_as_string, s);
|
|
|
|
|
assert_eq!(String::from_utf16_lossy(&u), s);
|
|
|
|
|
|
|
|
|
|
assert_eq!(String::from_utf16(&s_as_utf16).unwrap(), s);
|
std: Stabilize APIs for the 1.8 release
This commit is the result of the FCPs ending for the 1.8 release cycle for both
the libs and the lang suteams. The full list of changes are:
Stabilized
* `braced_empty_structs`
* `augmented_assignments`
* `str::encode_utf16` - renamed from `utf16_units`
* `str::EncodeUtf16` - renamed from `Utf16Units`
* `Ref::map`
* `RefMut::map`
* `ptr::drop_in_place`
* `time::Instant`
* `time::SystemTime`
* `{Instant,SystemTime}::now`
* `{Instant,SystemTime}::duration_since` - renamed from `duration_from_earlier`
* `{Instant,SystemTime}::elapsed`
* Various `Add`/`Sub` impls for `Time` and `SystemTime`
* `SystemTimeError`
* `SystemTimeError::duration`
* Various impls for `SystemTimeError`
* `UNIX_EPOCH`
* `ops::{Add,Sub,Mul,Div,Rem,BitAnd,BitOr,BitXor,Shl,Shr}Assign`
Deprecated
* Scoped TLS (the `scoped_thread_local!` macro)
* `Ref::filter_map`
* `RefMut::filter_map`
* `RwLockReadGuard::map`
* `RwLockWriteGuard::map`
* `Condvar::wait_timeout_with`
Closes #27714
Closes #27715
Closes #27746
Closes #27748
Closes #27908
Closes #29866
2016-02-25 17:52:29 -06:00
|
|
|
|
assert_eq!(u_as_string.encode_utf16().collect::<Vec<u16>>(), u);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_utf16_invalid() {
|
|
|
|
|
// completely positive cases tested above.
|
|
|
|
|
// lead + eof
|
|
|
|
|
assert!(String::from_utf16(&[0xD800]).is_err());
|
|
|
|
|
// lead + lead
|
|
|
|
|
assert!(String::from_utf16(&[0xD800, 0xD800]).is_err());
|
|
|
|
|
|
|
|
|
|
// isolated trail
|
|
|
|
|
assert!(String::from_utf16(&[0x0061, 0xDC00]).is_err());
|
|
|
|
|
|
|
|
|
|
// general
|
|
|
|
|
assert!(String::from_utf16(&[0xD800, 0xd801, 0xdc8b, 0xD800]).is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_utf16_lossy() {
|
|
|
|
|
// completely positive cases tested above.
|
|
|
|
|
// lead + eof
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(String::from_utf16_lossy(&[0xD800]), String::from("\u{FFFD}"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
// lead + lead
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(String::from_utf16_lossy(&[0xD800, 0xD800]), String::from("\u{FFFD}\u{FFFD}"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
// isolated trail
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(String::from_utf16_lossy(&[0x0061, 0xDC00]), String::from("a\u{FFFD}"));
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
// general
|
2019-12-22 16:42:04 -06:00
|
|
|
|
assert_eq!(
|
|
|
|
|
String::from_utf16_lossy(&[0xD800, 0xd801, 0xdc8b, 0xD800]),
|
|
|
|
|
String::from("\u{FFFD}𐒋\u{FFFD}")
|
|
|
|
|
);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_push_bytes() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("ABC");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
unsafe {
|
|
|
|
|
let mv = s.as_mut_vec();
|
2016-01-30 18:19:37 -06:00
|
|
|
|
mv.extend_from_slice(&[b'D']);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
assert_eq!(s, "ABCD");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_push_str() {
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
s.push_str("");
|
|
|
|
|
assert_eq!(&s[0..], "");
|
|
|
|
|
s.push_str("abc");
|
|
|
|
|
assert_eq!(&s[0..], "abc");
|
|
|
|
|
s.push_str("ประเทศไทย中华Việt Nam");
|
|
|
|
|
assert_eq!(&s[0..], "abcประเทศไทย中华Việt Nam");
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-26 13:30:50 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_add_assign() {
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
s += "";
|
|
|
|
|
assert_eq!(s.as_str(), "");
|
|
|
|
|
s += "abc";
|
|
|
|
|
assert_eq!(s.as_str(), "abc");
|
|
|
|
|
s += "ประเทศไทย中华Việt Nam";
|
|
|
|
|
assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam");
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 23:58:16 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_push() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut data = String::from("ประเทศไทย中");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
data.push('华');
|
|
|
|
|
data.push('b'); // 1 byte
|
|
|
|
|
data.push('¢'); // 2 byte
|
|
|
|
|
data.push('€'); // 3 byte
|
|
|
|
|
data.push('𤭢'); // 4 byte
|
|
|
|
|
assert_eq!(data, "ประเทศไทย中华b¢€𤭢");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_pop() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut data = String::from("ประเทศไทย中华b¢€𤭢");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(data.pop().unwrap(), '𤭢'); // 4 bytes
|
|
|
|
|
assert_eq!(data.pop().unwrap(), '€'); // 3 bytes
|
|
|
|
|
assert_eq!(data.pop().unwrap(), '¢'); // 2 bytes
|
|
|
|
|
assert_eq!(data.pop().unwrap(), 'b'); // 1 bytes
|
|
|
|
|
assert_eq!(data.pop().unwrap(), '华');
|
|
|
|
|
assert_eq!(data, "ประเทศไทย中");
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 12:54:55 -06:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_off_empty() {
|
|
|
|
|
let orig = "Hello, world!";
|
|
|
|
|
let mut split = String::from(orig);
|
|
|
|
|
let empty: String = split.split_off(orig.len());
|
|
|
|
|
assert!(empty.is_empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_split_off_past_end() {
|
|
|
|
|
let orig = "Hello, world!";
|
|
|
|
|
let mut split = String::from(orig);
|
2020-03-24 08:33:35 -05:00
|
|
|
|
let _ = split.split_off(orig.len() + 1);
|
2016-11-28 12:54:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_split_off_mid_char() {
|
2020-08-24 09:47:15 -05:00
|
|
|
|
let mut shan = String::from("山");
|
|
|
|
|
let _broken_mountain = shan.split_off(1);
|
2016-11-28 12:54:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_off_ascii() {
|
|
|
|
|
let mut ab = String::from("ABCD");
|
Optimize behavior of vec.split_off(0) (take all)
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current
behavior of copying the entire vector.
The change honors documented behavior that the method leaves the
original vector's "previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly applying `mem::replace()` could work, but `mem::` functions are
typically a last resort, when a developer is actively seeking better
performance than the standard library provides, for example.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
2020-09-13 13:58:43 -05:00
|
|
|
|
let orig_capacity = ab.capacity();
|
2016-11-28 12:54:55 -06:00
|
|
|
|
let cd = ab.split_off(2);
|
|
|
|
|
assert_eq!(ab, "AB");
|
|
|
|
|
assert_eq!(cd, "CD");
|
Optimize behavior of vec.split_off(0) (take all)
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current
behavior of copying the entire vector.
The change honors documented behavior that the method leaves the
original vector's "previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly applying `mem::replace()` could work, but `mem::` functions are
typically a last resort, when a developer is actively seeking better
performance than the standard library provides, for example.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
2020-09-13 13:58:43 -05:00
|
|
|
|
assert_eq!(ab.capacity(), orig_capacity);
|
2016-11-28 12:54:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_off_unicode() {
|
|
|
|
|
let mut nihon = String::from("日本語");
|
Optimize behavior of vec.split_off(0) (take all)
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current
behavior of copying the entire vector.
The change honors documented behavior that the method leaves the
original vector's "previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly applying `mem::replace()` could work, but `mem::` functions are
typically a last resort, when a developer is actively seeking better
performance than the standard library provides, for example.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
2020-09-13 13:58:43 -05:00
|
|
|
|
let orig_capacity = nihon.capacity();
|
2016-11-28 12:54:55 -06:00
|
|
|
|
let go = nihon.split_off("日本".len());
|
|
|
|
|
assert_eq!(nihon, "日本");
|
|
|
|
|
assert_eq!(go, "語");
|
Optimize behavior of vec.split_off(0) (take all)
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current
behavior of copying the entire vector.
The change honors documented behavior that the method leaves the
original vector's "previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly applying `mem::replace()` could work, but `mem::` functions are
typically a last resort, when a developer is actively seeking better
performance than the standard library provides, for example.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
2020-09-13 13:58:43 -05:00
|
|
|
|
assert_eq!(nihon.capacity(), orig_capacity);
|
2016-11-28 12:54:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 23:58:16 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_str_truncate() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
s.truncate(5);
|
|
|
|
|
assert_eq!(s, "12345");
|
|
|
|
|
s.truncate(3);
|
|
|
|
|
assert_eq!(s, "123");
|
|
|
|
|
s.truncate(0);
|
|
|
|
|
assert_eq!(s, "");
|
|
|
|
|
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
let p = s.as_ptr();
|
|
|
|
|
s.truncate(3);
|
|
|
|
|
s.push_str("6");
|
|
|
|
|
let p_ = s.as_ptr();
|
|
|
|
|
assert_eq!(p_, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_str_truncate_invalid_len() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
s.truncate(6);
|
2016-04-14 18:56:59 -05:00
|
|
|
|
assert_eq!(s, "12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_str_truncate_split_codepoint() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("\u{FC}"); // ü
|
2015-03-10 23:58:16 -05:00
|
|
|
|
s.truncate(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_str_clear() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
s.clear();
|
|
|
|
|
assert_eq!(s.len(), 0);
|
|
|
|
|
assert_eq!(s, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_str_add() {
|
2015-06-08 09:55:35 -05:00
|
|
|
|
let a = String::from("12345");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
let b = a + "2";
|
|
|
|
|
let b = b + "2";
|
|
|
|
|
assert_eq!(b.len(), 7);
|
|
|
|
|
assert_eq!(b, "1234522");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn remove() {
|
2015-09-27 21:26:12 -05:00
|
|
|
|
let mut s = "ศไทย中华Việt Nam; foobar".to_string();
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(s.remove(0), 'ศ');
|
|
|
|
|
assert_eq!(s.len(), 33);
|
|
|
|
|
assert_eq!(s, "ไทย中华Việt Nam; foobar");
|
|
|
|
|
assert_eq!(s.remove(17), 'ệ');
|
|
|
|
|
assert_eq!(s, "ไทย中华Vit Nam; foobar");
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-22 13:27:13 -05:00
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
2015-03-10 23:58:16 -05:00
|
|
|
|
fn remove_bad() {
|
|
|
|
|
"ศ".to_string().remove(1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-05 10:27:58 -06:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_remove_matches() {
|
|
|
|
|
let mut s = "abc".to_string();
|
|
|
|
|
|
|
|
|
|
s.remove_matches('b');
|
|
|
|
|
assert_eq!(s, "ac");
|
|
|
|
|
s.remove_matches('b');
|
|
|
|
|
assert_eq!(s, "ac");
|
|
|
|
|
|
|
|
|
|
let mut s = "abcb".to_string();
|
|
|
|
|
|
|
|
|
|
s.remove_matches('b');
|
|
|
|
|
assert_eq!(s, "ac");
|
|
|
|
|
|
|
|
|
|
let mut s = "ศไทย中华Việt Nam; foobarศ".to_string();
|
|
|
|
|
s.remove_matches('ศ');
|
|
|
|
|
assert_eq!(s, "ไทย中华Việt Nam; foobar");
|
|
|
|
|
|
|
|
|
|
let mut s = "".to_string();
|
|
|
|
|
s.remove_matches("");
|
|
|
|
|
assert_eq!(s, "");
|
|
|
|
|
|
|
|
|
|
let mut s = "aaaaa".to_string();
|
|
|
|
|
s.remove_matches('a');
|
|
|
|
|
assert_eq!(s, "");
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 17:09:32 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_retain() {
|
|
|
|
|
let mut s = String::from("α_β_γ");
|
|
|
|
|
|
|
|
|
|
s.retain(|_| true);
|
|
|
|
|
assert_eq!(s, "α_β_γ");
|
|
|
|
|
|
|
|
|
|
s.retain(|c| c != '_');
|
|
|
|
|
assert_eq!(s, "αβγ");
|
|
|
|
|
|
|
|
|
|
s.retain(|c| c != 'β');
|
|
|
|
|
assert_eq!(s, "αγ");
|
|
|
|
|
|
|
|
|
|
s.retain(|c| c == 'α');
|
|
|
|
|
assert_eq!(s, "α");
|
|
|
|
|
|
|
|
|
|
s.retain(|_| false);
|
|
|
|
|
assert_eq!(s, "");
|
2020-10-29 05:48:56 -05:00
|
|
|
|
|
|
|
|
|
let mut s = String::from("0è0");
|
|
|
|
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
s.retain(|_| {
|
|
|
|
|
count += 1;
|
|
|
|
|
match count {
|
|
|
|
|
1 => false,
|
|
|
|
|
2 => true,
|
|
|
|
|
_ => panic!(),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
assert!(std::str::from_utf8(s.as_bytes()).is_ok());
|
2017-07-26 17:09:32 -05:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 23:58:16 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn insert() {
|
|
|
|
|
let mut s = "foobar".to_string();
|
|
|
|
|
s.insert(0, 'ệ');
|
|
|
|
|
assert_eq!(s, "ệfoobar");
|
|
|
|
|
s.insert(6, 'ย');
|
|
|
|
|
assert_eq!(s, "ệfooยbar");
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-22 13:27:13 -05:00
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn insert_bad1() {
|
|
|
|
|
"".to_string().insert(1, 't');
|
|
|
|
|
}
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn insert_bad2() {
|
|
|
|
|
"ệ".to_string().insert(1, 't');
|
|
|
|
|
}
|
2015-03-10 23:58:16 -05:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_slicing() {
|
|
|
|
|
let s = "foobar".to_string();
|
|
|
|
|
assert_eq!("foobar", &s[..]);
|
|
|
|
|
assert_eq!("foo", &s[..3]);
|
|
|
|
|
assert_eq!("bar", &s[3..]);
|
|
|
|
|
assert_eq!("oob", &s[1..4]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_simple_types() {
|
|
|
|
|
assert_eq!(1.to_string(), "1");
|
|
|
|
|
assert_eq!((-1).to_string(), "-1");
|
|
|
|
|
assert_eq!(200.to_string(), "200");
|
|
|
|
|
assert_eq!(2.to_string(), "2");
|
|
|
|
|
assert_eq!(true.to_string(), "true");
|
|
|
|
|
assert_eq!(false.to_string(), "false");
|
|
|
|
|
assert_eq!(("hi".to_string()).to_string(), "hi");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_vectors() {
|
|
|
|
|
let x: Vec<i32> = vec![];
|
2022-02-12 13:16:17 -06:00
|
|
|
|
assert_eq!(format!("{x:?}"), "[]");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(format!("{:?}", vec![1]), "[1]");
|
|
|
|
|
assert_eq!(format!("{:?}", vec![1, 2, 3]), "[1, 2, 3]");
|
2016-05-22 13:27:13 -05:00
|
|
|
|
assert!(format!("{:?}", vec![vec![], vec![1], vec![1, 1]]) == "[[], [1], [1, 1]]");
|
2015-03-10 23:58:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_iterator() {
|
|
|
|
|
let s = "ศไทย中华Việt Nam".to_string();
|
|
|
|
|
let t = "ศไทย中华";
|
|
|
|
|
let u = "Việt Nam";
|
|
|
|
|
|
|
|
|
|
let a: String = s.chars().collect();
|
|
|
|
|
assert_eq!(s, a);
|
|
|
|
|
|
|
|
|
|
let mut b = t.to_string();
|
|
|
|
|
b.extend(u.chars());
|
|
|
|
|
assert_eq!(s, b);
|
|
|
|
|
|
2021-12-17 01:36:18 -06:00
|
|
|
|
let c: String = [t, u].into_iter().collect();
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(s, c);
|
|
|
|
|
|
|
|
|
|
let mut d = t.to_string();
|
2015-06-10 11:22:20 -05:00
|
|
|
|
d.extend(vec![u]);
|
2015-03-10 23:58:16 -05:00
|
|
|
|
assert_eq!(s, d);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-01 08:34:25 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_drain() {
|
|
|
|
|
let mut s = String::from("αβγ");
|
|
|
|
|
assert_eq!(s.drain(2..4).collect::<String>(), "β");
|
|
|
|
|
assert_eq!(s, "αγ");
|
|
|
|
|
|
|
|
|
|
let mut t = String::from("abcd");
|
|
|
|
|
t.drain(..0);
|
|
|
|
|
assert_eq!(t, "abcd");
|
|
|
|
|
t.drain(..1);
|
|
|
|
|
assert_eq!(t, "bcd");
|
|
|
|
|
t.drain(3..);
|
|
|
|
|
assert_eq!(t, "bcd");
|
|
|
|
|
t.drain(..);
|
|
|
|
|
assert_eq!(t, "");
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 19:00:00 -05:00
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_drain_start_overflow() {
|
|
|
|
|
let mut s = String::from("abc");
|
|
|
|
|
s.drain((Excluded(usize::MAX), Included(0)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_drain_end_overflow() {
|
|
|
|
|
let mut s = String::from("abc");
|
|
|
|
|
s.drain((Included(0), Included(usize::MAX)));
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-08 15:55:53 -05:00
|
|
|
|
#[test]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range() {
|
2017-04-08 15:55:53 -05:00
|
|
|
|
let mut s = "Hello, world!".to_owned();
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(7..12, "世界");
|
2017-04-08 15:55:53 -05:00
|
|
|
|
assert_eq!(s, "Hello, 世界!");
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-08 16:04:30 -05:00
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_char_boundary() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut s = "Hello, 世界!".to_owned();
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(..8, "");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_inclusive_range() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut v = String::from("12345");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
v.replace_range(2..=3, "789");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
assert_eq!(v, "127895");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
v.replace_range(1..=2, "A");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
assert_eq!(v, "1A895");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_out_of_bounds() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(5..6, "789");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_inclusive_out_of_bounds() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(5..=5, "789");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-03 19:00:00 -05:00
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_replace_range_start_overflow() {
|
|
|
|
|
let mut s = String::from("123");
|
|
|
|
|
s.replace_range((Excluded(usize::MAX), Included(0)), "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[should_panic]
|
|
|
|
|
fn test_replace_range_end_overflow() {
|
|
|
|
|
let mut s = String::from("456");
|
|
|
|
|
s.replace_range((Included(0), Included(usize::MAX)), "");
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-08 16:04:30 -05:00
|
|
|
|
#[test]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_empty() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(1..2, "");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
assert_eq!(s, "1345");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2018-04-01 23:50:22 -05:00
|
|
|
|
fn test_replace_range_unbounded() {
|
2017-04-08 16:04:30 -05:00
|
|
|
|
let mut s = String::from("12345");
|
2018-04-01 23:50:22 -05:00
|
|
|
|
s.replace_range(.., "");
|
2017-04-08 16:04:30 -05:00
|
|
|
|
assert_eq!(s, "");
|
2017-04-24 09:49:29 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-18 21:14:38 -06:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_replace_range_evil_start_bound() {
|
|
|
|
|
struct EvilRange(Cell<bool>);
|
|
|
|
|
|
|
|
|
|
impl RangeBounds<usize> for EvilRange {
|
|
|
|
|
fn start_bound(&self) -> Bound<&usize> {
|
|
|
|
|
Bound::Included(if self.0.get() {
|
|
|
|
|
&1
|
|
|
|
|
} else {
|
|
|
|
|
self.0.set(true);
|
|
|
|
|
&0
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
fn end_bound(&self) -> Bound<&usize> {
|
|
|
|
|
Bound::Unbounded
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut s = String::from("🦀");
|
|
|
|
|
s.replace_range(EvilRange(Cell::new(false)), "");
|
|
|
|
|
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_replace_range_evil_end_bound() {
|
|
|
|
|
struct EvilRange(Cell<bool>);
|
|
|
|
|
|
|
|
|
|
impl RangeBounds<usize> for EvilRange {
|
|
|
|
|
fn start_bound(&self) -> Bound<&usize> {
|
|
|
|
|
Bound::Included(&0)
|
|
|
|
|
}
|
|
|
|
|
fn end_bound(&self) -> Bound<&usize> {
|
|
|
|
|
Bound::Excluded(if self.0.get() {
|
|
|
|
|
&3
|
|
|
|
|
} else {
|
|
|
|
|
self.0.set(true);
|
|
|
|
|
&4
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut s = String::from("🦀");
|
|
|
|
|
s.replace_range(EvilRange(Cell::new(false)), "");
|
|
|
|
|
assert_eq!(Ok(""), str::from_utf8(s.as_bytes()));
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-03 05:38:42 -05:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_extend_ref() {
|
|
|
|
|
let mut a = "foo".to_string();
|
|
|
|
|
a.extend(&['b', 'a', 'r']);
|
|
|
|
|
|
|
|
|
|
assert_eq!(&a, "foobar");
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-10 02:04:06 -05:00
|
|
|
|
#[test]
|
2015-08-13 07:02:00 -05:00
|
|
|
|
fn test_into_boxed_str() {
|
2015-07-10 02:04:06 -05:00
|
|
|
|
let xs = String::from("hello my name is bob");
|
2015-08-13 07:02:00 -05:00
|
|
|
|
let ys = xs.into_boxed_str();
|
2015-07-10 02:04:06 -05:00
|
|
|
|
assert_eq!(&*ys, "hello my name is bob");
|
|
|
|
|
}
|
2018-03-08 08:36:43 -06:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_reserve_exact() {
|
|
|
|
|
// This is all the same as test_reserve
|
|
|
|
|
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
assert_eq!(s.capacity(), 0);
|
|
|
|
|
|
|
|
|
|
s.reserve_exact(2);
|
|
|
|
|
assert!(s.capacity() >= 2);
|
|
|
|
|
|
|
|
|
|
for _i in 0..16 {
|
|
|
|
|
s.push('0');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert!(s.capacity() >= 16);
|
|
|
|
|
s.reserve_exact(16);
|
|
|
|
|
assert!(s.capacity() >= 32);
|
|
|
|
|
|
|
|
|
|
s.push('0');
|
|
|
|
|
|
|
|
|
|
s.reserve_exact(16);
|
|
|
|
|
assert!(s.capacity() >= 33)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2019-12-07 05:42:19 -06:00
|
|
|
|
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
2020-04-09 09:55:12 -05:00
|
|
|
|
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
2018-03-08 08:36:43 -06:00
|
|
|
|
fn test_try_reserve() {
|
|
|
|
|
// These are the interesting cases:
|
|
|
|
|
// * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM)
|
|
|
|
|
// * > isize::MAX should always fail
|
|
|
|
|
// * On 16/32-bit should CapacityOverflow
|
|
|
|
|
// * On 64-bit should OOM
|
|
|
|
|
// * overflow may trigger when adding `len` to `cap` (in number of elements)
|
|
|
|
|
// * overflow may trigger when multiplying `new_cap` by size_of::<T> (to get bytes)
|
|
|
|
|
|
|
|
|
|
const MAX_CAP: usize = isize::MAX as usize;
|
|
|
|
|
const MAX_USIZE: usize = usize::MAX;
|
|
|
|
|
|
|
|
|
|
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
|
|
|
|
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
|
|
|
|
// Any platform that succeeds for these requests is technically broken with
|
|
|
|
|
// ptr::offset because LLVM is the worst.
|
2020-09-08 14:39:13 -05:00
|
|
|
|
let guards_against_isize = usize::BITS < 64;
|
2018-03-08 08:36:43 -06:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Note: basic stuff is checked by test_reserve
|
|
|
|
|
let mut empty_string: String = String::new();
|
|
|
|
|
|
|
|
|
|
// Check isize::MAX doesn't count as an overflow
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP).map_err(|e| e.kind()) {
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
|
|
|
|
// Play it again, frank! (just to be sure)
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP).map_err(|e| e.kind()) {
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if guards_against_isize {
|
|
|
|
|
// Check isize::MAX + 1 does count as overflow
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"isize::MAX + 1 should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
|
|
|
|
|
// Check usize::MAX does count as overflow
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"usize::MAX should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
} else {
|
|
|
|
|
// Check isize::MAX + 1 is an OOM
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"isize::MAX + 1 should trigger an OOM!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
|
|
|
|
|
// Check usize::MAX is an OOM
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"usize::MAX should trigger an OOM!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Same basic idea, but with non-zero len
|
|
|
|
|
let mut ten_bytes: String = String::from("0123456789");
|
|
|
|
|
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
|
|
|
|
if guards_against_isize {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"isize::MAX + 1 should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
} else {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"isize::MAX + 1 should trigger an OOM!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
}
|
|
|
|
|
// Should always overflow in the add-to-len
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"usize::MAX should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2019-12-07 05:42:19 -06:00
|
|
|
|
#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM
|
2020-04-09 09:55:12 -05:00
|
|
|
|
#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc
|
2018-03-08 08:36:43 -06:00
|
|
|
|
fn test_try_reserve_exact() {
|
|
|
|
|
// This is exactly the same as test_try_reserve with the method changed.
|
|
|
|
|
// See that test for comments.
|
|
|
|
|
|
|
|
|
|
const MAX_CAP: usize = isize::MAX as usize;
|
|
|
|
|
const MAX_USIZE: usize = usize::MAX;
|
|
|
|
|
|
2020-09-08 14:39:13 -05:00
|
|
|
|
let guards_against_isize = usize::BITS < 64;
|
2018-03-08 08:36:43 -06:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut empty_string: String = String::new();
|
|
|
|
|
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP).map_err(|e| e.kind())
|
|
|
|
|
{
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP).map_err(|e| e.kind())
|
|
|
|
|
{
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if guards_against_isize {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"isize::MAX + 1 should trigger an overflow!"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"usize::MAX should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
} else {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"isize::MAX + 1 should trigger an OOM!"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
|
empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"usize::MAX should trigger an OOM!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut ten_bytes: String = String::from("0123456789");
|
|
|
|
|
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) =
|
|
|
|
|
ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind())
|
|
|
|
|
{
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
2021-07-23 10:40:43 -05:00
|
|
|
|
if let Err(CapacityOverflow) =
|
|
|
|
|
ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind())
|
|
|
|
|
{
|
2018-03-08 08:36:43 -06:00
|
|
|
|
panic!("isize::MAX shouldn't trigger an overflow!");
|
|
|
|
|
}
|
|
|
|
|
if guards_against_isize {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"isize::MAX + 1 should trigger an overflow!"
|
|
|
|
|
);
|
2019-12-22 16:42:04 -06:00
|
|
|
|
} else {
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
|
|
|
|
Err(AllocError { .. }),
|
|
|
|
|
"isize::MAX + 1 should trigger an OOM!"
|
|
|
|
|
);
|
2019-12-22 16:42:04 -06:00
|
|
|
|
}
|
2021-08-07 06:51:58 -05:00
|
|
|
|
assert_matches!(
|
|
|
|
|
ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
|
|
|
|
Err(CapacityOverflow),
|
|
|
|
|
"usize::MAX should trigger an overflow!"
|
|
|
|
|
);
|
2018-03-08 08:36:43 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-18 04:19:03 -05:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_from_char() {
|
|
|
|
|
assert_eq!(String::from('a'), 'a'.to_string());
|
|
|
|
|
let s: String = 'x'.into();
|
|
|
|
|
assert_eq!(s, 'x'.to_string());
|
|
|
|
|
}
|
2020-09-05 22:53:40 -05:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_str_concat() {
|
|
|
|
|
let a: String = "hello".to_string();
|
|
|
|
|
let b: String = "world".to_string();
|
2022-02-12 13:16:17 -06:00
|
|
|
|
let s: String = format!("{a}{b}");
|
2020-09-05 22:53:40 -05:00
|
|
|
|
assert_eq!(s.as_bytes()[9], 'd' as u8);
|
|
|
|
|
}
|