Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"225818af89ecfab6e3c95d2c64ef840b0ac6de32ae49eb2964427c3c7299316c",".github/workflows/build.yaml":"13a784264fec68fa21deb0581a08da816bf03b698db673b0ec5b49ccc9a4184c",".github/workflows/fuzz.yaml":"9e23a99373a2dd2933e301cc11aed21c19c52cb1b03f772742a900c097613dc6",".github/workflows/publish.yaml":"1acd215e1e86cba82719dd6619b045baae04f10edcdc8d875d3f8543c96ce4f3",".rustfmt.toml":"bc44a62358fe8857bd97d33209253d6b073acf0c18383f50882113f9db4c7a9d","CHANGELOG.md":"4f762219d91ec3f7b6f4f25c6c4f4a83a5c3a0db1503827d9e1541c5fb8b62a1","Cargo.lock":"271f75f27f89d23492a15f55c4812bfb10eac6bfcd50f6c5c0b28c83b57ebcc7","Cargo.toml":"6981052c018f374476e9dd4186c0df3f4f5f0108a6c6b627409a2644b06e925b","Cargo.toml.orig":"b299cbab838a5f3f090673d7e75d567bb4ae95bfd639e047f50e24da51d01ef4","LICENSE.md":"42a35170233e83e18856792e748de4c1ce4a63b2afce9a370c89ef3fe23f9f2d","README.md":"a26b02ad90c989bc5c2c8a7155bd6f47c9542060cdbf5812097b9cf8c82c6b5a","src/hash.rs":"b5d0aea3ad486cccb67a156606e932b857cf0db5014393df50f290a495836d17","src/imp/avx2.rs":"a3c11163ab0c1a72d43a5d4d1ecc8baa1376261cd0a0ef8525c91d8f4a3a2858","src/imp/avx512.rs":"8fbd3d9a70a410f2345a7c9e614166a77a0f5a26dc8f1ba26b85cb10abae7b84","src/imp/mod.rs":"a2fbd6c32879af23658e7c53cb07a9e338c8b1760d5599ec6971cf057edd8121","src/imp/neon.rs":"41a418417214ca11634d93d38824fbb51edfa7e77b874aba7f36c4e89665c2e3","src/imp/scalar.rs":"b76bbe1a726b1307746c9492ce821f986d043a925cdf056c099d2b76da96b462","src/imp/sse2.rs":"4c42661c6ac33f2f9aef3ecc946f07507b5a24f476b0f4281168e52a9e7c6e19","src/imp/ssse3.rs":"bd8220e4ae030fe21075de95fbef4cc9e6218103fff4e8004262563d2a08d0af","src/imp/wasm.rs":"8399d32827f2ddfceb1d8b1103e22c6db27051b52f291e92f99cf2c558f4ed63","src/lib.rs":"beaba13326630edbccc501f02e78cf9064a8dea7927839ad2cf0381af33d6b32"},"package":"3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"}
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "d93164f98d7fe2daf883f2e1bec82778dd26199a"
},
"path_in_vcs": ""
}
@@ -0,0 +1,213 @@
name: build
on:
push:
pull_request:
branches: [main]
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
test-doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- uses: Swatinem/rust-cache@v1
- run: cargo install cargo-deadlinks
- name: doc
env:
RUSTDOCFLAGS: --cfg doc_cfg
run: cargo deadlinks --ignore-fragments -- --all
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
toolchain: stable
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
toolchain: stable
- os: macos-26-intel
target: x86_64-apple-darwin
toolchain: stable
- os: macos-latest
target: aarch64-apple-darwin
toolchain: stable
- os: windows-latest
target: x86_64-pc-windows-gnu
toolchain: stable
- os: windows-latest
target: x86_64-pc-windows-msvc
toolchain: beta
# Test both windows-gnu and windows-msvc; use beta rust on one
- os: ubuntu-latest
deps: sudo apt-get update ; sudo apt install gcc-multilib
target: i686-unknown-linux-gnu
toolchain: nightly
- os: macos-latest
target: aarch64-apple-darwin
toolchain: nightly
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
toolchain: nightly
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
toolchain: nightly
variant: minimal_versions
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}
override: true
- uses: Swatinem/rust-cache@v1
- run: ${{ matrix.deps }}
- name: Maybe minimal versions
if: ${{ matrix.variant == 'minimal_versions' }}
run: cargo generate-lockfile -Z minimal-versions
- name: Test
run: |
cargo test --lib --target ${{ matrix.target }} ${{ matrix.toolchain == 'nightly' && '--features=nightly' || '' }}
env:
# Enables address sanitization if supported target and nightly.
RUSTFLAGS: |
${{
(
matrix.toolchain == 'nightly' && (
matrix.target == 'aarch64-apple-darwin' ||
matrix.target == 'aarch64-fuchsia' ||
matrix.target == 'aarch64-unknown-linux-gnu' ||
matrix.target == 'x86_64-apple-darwin' ||
matrix.target == 'x86_64-fuchsia' ||
matrix.target == 'x86_64-unknown-freebsd' ||
matrix.target == 'x86_64-unknown-linux-gnu'
)
)
&& '-Z sanitizer=address'
|| ''
}}
test-cross:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: arm-unknown-linux-gnueabi
toolchain: stable
- os: ubuntu-latest
target: arm-unknown-linux-gnueabi
toolchain: nightly
- os: ubuntu-latest
target: thumbv7neon-unknown-linux-gnueabihf
toolchain: stable
- os: ubuntu-latest
target: thumbv7neon-unknown-linux-gnueabihf
toolchain: nightly
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}
override: true
- uses: Swatinem/rust-cache@v1
- name: Install cross
run: cargo install cross || true
- name: Test
run: |
cross test --no-fail-fast --target ${{ matrix.target }} ${{ matrix.toolchain == 'nightly' && '--features=nightly' || '' }}
test-msrv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
target: x86_64-unknown-linux-gnu
toolchain: 1.36.0 # MSRV
- uses: Swatinem/rust-cache@v1
- name: Test
run: cargo test --target=x86_64-unknown-linux-gnu --no-default-features --features=std
test-no-std:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
target: thumbv6m-none-eabi
override: true
- uses: Swatinem/rust-cache@v1
- name: Build top-level only
run: cargo build --target=thumbv6m-none-eabi --no-default-features
test-miri:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- uses: Swatinem/rust-cache@v1
- name: Install Miri
run: |
rustup toolchain install nightly --component miri
rustup override set nightly
cargo miri setup
- name: Run scalar tests under miri
run: cargo +nightly miri test scalar
- name: Run sse2 tests under miri
run: cargo +nightly miri test sse2
- name: Run ssse3 tests under miri
run: cargo +nightly miri test --lib ssse3
env:
RUSTFLAGS: "-Ctarget-cpu=x86-64-v2"
- name: Run avx2 tests under miri
run: cargo +nightly miri test --lib avx2
env:
RUSTFLAGS: "-Ctarget-cpu=x86-64-v3"
- name: Run avx512 tests under miri
run: cargo +nightly miri test --lib --features=nightly avx512
env:
RUSTFLAGS: "-Ctarget-cpu=x86-64-v4"
@@ -0,0 +1,62 @@
name: fuzz
on:
schedule:
# Run every 24h
- cron: "0 0 * * *"
defaults:
run:
working-directory: fuzz
jobs:
fuzz:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
target: [avx2, sse2, ssse3]
include:
- os: ubuntu-24.04-arm
target: neon
steps:
- uses: actions/checkout@v2
- name: setup
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- uses: Swatinem/rust-cache@v1
- run: cargo install cargo-fuzz
- name: Download corpus artifact
run: |
wget https://nightly.link/mcountryman/simd-adler32/workflows/fuzz.yaml/main/corpus.zip
mkdir -p corpus
unzip corpus.zip -d corpus
# keep going if artifact doesn't exist
continue-on-error: true
- name: Run fuzz test (30m)
run: |
cargo fuzz run ${{ matrix.target }} -- -max_total_time=1800
- name: Archive artifacts
uses: actions/upload-artifact@v2
if: failure()
with:
name: artifacts
path: fuzz/artifacts
# big decision. do we store corpus from failed jobs? for now we don't..
- uses: actions/upload-artifact@v2
with:
name: corpus
path: |
fuzz/corpus/
fuzz/corpus/${{ matrix.target }}
@@ -0,0 +1,35 @@
name: publish
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure git user
run: |
git config user.name "GitHub Actions"
git config user.email noreply@github.com
- name: Install cargo-release
uses: actions-rs/install@v0.1
with:
crate: cargo-release
version: latest
- name: Publish
run: |
echo "${{ secrets.CRATES_IO_TOKEN }}" | cargo login
cargo --locked release --no-confirm --no-push -x
- name: Push changes
uses: ad-m/github-push-action@master
with:
tags: true
force: true
branch: ${{ github.ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}
+2
View File
@@ -0,0 +1,2 @@
max_width = 90
tab_spaces = 2
+12
View File
@@ -0,0 +1,12 @@
# Changelog
## 0.3.3 - 2021-04-14
### Features
- **from_checksum**: add `Adler32::from_checksum`
### Performance Improvements
- **scalar**: improve scalar performance by 90-600%
- Defer modulo until right before u16 overflow
+657
View File
@@ -0,0 +1,657 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
dependencies = [
"memchr",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"bitflags",
"textwrap",
"unicode-width",
]
[[package]]
name = "criterion"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "csv"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi 0.3.2",
"libc",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b"
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "simd-adler32"
version = "0.3.10"
dependencies = [
"adler2",
"adler32",
"criterion",
"rand",
]
[[package]]
name = "syn"
version = "2.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "web-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+68
View File
@@ -0,0 +1,68 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "simd-adler32"
version = "0.3.10"
authors = ["Marvin Countryman <me@maar.vin>"]
build = false
exclude = ["bench"]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A SIMD-accelerated Adler-32 hash algorithm implementation."
readme = "README.md"
keywords = [
"simd",
"avx2",
"ssse3",
"adler",
"adler32",
]
categories = [
"algorithms",
"no-std",
]
license = "MIT"
repository = "https://github.com/mcountryman/simd-adler32"
[features]
const-generics = []
default = [
"std",
"const-generics",
]
nightly = []
std = []
[lib]
name = "simd_adler32"
path = "src/lib.rs"
[dev-dependencies.adler2]
version = "2.0.1"
[dev-dependencies.adler32]
version = "1.2.0"
[dev-dependencies.criterion]
version = "0.3"
[dev-dependencies.rand]
version = "0.8"
features = ["small_rng"]
[profile.release]
opt-level = 2
debug = 2
+39
View File
@@ -0,0 +1,39 @@
[package]
name = "simd-adler32"
authors = ["Marvin Countryman <me@maar.vin>"]
license = "MIT"
version = "0.3.10"
edition = "2018"
keywords = ["simd", "avx2", "ssse3", "adler", "adler32"]
categories = ["algorithms", "no-std"]
repository = "https://github.com/mcountryman/simd-adler32"
description = "A SIMD-accelerated Adler-32 hash algorithm implementation."
exclude = ["bench"]
[profile.release]
debug = true
opt-level = 2
[[bench]]
name = "alts"
path = "bench/alts.rs"
harness = false
[[bench]]
name = "variants"
path = "bench/variants.rs"
harness = false
[features]
default = ["std", "const-generics"]
std = []
nightly = []
const-generics = []
[dev-dependencies]
rand = { version = "0.8", features = ["small_rng"] }
criterion = "0.3"
# competition
adler2 = "2.0.1"
adler32 = "1.2.0"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) [2021] [Marvin Countryman]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+143
View File
@@ -0,0 +1,143 @@
<h1 align="center">simd-adler32</h1>
<p align="center">
<a href="https://docs.rs/simd-adler32">
<img alt="docs.rs badge" src="https://img.shields.io/docsrs/simd-adler32?style=flat-square">
</a>
<a href="https://crates.io/crates/simd-adler32">
<img alt="crates.io badge" src="https://img.shields.io/crates/v/simd-adler32?style=flat-square">
</a>
<a href="https://github.com/mcountryman/simd-adler32/blob/main/LICENSE.md">
<img alt="mit license badge" src="https://img.shields.io/github/license/mcountryman/simd-adler32?style=flat-square">
</a>
</p>
A SIMD-accelerated Adler-32 hash algorithm implementation.
## Features
- No dependencies
- Support `no_std` (with `default-features = false`)
- Runtime CPU feature detection (when `std` enabled)
- Blazing fast performance on as many targets as possible (currently only x86 and x86_64)
- Default to scalar implementation when simd not available
## Quick start
> Cargo.toml
```toml
[dependencies]
simd-adler32 = "*"
```
> example.rs
```rust
use simd_adler32::Adler32;
let mut adler = Adler32::new();
adler.write(b"rust is pretty cool, man");
let hash = adler.finish();
println!("{}", hash);
// 1921255656
```
## Support
**CPU Features**
| impl | arch | feature |
| ---- | ---------------- | ------- |
| ✅ | `x86`, `x86_64` | avx512 |
| ✅ | `x86`, `x86_64` | avx2 |
| ✅ | `x86`, `x86_64` | ssse3 |
| ✅ | `x86`, `x86_64` | sse2 |
| ✅ | `arm`, `aarch64` | neon |
| ✅ | `wasm32` | simd128 |
**MSRV** `1.36.0`\*\*
Minimum supported rust version is tested before a new version is published. [**] Feature
`const-generics` needs to disabled to build on rustc versions `<1.51` which can be done
by updating your dependency definition to the following.
> Cargo.toml
```toml
[dependencies]
simd-adler32 = { version "*", default-features = false, features = ["std"] }
```
## Performance
Benchmarks listed display number of randomly generated bytes (10k / 100k) and library
name. Benchmarks sources can be found under the [bench](/bench) directory. Crates used for
comparison are [adler](https://crates.io/crates/adler) and
[adler32](https://crates.io/crates/adler32).
> Windows 10 Pro - Intel i5-8300H @ 2.30GHz
| name | avg. time | avg. thrpt |
| ----------------------- | --------------- | ------------------ |
| **10k/simd-adler32** | **212.61 ns** | **43.805 GiB/s** |
| 10k/wuffs | 3843 ns | 2.63 GiB/s\* |
| 10k/adler32 | 4.8084 us | 1.9369 GiB/s |
| 10k/adler | 17.979 us | 530.43 MiB/s |
| ----------------------- | --------------- | ------------------ |
| **100k/simd-adler32** | **2.7951 us** | **33.320 GiB/s** |
| 100k/wuffs | 34733 ns | 2.6814 GiB/s\* |
| 100k/adler32 | 48.488 us | 1.9207 GiB/s |
| 100k/adler | 178.36 us | 534.69 MiB/s |
\* wuffs ran using mingw64/gcc, ran with `wuffs bench -ccompilers=gcc -reps=1 -iterscale=300 std/adler32`.
> MacBookPro16,1 - Intel i9-9880H CPU @ 2.30GHz
| name | avg. time | avg. thrpt |
| ----------------------- | --------------- | ------------------ |
| **10k/simd-adler32** | **200.37 ns** | **46.480 GiB/s** |
| 10k/adler32 | 4.1516 us | 2.2433 GiB/s |
| 10k/adler | 10.220 us | 933.15 MiB/s |
| ----------------------- | --------------- | ------------------ |
| **100k/simd-adler32** | **2.3282 us** | **40.003 GiB/s** |
| 100k/adler32 | 41.130 us | 2.2643 GiB/s |
| 100k/adler | 83.776 us | 534.69 MiB/s |
> MacBook Pro 14" - Apple M3 Pro CPU @ 4.05GHz
| name | avg. time | avg. thrpt |
| ----------------------- | --------------- | ------------------ |
| **10k/simd-adler32** | **251.10 ns** | **37.090 GiB/s** |
| 10k/adler32 | 2.7478 µs | 3.3894 GiB/s |
| 10k/adler2 | 1.5198 µs | 6.1280 GiB/s |
| ----------------------- | --------------- | ------------------ |
| **100k/simd-adler32** | **2.5154 µs** | **37.024 GiB/s** |
| 100k/adler32 | 27.961 µs | 3.3308 GiB/s |
| 100k/adler2 | 15.705 µs | 5.9300 GiB/s |
## Safety
This crate contains a significant amount of `unsafe` code due to the requirement of `unsafe`
for simd intrinsics. Fuzzing is done on release and debug builds prior to publishing via
`afl`. Fuzzy tests can be found under [fuzz](/fuzz) the directory.
## Resources
- [LICENSE](./LICENSE.md) - MIT
- [CHANGELOG](./CHANGELOG.md)
## Credits
Thank you to the contributors of the following projects.
- [adler](https://github.com/jonas-schievink/adler)
- [adler32](https://github.com/remram44/adler32-rs)
- [crc32fast](https://github.com/srijs/rust-crc32fast)
- [wuffs](https://github.com/google/wuffs)
- [chromium](https://bugs.chromium.org/p/chromium/issues/detail?id=762564)
- [zlib](https://zlib.net/)
## Contributing
Feel free to submit a issue or pull request. :smile:
+156
View File
@@ -0,0 +1,156 @@
use crate::{Adler32, Adler32Hash};
impl Adler32Hash for &[u8] {
fn hash(&self) -> u32 {
let mut hash = Adler32::new();
hash.write(self);
hash.finish()
}
}
impl Adler32Hash for &str {
fn hash(&self) -> u32 {
let mut hash = Adler32::new();
hash.write(self.as_bytes());
hash.finish()
}
}
#[cfg(feature = "const-generics")]
impl<const SIZE: usize> Adler32Hash for [u8; SIZE] {
fn hash(&self) -> u32 {
let mut hash = Adler32::new();
hash.write(self);
hash.finish()
}
}
macro_rules! array_impl {
($s:expr, $($size:expr),+) => {
array_impl!($s);
$(array_impl!{$size})*
};
($size:expr) => {
#[cfg(not(feature = "const-generics"))]
impl Adler32Hash for [u8; $size] {
fn hash(&self) -> u32 {
let mut hash = Adler32::new();
hash.write(self);
hash.finish()
}
}
};
}
array_impl!(
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
1024,
1024 * 1024,
1024 * 1024 * 1024,
2048,
4096
);
+220
View File
@@ -0,0 +1,220 @@
use super::Adler32Imp;
/// Resolves update implementation if CPU supports avx2 instructions.
pub fn get_imp() -> Option<Adler32Imp> {
get_imp_inner()
}
#[inline]
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
fn get_imp_inner() -> Option<Adler32Imp> {
if std::is_x86_feature_detected!("avx2") {
Some(imp::update)
} else {
None
}
}
#[inline]
#[cfg(all(
target_feature = "avx2",
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
Some(imp::update)
}
#[inline]
#[cfg(all(
not(target_feature = "avx2"),
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
None
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "std", target_feature = "avx2")
))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 32;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
unsafe { update_imp(a, b, data) }
}
#[inline]
#[target_feature(enable = "avx2")]
unsafe fn update_imp(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
#[inline]
unsafe fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
*a %= MOD;
*b %= MOD;
}
#[inline]
unsafe fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
#[inline(always)]
unsafe fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
let one_v = _mm256_set1_epi16(1);
let zero_v = _mm256_setzero_si256();
let weights = get_weights();
let mut p_v = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, (*a * blocks.len() as u32) as _);
let mut a_v = _mm256_setzero_si256();
let mut b_v = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, *b as _);
for block in blocks {
let block_ptr = block.as_ptr() as *const _;
let block = _mm256_loadu_si256(block_ptr);
p_v = _mm256_add_epi32(p_v, a_v);
a_v = _mm256_add_epi32(a_v, _mm256_sad_epu8(block, zero_v));
let mad = _mm256_maddubs_epi16(block, weights);
b_v = _mm256_add_epi32(b_v, _mm256_madd_epi16(mad, one_v));
}
b_v = _mm256_add_epi32(b_v, _mm256_slli_epi32(p_v, 5));
*a += reduce_add(a_v);
*b = reduce_add(b_v);
blocks_remainder
}
#[inline(always)]
unsafe fn reduce_add(v: __m256i) -> u32 {
let sum = _mm_add_epi32(_mm256_castsi256_si128(v), _mm256_extracti128_si256(v, 1));
let hi = _mm_unpackhi_epi64(sum, sum);
let sum = _mm_add_epi32(hi, sum);
let hi = _mm_shuffle_epi32(sum, crate::imp::_MM_SHUFFLE(2, 3, 0, 1));
let sum = _mm_add_epi32(sum, hi);
_mm_cvtsi128_si32(sum) as _
}
#[inline(always)]
unsafe fn get_weights() -> __m256i {
_mm256_set_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32,
)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 1024 - 5]);
#[cfg(not(miri))]
assert_sum_eq(&[0; 1024 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 1024 - 5]); // non-power-of-2 to test remainder handling
#[cfg(not(miri))]
assert_sum_eq(&[1; 1024 * 1024]);
}
#[test]
fn random() {
if super::get_imp().is_none() { return; } // don't do any work if we're not on this target
let mut random = [0; 1024 * 10];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..1024 - 5]); // non-power-of-2 to test remainder handling
assert_sum_eq(&random[..1024 * 10]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+248
View File
@@ -0,0 +1,248 @@
use super::Adler32Imp;
/// Resolves update implementation if CPU supports avx512f and avx512bw instructions.
pub fn get_imp() -> Option<Adler32Imp> {
get_imp_inner()
}
#[inline]
#[cfg(all(
feature = "std",
feature = "nightly",
any(target_arch = "x86", target_arch = "x86_64")
))]
fn get_imp_inner() -> Option<Adler32Imp> {
let has_avx512f = std::is_x86_feature_detected!("avx512f");
let has_avx512bw = std::is_x86_feature_detected!("avx512bw");
if has_avx512f && has_avx512bw {
Some(imp::update)
} else {
None
}
}
#[inline]
#[cfg(all(
feature = "nightly",
all(target_feature = "avx512f", target_feature = "avx512bw"),
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
Some(imp::update)
}
#[inline]
#[cfg(all(
not(all(feature = "nightly", target_feature = "avx512f", target_feature = "avx512bw")),
not(all(
feature = "std",
feature = "nightly",
any(target_arch = "x86", target_arch = "x86_64")
))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
None
}
#[cfg(all(
feature = "nightly",
any(target_arch = "x86", target_arch = "x86_64"),
any(
feature = "std",
all(target_feature = "avx512f", target_feature = "avx512bw")
)
))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 64;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
unsafe { update_imp(a, b, data) }
}
#[inline]
#[target_feature(enable = "avx512f")]
#[target_feature(enable = "avx512bw")]
unsafe fn update_imp(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
#[inline]
unsafe fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
*a %= MOD;
*b %= MOD;
}
#[inline]
unsafe fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
#[inline(always)]
unsafe fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
let one_v = _mm512_set1_epi16(1);
let zero_v = _mm512_setzero_si512();
let weights = get_weights();
let p_v = (*a * blocks.len() as u32) as _;
let mut p_v = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, p_v);
let mut a_v = _mm512_setzero_si512();
let mut b_v = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, *b as _);
for block in blocks {
let block_ptr = block.as_ptr() as *const _;
let block = _mm512_loadu_si512(block_ptr);
p_v = _mm512_add_epi32(p_v, a_v);
a_v = _mm512_add_epi32(a_v, _mm512_sad_epu8(block, zero_v));
let mad = _mm512_maddubs_epi16(block, weights);
b_v = _mm512_add_epi32(b_v, _mm512_madd_epi16(mad, one_v));
}
b_v = _mm512_add_epi32(b_v, _mm512_slli_epi32(p_v, 6));
*a += reduce_add(a_v);
*b = reduce_add(b_v);
blocks_remainder
}
#[inline(always)]
unsafe fn reduce_add(v: __m512i) -> u32 {
let v: [__m256i; 2] = core::mem::transmute(v);
reduce_add_256(v[0]) + reduce_add_256(v[1])
}
#[inline(always)]
unsafe fn reduce_add_256(v: __m256i) -> u32 {
let v: [__m128i; 2] = core::mem::transmute(v);
let sum = _mm_add_epi32(v[0], v[1]);
let hi = _mm_unpackhi_epi64(sum, sum);
let sum = _mm_add_epi32(hi, sum);
let hi = _mm_shuffle_epi32(sum, crate::imp::_MM_SHUFFLE(2, 3, 0, 1));
let sum = _mm_add_epi32(sum, hi);
let sum = _mm_cvtsi128_si32(sum) as _;
sum
}
#[inline(always)]
unsafe fn get_weights() -> __m512i {
_mm512_set_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 1024 - 5]);
#[cfg(not(miri))]
assert_sum_eq(&[0; 1024 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 1024 - 5]); // non-power-of-2 to test remainder handling
#[cfg(not(miri))]
assert_sum_eq(&[1; 1024 * 1024]);
}
#[test]
fn random() {
if super::get_imp().is_none() { return; } // don't do any work if we're not on this target
let mut random = [0; 1024 * 10];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..1024 - 5]); // non-power-of-2 to test remainder handling
assert_sum_eq(&random[..1024 * 10]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+25
View File
@@ -0,0 +1,25 @@
pub mod avx2;
pub mod avx512;
pub mod neon;
pub mod scalar;
pub mod sse2;
pub mod ssse3;
pub mod wasm;
pub type Adler32Imp = fn(u16, u16, &[u8]) -> (u16, u16);
#[inline]
#[allow(non_snake_case)]
pub const fn _MM_SHUFFLE(z: u32, y: u32, x: u32, w: u32) -> i32 {
((z << 6) | (y << 4) | (x << 2) | w) as i32
}
pub fn get_imp() -> Adler32Imp {
avx512::get_imp()
.or_else(neon::get_imp)
.or_else(avx2::get_imp)
.or_else(ssse3::get_imp)
.or_else(sse2::get_imp)
.or_else(wasm::get_imp)
.unwrap_or(scalar::update)
}
+201
View File
@@ -0,0 +1,201 @@
use super::Adler32Imp;
#[cfg(all(target_feature = "neon", any(target_arch = "aarch64", feature = "nightly")))]
pub fn get_imp() -> Option<Adler32Imp> {
Some(imp::update)
}
#[cfg(not(all(target_feature = "neon", any(target_arch = "aarch64", feature = "nightly"))))]
pub fn get_imp() -> Option<Adler32Imp> {
None
}
#[cfg(all(target_feature = "neon", any(target_arch = "aarch64", feature = "nightly")))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 32;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "aarch64")]
use core::arch::aarch64::*;
#[cfg(target_arch = "arm")]
use core::arch::arm::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
}
fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
// Conversion of the code from Chromium zlib:
// https://chromium.googlesource.com/chromium/src/third_party/+/main/zlib/adler32_simd.c
unsafe {
// a and b accumulators are initially zero.
let mut a_v: uint32x4_t = vdupq_n_u32(0);
let mut b_v: uint32x4_t = vdupq_n_u32(0);
// b_v[3] contains the last term (n) for the B part
b_v = vsetq_lane_u32(*a * (blocks.len() as u32), b_v, 3);
// Computing the unrolled prefix-sum
let mut v_column_sum_1: uint16x8_t = vdupq_n_u16(0);
let mut v_column_sum_2: uint16x8_t = vdupq_n_u16(0);
let mut v_column_sum_3: uint16x8_t = vdupq_n_u16(0);
let mut v_column_sum_4: uint16x8_t = vdupq_n_u16(0);
for block in blocks {
let block_ptr = block.as_ptr();
// Slurp in 32 bytes
let bytes1: uint8x16_t = vld1q_u8(block_ptr);
let bytes2: uint8x16_t = vld1q_u8(block_ptr.add(16));
// Wrapping-add the sums from the previous block together.
// b_v[i] += a_v[i]
b_v = vaddq_u32(b_v, a_v);
// Unsigned add, accumulate long pairwise.
// Adjacent elements in bytes1 are zipped, added, lengthened.
a_v = vpadalq_u16(a_v, vpadalq_u8(vpaddlq_u8(bytes1), bytes2));
// Have to oscillate between low and high elements, since vaddw's first
// argument is already q-length.
v_column_sum_1 = vaddw_u8(v_column_sum_1, vget_low_u8(bytes1));
v_column_sum_2 = vaddw_u8(v_column_sum_2, vget_high_u8(bytes1));
v_column_sum_3 = vaddw_u8(v_column_sum_3, vget_low_u8(bytes2));
v_column_sum_4 = vaddw_u8(v_column_sum_4, vget_high_u8(bytes2));
}
// No more data/updates to a, so now we shake out all of the accumulated data
// Previous block was 32 indices ago, so multiply B to start
b_v = vshlq_n_u32(b_v, 5);
// Then product-sum of each D column.
let w1: [u16; 4] = [32, 31, 30, 29];
let w2: [u16; 4] = [28, 27, 26, 25];
let w3: [u16; 4] = [24, 23, 22, 21];
let w4: [u16; 4] = [20, 19, 18, 17];
let w5: [u16; 4] = [16, 15, 14, 13];
let w6: [u16; 4] = [12, 11, 10, 9];
let w7: [u16; 4] = [8, 7, 6, 5];
let w8: [u16; 4] = [4, 3, 2, 1];
b_v = vmlal_u16(b_v, vget_low_u16(v_column_sum_1), vld1_u16(w1.as_ptr()));
b_v = vmlal_u16(b_v, vget_high_u16(v_column_sum_1), vld1_u16(w2.as_ptr()));
b_v = vmlal_u16(b_v, vget_low_u16(v_column_sum_2), vld1_u16(w3.as_ptr()));
b_v = vmlal_u16(b_v, vget_high_u16(v_column_sum_2), vld1_u16(w4.as_ptr()));
b_v = vmlal_u16(b_v, vget_low_u16(v_column_sum_3), vld1_u16(w5.as_ptr()));
b_v = vmlal_u16(b_v, vget_high_u16(v_column_sum_3), vld1_u16(w6.as_ptr()));
b_v = vmlal_u16(b_v, vget_low_u16(v_column_sum_4), vld1_u16(w7.as_ptr()));
b_v = vmlal_u16(b_v, vget_high_u16(v_column_sum_4), vld1_u16(w8.as_ptr()));
// Pyramid pairwise-add to get the final output.
// *a = vaddvq_u32(a_v) would also do the job.
let sum1: uint32x2_t = vpadd_u32(vget_low_u32(a_v), vget_high_u32(a_v));
let sum2: uint32x2_t = vpadd_u32(vget_low_u32(b_v), vget_high_u32(b_v));
let sum3: uint32x2_t = vpadd_u32(sum1, sum2);
*a += vget_lane_u32(sum3, 0);
*b += vget_lane_u32(sum3, 1);
*a %= MOD;
*b %= MOD;
blocks_remainder
}
}
}
#[cfg(test)]
mod tests {
use rand::{rngs::SmallRng, Rng, SeedableRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 1024 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 1024 * 1024]);
}
#[test]
fn random() {
let mut random = [0; 1024 * 1024];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..1024 * 1024]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+83
View File
@@ -0,0 +1,83 @@
const MOD: u32 = 65521;
const NMAX: usize = 5552;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(NMAX);
let remainder = chunks.remainder();
for chunk in chunks {
for byte in chunk {
a = a.wrapping_add(*byte as _);
b = b.wrapping_add(a);
}
a %= MOD;
b %= MOD;
}
for byte in remainder {
a = a.wrapping_add(*byte as _);
b = b.wrapping_add(a);
}
a %= MOD;
b %= MOD;
(a as u16, b as u16)
}
#[cfg(test)]
mod tests {
#[test]
fn zeroes_short() {
assert_eq!(adler32(&[]), 1);
assert_eq!(adler32(&[0]), 1 | 1 << 16);
assert_eq!(adler32(&[0, 0]), 1 | 2 << 16);
assert_eq!(adler32(&[0; 100]), 0x00640001);
assert_eq!(adler32(&[0; 1024]), 0x04000001);
}
#[test]
#[cfg_attr(miri, ignore)]
fn zeroes_long() {
assert_eq!(adler32(&[0; 1024 * 1024]), 0x00f00001);
}
#[test]
fn ones_short() {
assert_eq!(adler32(&[0xff; 1024]), 0x79a6fc2e);
}
#[test]
#[cfg_attr(miri, ignore)]
fn ones_long() {
assert_eq!(adler32(&[0xff; 1024 * 1024]), 0x8e88ef11);
}
#[test]
fn mixed_short() {
assert_eq!(adler32(&[1]), 2 | 2 << 16);
assert_eq!(adler32(&[40]), 41 | 41 << 16);
}
#[test]
#[cfg_attr(miri, ignore)]
fn mixed_long() {
assert_eq!(adler32(&[0xA5; 1024 * 1024]), 0xd5009ab1);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_eq!(adler32(b"Wikipedia"), 0x11E60398);
}
fn adler32(data: &[u8]) -> u32 {
let (a, b) = super::update(1, 0, data);
u32::from(b) << 16 | u32::from(a)
}
}
+239
View File
@@ -0,0 +1,239 @@
use super::Adler32Imp;
/// Resolves update implementation if CPU supports sse2 instructions.
pub fn get_imp() -> Option<Adler32Imp> {
get_imp_inner()
}
#[inline]
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
fn get_imp_inner() -> Option<Adler32Imp> {
if std::is_x86_feature_detected!("sse2") {
Some(imp::update)
} else {
None
}
}
#[inline]
#[cfg(all(
target_feature = "sse2",
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
Some(imp::update)
}
#[inline]
#[cfg(all(
not(target_feature = "sse2"),
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
None
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "std", target_feature = "sse2")
))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 32;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
unsafe { update_imp(a, b, data) }
}
#[inline]
#[target_feature(enable = "sse2")]
unsafe fn update_imp(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
unsafe fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
*a %= MOD;
*b %= MOD;
}
unsafe fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
#[inline(always)]
unsafe fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
let zero_v = _mm_setzero_si128();
let weight_hi_v = get_weight_hi();
let weight_lo_v = get_weight_lo();
let mut p_v = _mm_set_epi32(0, 0, 0, (*a * blocks.len() as u32) as _);
let mut a_v = _mm_setzero_si128();
let mut b_v = _mm_set_epi32(0, 0, 0, *b as _);
for block in blocks {
let block_ptr = block.as_ptr() as *const _;
let left_v = _mm_loadu_si128(block_ptr);
let right_v = _mm_loadu_si128(block_ptr.add(1));
p_v = _mm_add_epi32(p_v, a_v);
a_v = _mm_add_epi32(a_v, _mm_sad_epu8(left_v, zero_v));
let mad = maddubs(left_v, weight_hi_v);
b_v = _mm_add_epi32(b_v, mad);
a_v = _mm_add_epi32(a_v, _mm_sad_epu8(right_v, zero_v));
let mad = maddubs(right_v, weight_lo_v);
b_v = _mm_add_epi32(b_v, mad);
}
b_v = _mm_add_epi32(b_v, _mm_slli_epi32(p_v, 5));
*a += reduce_add(a_v);
*b = reduce_add(b_v);
blocks_remainder
}
#[inline(always)]
unsafe fn maddubs(a: __m128i, b: __m128i) -> __m128i {
let a_lo = _mm_unpacklo_epi8(a, _mm_setzero_si128());
let a_hi = _mm_unpackhi_epi8(a, _mm_setzero_si128());
let b_lo = _mm_unpacklo_epi8(b, _mm_setzero_si128());
let b_hi = _mm_unpackhi_epi8(b, _mm_setzero_si128());
let lo = _mm_madd_epi16(a_lo, b_lo);
let hi = _mm_madd_epi16(a_hi, b_hi);
_mm_add_epi32(lo, hi)
}
#[inline(always)]
unsafe fn reduce_add(v: __m128i) -> u32 {
let hi = _mm_unpackhi_epi64(v, v);
let sum = _mm_add_epi32(hi, v);
let hi = _mm_shuffle_epi32(sum, crate::imp::_MM_SHUFFLE(2, 3, 0, 1));
let sum = _mm_add_epi32(sum, hi);
_mm_cvtsi128_si32(sum) as _
}
#[inline(always)]
unsafe fn get_weight_lo() -> __m128i {
_mm_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
}
#[inline(always)]
unsafe fn get_weight_hi() -> __m128i {
_mm_set_epi8(
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 1024 - 5]);
#[cfg(not(miri))]
assert_sum_eq(&[0; 1024 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 1024 - 5]); // non-power-of-2 to test remainder handling
#[cfg(not(miri))]
assert_sum_eq(&[1; 1024 * 1024]);
}
#[test]
fn random() {
if super::get_imp().is_none() { return; } // don't do any work if we're not on this target
let mut random = [0; 1024 * 10];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..1024 - 5]); // non-power-of-2 to test remainder handling
assert_sum_eq(&random[..1024 * 10]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+225
View File
@@ -0,0 +1,225 @@
use super::Adler32Imp;
/// Resolves update implementation if CPU supports ssse3 instructions.
pub fn get_imp() -> Option<Adler32Imp> {
get_imp_inner()
}
#[inline]
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
fn get_imp_inner() -> Option<Adler32Imp> {
if std::is_x86_feature_detected!("ssse3") {
Some(imp::update)
} else {
None
}
}
#[inline]
#[cfg(all(
target_feature = "ssse3",
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
Some(imp::update)
}
#[inline]
#[cfg(all(
not(target_feature = "ssse3"),
not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
))]
fn get_imp_inner() -> Option<Adler32Imp> {
None
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "std", target_feature = "ssse3")
))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 32;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
unsafe { update_imp(a, b, data) }
}
#[inline]
#[target_feature(enable = "ssse3")]
unsafe fn update_imp(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
unsafe fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
*a %= MOD;
*b %= MOD;
}
unsafe fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
#[inline(always)]
unsafe fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
let one_v = _mm_set1_epi16(1);
let zero_v = _mm_set1_epi16(0);
let weight_hi_v = get_weight_hi();
let weight_lo_v = get_weight_lo();
let mut p_v = _mm_set_epi32(0, 0, 0, (*a * blocks.len() as u32) as _);
let mut a_v = _mm_set_epi32(0, 0, 0, 0);
let mut b_v = _mm_set_epi32(0, 0, 0, *b as _);
for block in blocks {
let block_ptr = block.as_ptr() as *const _;
let left_v = _mm_loadu_si128(block_ptr);
let right_v = _mm_loadu_si128(block_ptr.add(1));
p_v = _mm_add_epi32(p_v, a_v);
a_v = _mm_add_epi32(a_v, _mm_sad_epu8(left_v, zero_v));
let mad = _mm_maddubs_epi16(left_v, weight_hi_v);
b_v = _mm_add_epi32(b_v, _mm_madd_epi16(mad, one_v));
a_v = _mm_add_epi32(a_v, _mm_sad_epu8(right_v, zero_v));
let mad = _mm_maddubs_epi16(right_v, weight_lo_v);
b_v = _mm_add_epi32(b_v, _mm_madd_epi16(mad, one_v));
}
b_v = _mm_add_epi32(b_v, _mm_slli_epi32(p_v, 5));
*a += reduce_add(a_v);
*b = reduce_add(b_v);
blocks_remainder
}
#[inline(always)]
unsafe fn reduce_add(v: __m128i) -> u32 {
let hi = _mm_unpackhi_epi64(v, v);
let sum = _mm_add_epi32(hi, v);
let hi = _mm_shuffle_epi32(sum, crate::imp::_MM_SHUFFLE(2, 3, 0, 1));
let sum = _mm_add_epi32(sum, hi);
_mm_cvtsi128_si32(sum) as _
}
#[inline(always)]
unsafe fn get_weight_lo() -> __m128i {
_mm_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
}
#[inline(always)]
unsafe fn get_weight_hi() -> __m128i {
_mm_set_epi8(
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 1024 - 5]);
#[cfg(not(miri))]
assert_sum_eq(&[0; 1024 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 1024 - 5]); // non-power-of-2 to test remainder handling
#[cfg(not(miri))]
assert_sum_eq(&[1; 1024 * 1024]);
}
#[test]
fn random() {
if super::get_imp().is_none() { return; } // don't do any work if we're not on this target
let mut random = [0; 1024 * 10];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..1024 - 5]); // non-power-of-2 to test remainder handling
assert_sum_eq(&random[..1024 * 10]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+236
View File
@@ -0,0 +1,236 @@
use super::Adler32Imp;
/// Resolves update implementation if CPU supports simd128 instructions.
pub fn get_imp() -> Option<Adler32Imp> {
get_imp_inner()
}
#[inline]
#[cfg(all(
target_feature = "simd128",
any(
target_arch = "wasm32",
all(feature = "nightly", target_arch = "wasm64")
)
))]
fn get_imp_inner() -> Option<Adler32Imp> {
Some(imp::update)
}
#[inline]
#[cfg(not(all(
target_feature = "simd128",
any(
target_arch = "wasm32",
all(feature = "nightly", target_arch = "wasm64")
)
)))]
fn get_imp_inner() -> Option<Adler32Imp> {
None
}
#[cfg(all(
target_feature = "simd128",
any(
target_arch = "wasm32",
all(feature = "nightly", target_arch = "wasm64")
)
))]
mod imp {
const MOD: u32 = 65521;
const NMAX: usize = 5552;
const BLOCK_SIZE: usize = 32;
const CHUNK_SIZE: usize = NMAX / BLOCK_SIZE * BLOCK_SIZE;
#[cfg(target_arch = "wasm32")]
use core::arch::wasm32::*;
#[cfg(target_arch = "wasm64")]
use core::arch::wasm64::*;
pub fn update(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
update_imp(a, b, data)
}
#[inline]
#[target_feature(enable = "simd128")]
fn update_imp(a: u16, b: u16, data: &[u8]) -> (u16, u16) {
let mut a = a as u32;
let mut b = b as u32;
let chunks = data.chunks_exact(CHUNK_SIZE);
let remainder = chunks.remainder();
for chunk in chunks {
update_chunk_block(&mut a, &mut b, chunk);
}
update_block(&mut a, &mut b, remainder);
(a as u16, b as u16)
}
fn update_chunk_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert_eq!(
chunk.len(),
CHUNK_SIZE,
"Unexpected chunk size (expected {}, got {})",
CHUNK_SIZE,
chunk.len()
);
reduce_add_blocks(a, b, chunk);
*a %= MOD;
*b %= MOD;
}
fn update_block(a: &mut u32, b: &mut u32, chunk: &[u8]) {
debug_assert!(
chunk.len() <= CHUNK_SIZE,
"Unexpected chunk size (expected <= {}, got {})",
CHUNK_SIZE,
chunk.len()
);
for byte in reduce_add_blocks(a, b, chunk) {
*a += *byte as u32;
*b += *a;
}
*a %= MOD;
*b %= MOD;
}
#[inline(always)]
fn reduce_add_blocks<'a>(a: &mut u32, b: &mut u32, chunk: &'a [u8]) -> &'a [u8] {
if chunk.len() < BLOCK_SIZE {
return chunk;
}
let blocks = chunk.chunks_exact(BLOCK_SIZE);
let blocks_remainder = blocks.remainder();
let weight_hi_v = get_weight_hi();
let weight_lo_v = get_weight_lo();
let mut p_v = u32x4(*a * blocks.len() as u32, 0, 0, 0);
let mut a_v = u32x4(0, 0, 0, 0);
let mut b_v = u32x4(*b, 0, 0, 0);
for block in blocks {
let block_ptr = block.as_ptr() as *const v128;
let v_lo = unsafe { block_ptr.read_unaligned() };
let v_hi = unsafe { block_ptr.add(1).read_unaligned() };
p_v = u32x4_add(p_v, a_v);
a_v = u32x4_add(a_v, u32x4_extadd_quarters_u8x16(v_lo));
let mad = i32x4_dot_i8x16(v_lo, weight_lo_v);
b_v = u32x4_add(b_v, mad);
a_v = u32x4_add(a_v, u32x4_extadd_quarters_u8x16(v_hi));
let mad = i32x4_dot_i8x16(v_hi, weight_hi_v);
b_v = u32x4_add(b_v, mad);
}
b_v = u32x4_add(b_v, u32x4_shl(p_v, 5));
*a += reduce_add(a_v);
*b = reduce_add(b_v);
blocks_remainder
}
#[inline(always)]
fn i32x4_dot_i8x16(a: v128, b: v128) -> v128 {
let a_lo = u16x8_extend_low_u8x16(a);
let a_hi = u16x8_extend_high_u8x16(a);
let b_lo = u16x8_extend_low_u8x16(b);
let b_hi = u16x8_extend_high_u8x16(b);
let lo = i32x4_dot_i16x8(a_lo, b_lo);
let hi = i32x4_dot_i16x8(a_hi, b_hi);
i32x4_add(lo, hi)
}
#[inline(always)]
fn u32x4_extadd_quarters_u8x16(a: v128) -> v128 {
u32x4_extadd_pairwise_u16x8(u16x8_extadd_pairwise_u8x16(a))
}
#[inline(always)]
fn reduce_add(v: v128) -> u32 {
let arr: [u32; 4] = unsafe { core::mem::transmute(v) };
let mut sum = 0u32;
for val in arr {
sum = sum.wrapping_add(val);
}
sum
}
#[inline(always)]
fn get_weight_lo() -> v128 {
u8x16(
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
)
}
#[inline(always)]
fn get_weight_hi() -> v128 {
u8x16(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
}
}
#[cfg(test)]
mod tests {
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[test]
fn zeroes() {
assert_sum_eq(&[]);
assert_sum_eq(&[0]);
assert_sum_eq(&[0, 0]);
assert_sum_eq(&[0; 100]);
assert_sum_eq(&[0; 1024]);
assert_sum_eq(&[0; 512 * 1024]);
}
#[test]
fn ones() {
assert_sum_eq(&[]);
assert_sum_eq(&[1]);
assert_sum_eq(&[1, 1]);
assert_sum_eq(&[1; 100]);
assert_sum_eq(&[1; 1024]);
assert_sum_eq(&[1; 512 * 1024]);
}
#[test]
fn random() {
if super::get_imp().is_none() { return; } // don't do any work if we're not on this target
let mut random = [0; 512 * 1024];
SmallRng::from_entropy().fill(&mut random[..]);
assert_sum_eq(&random[..1]);
assert_sum_eq(&random[..100]);
assert_sum_eq(&random[..1024]);
assert_sum_eq(&random[..512 * 1024]);
}
/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_sum_eq(b"Wikipedia");
}
fn assert_sum_eq(data: &[u8]) {
if let Some(update) = super::get_imp() {
let (a, b) = update(1, 0, data);
let left = u32::from(b) << 16 | u32::from(a);
let right = adler2::adler32_slice(data);
assert_eq!(left, right, "len({})", data.len());
}
}
}
+325
View File
@@ -0,0 +1,325 @@
//! # simd-adler32
//!
//! A SIMD-accelerated Adler-32 hash algorithm implementation.
//!
//! ## Features
//!
//! - No dependencies
//! - Support `no_std` (with `default-features = false`)
//! - Runtime CPU feature detection (when `std` enabled)
//! - Blazing fast performance on as many targets as possible (currently only x86 and x86_64)
//! - Default to scalar implementation when simd not available
//!
//! ## Quick start
//!
//! > Cargo.toml
//!
//! ```toml
//! [dependencies]
//! simd-adler32 = "*"
//! ```
//!
//! > example.rs
//!
//! ```rust
//! use simd_adler32::Adler32;
//!
//! let mut adler = Adler32::new();
//! adler.write(b"rust is pretty cool, man");
//! let hash = adler.finish();
//!
//! println!("{}", hash);
//! // 1921255656
//! ```
//!
//! ## Feature flags
//!
//! * `std` - Enabled by default
//!
//! Enables std support, see [CPU Feature Detection](#cpu-feature-detection) for runtime
//! detection support.
//! * `nightly`
//!
//! Enables nightly features required for avx512 support.
//!
//! * `const-generics` - Enabled by default
//!
//! Enables const-generics support allowing for user-defined array hashing by value. See
//! [`Adler32Hash`] for details.
//!
//! ## Support
//!
//! **CPU Features**
//!
//! | impl | arch | feature |
//! | ---- | ---------------- | ------- |
//! | ✅ | `x86`, `x86_64` | avx512 |
//! | ✅ | `x86`, `x86_64` | avx2 |
//! | ✅ | `x86`, `x86_64` | ssse3 |
//! | ✅ | `x86`, `x86_64` | sse2 |
//! | 🚧 | `arm`, `aarch64` | neon |
//! | | `wasm32` | simd128 |
//!
//! **MSRV** `1.36.0`\*\*
//!
//! Minimum supported rust version is tested before a new version is published. [**] Feature
//! `const-generics` needs to disabled to build on rustc versions `<1.51` which can be done
//! by updating your dependency definition to the following.
//!
//! ## CPU Feature Detection
//! simd-adler32 supports both runtime and compile time CPU feature detection using the
//! `std::is_x86_feature_detected` macro when the `Adler32` struct is instantiated with
//! the `new` fn.
//!
//! Without `std` feature enabled simd-adler32 falls back to compile time feature detection
//! using `target-feature` or `target-cpu` flags supplied to rustc. See [https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html](https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html)
//! for more information.
//!
//! Feature detection tries to use the fastest supported feature first.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(
all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64")),
feature(stdarch_x86_avx512, avx512_target_feature)
)]
#![cfg_attr(
all(feature = "nightly", target_arch = "arm"),
feature(stdarch_arm_neon_intrinsics)
)]
#![cfg_attr(
all(
feature = "nightly",
target_arch = "wasm64",
target_feature = "simd128"
),
feature(simd_wasm64)
)]
#[doc(hidden)]
pub mod hash;
#[doc(hidden)]
pub mod imp;
pub use hash::*;
use imp::{get_imp, Adler32Imp};
/// An adler32 hash generator type.
#[derive(Clone)]
pub struct Adler32 {
a: u16,
b: u16,
update: Adler32Imp,
}
impl Adler32 {
/// Constructs a new `Adler32`.
///
/// Potential overhead here due to runtime feature detection although in testing on 100k
/// and 10k random byte arrays it was not really noticeable.
///
/// # Examples
/// ```rust
/// use simd_adler32::Adler32;
///
/// let mut adler = Adler32::new();
/// ```
pub fn new() -> Self {
Default::default()
}
/// Constructs a new `Adler32` using existing checksum.
///
/// Potential overhead here due to runtime feature detection although in testing on 100k
/// and 10k random byte arrays it was not really noticeable.
///
/// # Examples
/// ```rust
/// use simd_adler32::Adler32;
///
/// let mut adler = Adler32::from_checksum(0xdeadbeaf);
/// ```
pub fn from_checksum(checksum: u32) -> Self {
Self {
a: checksum as u16,
b: (checksum >> 16) as u16,
update: get_imp(),
}
}
/// Computes hash for supplied data and stores results in internal state.
pub fn write(&mut self, data: &[u8]) {
let (a, b) = (self.update)(self.a, self.b, data);
self.a = a;
self.b = b;
}
/// Returns the hash value for the values written so far.
///
/// Despite its name, the method does not reset the hashers internal state. Additional
/// writes will continue from the current value. If you need to start a fresh hash
/// value, you will have to use `reset`.
pub fn finish(&self) -> u32 {
(u32::from(self.b) << 16) | u32::from(self.a)
}
/// Resets the internal state.
pub fn reset(&mut self) {
self.a = 1;
self.b = 0;
}
}
/// Compute Adler-32 hash on `Adler32Hash` type.
///
/// # Arguments
/// * `hash` - A Adler-32 hash-able type.
///
/// # Examples
/// ```rust
/// use simd_adler32::adler32;
///
/// let hash = adler32(b"Adler-32");
/// println!("{}", hash); // 800813569
/// ```
pub fn adler32<H: Adler32Hash>(hash: &H) -> u32 {
hash.hash()
}
/// A Adler-32 hash-able type.
pub trait Adler32Hash {
/// Feeds this value into `Adler32`.
fn hash(&self) -> u32;
}
impl Default for Adler32 {
fn default() -> Self {
Self {
a: 1,
b: 0,
update: get_imp(),
}
}
}
#[cfg(feature = "std")]
pub mod read {
//! Reader-based hashing.
//!
//! # Example
//! ```rust
//! use std::io::Cursor;
//! use simd_adler32::read::adler32;
//!
//! let mut reader = Cursor::new(b"Hello there");
//! let hash = adler32(&mut reader).unwrap();
//!
//! println!("{}", hash) // 800813569
//! ```
use crate::Adler32;
use std::io::{Read, Result};
/// Compute Adler-32 hash on reader until EOF.
///
/// # Example
/// ```rust
/// use std::io::Cursor;
/// use simd_adler32::read::adler32;
///
/// let mut reader = Cursor::new(b"Hello there");
/// let hash = adler32(&mut reader).unwrap();
///
/// println!("{}", hash) // 800813569
/// ```
pub fn adler32<R: Read>(reader: &mut R) -> Result<u32> {
let mut hash = Adler32::new();
let mut buf = [0; 4096];
loop {
match reader.read(&mut buf) {
Ok(0) => return Ok(hash.finish()),
Ok(n) => {
hash.write(&buf[..n]);
}
Err(err) => return Err(err),
}
}
}
}
#[cfg(feature = "std")]
pub mod bufread {
//! BufRead-based hashing.
//!
//! Separate `BufRead` trait implemented to allow for custom buffer size optimization.
//!
//! # Example
//! ```rust
//! use std::io::{Cursor, BufReader};
//! use simd_adler32::bufread::adler32;
//!
//! let mut reader = Cursor::new(b"Hello there");
//! let mut reader = BufReader::new(reader);
//! let hash = adler32(&mut reader).unwrap();
//!
//! println!("{}", hash) // 800813569
//! ```
use crate::Adler32;
use std::io::{BufRead, ErrorKind, Result};
/// Compute Adler-32 hash on buf reader until EOF.
///
/// # Example
/// ```rust
/// use std::io::{Cursor, BufReader};
/// use simd_adler32::bufread::adler32;
///
/// let mut reader = Cursor::new(b"Hello there");
/// let mut reader = BufReader::new(reader);
/// let hash = adler32(&mut reader).unwrap();
///
/// println!("{}", hash) // 800813569
/// ```
pub fn adler32<R: BufRead>(reader: &mut R) -> Result<u32> {
let mut hash = Adler32::new();
loop {
let consumed = match reader.fill_buf() {
Ok(buf) => {
if buf.is_empty() {
return Ok(hash.finish());
}
hash.write(buf);
buf.len()
}
Err(err) => match err.kind() {
ErrorKind::Interrupted => continue,
ErrorKind::UnexpectedEof => return Ok(hash.finish()),
_ => return Err(err),
},
};
reader.consume(consumed);
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_from_checksum() {
let buf = b"rust is pretty cool man";
let sum = 0xdeadbeaf;
let mut simd = super::Adler32::from_checksum(sum);
let mut adler = adler2::Adler32::from_checksum(sum);
simd.write(buf);
adler.write_slice(buf);
let simd = simd.finish();
let scalar = adler.checksum();
assert_eq!(simd, scalar);
}
}