Skip to content

Commit

Permalink
Move into_ascii_{low,upp}ercase (back) to a separate trait.
Browse files Browse the repository at this point in the history
Implementing `AsciiExt` for `String` and `Vec<u8>` caused a regression:
rust-lang#32074
and the `where Self: Sized` hack to have the `into_` methods in that trait
(which is also implemented for DST `str` and `[u8]`) was rather clunky.

CC rust-lang#27809
  • Loading branch information
SimonSapin committed Mar 6, 2016
1 parent c116ae3 commit f1d3c6d
Showing 1 changed file with 53 additions and 102 deletions.
155 changes: 53 additions & 102 deletions src/libstd/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,108 +160,6 @@ pub trait AsciiExt {
/// ```
#[unstable(feature = "ascii", issue = "27809")]
fn make_ascii_lowercase(&mut self);

/// Converts this type to its ASCII upper case,
/// consuming the value to avoid allocating memory where `to_ascii_uppercase` would.
///
/// See `to_ascii_uppercase` for more information.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii: String = "a".to_owned();
///
/// let upper = ascii.into_ascii_uppercase();
///
/// assert_eq!(upper, "A");
/// ```
#[stable(feature = "into_ascii", since = "1.8.0")]
fn into_ascii_uppercase(self) -> Self::Owned where Self: Sized {
self.to_ascii_uppercase()
}

/// Converts this type to its ASCII lower case,
/// consuming the value to avoid allocating memory where `to_ascii_lowercase` would.
///
/// See `to_ascii_lowercase` for more information.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii: String = "A".to_owned();
///
/// let lower = ascii.into_ascii_lowercase();
///
/// assert_eq!(lower, "a");
/// ```
#[stable(feature = "into_ascii", since = "1.8.0")]
fn into_ascii_lowercase(self) -> Self::Owned where Self: Sized {
self.to_ascii_lowercase()
}
}

/// Implement `into_ascii_lowercase` and `into_ascii_uppercase` without memory allocation,
/// defer other methods to `str`.
#[stable(feature = "into_ascii", since = "1.8.0")]
impl AsciiExt for String {
type Owned = Self;

#[inline] fn is_ascii(&self) -> bool { (**self).is_ascii() }
#[inline] fn to_ascii_uppercase(&self) -> Self { (**self).to_ascii_uppercase() }
#[inline] fn to_ascii_lowercase(&self) -> Self { (**self).to_ascii_lowercase() }
#[inline] fn eq_ignore_ascii_case(&self, o: &Self) -> bool { (**self).eq_ignore_ascii_case(o) }
#[inline] fn make_ascii_uppercase(&mut self) { (**self).make_ascii_uppercase() }
#[inline] fn make_ascii_lowercase(&mut self) { (**self).make_ascii_lowercase() }

fn into_ascii_lowercase(mut self) -> Self {
unsafe {
for byte in self.as_mut_vec() {
*byte = byte.to_ascii_lowercase()
}
}
self
}

fn into_ascii_uppercase(mut self) -> Self {
unsafe {
for byte in self.as_mut_vec() {
*byte = byte.to_ascii_uppercase()
}
}
self
}
}

/// Implement `into_ascii_lowercase` and `into_ascii_uppercase` without memory allocation,
/// defer other methods to `[u8]`.
#[stable(feature = "into_ascii", since = "1.8.0")]
impl AsciiExt for Vec<u8> {
type Owned = Self;

#[inline] fn is_ascii(&self) -> bool { (**self).is_ascii() }
#[inline] fn to_ascii_uppercase(&self) -> Self { (**self).to_ascii_uppercase() }
#[inline] fn to_ascii_lowercase(&self) -> Self { (**self).to_ascii_lowercase() }
#[inline] fn eq_ignore_ascii_case(&self, o: &Self) -> bool { (**self).eq_ignore_ascii_case(o) }
#[inline] fn make_ascii_uppercase(&mut self) { (**self).make_ascii_uppercase() }
#[inline] fn make_ascii_lowercase(&mut self) { (**self).make_ascii_lowercase() }

fn into_ascii_lowercase(mut self) -> Self {
for byte in &mut self {
*byte = byte.to_ascii_lowercase()
}
self
}

fn into_ascii_uppercase(mut self) -> Self {
for byte in &mut self {
*byte = byte.to_ascii_uppercase()
}
self
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -404,6 +302,52 @@ impl AsciiExt for char {
fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); }
}

/// Extension methods for ASCII-subset only operations on owned strings
#[unstable(feature = "owned_ascii_ext", reason = "recently (re) added", issue = "27809")]
pub trait OwnedAsciiExt {
/// Converts the string to ASCII upper case:
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
/// but non-ASCII letters are unchanged.
#[unstable(feature = "owned_ascii_ext", reason = "recently (re) added", issue = "27809")]
fn into_ascii_uppercase(self) -> Self;

/// Converts the string to ASCII lower case:
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
/// but non-ASCII letters are unchanged.
#[unstable(feature = "owned_ascii_ext", reason = "recently (re) added", issue = "27809")]
fn into_ascii_lowercase(self) -> Self;
}

#[unstable(feature = "owned_ascii_ext", reason = "recently (re) added", issue = "27809")]
impl OwnedAsciiExt for String {
#[inline]
fn into_ascii_uppercase(self) -> String {
// Vec<u8>::into_ascii_uppercase() preserves the UTF-8 invariant.
unsafe { String::from_utf8_unchecked(self.into_bytes().into_ascii_uppercase()) }
}

#[inline]
fn into_ascii_lowercase(self) -> String {
// Vec<u8>::into_ascii_lowercase() preserves the UTF-8 invariant.
unsafe { String::from_utf8_unchecked(self.into_bytes().into_ascii_lowercase()) }
}
}

#[unstable(feature = "owned_ascii_ext", reason = "recently (re) added", issue = "27809")]
impl OwnedAsciiExt for Vec<u8> {
#[inline]
fn into_ascii_uppercase(mut self) -> Vec<u8> {
self.make_ascii_uppercase();
self
}

#[inline]
fn into_ascii_lowercase(mut self) -> Vec<u8> {
self.make_ascii_lowercase();
self
}
}

/// An iterator over the escaped version of a byte, constructed via
/// `std::ascii::escape_default`.
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -669,4 +613,11 @@ mod tests {
&from_u32(lower).unwrap().to_string()));
}
}

#[test]
/// https://github.com/rust-lang/rust/issues/32074
fn test_issue_32074_regression() {
let x = "a".to_string();
assert!(x.eq_ignore_ascii_case("A"));
}
}

0 comments on commit f1d3c6d

Please sign in to comment.