diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ead2a6f..599ea56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,7 @@ jobs: - simd - check_x86 - aarch64 - - msrv_x64 - - msrv_aarch64 + - msrv - miri - clippy_check steps: @@ -44,7 +43,7 @@ jobs: runs-on: ${{ matrix.os }} env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD: 1 + RUSTFLAGS: --cfg httparse_disable_simd steps: - name: Checkout @@ -76,14 +75,10 @@ jobs: - nightly target_feature: + - "" # runtime detection - "+sse4.2" - "+avx2" - "+sse4.2,+avx2" - - disable_compiletime: - - 0 - - 1 - steps: - name: Checkout uses: actions/checkout@v5 @@ -96,7 +91,6 @@ jobs: - name: Test env: RUSTFLAGS: -C target_feature=${{ matrix.target_feature }} - CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME: ${{ matrix.disable_compiletime }} run: cargo test check_x86: @@ -113,48 +107,26 @@ jobs: - name: Test without SIMD env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME: 1 + RUSTFLAGS: --cfg httparse_disable_simd run: cargo test --target i686-unknown-linux-musl - name: Test run: cargo test --target i686-unknown-linux-musl - msrv_x64: - name: msrv (x64) + msrv: + name: msrv runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v5 - name: Install Rust - uses: dtolnay/rust-toolchain@1.47.0 + uses: dtolnay/rust-toolchain@1.59.0 - # Only build, dev-dependencies don't compile on 1.47.0 + # Only build, dev-dependencies don't compile on 1.59.0 - name: Build run: cargo build - # This tests that aarch64 gracefully fallbacks to SWAR if neon_intrinsics aren't available (<1.59) - msrv_aarch64: - name: msrv (aarch64) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Install cross-compiling dependencies - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu - - - name: Setup Rust - uses: dtolnay/rust-toolchain@1.47.0 - with: - target: aarch64-unknown-linux-gnu - - # Only build, dev-dependencies don't compile on 1.47.0 - - name: Build - run: cargo build --target aarch64-unknown-linux-gnu - clippy_check: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/cibench.yml b/.github/workflows/cibench.yml index 647b91a..323e5d4 100644 --- a/.github/workflows/cibench.yml +++ b/.github/workflows/cibench.yml @@ -5,12 +5,13 @@ on: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C target-cpu=native" jobs: benchmark: name: Run benchmarks runs-on: ubuntu-latest + env: + RUSTFLAGS: "-C target-cpu=native" steps: - uses: actions/checkout@v5 with: @@ -41,7 +42,17 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - feature: [swar, sse42, avx2] + include: + - feature: runtime + rustflags: "" + - feature: swar + rustflags: "--cfg httparse_disable_simd" + - feature: sse4.2 + rustflags: "-C target-feature=+sse4.2" + - feature: avx2 + rustflags: "-C target-feature=+avx2" + env: + RUSTFLAGS: ${{ matrix.rustflags }} steps: - uses: actions/checkout@v5 with: @@ -57,17 +68,11 @@ jobs: run: | git checkout master cargo bench --bench parse -- --save-baseline master-${{ matrix.feature }} - env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD: ${{ matrix.feature == 'swar' && '1' || '0' }} - RUSTFLAGS: ${{ matrix.feature != 'swar' && format('-C target-feature=+{0}', matrix.feature) || '' }} - name: Run benchmarks (PR) run: | git checkout ${{ github.event.pull_request.head.sha }} cargo bench --bench parse -- --save-baseline pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}-${{ matrix.feature }} - env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD: ${{ matrix.feature == 'swar' && '1' || '0' }} - RUSTFLAGS: ${{ matrix.feature != 'swar' && format('-C target-feature=+{0}', matrix.feature) || '' }} - name: Compare benchmarks run: | @@ -78,7 +83,13 @@ jobs: runs-on: macos-latest strategy: matrix: - feature: [swar, neon] + include: + - feature: swar + rustflags: "--cfg httparse_disable_simd" + - feature: neon + rustflags: "-C target-feature=+neon" + env: + RUSTFLAGS: ${{ matrix.rustflags }} steps: - uses: actions/checkout@v5 with: @@ -94,17 +105,11 @@ jobs: run: | git checkout master cargo bench --bench parse -- --save-baseline master-aarch64-${{ matrix.feature }} - env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD: ${{ matrix.feature == 'swar' && '1' || '0' }} - RUSTFLAGS: ${{ matrix.feature == 'neon' && '-C target-feature=+neon' || '' }} - name: Run benchmarks (PR) run: | git checkout ${{ github.event.pull_request.head.sha }} cargo bench --bench parse -- --save-baseline pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}-aarch64-${{ matrix.feature }} - env: - CARGO_CFG_HTTPARSE_DISABLE_SIMD: ${{ matrix.feature == 'swar' && '1' || '0' }} - RUSTFLAGS: ${{ matrix.feature == 'neon' && '-C target-feature=+neon' || '' }} - name: Compare benchmarks run: | diff --git a/Cargo.toml b/Cargo.toml index 6cc7d42..7d60497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ documentation = "https://docs.rs/httparse" readme = "README.md" keywords = ["http", "parser", "no_std"] categories = ["network-programming", "no-std", "parser-implementations", "web-programming"] -edition = "2018" -build = "build.rs" +edition = "2021" +rust-version = "1.59" [features] default = ["std"] @@ -34,8 +34,5 @@ opt-level = 3 [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = [ - 'cfg(httparse_simd)', - 'cfg(httparse_simd_target_feature_avx2)', - 'cfg(httparse_simd_target_feature_sse42)', - 'cfg(httparse_simd_neon_intrinsics)', + 'cfg(httparse_disable_simd)', ] } diff --git a/build.rs b/build.rs deleted file mode 100644 index 6c45358..0000000 --- a/build.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - // We check rustc version to enable features beyond MSRV, such as: - // - 1.59 => neon_intrinsics - let rustc = env::var_os("RUSTC").unwrap_or(OsString::from("rustc")); - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to check 'rustc --version'") - .stdout; - - let raw_version = String::from_utf8(output) - .expect("rustc version output should be utf-8"); - - let version = match Version::parse(&raw_version) { - Ok(version) => version, - Err(err) => { - println!("cargo:warning=failed to parse `rustc --version`: {}", err); - return; - } - }; - - enable_new_features(version); -} - -fn enable_new_features(version: Version) { - enable_simd(version); -} - -fn enable_simd(version: Version) { - if env::var_os("CARGO_FEATURE_STD").is_none() { - println!("cargo:warning=building for no_std disables httparse SIMD"); - return; - } - if env::var_os("CARGO_CFG_MIRI").is_some() { - println!("cargo:warning=building for Miri disables httparse SIMD"); - return; - } - - let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD"; - if var_is(env_disable, "1") { - println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable); - return; - } - - // 1.59.0 is the first version to support neon_intrinsics - if version >= Version(1, 59, 0) { - println!("cargo:rustc-cfg=httparse_simd_neon_intrinsics"); - } - - println!("cargo:rustc-cfg=httparse_simd"); - - // cfg(target_feature) isn't stable yet, but CARGO_CFG_TARGET_FEATURE has - // a list... We aren't doing anything unsafe, since the is_x86_feature_detected - // macro still checks in the actual lib, BUT! - // - // By peeking at the list here, we can change up slightly how we do feature - // detection in the lib. If our features aren't in the feature list, we - // stick with a cached runtime detection strategy. - // - // But if the features *are* in the list, we benefit from removing our cache, - // since the compiler will eliminate several branches with its internal - // cfg(target_feature) usage. - - - let env_runtime_only = "CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME"; - if var_is(env_runtime_only, "1") { - println!("cargo:warning=detected {} environment variable, using runtime SIMD detection only", env_runtime_only); - return; - } - let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") { - Some(var) => match var.into_string() { - Ok(s) => s, - Err(_) => { - println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not valid utf-8"); - return; - }, - }, - None => { - println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not set"); - return - }, - }; - - let features = feature_list.split(',').map(|s| s.trim()); - if features.clone().any(|f| f == "sse4.2") { - println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42"); - } - if features.clone().any(|f| f == "avx2") { - println!("cargo:rustc-cfg=httparse_simd_target_feature_avx2"); - } -} - -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -struct Version (u32, u32, u32); - -impl Version { - fn parse(s: &str) -> Result { - let s = match s.strip_prefix("rustc ") { - Some(s) => s, - None => return Err(format!("unrecognized version string: {}", s)), - }; - - let mut iter = s - .split('.') - .take(3) - .map(|s| match s.find(|c: char| !c.is_ascii_digit()) { - Some(end) => &s[..end], - None => s, - }) - .map(|s| s.parse::().map_err(|e| e.to_string())); - - if iter.clone().count() != 3 { - return Err(format!("not enough version parts: {:?}", s)); - } - - let major = iter.next().unwrap()?; - let minor = iter.next().unwrap()?; - let patch = iter.next().unwrap()?; - - Ok(Version(major, minor, patch)) - } -} - -fn var_is(key: &str, val: &str) -> bool { - match env::var(key) { - Ok(v) => v == val, - Err(_) => false, - } -} diff --git a/clippy.toml b/clippy.toml deleted file mode 100644 index 7846a3e..0000000 --- a/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.47" diff --git a/src/macros.rs b/src/macros.rs index 751f60b..50ab156 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,10 +10,10 @@ macro_rules! next { } macro_rules! expect { - ($bytes:ident.next() == $pat:pat => $ret:expr) => { + ($bytes:ident.next() == $pat:pat_param => $ret:expr) => { expect!(next!($bytes) => $pat |? $ret) }; - ($e:expr => $pat:pat |? $ret:expr) => { + ($e:expr => $pat:pat_param |? $ret:expr) => { match $e { v@$pat => v, _ => return $ret @@ -31,7 +31,7 @@ macro_rules! complete { } macro_rules! byte_map { - ($($p:pat)|+) => {{ + ($($p:pat_param)|+) => {{ const fn make_map() -> [bool; 256] { let mut ret = [false; 256]; let mut i = 0; diff --git a/src/simd/mod.rs b/src/simd/mod.rs index 0e4493a..39e61ce 100644 --- a/src/simd/mod.rs +++ b/src/simd/mod.rs @@ -1,21 +1,24 @@ mod swar; -#[cfg(not(all( - httparse_simd, - any( +#[cfg(any( + httparse_disable_simd, + miri, + not(feature = "std"), + not(any( target_arch = "x86", target_arch = "x86_64", all( target_arch = "aarch64", - httparse_simd_neon_intrinsics, + target_feature = "neon", ) - ), -)))] + )) +))] pub use self::swar::*; #[cfg(all( - httparse_simd, - not(httparse_simd_target_feature_avx2), + not(any(httparse_disable_simd, miri)), + feature = "std", + not(target_feature = "avx2"), any( target_arch = "x86", target_arch = "x86_64", @@ -24,10 +27,11 @@ pub use self::swar::*; mod sse42; #[cfg(all( - httparse_simd, + not(any(httparse_disable_simd, miri)), + feature = "std", any( - httparse_simd_target_feature_avx2, - not(httparse_simd_target_feature_sse42), + target_feature = "avx2", + not(target_feature = "sse4.2"), ), any( target_arch = "x86", @@ -37,10 +41,11 @@ mod sse42; mod avx2; #[cfg(all( - httparse_simd, + not(any(httparse_disable_simd, miri)), + feature = "std", not(any( - httparse_simd_target_feature_sse42, - httparse_simd_target_feature_avx2, + target_feature = "sse4.2", + target_feature = "avx2", )), any( target_arch = "x86", @@ -50,10 +55,11 @@ mod avx2; mod runtime; #[cfg(all( - httparse_simd, + not(any(httparse_disable_simd, miri)), + feature = "std", not(any( - httparse_simd_target_feature_sse42, - httparse_simd_target_feature_avx2, + target_feature = "sse4.2", + target_feature = "avx2", )), any( target_arch = "x86", @@ -63,9 +69,10 @@ mod runtime; pub use self::runtime::*; #[cfg(all( - httparse_simd, - httparse_simd_target_feature_sse42, - not(httparse_simd_target_feature_avx2), + not(any(httparse_disable_simd, miri)), + feature = "std", + target_feature = "sse4.2", + not(target_feature = "avx2"), any( target_arch = "x86", target_arch = "x86_64", @@ -79,21 +86,20 @@ mod sse42_compile_time { #[inline(always)] pub fn match_uri_vectored(b: &mut crate::iter::Bytes<'_>) { - // SAFETY: calls are guarded by a compile time feature check - unsafe { crate::simd::sse42::match_uri_vectored(b) } + unsafe { super::sse42::match_uri_vectored(b) } } - + #[inline(always)] pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) { - // SAFETY: calls are guarded by a compile time feature check - unsafe { crate::simd::sse42::match_header_value_vectored(b) } + unsafe { super::sse42::match_header_value_vectored(b) } } } #[cfg(all( - httparse_simd, - httparse_simd_target_feature_sse42, - not(httparse_simd_target_feature_avx2), + not(any(httparse_disable_simd, miri)), + feature = "std", + target_feature = "sse4.2", + not(target_feature = "avx2"), any( target_arch = "x86", target_arch = "x86_64", @@ -102,8 +108,9 @@ mod sse42_compile_time { pub use self::sse42_compile_time::*; #[cfg(all( - httparse_simd, - httparse_simd_target_feature_avx2, + not(any(httparse_disable_simd, miri)), + feature = "std", + target_feature = "avx2", any( target_arch = "x86", target_arch = "x86_64", @@ -117,20 +124,19 @@ mod avx2_compile_time { #[inline(always)] pub fn match_uri_vectored(b: &mut crate::iter::Bytes<'_>) { - // SAFETY: calls are guarded by a compile time feature check - unsafe { crate::simd::avx2::match_uri_vectored(b) } + unsafe { super::avx2::match_uri_vectored(b) } } - + #[inline(always)] pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) { - // SAFETY: calls are guarded by a compile time feature check - unsafe { crate::simd::avx2::match_header_value_vectored(b) } + unsafe { super::avx2::match_header_value_vectored(b) } } } #[cfg(all( - httparse_simd, - httparse_simd_target_feature_avx2, + not(any(httparse_disable_simd, miri)), + feature = "std", + target_feature = "avx2", any( target_arch = "x86", target_arch = "x86_64", @@ -139,15 +145,17 @@ mod avx2_compile_time { pub use self::avx2_compile_time::*; #[cfg(all( - httparse_simd, + not(any(httparse_disable_simd, miri)), + feature = "std", target_arch = "aarch64", - httparse_simd_neon_intrinsics, + target_feature = "neon", ))] mod neon; #[cfg(all( - httparse_simd, + not(any(httparse_disable_simd, miri)), + feature = "std", target_arch = "aarch64", - httparse_simd_neon_intrinsics, + target_feature = "neon", ))] pub use self::neon::*;