Skip to content

Commit

Permalink
Write a CountPred for line counting, and make Lines::count use it
Browse files Browse the repository at this point in the history
  • Loading branch information
thomcc committed Apr 7, 2024
1 parent ade7869 commit 1258d48
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
23 changes: 22 additions & 1 deletion library/core/src/str/count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ use core::intrinsics::unlikely;

const USIZE_SIZE: usize = core::mem::size_of::<usize>();
const UNROLL_INNER: usize = 4;
const LSB: usize = usize::repeat_u8(0x01);

#[inline]
pub(super) fn count_chars(s: &str) -> usize {
count::<CharCount>(s)
}

#[inline]
pub(super) fn count_lines(s: &str) -> usize {
count::<NewlineCount>(s) + 1
}

trait CountPred {
/// Bytes in `u` which match the pred must be `0x01` in the result, bytes
/// which fail the pred must be `0x00`.
Expand All @@ -46,6 +52,22 @@ impl CountPred for CharCount {
contains_non_continuation_byte(u)
}
}
struct NewlineCount;
impl CountPred for NewlineCount {
#[inline]
fn count_general_case(s: &[u8]) -> usize {
s.iter().filter(|b| **b == b'\n').count()
}
#[inline]
fn test_each_byte_in_word(u: usize) -> usize {
const NEWLINES: usize = usize::repeat_u8(b'\n');
const NOT_MSB: usize = usize::repeat_u8(0x7f);
// bytes of `diff` are nonzero when bytes of `u` don't contain newline
let diff = u ^ NEWLINES;
let res = !(((diff & NOT_MSB).wrapping_add(NOT_MSB) | diff) >> 7);
res & LSB
}
}

#[inline]
fn count<P: CountPred>(s: &str) -> usize {
Expand Down Expand Up @@ -137,7 +159,6 @@ fn do_count<P: CountPred>(s: &str) -> usize {
// true)
#[inline]
fn contains_non_continuation_byte(w: usize) -> usize {
const LSB: usize = usize::repeat_u8(0x01);
((!w >> 7) | (w >> 6)) & LSB
}

Expand Down
5 changes: 5 additions & 0 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,11 @@ impl<'a> Iterator for Lines<'a> {
fn last(mut self) -> Option<&'a str> {
self.next_back()
}

#[inline]
fn count(self) -> usize {
self.remainder().map(super::count::count_lines).unwrap_or_default()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down

0 comments on commit 1258d48

Please sign in to comment.