From c9550f51dae6384c391b3b34153f4c7f1ea56859 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sun, 19 Mar 2023 11:49:19 +0100 Subject: [PATCH] io: use memchr from libc --- tokio/Cargo.toml | 3 +- tokio/src/io/util/read_until.rs | 1 + tokio/src/util/memchr.rs | 74 +++++++++++++++++++++++++++++++++ tokio/src/util/mod.rs | 3 ++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tokio/src/util/memchr.rs diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index e63c09f854d..b9e5934b442 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -42,7 +42,7 @@ full = [ ] fs = [] -io-util = ["memchr", "bytes"] +io-util = ["bytes"] # stdin, stdout, stderr io-std = [] macros = ["tokio-macros"] @@ -108,7 +108,6 @@ pin-project-lite = "0.2.0" # Everything else is optional... bytes = { version = "1.0.0", optional = true } -memchr = { version = "2.2", optional = true } mio = { version = "0.8.1", optional = true, default-features = false } socket2 = { version = "0.4.4", optional = true, features = [ "all" ] } num_cpus = { version = "1.8.0", optional = true } diff --git a/tokio/src/io/util/read_until.rs b/tokio/src/io/util/read_until.rs index 90a0e8a18d5..fb6fb22d9f1 100644 --- a/tokio/src/io/util/read_until.rs +++ b/tokio/src/io/util/read_until.rs @@ -1,4 +1,5 @@ use crate::io::AsyncBufRead; +use crate::util::memchr; use pin_project_lite::pin_project; use std::future::Future; diff --git a/tokio/src/util/memchr.rs b/tokio/src/util/memchr.rs new file mode 100644 index 00000000000..44fd2da3719 --- /dev/null +++ b/tokio/src/util/memchr.rs @@ -0,0 +1,74 @@ +//! Search for a byte in a byte array using libc. +//! +//! When nothing pulls in libc, then just use a trivial implementation. Note +//! that we only depend on libc on unix. + +#[cfg(not(all(unix, feature = "libc")))] +pub(crate) fn memchr(needle: u8, haystack: &[u8]) -> Option { + haystack.iter().position(|val| needle == *val) +} + +#[cfg(all(unix, feature = "libc"))] +pub(crate) fn memchr(needle: u8, haystack: &[u8]) -> Option { + let start = haystack.as_ptr(); + + // SAFETY: `start` is valid for `haystack.len()` bytes. + let ptr = unsafe { libc::memchr(start.cast(), needle as _, haystack.len()) }; + + if ptr.is_null() { + None + } else { + Some(ptr as usize - start as usize) + } +} + +#[cfg(test)] +mod tests { + use super::memchr; + + #[test] + fn memchr_test() { + let haystack = b"123abc456\0\xffabc\n"; + + assert_eq!(memchr(b'1', haystack), Some(0)); + assert_eq!(memchr(b'2', haystack), Some(1)); + assert_eq!(memchr(b'3', haystack), Some(2)); + assert_eq!(memchr(b'4', haystack), Some(6)); + assert_eq!(memchr(b'5', haystack), Some(7)); + assert_eq!(memchr(b'6', haystack), Some(8)); + assert_eq!(memchr(b'7', haystack), None); + assert_eq!(memchr(b'a', haystack), Some(3)); + assert_eq!(memchr(b'b', haystack), Some(4)); + assert_eq!(memchr(b'c', haystack), Some(5)); + assert_eq!(memchr(b'd', haystack), None); + assert_eq!(memchr(b'A', haystack), None); + assert_eq!(memchr(0, haystack), Some(9)); + assert_eq!(memchr(0xff, haystack), Some(10)); + assert_eq!(memchr(0xfe, haystack), None); + assert_eq!(memchr(1, haystack), None); + assert_eq!(memchr(b'\n', haystack), Some(14)); + assert_eq!(memchr(b'\r', haystack), None); + } + + #[test] + fn memchr_all() { + let mut arr = Vec::new(); + for b in 0..=255 { + arr.push(b); + } + for b in 0..=255 { + assert_eq!(memchr(b, &arr), Some(b as usize)); + } + arr.reverse(); + for b in 0..=255 { + assert_eq!(memchr(b, &arr), Some(255 - b as usize)); + } + } + + #[test] + fn memchr_empty() { + for b in 0..=255 { + assert_eq!(memchr(b, b""), None); + } + } +} diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index 0d538c147c2..6f8aa38dbc3 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -90,3 +90,6 @@ pub use self::rand::thread_rng_n; all(unix, feature = "signal") ))] pub(crate) mod error; + +#[cfg(feature = "io-util")] +pub(crate) mod memchr;