Skip to content

Commit

Permalink
Rollup merge of #88858 - spektom:to_lower_upper_rev, r=dtolnay
Browse files Browse the repository at this point in the history
Allow reverse iteration of lowercase'd/uppercase'd chars

The PR implements `DoubleEndedIterator` trait for `ToLowercase` and `ToUppercase`.

This enables reverse iteration of lowercase/uppercase variants of character sequences.
One of use cases:  determining whether a char sequence is a suffix of another one.

Example:

```rust
fn endswith_ignore_case(s1: &str, s2: &str) -> bool {
    for eob in s1
        .chars()
        .flat_map(|c| c.to_lowercase())
        .rev()
        .zip_longest(s2.chars().flat_map(|c| c.to_lowercase()).rev())
    {
        match eob {
            EitherOrBoth::Both(c1, c2) => {
                if c1 != c2 {
                    return false;
                }
            }
            EitherOrBoth::Left(_) => return true,
            EitherOrBoth::Right(_) => return false,
        }
    }
    true
}
```
  • Loading branch information
matthiaskrgr committed Dec 22, 2021
2 parents 34926f0 + 417b6f3 commit 60625a6
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
31 changes: 31 additions & 0 deletions library/alloc/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,37 @@ fn test_rev_iterator() {
assert_eq!(pos, v.len());
}

#[test]
fn test_to_lowercase_rev_iterator() {
let s = "AÖßÜ💩ΣΤΙΓΜΑΣDžfiİ";
let v = ['\u{307}', 'i', 'fi', 'dž', 'σ', 'α', 'μ', 'γ', 'ι', 'τ', 'σ', '💩', 'ü', 'ß', 'ö', 'a'];

let mut pos = 0;
let it = s.chars().flat_map(|c| c.to_lowercase()).rev();

for c in it {
assert_eq!(c, v[pos]);
pos += 1;
}
assert_eq!(pos, v.len());
}

#[test]
fn test_to_uppercase_rev_iterator() {
let s = "aößü💩στιγμαςDžfiᾀ";
let v =
['Ι', 'Ἀ', 'I', 'F', 'DŽ', 'Σ', 'Α', 'Μ', 'Γ', 'Ι', 'Τ', 'Σ', '💩', 'Ü', 'S', 'S', 'Ö', 'A'];

let mut pos = 0;
let it = s.chars().flat_map(|c| c.to_uppercase()).rev();

for c in it {
assert_eq!(c, v[pos]);
pos += 1;
}
assert_eq!(pos, v.len());
}

#[test]
#[cfg_attr(miri, ignore)] // Miri is too slow
fn test_chars_decoding() {
Expand Down
34 changes: 34 additions & 0 deletions library/core/src/char/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,13 @@ impl Iterator for ToLowercase {
}
}

#[stable(feature = "case_mapping_double_ended", since = "1.59.0")]
impl DoubleEndedIterator for ToLowercase {
fn next_back(&mut self) -> Option<char> {
self.0.next_back()
}
}

#[stable(feature = "fused", since = "1.26.0")]
impl FusedIterator for ToLowercase {}

Expand Down Expand Up @@ -420,6 +427,13 @@ impl Iterator for ToUppercase {
}
}

#[stable(feature = "case_mapping_double_ended", since = "1.59.0")]
impl DoubleEndedIterator for ToUppercase {
fn next_back(&mut self) -> Option<char> {
self.0.next_back()
}
}

#[stable(feature = "fused", since = "1.26.0")]
impl FusedIterator for ToUppercase {}

Expand Down Expand Up @@ -479,6 +493,26 @@ impl Iterator for CaseMappingIter {
}
}

impl DoubleEndedIterator for CaseMappingIter {
fn next_back(&mut self) -> Option<char> {
match *self {
CaseMappingIter::Three(a, b, c) => {
*self = CaseMappingIter::Two(a, b);
Some(c)
}
CaseMappingIter::Two(b, c) => {
*self = CaseMappingIter::One(b);
Some(c)
}
CaseMappingIter::One(c) => {
*self = CaseMappingIter::Zero;
Some(c)
}
CaseMappingIter::Zero => None,
}
}
}

impl fmt::Display for CaseMappingIter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Expand Down
6 changes: 6 additions & 0 deletions library/core/tests/char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ fn test_to_lowercase() {
let iter: String = c.to_lowercase().collect();
let disp: String = c.to_lowercase().to_string();
assert_eq!(iter, disp);
let iter_rev: String = c.to_lowercase().rev().collect();
let disp_rev: String = disp.chars().rev().collect();
assert_eq!(iter_rev, disp_rev);
iter
}
assert_eq!(lower('A'), "a");
Expand Down Expand Up @@ -130,6 +133,9 @@ fn test_to_uppercase() {
let iter: String = c.to_uppercase().collect();
let disp: String = c.to_uppercase().to_string();
assert_eq!(iter, disp);
let iter_rev: String = c.to_uppercase().rev().collect();
let disp_rev: String = disp.chars().rev().collect();
assert_eq!(iter_rev, disp_rev);
iter
}
assert_eq!(upper('a'), "A");
Expand Down

0 comments on commit 60625a6

Please sign in to comment.