Skip to content

Commit 6e897d7

Browse files
committed
Rollup merge of rust-lang#33849 - ranma42:escape-iters-count, r=alexcrichton
Implement `count` for `EscapeUnicode` and cleanup the code for `count` for `EscapeDefault` (instead of repeating the `match` for `size_hint` and `count`). This PR marks EscapeUnicode and EscapeDefault as ExactSizeIterator. The constraints for the trait implementations held even before this PR, but I am not sure if this is something we want to guarantee/expose (I would love feedback on this, especially on what would be the appropriate way to handle stabilisation, if needed). Part of rust-lang#24214, split from rust-lang#31049. The test for `count` was added in rust-lang#33103.
2 parents 320dd04 + 6b5e86b commit 6e897d7

File tree

2 files changed

+56
-28
lines changed

2 files changed

+56
-28
lines changed

src/libcore/char.rs

+50-28
Original file line numberDiff line numberDiff line change
@@ -411,14 +411,17 @@ pub struct EscapeUnicode {
411411
hex_digit_idx: usize,
412412
}
413413

414+
// The enum values are ordered so that their representation is the
415+
// same as the remaining length (besides the hexadecimal digits). This
416+
// likely makes `len()` a single load from memory) and inline-worth.
414417
#[derive(Clone, Debug)]
415418
enum EscapeUnicodeState {
416-
Backslash,
417-
Type,
418-
LeftBrace,
419-
Value,
420-
RightBrace,
421419
Done,
420+
RightBrace,
421+
Value,
422+
LeftBrace,
423+
Type,
424+
Backslash,
422425
}
423426

424427
#[stable(feature = "rust1", since = "1.0.0")]
@@ -457,19 +460,17 @@ impl Iterator for EscapeUnicode {
457460
}
458461
}
459462

463+
#[inline]
460464
fn size_hint(&self) -> (usize, Option<usize>) {
461-
let n = match self.state {
462-
EscapeUnicodeState::Backslash => 5,
463-
EscapeUnicodeState::Type => 4,
464-
EscapeUnicodeState::LeftBrace => 3,
465-
EscapeUnicodeState::Value => 2,
466-
EscapeUnicodeState::RightBrace => 1,
467-
EscapeUnicodeState::Done => 0,
468-
};
469-
let n = n + self.hex_digit_idx;
465+
let n = self.len();
470466
(n, Some(n))
471467
}
472468

469+
#[inline]
470+
fn count(self) -> usize {
471+
self.len()
472+
}
473+
473474
fn last(self) -> Option<char> {
474475
match self.state {
475476
EscapeUnicodeState::Done => None,
@@ -483,6 +484,22 @@ impl Iterator for EscapeUnicode {
483484
}
484485
}
485486

487+
#[stable(feature = "exact_size_escape", since = "1.11.0")]
488+
impl ExactSizeIterator for EscapeUnicode {
489+
#[inline]
490+
fn len(&self) -> usize {
491+
// The match is a single memory access with no branching
492+
self.hex_digit_idx + match self.state {
493+
EscapeUnicodeState::Done => 0,
494+
EscapeUnicodeState::RightBrace => 1,
495+
EscapeUnicodeState::Value => 2,
496+
EscapeUnicodeState::LeftBrace => 3,
497+
EscapeUnicodeState::Type => 4,
498+
EscapeUnicodeState::Backslash => 5,
499+
}
500+
}
501+
}
502+
486503
/// An iterator that yields the literal escape code of a `char`.
487504
///
488505
/// This `struct` is created by the [`escape_default()`] method on [`char`]. See
@@ -498,9 +515,9 @@ pub struct EscapeDefault {
498515

499516
#[derive(Clone, Debug)]
500517
enum EscapeDefaultState {
501-
Backslash(char),
502-
Char(char),
503518
Done,
519+
Char(char),
520+
Backslash(char),
504521
Unicode(EscapeUnicode),
505522
}
506523

@@ -523,22 +540,15 @@ impl Iterator for EscapeDefault {
523540
}
524541
}
525542

543+
#[inline]
526544
fn size_hint(&self) -> (usize, Option<usize>) {
527-
match self.state {
528-
EscapeDefaultState::Char(_) => (1, Some(1)),
529-
EscapeDefaultState::Backslash(_) => (2, Some(2)),
530-
EscapeDefaultState::Unicode(ref iter) => iter.size_hint(),
531-
EscapeDefaultState::Done => (0, Some(0)),
532-
}
545+
let n = self.len();
546+
(n, Some(n))
533547
}
534548

549+
#[inline]
535550
fn count(self) -> usize {
536-
match self.state {
537-
EscapeDefaultState::Char(_) => 1,
538-
EscapeDefaultState::Unicode(iter) => iter.count(),
539-
EscapeDefaultState::Done => 0,
540-
EscapeDefaultState::Backslash(_) => 2,
541-
}
551+
self.len()
542552
}
543553

544554
fn nth(&mut self, n: usize) -> Option<char> {
@@ -578,6 +588,18 @@ impl Iterator for EscapeDefault {
578588
}
579589
}
580590

591+
#[stable(feature = "exact_size_escape", since = "1.11.0")]
592+
impl ExactSizeIterator for EscapeDefault {
593+
fn len(&self) -> usize {
594+
match self.state {
595+
EscapeDefaultState::Done => 0,
596+
EscapeDefaultState::Char(_) => 1,
597+
EscapeDefaultState::Backslash(_) => 2,
598+
EscapeDefaultState::Unicode(ref iter) => iter.len(),
599+
}
600+
}
601+
}
602+
581603
/// An iterator over `u8` entries represending the UTF-8 encoding of a `char`
582604
/// value.
583605
///

src/libcoretest/char.rs

+6
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ fn eu_iterator_specializations() {
276276
// Check last
277277
assert_eq!(iter.clone().last(), Some('}'));
278278

279+
// Check len
280+
assert_eq!(iter.len(), len - offset);
281+
282+
// Check size_hint (= len in ExactSizeIterator)
283+
assert_eq!(iter.size_hint(), (iter.len(), Some(iter.len())));
284+
279285
// Check counting
280286
assert_eq!(iter.clone().count(), len - offset);
281287

0 commit comments

Comments
 (0)