Skip to content

Commit af61c28

Browse files
authored
Merge pull request #157 from glebm/fix-mernchr-bug
Fix a bug in memrnchr
2 parents 5dd95a9 + cd2ef4a commit af61c28

File tree

1 file changed

+13
-36
lines changed

1 file changed

+13
-36
lines changed

src/memrnchr.rs

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,7 @@
33
// We use this mainly to skip repeated `/`. If there is only one slash, `memrnchr` performs the same
44
// as a naive version (e.g. `rposition`). However, it is much faster in pathological cases.
55

6-
const LO_U64: u64 = 0x0101010101010101;
7-
const HI_U64: u64 = 0x8080808080808080;
8-
9-
// use truncation
10-
const LO_USIZE: usize = LO_U64 as usize;
11-
const HI_USIZE: usize = HI_U64 as usize;
12-
13-
#[cfg(target_pointer_width = "32")]
14-
const USIZE_BYTES: usize = 4;
15-
#[cfg(target_pointer_width = "64")]
16-
const USIZE_BYTES: usize = 8;
6+
use std::mem::size_of;
177

188
// Returns the byte offset of the last byte that is NOT equal to the given one.
199
#[inline(always)]
@@ -28,12 +18,11 @@ pub fn memrnchr(x: u8, text: &[u8]) -> Option<usize> {
2818
let ptr = text.as_ptr();
2919

3020
// search to an aligned boundary
31-
let end_align = (ptr as usize + len) & (USIZE_BYTES - 1);
21+
let end_align = (ptr as usize + len) & (size_of::<usize>() - 1);
3222
let mut offset;
3323
if end_align > 0 {
3424
offset = if end_align >= len { 0 } else { len - end_align };
35-
let pos = text[offset..].iter().rposition(|elt| *elt != x);
36-
if let Some(index) = pos {
25+
if let Some(index) = memrnchr_naive(x, &text[offset..]) {
3726
return Some(offset + index);
3827
}
3928
} else {
@@ -42,37 +31,25 @@ pub fn memrnchr(x: u8, text: &[u8]) -> Option<usize> {
4231

4332
// search the body of the text
4433
let repeated_x = repeat_byte(x);
45-
46-
while offset >= 2 * USIZE_BYTES {
47-
debug_assert_eq!((ptr as usize + offset) % USIZE_BYTES, 0);
34+
while offset >= 2 * size_of::<usize>() {
35+
debug_assert_eq!((ptr as usize + offset) % size_of::<usize>(), 0);
4836
unsafe {
49-
let u = *(ptr.offset(offset as isize - 2 * USIZE_BYTES as isize) as *const usize);
50-
let v = *(ptr.offset(offset as isize - USIZE_BYTES as isize) as *const usize);
51-
52-
// break if there is a matching byte
53-
let zu = contains_zero_byte(u ^ repeated_x);
54-
let zv = contains_zero_byte(v ^ repeated_x);
55-
if !zu || !zv {
37+
let u = *(ptr.offset(offset as isize - 2 * size_of::<usize>() as isize) as *const usize);
38+
let v = *(ptr.offset(offset as isize - size_of::<usize>() as isize) as *const usize);
39+
if u & repeated_x != usize::max_value() || v & repeated_x != usize::max_value() {
5640
break;
5741
}
5842
}
59-
offset -= 2 * USIZE_BYTES;
43+
offset -= 2 * size_of::<usize>();
6044
}
6145

6246
// find the byte before the point the body loop stopped
63-
text[..offset].iter().rposition(|elt| *elt != x)
47+
memrnchr_naive(x, &text[..offset])
6448
}
6549

66-
/// Return `true` if `x` contains any zero byte.
67-
///
68-
/// From *Matters Computational*, J. Arndt
69-
///
70-
/// "The idea is to subtract one from each of the bytes and then look for
71-
/// bytes where the borrow propagated all the way to the most significant
72-
/// bit."
73-
#[inline]
74-
fn contains_zero_byte(x: usize) -> bool {
75-
x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
50+
#[inline(always)]
51+
fn memrnchr_naive(x: u8, text: &[u8]) -> Option<usize> {
52+
text.iter().rposition(|c| *c != x)
7653
}
7754

7855
#[cfg(target_pointer_width = "32")]

0 commit comments

Comments
 (0)