Skip to content

Commit

Permalink
Merge pull request rust-lang#2 from nicokoch/memrchr
Browse files Browse the repository at this point in the history
Implement safe interface to memrchr.
  • Loading branch information
BurntSushi committed Aug 15, 2015
2 parents a91e633 + 7c2c2aa commit 580aaf8
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 3 deletions.
19 changes: 19 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
- TARGET: i686-pc-windows-msvc
- TARGET: i686-pc-windows-gnu
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
- cargo -V

build: false

test_script:
- cargo build --verbose
- cargo test --verbose
- cargo bench --verbose
18 changes: 18 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,21 @@ fn libc_memchr(b: &mut test::Bencher) {
assert!(memchr::memchr(needle, &haystack).is_none());
});
}

#[bench]
fn iterator_reversed(b: &mut test::Bencher) {
let haystack = bench_data();
let needle = b'a';
b.iter(|| {
assert!(haystack.iter().rposition(|&b| b == needle).is_none());
});
}

#[bench]
fn libc_memrchr(b: &mut test::Bencher) {
let haystack = bench_data();
let needle = b'a';
b.iter(|| {
assert!(memchr::memchr(needle, &haystack).is_none());
});
}
100 changes: 97 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*!
This crate defines a single function, `memchr`, which exposes a safe interface
to the corresponding function in `libc`.
This crate defines two functions, `memchr` and `memrchr`, which expose a safe interface
to the corresponding functions in `libc`.
*/

#![deny(missing_docs)]
Expand Down Expand Up @@ -44,11 +44,62 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
}
}

/// A safe interface to `memrchr`.
///
/// Returns the index corresponding to the last occurrence of `needle` in
/// `haystack`, or `None` if one is not found.
///
/// # Example
///
/// This shows how to find the last position of a byte in a byte string.
///
/// ```rust
/// use memchr::memrchr;
///
/// let haystack = b"the quick brown fox";
/// assert_eq!(memrchr(b'o', haystack), Some(17));
/// ```
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {

#[cfg(target_os = "linux")]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
// GNU's memrchr() will - unlike memchr() - error if haystack is empty.
if haystack.is_empty() {return None}
let p = unsafe {
ffi::memrchr(
haystack.as_ptr() as *const c_void,
needle as c_int,
haystack.len() as size_t)
};
if p.is_null() {
None
} else {
Some(p as usize - (haystack.as_ptr() as usize))
}
}

#[cfg(not(target_os = "linux"))]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
haystack.iter().rposition(|&b| b == needle)
}

memrchr_specific(needle, haystack)
}

#[cfg(target_os = "linux")]
mod ffi {
use libc::types::common::c95::c_void;
use libc::types::os::arch::c95::{c_int, size_t};
extern {
pub fn memrchr(cx: *const c_void, c: c_int, n: size_t) -> *mut c_void;
}
}

#[cfg(test)]
mod tests {
extern crate quickcheck;

use super::memchr;
use super::{memchr, memrchr};

#[test]
fn matches_one() {
Expand Down Expand Up @@ -92,4 +143,47 @@ mod tests {
}
quickcheck::quickcheck(prop as fn(u8, Vec<u8>) -> bool);
}

#[test]
fn matches_one_reversed() {
assert_eq!(Some(0), memrchr(b'a', b"a"));
}

#[test]
fn matches_begin_reversed() {
assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
}

#[test]
fn matches_end_reversed() {
assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
}

#[test]
fn matches_nul_reversed() {
assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
}

#[test]
fn matches_past_nul_reversed() {
assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
}

#[test]
fn no_match_empty_reversed() {
assert_eq!(None, memrchr(b'a', b""));
}

#[test]
fn no_match_reversed() {
assert_eq!(None, memrchr(b'a', b"xyz"));
}

#[test]
fn qc_never_fail_reversed() {
fn prop(needle: u8, haystack: Vec<u8>) -> bool {
memrchr(needle, &haystack); true
}
quickcheck::quickcheck(prop as fn(u8, Vec<u8>) -> bool);
}
}

0 comments on commit 580aaf8

Please sign in to comment.