Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fea1bd4

Browse files
committedOct 1, 2016
std: Move platform specific memchr code into sys
1 parent 5c21562 commit fea1bd4

File tree

7 files changed

+309
-271
lines changed

7 files changed

+309
-271
lines changed
 

‎src/libstd/memchr.rs

Lines changed: 4 additions & 271 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// Original implementation taken from rust-memchr
1212
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
1313

14-
15-
1614
/// A safe interface to `memchr`.
1715
///
1816
/// Returns the index corresponding to the first occurrence of `needle` in
@@ -32,32 +30,9 @@
3230
/// let haystack = b"the quick brown fox";
3331
/// assert_eq!(memchr(b'k', haystack), Some(8));
3432
/// ```
33+
#[inline]
3534
pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
36-
// libc memchr
37-
#[cfg(not(target_os = "windows"))]
38-
fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
39-
use libc;
40-
41-
let p = unsafe {
42-
libc::memchr(
43-
haystack.as_ptr() as *const libc::c_void,
44-
needle as libc::c_int,
45-
haystack.len() as libc::size_t)
46-
};
47-
if p.is_null() {
48-
None
49-
} else {
50-
Some(p as usize - (haystack.as_ptr() as usize))
51-
}
52-
}
53-
54-
// use fallback on windows, since it's faster
55-
#[cfg(target_os = "windows")]
56-
fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
57-
fallback::memchr(needle, haystack)
58-
}
59-
60-
memchr_specific(needle, haystack)
35+
::sys::memchr::memchr(needle, haystack)
6136
}
6237

6338
/// A safe interface to `memrchr`.
@@ -75,251 +50,9 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
7550
/// let haystack = b"the quick brown fox";
7651
/// assert_eq!(memrchr(b'o', haystack), Some(17));
7752
/// ```
53+
#[inline]
7854
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
79-
80-
#[cfg(target_os = "linux")]
81-
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
82-
use libc;
83-
84-
// GNU's memrchr() will - unlike memchr() - error if haystack is empty.
85-
if haystack.is_empty() {return None}
86-
let p = unsafe {
87-
libc::memrchr(
88-
haystack.as_ptr() as *const libc::c_void,
89-
needle as libc::c_int,
90-
haystack.len() as libc::size_t)
91-
};
92-
if p.is_null() {
93-
None
94-
} else {
95-
Some(p as usize - (haystack.as_ptr() as usize))
96-
}
97-
}
98-
99-
#[cfg(not(target_os = "linux"))]
100-
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
101-
fallback::memrchr(needle, haystack)
102-
}
103-
104-
memrchr_specific(needle, haystack)
105-
}
106-
107-
#[allow(dead_code)]
108-
mod fallback {
109-
use cmp;
110-
use mem;
111-
112-
const LO_U64: u64 = 0x0101010101010101;
113-
const HI_U64: u64 = 0x8080808080808080;
114-
115-
// use truncation
116-
const LO_USIZE: usize = LO_U64 as usize;
117-
const HI_USIZE: usize = HI_U64 as usize;
118-
119-
/// Return `true` if `x` contains any zero byte.
120-
///
121-
/// From *Matters Computational*, J. Arndt
122-
///
123-
/// "The idea is to subtract one from each of the bytes and then look for
124-
/// bytes where the borrow propagated all the way to the most significant
125-
/// bit."
126-
#[inline]
127-
fn contains_zero_byte(x: usize) -> bool {
128-
x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
129-
}
130-
131-
#[cfg(target_pointer_width = "32")]
132-
#[inline]
133-
fn repeat_byte(b: u8) -> usize {
134-
let mut rep = (b as usize) << 8 | b as usize;
135-
rep = rep << 16 | rep;
136-
rep
137-
}
138-
139-
#[cfg(target_pointer_width = "64")]
140-
#[inline]
141-
fn repeat_byte(b: u8) -> usize {
142-
let mut rep = (b as usize) << 8 | b as usize;
143-
rep = rep << 16 | rep;
144-
rep = rep << 32 | rep;
145-
rep
146-
}
147-
148-
/// Return the first index matching the byte `a` in `text`.
149-
pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
150-
// Scan for a single byte value by reading two `usize` words at a time.
151-
//
152-
// Split `text` in three parts
153-
// - unaligned initial part, before the first word aligned address in text
154-
// - body, scan by 2 words at a time
155-
// - the last remaining part, < 2 word size
156-
let len = text.len();
157-
let ptr = text.as_ptr();
158-
let usize_bytes = mem::size_of::<usize>();
159-
160-
// search up to an aligned boundary
161-
let align = (ptr as usize) & (usize_bytes- 1);
162-
let mut offset;
163-
if align > 0 {
164-
offset = cmp::min(usize_bytes - align, len);
165-
if let Some(index) = text[..offset].iter().position(|elt| *elt == x) {
166-
return Some(index);
167-
}
168-
} else {
169-
offset = 0;
170-
}
171-
172-
// search the body of the text
173-
let repeated_x = repeat_byte(x);
174-
175-
if len >= 2 * usize_bytes {
176-
while offset <= len - 2 * usize_bytes {
177-
unsafe {
178-
let u = *(ptr.offset(offset as isize) as *const usize);
179-
let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize);
180-
181-
// break if there is a matching byte
182-
let zu = contains_zero_byte(u ^ repeated_x);
183-
let zv = contains_zero_byte(v ^ repeated_x);
184-
if zu || zv {
185-
break;
186-
}
187-
}
188-
offset += usize_bytes * 2;
189-
}
190-
}
191-
192-
// find the byte after the point the body loop stopped
193-
text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i)
194-
}
195-
196-
/// Return the last index matching the byte `a` in `text`.
197-
pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
198-
// Scan for a single byte value by reading two `usize` words at a time.
199-
//
200-
// Split `text` in three parts
201-
// - unaligned tail, after the last word aligned address in text
202-
// - body, scan by 2 words at a time
203-
// - the first remaining bytes, < 2 word size
204-
let len = text.len();
205-
let ptr = text.as_ptr();
206-
let usize_bytes = mem::size_of::<usize>();
207-
208-
// search to an aligned boundary
209-
let end_align = (ptr as usize + len) & (usize_bytes - 1);
210-
let mut offset;
211-
if end_align > 0 {
212-
offset = if end_align >= len { 0 } else { len - end_align };
213-
if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) {
214-
return Some(offset + index);
215-
}
216-
} else {
217-
offset = len;
218-
}
219-
220-
// search the body of the text
221-
let repeated_x = repeat_byte(x);
222-
223-
while offset >= 2 * usize_bytes {
224-
unsafe {
225-
let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize);
226-
let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize);
227-
228-
// break if there is a matching byte
229-
let zu = contains_zero_byte(u ^ repeated_x);
230-
let zv = contains_zero_byte(v ^ repeated_x);
231-
if zu || zv {
232-
break;
233-
}
234-
}
235-
offset -= 2 * usize_bytes;
236-
}
237-
238-
// find the byte before the point the body loop stopped
239-
text[..offset].iter().rposition(|elt| *elt == x)
240-
}
241-
242-
// test fallback implementations on all platforms
243-
#[test]
244-
fn matches_one() {
245-
assert_eq!(Some(0), memchr(b'a', b"a"));
246-
}
247-
248-
#[test]
249-
fn matches_begin() {
250-
assert_eq!(Some(0), memchr(b'a', b"aaaa"));
251-
}
252-
253-
#[test]
254-
fn matches_end() {
255-
assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
256-
}
257-
258-
#[test]
259-
fn matches_nul() {
260-
assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
261-
}
262-
263-
#[test]
264-
fn matches_past_nul() {
265-
assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
266-
}
267-
268-
#[test]
269-
fn no_match_empty() {
270-
assert_eq!(None, memchr(b'a', b""));
271-
}
272-
273-
#[test]
274-
fn no_match() {
275-
assert_eq!(None, memchr(b'a', b"xyz"));
276-
}
277-
278-
#[test]
279-
fn matches_one_reversed() {
280-
assert_eq!(Some(0), memrchr(b'a', b"a"));
281-
}
282-
283-
#[test]
284-
fn matches_begin_reversed() {
285-
assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
286-
}
287-
288-
#[test]
289-
fn matches_end_reversed() {
290-
assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
291-
}
292-
293-
#[test]
294-
fn matches_nul_reversed() {
295-
assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
296-
}
297-
298-
#[test]
299-
fn matches_past_nul_reversed() {
300-
assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
301-
}
302-
303-
#[test]
304-
fn no_match_empty_reversed() {
305-
assert_eq!(None, memrchr(b'a', b""));
306-
}
307-
308-
#[test]
309-
fn no_match_reversed() {
310-
assert_eq!(None, memrchr(b'a', b"xyz"));
311-
}
312-
313-
#[test]
314-
fn each_alignment_reversed() {
315-
let mut data = [1u8; 64];
316-
let needle = 2;
317-
let pos = 40;
318-
data[pos] = needle;
319-
for start in 0..16 {
320-
assert_eq!(Some(pos - start), memrchr(needle, &data[start..]));
321-
}
322-
}
55+
::sys::memchr::memrchr(needle, haystack)
32356
}
32457

32558
#[cfg(test)]

‎src/libstd/sys/common/memchr.rs

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// Original implementation taken from rust-memchr
12+
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
13+
14+
#[allow(dead_code)]
15+
pub mod fallback {
16+
use cmp;
17+
use mem;
18+
19+
const LO_U64: u64 = 0x0101010101010101;
20+
const HI_U64: u64 = 0x8080808080808080;
21+
22+
// use truncation
23+
const LO_USIZE: usize = LO_U64 as usize;
24+
const HI_USIZE: usize = HI_U64 as usize;
25+
26+
/// Return `true` if `x` contains any zero byte.
27+
///
28+
/// From *Matters Computational*, J. Arndt
29+
///
30+
/// "The idea is to subtract one from each of the bytes and then look for
31+
/// bytes where the borrow propagated all the way to the most significant
32+
/// bit."
33+
#[inline]
34+
fn contains_zero_byte(x: usize) -> bool {
35+
x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
36+
}
37+
38+
#[cfg(target_pointer_width = "32")]
39+
#[inline]
40+
fn repeat_byte(b: u8) -> usize {
41+
let mut rep = (b as usize) << 8 | b as usize;
42+
rep = rep << 16 | rep;
43+
rep
44+
}
45+
46+
#[cfg(target_pointer_width = "64")]
47+
#[inline]
48+
fn repeat_byte(b: u8) -> usize {
49+
let mut rep = (b as usize) << 8 | b as usize;
50+
rep = rep << 16 | rep;
51+
rep = rep << 32 | rep;
52+
rep
53+
}
54+
55+
/// Return the first index matching the byte `a` in `text`.
56+
pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
57+
// Scan for a single byte value by reading two `usize` words at a time.
58+
//
59+
// Split `text` in three parts
60+
// - unaligned initial part, before the first word aligned address in text
61+
// - body, scan by 2 words at a time
62+
// - the last remaining part, < 2 word size
63+
let len = text.len();
64+
let ptr = text.as_ptr();
65+
let usize_bytes = mem::size_of::<usize>();
66+
67+
// search up to an aligned boundary
68+
let align = (ptr as usize) & (usize_bytes- 1);
69+
let mut offset;
70+
if align > 0 {
71+
offset = cmp::min(usize_bytes - align, len);
72+
if let Some(index) = text[..offset].iter().position(|elt| *elt == x) {
73+
return Some(index);
74+
}
75+
} else {
76+
offset = 0;
77+
}
78+
79+
// search the body of the text
80+
let repeated_x = repeat_byte(x);
81+
82+
if len >= 2 * usize_bytes {
83+
while offset <= len - 2 * usize_bytes {
84+
unsafe {
85+
let u = *(ptr.offset(offset as isize) as *const usize);
86+
let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize);
87+
88+
// break if there is a matching byte
89+
let zu = contains_zero_byte(u ^ repeated_x);
90+
let zv = contains_zero_byte(v ^ repeated_x);
91+
if zu || zv {
92+
break;
93+
}
94+
}
95+
offset += usize_bytes * 2;
96+
}
97+
}
98+
99+
// find the byte after the point the body loop stopped
100+
text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i)
101+
}
102+
103+
/// Return the last index matching the byte `a` in `text`.
104+
pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
105+
// Scan for a single byte value by reading two `usize` words at a time.
106+
//
107+
// Split `text` in three parts
108+
// - unaligned tail, after the last word aligned address in text
109+
// - body, scan by 2 words at a time
110+
// - the first remaining bytes, < 2 word size
111+
let len = text.len();
112+
let ptr = text.as_ptr();
113+
let usize_bytes = mem::size_of::<usize>();
114+
115+
// search to an aligned boundary
116+
let end_align = (ptr as usize + len) & (usize_bytes - 1);
117+
let mut offset;
118+
if end_align > 0 {
119+
offset = if end_align >= len { 0 } else { len - end_align };
120+
if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) {
121+
return Some(offset + index);
122+
}
123+
} else {
124+
offset = len;
125+
}
126+
127+
// search the body of the text
128+
let repeated_x = repeat_byte(x);
129+
130+
while offset >= 2 * usize_bytes {
131+
unsafe {
132+
let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize);
133+
let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize);
134+
135+
// break if there is a matching byte
136+
let zu = contains_zero_byte(u ^ repeated_x);
137+
let zv = contains_zero_byte(v ^ repeated_x);
138+
if zu || zv {
139+
break;
140+
}
141+
}
142+
offset -= 2 * usize_bytes;
143+
}
144+
145+
// find the byte before the point the body loop stopped
146+
text[..offset].iter().rposition(|elt| *elt == x)
147+
}
148+
149+
// test fallback implementations on all platforms
150+
#[test]
151+
fn matches_one() {
152+
assert_eq!(Some(0), memchr(b'a', b"a"));
153+
}
154+
155+
#[test]
156+
fn matches_begin() {
157+
assert_eq!(Some(0), memchr(b'a', b"aaaa"));
158+
}
159+
160+
#[test]
161+
fn matches_end() {
162+
assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
163+
}
164+
165+
#[test]
166+
fn matches_nul() {
167+
assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
168+
}
169+
170+
#[test]
171+
fn matches_past_nul() {
172+
assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
173+
}
174+
175+
#[test]
176+
fn no_match_empty() {
177+
assert_eq!(None, memchr(b'a', b""));
178+
}
179+
180+
#[test]
181+
fn no_match() {
182+
assert_eq!(None, memchr(b'a', b"xyz"));
183+
}
184+
185+
#[test]
186+
fn matches_one_reversed() {
187+
assert_eq!(Some(0), memrchr(b'a', b"a"));
188+
}
189+
190+
#[test]
191+
fn matches_begin_reversed() {
192+
assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
193+
}
194+
195+
#[test]
196+
fn matches_end_reversed() {
197+
assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
198+
}
199+
200+
#[test]
201+
fn matches_nul_reversed() {
202+
assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
203+
}
204+
205+
#[test]
206+
fn matches_past_nul_reversed() {
207+
assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
208+
}
209+
210+
#[test]
211+
fn no_match_empty_reversed() {
212+
assert_eq!(None, memrchr(b'a', b""));
213+
}
214+
215+
#[test]
216+
fn no_match_reversed() {
217+
assert_eq!(None, memrchr(b'a', b"xyz"));
218+
}
219+
220+
#[test]
221+
fn each_alignment_reversed() {
222+
let mut data = [1u8; 64];
223+
let needle = 2;
224+
let pos = 40;
225+
data[pos] = needle;
226+
for start in 0..16 {
227+
assert_eq!(Some(pos - start), memrchr(needle, &data[start..]));
228+
}
229+
}
230+
}

‎src/libstd/sys/common/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub mod at_exit_imp;
3131
pub mod backtrace;
3232
pub mod condvar;
3333
pub mod io;
34+
pub mod memchr;
3435
pub mod mutex;
3536
pub mod net;
3637
pub mod poison;

‎src/libstd/sys/unix/memchr.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// Original implementation taken from rust-memchr
12+
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
13+
14+
pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
15+
use libc;
16+
17+
let p = unsafe {
18+
libc::memchr(
19+
haystack.as_ptr() as *const libc::c_void,
20+
needle as libc::c_int,
21+
haystack.len() as libc::size_t)
22+
};
23+
if p.is_null() {
24+
None
25+
} else {
26+
Some(p as usize - (haystack.as_ptr() as usize))
27+
}
28+
}
29+
30+
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
31+
32+
#[cfg(target_os = "linux")]
33+
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
34+
use libc;
35+
36+
// GNU's memrchr() will - unlike memchr() - error if haystack is empty.
37+
if haystack.is_empty() {return None}
38+
let p = unsafe {
39+
libc::memrchr(
40+
haystack.as_ptr() as *const libc::c_void,
41+
needle as libc::c_int,
42+
haystack.len() as libc::size_t)
43+
};
44+
if p.is_null() {
45+
None
46+
} else {
47+
Some(p as usize - (haystack.as_ptr() as usize))
48+
}
49+
}
50+
51+
#[cfg(not(target_os = "linux"))]
52+
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
53+
::sys_common::memchr::fallback::memrchr(needle, haystack)
54+
}
55+
56+
memrchr_specific(needle, haystack)
57+
}

‎src/libstd/sys/unix/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod env;
3838
pub mod ext;
3939
pub mod fd;
4040
pub mod fs;
41+
pub mod memchr;
4142
pub mod mutex;
4243
pub mod net;
4344
pub mod os;

‎src/libstd/sys/windows/memchr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// Original implementation taken from rust-memchr
12+
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
13+
14+
// Fallback memchr is fastest on windows
15+
pub use sys_common::memchr::fallback::{memchr, memrchr};

‎src/libstd/sys/windows/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod env;
2626
pub mod ext;
2727
pub mod fs;
2828
pub mod handle;
29+
pub mod memchr;
2930
pub mod mutex;
3031
pub mod net;
3132
pub mod os;

0 commit comments

Comments
 (0)
Please sign in to comment.