Repository: RustCrypto/hashes (language: rust)

Issue:
Update crypto-mac to 0.11 in blake2
Trying to cleanup a dependency tree where we have 3 versions of crypto-mac, one of them from blake2. Would be nice if you could update it.

I tried doing it myself but can't figure out why tests are broken. Here's the diff:

```diff
diff --git a/blake2/Cargo.toml b/blake2/Cargo.toml
index 6d25e15..925a325 100644
--- a/blake2/Cargo.toml
+++ b/blake2/Cargo.toml
@@ -13,12 +13,12 @@ categories = ["cryptography", "no-std"]

 [dependencies]
 digest = "0.9"
-crypto-mac = "0.8"
+crypto-mac = "0.11.1"
 opaque-debug = "0.3"

 [dev-dependencies]
 digest = { version = "0.9", features = ["dev"] }
-crypto-mac = { version = "0.8", features = ["dev"] }
+crypto-mac = { version = "0.11.1", features = ["dev"] }
 hex-literal = "0.2"

 [features]
diff --git a/blake2/src/blake2.rs b/blake2/src/blake2.rs
index 3255a30..e7ea513 100644
--- a/blake2/src/blake2.rs
+++ b/blake2/src/blake2.rs
@@ -375,7 +375,7 @@ macro_rules! blake2_impl {
                 Self { state }
             }

-            fn new_varkey(key: &[u8]) -> Result<Self, InvalidKeyLength> {
+            fn new_from_slice(key: &[u8]) -> Result<Self, InvalidKeyLength> {
                 if key.len() > $bytes::to_usize() {
                     Err(InvalidKeyLength)
                 } else {
```




Discussion / hints:
`crypto-mac` functionality will be merged into `digest v0.10`, so this issue will be fixed in the next `blake2` release, see #217.

The tests are failing because storage format of test vectors has changed a bit between `crypto-mac` v0.10 and v0.11

Fix patch:
diff --git a/.github/workflows/blake2.yml b/.github/workflows/blake2.yml
index 6aed43dc6..51f49050a 100644
--- a/.github/workflows/blake2.yml
+++ b/.github/workflows/blake2.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/fsb.yml b/.github/workflows/fsb.yml
index f8a36ffa4..3a1b1a51d 100644
--- a/.github/workflows/fsb.yml
+++ b/.github/workflows/fsb.yml
@@ -22,7 +22,7 @@ jobs:
     strategy:
       matrix:
         rust:
-          - 1.47.0 # MSRV
+          - 1.41.0 # MSRV
           - stable
         target:
           - thumbv7em-none-eabi
@@ -35,14 +35,14 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
     strategy:
       matrix:
         rust:
-          - 1.47.0 # MSRV
+          - 1.41.0 # MSRV
           - stable
     steps:
       - uses: actions/checkout@v1
@@ -54,5 +54,4 @@ jobs:
       - run: cargo check --all-features
       - run: cargo test --no-default-features
       - run: cargo test
-      - run: cargo test --features asm
       - run: cargo test --all-features
diff --git a/.github/workflows/gost94.yml b/.github/workflows/gost94.yml
index 10cbaee42..99c2a8feb 100644
--- a/.github/workflows/gost94.yml
+++ b/.github/workflows/gost94.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/groestl.yml b/.github/workflows/groestl.yml
index 411d866d3..481e69718 100644
--- a/.github/workflows/groestl.yml
+++ b/.github/workflows/groestl.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/k12.yml b/.github/workflows/k12.yml
index 7e993c8a5..5899fa6fd 100644
--- a/.github/workflows/k12.yml
+++ b/.github/workflows/k12.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/md2.yml b/.github/workflows/md2.yml
index f9b090009..26a2cf675 100644
--- a/.github/workflows/md2.yml
+++ b/.github/workflows/md2.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/md4.yml b/.github/workflows/md4.yml
index 9c4bde676..84a15538f 100644
--- a/.github/workflows/md4.yml
+++ b/.github/workflows/md4.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/md5.yml b/.github/workflows/md5.yml
index 0fbfa4dff..86cab99c2 100644
--- a/.github/workflows/md5.yml
+++ b/.github/workflows/md5.yml
@@ -38,7 +38,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/ripemd320.yml b/.github/workflows/ripemd.yml
similarity index 86%
rename from .github/workflows/ripemd320.yml
rename to .github/workflows/ripemd.yml
index dbd541b01..4168cc285 100644
--- a/.github/workflows/ripemd320.yml
+++ b/.github/workflows/ripemd.yml
@@ -1,16 +1,16 @@
-name: ripemd320
+name: ripemd
 
 on:
   pull_request:
     paths:
-      - "ripemd320/**"
+      - "ripemd/**"
       - "Cargo.*"
   push:
     branches: master
 
 defaults:
   run:
-    working-directory: ripemd320
+    working-directory: ripemd
 
 env:
   CARGO_INCREMENTAL: 0
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/ripemd160.yml b/.github/workflows/ripemd160.yml
deleted file mode 100644
index 7d78d12d6..000000000
--- a/.github/workflows/ripemd160.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: ripemd160
-
-on:
-  pull_request:
-    paths:
-      - "ripemd160/**"
-      - "Cargo.*"
-  push:
-    branches: master
-
-defaults:
-  run:
-    working-directory: ripemd160
-
-env:
-  CARGO_INCREMENTAL: 0
-  RUSTFLAGS: "-Dwarnings"
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        rust:
-          - 1.41.0 # MSRV
-          - stable
-        target:
-          - thumbv7em-none-eabi
-          - wasm32-unknown-unknown
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: ${{ matrix.rust }}
-          target: ${{ matrix.target }}
-          override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
-
-  test:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        rust:
-          - 1.41.0 # MSRV
-          - stable
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: ${{ matrix.rust }}
-          override: true
-      - run: cargo check --all-features
-      - run: cargo test --no-default-features
-      - run: cargo test
-      - run: cargo test --all-features
-
diff --git a/.github/workflows/ripemd256.yml b/.github/workflows/ripemd256.yml
deleted file mode 100644
index 324f85b5e..000000000
--- a/.github/workflows/ripemd256.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: ripemd256
-
-on:
-  pull_request:
-    paths:
-      - "ripemd256/**"
-      - "Cargo.*"
-  push:
-    branches: master
-
-defaults:
-  run:
-    working-directory: ripemd256
-
-env:
-  CARGO_INCREMENTAL: 0
-  RUSTFLAGS: "-Dwarnings"
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        rust:
-          - 1.41.0 # MSRV
-          - stable
-        target:
-          - thumbv7em-none-eabi
-          - wasm32-unknown-unknown
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: ${{ matrix.rust }}
-          target: ${{ matrix.target }}
-          override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
-
-  test:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        rust:
-          - 1.41.0 # MSRV
-          - stable
-    steps:
-      - uses: actions/checkout@v1
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: ${{ matrix.rust }}
-          override: true
-      - run: cargo check --all-features
-      - run: cargo test --no-default-features
-      - run: cargo test
-      - run: cargo test --all-features
-
diff --git a/.github/workflows/sha1.yml b/.github/workflows/sha1.yml
index 803cc99bf..2b5960946 100644
--- a/.github/workflows/sha1.yml
+++ b/.github/workflows/sha1.yml
@@ -36,7 +36,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   # Linux tests
   linux:
@@ -69,10 +69,10 @@ jobs:
           target: ${{ matrix.target }}
           override: true
       - run: ${{ matrix.deps }}
-      - run: cargo test --target ${{ matrix.target }} --release --no-default-features
-      - run: cargo test --target ${{ matrix.target }} --release
-      - run: cargo test --target ${{ matrix.target }} --release --features asm
-      - run: cargo test --target ${{ matrix.target }} --release --all-features
+      - run: cargo test --target ${{ matrix.target }} --no-default-features
+      - run: cargo test --target ${{ matrix.target }}
+      - run: cargo test --target ${{ matrix.target }} --features asm
+      - run: cargo test --target ${{ matrix.target }} --all-features
 
   # macOS tests
   macos:
@@ -91,10 +91,10 @@ jobs:
           toolchain: ${{ matrix.toolchain }}
           target: x86_64-apple-darwin
           override: true
-      - run: cargo test --release --no-default-features
-      - run: cargo test --release
-      - run: cargo test --release --features asm
-      - run: cargo test --release --all-features
+      - run: cargo test --no-default-features
+      - run: cargo test
+      - run: cargo test --features asm
+      - run: cargo test --all-features
 
   # Windows tests
   windows:
@@ -118,7 +118,7 @@ jobs:
           target: ${{ matrix.target }}
           override: true
       - uses: msys2/setup-msys2@v2
-      - run: cargo test --target ${{ matrix.target }} --release
+      - run: cargo test --target ${{ matrix.target }}
 
   # Cross-compiled tests
   # *** NOTE: Currently broken with `asm` feature enabled! See:
@@ -144,5 +144,5 @@ jobs:
   #          target: ${{ matrix.target }}
   #          override: true
   #      - run: cargo install cross
-  #      - run: cross test --target ${{ matrix.target }} --release
-  #      - run: cross test --target ${{ matrix.target }} --release --features asm
+  #      - run: cross test --target ${{ matrix.target }}
+  #      - run: cross test --target ${{ matrix.target }} --features asm
diff --git a/.github/workflows/sha2.yml b/.github/workflows/sha2.yml
index 75ae8d9a6..6b2227292 100644
--- a/.github/workflows/sha2.yml
+++ b/.github/workflows/sha2.yml
@@ -36,7 +36,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   # Linux tests
   linux:
@@ -67,10 +67,10 @@ jobs:
           target: ${{ matrix.target }}
           override: true
       - run: ${{ matrix.deps }}
-      - run: cargo test --target ${{ matrix.target }} --release --no-default-features
-      - run: cargo test --target ${{ matrix.target }} --release
-      - run: cargo test --target ${{ matrix.target }} --release --features asm
-      - run: cargo test --target ${{ matrix.target }} --release --all-features
+      - run: cargo test --target ${{ matrix.target }} --no-default-features
+      - run: cargo test --target ${{ matrix.target }}
+      - run: cargo test --target ${{ matrix.target }} --features asm
+      - run: cargo test --target ${{ matrix.target }} --all-features
 
   # macOS tests
   macos:
@@ -89,9 +89,9 @@ jobs:
           toolchain: ${{ matrix.toolchain }}
           target: x86_64-apple-darwin
           override: true
-      - run: cargo test --release --no-default-features
-      - run: cargo test --release
-      - run: cargo test --release --features asm
+      - run: cargo test --no-default-features
+      - run: cargo test
+      - run: cargo test --features asm
 
   # Windows tests
   windows:
@@ -115,7 +115,7 @@ jobs:
           target: ${{ matrix.target }}
           override: true
       - uses: msys2/setup-msys2@v2
-      - run: cargo test --target ${{ matrix.target }} --release
+      - run: cargo test --target ${{ matrix.target }}
 
   # Cross-compiled tests
   cross:
@@ -129,6 +129,10 @@ jobs:
             rust: stable
 
     runs-on: ubuntu-latest
+    defaults:
+      run:
+      # Cross mounts only current package, i.e. by default it ignores workspace's Cargo.toml
+        working-directory: .
     steps:
       - uses: actions/checkout@v1
       - uses: actions-rs/toolchain@v1
@@ -137,6 +141,11 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo install cross
-      - run: cross test --target ${{ matrix.target }} --release
-      - run: cross test --target ${{ matrix.target }} --release --features asm
+      - name: Install precompiled cross
+        run: |
+          export URL=$(curl -s https://api.github.com/repos/rust-embedded/cross/releases/latest | jq -r '.assets[] | select(.name | contains("x86_64-unknown-linux-gnu.tar.gz")) | .browser_download_url')
+          wget -O /tmp/binaries.tar.gz $URL
+          tar -C /tmp -xzf /tmp/binaries.tar.gz
+          mv /tmp/cross ~/.cargo/bin
+      - run: cross test --package sha2 --target ${{ matrix.target }}
+      - run: cross test --package sha2 --target ${{ matrix.target }} --features asm
diff --git a/.github/workflows/sha3.yml b/.github/workflows/sha3.yml
index 94abb4a30..6e9c9dd1d 100644
--- a/.github/workflows/sha3.yml
+++ b/.github/workflows/sha3.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/shabal.yml b/.github/workflows/shabal.yml
index f9d198b37..dfb7f5867 100644
--- a/.github/workflows/shabal.yml
+++ b/.github/workflows/shabal.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/sm3.yml b/.github/workflows/sm3.yml
index e954778f6..8f353134f 100644
--- a/.github/workflows/sm3.yml
+++ b/.github/workflows/sm3.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/streebog.yml b/.github/workflows/streebog.yml
index b523c45ba..46f00fd50 100644
--- a/.github/workflows/streebog.yml
+++ b/.github/workflows/streebog.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
@@ -55,4 +55,3 @@ jobs:
       - run: cargo test --no-default-features
       - run: cargo test
       - run: cargo test --all-features
-
diff --git a/.github/workflows/tiger.yml b/.github/workflows/tiger.yml
index 8e1e6ff59..273078bf8 100644
--- a/.github/workflows/tiger.yml
+++ b/.github/workflows/tiger.yml
@@ -38,7 +38,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/whirlpool.yml b/.github/workflows/whirlpool.yml
index 42c2b4704..fd506d74c 100644
--- a/.github/workflows/whirlpool.yml
+++ b/.github/workflows/whirlpool.yml
@@ -35,7 +35,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           target: ${{ matrix.target }}
           override: true
-      - run: cargo build --no-default-features --release --target ${{ matrix.target }}
+      - run: cargo build --no-default-features --target ${{ matrix.target }}
 
   test:
     runs-on: ubuntu-latest
diff --git a/Cargo.lock b/Cargo.lock
index cba495698..299f76dcf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,50 +4,32 @@ version = 3
 
 [[package]]
 name = "blake2"
-version = "0.9.2"
+version = "0.10.0"
 dependencies = [
- "crypto-mac",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "blobby"
-version = "0.1.2"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fe5f8c2940b65859ece4b3b2ba02d2b12c87cab455fd42dee2556a187bb2cf6"
-dependencies = [
- "byteorder",
-]
+checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec"
 
 [[package]]
 name = "block-buffer"
-version = "0.9.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
 dependencies = [
- "block-padding",
  "generic-array",
 ]
 
-[[package]]
-name = "block-padding"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
 [[package]]
 name = "cc"
-version = "1.0.69"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
 [[package]]
 name = "cfg-if"
@@ -65,34 +47,33 @@ dependencies = [
 ]
 
 [[package]]
-name = "crypto-mac"
-version = "0.8.0"
+name = "crypto-common"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
+checksum = "567569e659735adb39ff2d4c20600f7cd78be5471f8c58ab162bce3c03fdbc5f"
 dependencies = [
- "blobby",
  "generic-array",
- "subtle",
 ]
 
 [[package]]
 name = "digest"
-version = "0.9.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+checksum = "8549e6bfdecd113b7e221fe60b433087f6957387a20f8118ebca9b12af19143d"
 dependencies = [
  "blobby",
+ "block-buffer",
+ "crypto-common",
  "generic-array",
+ "subtle",
 ]
 
 [[package]]
 name = "fsb"
-version = "0.0.2"
+version = "0.1.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
  "whirlpool",
 ]
 
@@ -108,22 +89,18 @@ dependencies = [
 
 [[package]]
 name = "gost94"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "groestl"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
@@ -147,7 +124,7 @@ dependencies = [
 
 [[package]]
 name = "k12"
-version = "0.1.0"
+version = "0.2.0"
 dependencies = [
  "digest",
  "hex-literal",
@@ -161,56 +138,44 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
 
 [[package]]
 name = "libc"
-version = "0.2.101"
+version = "0.2.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
+checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
 
 [[package]]
 name = "md-5"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
  "md5-asm",
- "opaque-debug",
 ]
 
 [[package]]
 name = "md2"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "md4"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "md5-asm"
-version = "0.4.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b47e5a1261ecd6ba951d1ad392534743310e513ef1e2609023368798829f33e"
+checksum = "73b9a6f25ec11ea27e22d7fc8beafda909da44ece95f63e94f1eeb23d19bb5c7"
 dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "opaque-debug"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-
 [[package]]
 name = "proc-macro-hack"
 version = "0.5.19"
@@ -218,45 +183,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
 [[package]]
-name = "ripemd160"
-version = "0.9.1"
-dependencies = [
- "block-buffer",
- "digest",
- "hex-literal",
- "opaque-debug",
-]
-
-[[package]]
-name = "ripemd256"
+name = "ripemd"
 version = "0.1.0"
 dependencies = [
- "block-buffer",
- "digest",
- "hex-literal",
- "opaque-debug",
-]
-
-[[package]]
-name = "ripemd320"
-version = "0.9.0"
-dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "sha-1"
-version = "0.9.8"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "cfg-if",
  "cpufeatures",
  "digest",
  "hex-literal",
- "opaque-debug",
  "sha1-asm",
 ]
 
@@ -271,14 +212,12 @@ dependencies = [
 
 [[package]]
 name = "sha2"
-version = "0.9.8"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "cfg-if",
  "cpufeatures",
  "digest",
  "hex-literal",
- "opaque-debug",
  "sha2-asm",
 ]
 
@@ -293,43 +232,35 @@ dependencies = [
 
 [[package]]
 name = "sha3"
-version = "0.9.1"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
  "keccak",
- "opaque-debug",
 ]
 
 [[package]]
 name = "shabal"
-version = "0.3.0"
+version = "0.4.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "sm3"
-version = "0.3.0"
+version = "0.4.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
 name = "streebog"
-version = "0.9.2"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
 ]
 
 [[package]]
@@ -340,19 +271,17 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
 [[package]]
 name = "tiger"
-version = "0.1.0"
+version = "0.2.0"
 dependencies = [
- "block-buffer",
- "byteorder",
  "digest",
  "hex-literal",
 ]
 
 [[package]]
 name = "typenum"
-version = "1.13.0"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
+checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
 
 [[package]]
 name = "version_check"
@@ -362,20 +291,18 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 
 [[package]]
 name = "whirlpool"
-version = "0.9.0"
+version = "0.10.0"
 dependencies = [
- "block-buffer",
  "digest",
  "hex-literal",
- "opaque-debug",
  "whirlpool-asm",
 ]
 
 [[package]]
 name = "whirlpool-asm"
-version = "0.5.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbb23be8b0dec6fafef14563c42ec98bbfc33f2a97ed5bc99cd8b50ea5c68097"
+checksum = "4b0930846e800a97c78fd09a494b25d1f0780be9face03b7a05151e3104a8284"
 dependencies = [
  "cc",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 00547e603..acf46a65c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,16 +1,14 @@
 [workspace]
 members = [
-    "fsb",
     "blake2",
+    "fsb",
     "gost94",
     "groestl",
     "k12",
     "md2",
     "md4",
     "md5",
-    "ripemd160",
-    "ripemd256",
-    "ripemd320",
+    "ripemd",
     "sha1",
     "sha2",
     "sha3",
diff --git a/README.md b/README.md
index c2b1fb186..c7e0b85e6 100644
--- a/README.md
+++ b/README.md
@@ -1,99 +1,91 @@
-# RustCrypto: hashes [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link]
+# RustCrypto: Hashes
+
+[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image]
 
 Collection of [cryptographic hash functions][1] written in pure Rust.
 
-All algorithms reside in the separate crates and implemented using traits from
-[`digest`](https://docs.rs/digest/) crate. Additionally all crates do not
-require the standard library (i.e. `no_std` capable) and can be easily used for
-bare-metal or WebAssembly programming.
-
-## Supported algorithms
-**Note:** For new applications, or where compatibility with other existing
-standards is not a primary concern, we strongly recommend to use either
-BLAKE2, SHA-2 or SHA-3.
-
-| Name        | Algorithm  | Crates.io | Documentation | Build Status | [Security] |
-|-------------|------------|-----------|---------------|--------------|------------|
-| `blake2`    | [BLAKE2] | [![crates.io](https://img.shields.io/crates/v/blake2.svg)](https://crates.io/crates/blake2) | [![Documentation](https://docs.rs/blake2/badge.svg)](https://docs.rs/blake2) | [![build](https://github.com/rustcrypto/hashes/workflows/blake2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:blake2+branch:master) | :green_heart: |
-| `fsb`       | [FSB] | [![crates.io](https://img.shields.io/crates/v/fsb.svg)](https://crates.io/crates/fsb) | [![Documentation](https://docs.rs/fsb/badge.svg)](https://docs.rs/fsb) | [![build](https://github.com/rustcrypto/hashes/workflows/fsb/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/fsb/actions?query=workflow:fsb+branch:master) | :green_heart: |
-| `gost94`    | [GOST94] (GOST R 34.11-94) | [![crates.io](https://img.shields.io/crates/v/gost94.svg)](https://crates.io/crates/gost94) | [![Documentation](https://docs.rs/gost94/badge.svg)](https://docs.rs/gost94) | [![build](https://github.com/rustcrypto/hashes/workflows/gost94/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:gost94+branch:master) | :yellow_heart: |
-| `groestl`   | [Grøstl] (Groestl) | [![crates.io](https://img.shields.io/crates/v/groestl.svg)](https://crates.io/crates/groestl) | [![Documentation](https://docs.rs/groestl/badge.svg)](https://docs.rs/groestl) | [![build](https://github.com/rustcrypto/hashes/workflows/groestl/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:groestl+branch:master) | :green_heart: |
-| `k12`       | [KangarooTwelve] | [![crates.io](https://img.shields.io/crates/v/k12.svg)](https://crates.io/crates/k12) | [![Documentation](https://docs.rs/k12/badge.svg)](https://docs.rs/k12) | [![build](https://github.com/rustcrypto/hashes/workflows/k12/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:k12+branch:master) | :green_heart: |
-| `md2`       | [MD2] | [![crates.io](https://img.shields.io/crates/v/md2.svg)](https://crates.io/crates/md2) |  [![Documentation](https://docs.rs/md2/badge.svg)](https://docs.rs/md2) | [![build](https://github.com/rustcrypto/hashes/workflows/md2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md2+branch:master) | :broken_heart: |
-| `md4`       | [MD4] | [![crates.io](https://img.shields.io/crates/v/md4.svg)](https://crates.io/crates/md4) |  [![Documentation](https://docs.rs/md4/badge.svg)](https://docs.rs/md4) | [![build](https://github.com/rustcrypto/hashes/workflows/md4/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md4+branch:master) | :broken_heart: |
-| `md-5` [:exclamation:] | [MD5]  | [![crates.io](https://img.shields.io/crates/v/md-5.svg)](https://crates.io/crates/md-5) | [![Documentation](https://docs.rs/md-5/badge.svg)](https://docs.rs/md-5) | [![build](https://github.com/rustcrypto/hashes/workflows/md5/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:md5+branch:master) | :broken_heart: |
-| `ripemd160` | [RIPEMD-160] | [![crates.io](https://img.shields.io/crates/v/ripemd160.svg)](https://crates.io/crates/ripemd160) |  [![Documentation](https://docs.rs/ripemd160/badge.svg)](https://docs.rs/ripemd160) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd160/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd160+branch:master) | :green_heart: |
-| `ripemd256` | [RIPEMD-256] | [![crates.io](https://img.shields.io/crates/v/ripemd256.svg)](https://crates.io/crates/ripemd256) |  [![Documentation](https://docs.rs/ripemd256/badge.svg)](https://docs.rs/ripemd256) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd256/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd256+branch:master) | :green_heart:* |
-| `ripemd320` | [RIPEMD-320] | [![crates.io](https://img.shields.io/crates/v/ripemd320.svg)](https://crates.io/crates/ripemd320) |  [![Documentation](https://docs.rs/ripemd320/badge.svg)](https://docs.rs/ripemd320) | [![build](https://github.com/rustcrypto/hashes/workflows/ripemd320/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:ripemd320+branch:master) | :green_heart:* |
-| `sha-1` [:exclamation:] | [SHA-1] | [![crates.io](https://img.shields.io/crates/v/sha-1.svg)](https://crates.io/crates/sha-1) | [![Documentation](https://docs.rs/sha-1/badge.svg)](https://docs.rs/sha-1) | [![build](https://github.com/rustcrypto/hashes/workflows/sha1/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha1+branch:master) | :broken_heart: |
-| `sha2`      | [SHA-2] | [![crates.io](https://img.shields.io/crates/v/sha2.svg)](https://crates.io/crates/sha2) |  [![Documentation](https://docs.rs/sha2/badge.svg)](https://docs.rs/sha2) | [![build](https://github.com/rustcrypto/hashes/workflows/sha2/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha2+branch:master) | :green_heart: |
-| `sha3`      | [SHA-3] (Keccak) | [![crates.io](https://img.shields.io/crates/v/sha3.svg)](https://crates.io/crates/sha3) |  [![Documentation](https://docs.rs/sha3/badge.svg)](https://docs.rs/sha3) | [![build](https://github.com/rustcrypto/hashes/workflows/sha3/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sha3+branch:master) | :green_heart: |
-| `shabal`    | [SHABAL] | [![crates.io](https://img.shields.io/crates/v/shabal.svg)](https://crates.io/crates/shabal) |  [![Documentation](https://docs.rs/shabal/badge.svg)](https://docs.rs/shabal) | [![build](https://github.com/rustcrypto/hashes/workflows/shabal/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:shabal+branch:master) | :green_heart: |
-| `sm3`       | [SM3 (OSCCA GM/T 0004-2012)][SM3] | [![crates.io](https://img.shields.io/crates/v/sm3.svg)](https://crates.io/crates/sm3) |  [![Documentation](https://docs.rs/sm3/badge.svg)](https://docs.rs/sm3) | [![build](https://github.com/rustcrypto/hashes/workflows/sm3/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:sm3+branch:master) | :green_heart: |
-| `streebog`  | [Streebog] (GOST R 34.11-2012) | [![crates.io](https://img.shields.io/crates/v/streebog.svg)](https://crates.io/crates/streebog) |  [![Documentation](https://docs.rs/streebog/badge.svg)](https://docs.rs/streebog) | [![build](https://github.com/rustcrypto/hashes/workflows/streebog/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:streebog+branch:master) | :yellow_heart: |
-| `tiger`  | [Tiger] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) |  [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | [![build](https://github.com/rustcrypto/hashes/workflows/tiger/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:tiger+branch:master) | :green_heart: |
-| `whirlpool` | [Whirlpool] | [![crates.io](https://img.shields.io/crates/v/whirlpool.svg)](https://crates.io/crates/whirlpool) |  [![Documentation](https://docs.rs/whirlpool/badge.svg)](https://docs.rs/whirlpool) | [![build](https://github.com/rustcrypto/hashes/workflows/whirlpool/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/hashes/actions?query=workflow:whirlpool+branch:master) | :green_heart: |
-
-NOTE: the [BLAKE3 crate](https://github.com/BLAKE3-team/BLAKE3) implements the `digest` (and `crypto-mac`) traits used by the rest of the hashes in this repository, but is maintained by the BLAKE3 team.
-
-\* RIPEMD-256 provides only the same security as RIPEMD-128, and RIPEMD-320 provides only the same security as RIPEMD-160
+All algorithms reside in the separate crates and implemented using traits from [`digest`] crate.
+Additionally all crates do not require the standard library (i.e. `no_std` capable) and can be easily used for bare-metal or WebAssembly programming.
+
+## Supported Algorithms
+
+**Note:** For new applications, or where compatibility with other existing standards is not a primary concern, we strongly recommend to use either BLAKE2, SHA-2 or SHA-3.
+
+| Algorithm | Crate | Crates.io | Documentation | MSRV | [Security] |
+|-----------|-------|:---------:|:-------------:|:----:|:----------:|
+| [BLAKE2] | [`blake2`] | [![crates.io](https://img.shields.io/crates/v/blake2.svg)](https://crates.io/crates/blake2) | [![Documentation](https://docs.rs/blake2/badge.svg)](https://docs.rs/blake2) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [FSB] | [`fsb`] | [![crates.io](https://img.shields.io/crates/v/fsb.svg)](https://crates.io/crates/fsb) | [![Documentation](https://docs.rs/fsb/badge.svg)](https://docs.rs/fsb) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [GOST R 34.11-94][GOST94] | [`gost94`] | [![crates.io](https://img.shields.io/crates/v/gost94.svg)](https://crates.io/crates/gost94) | [![Documentation](https://docs.rs/gost94/badge.svg)](https://docs.rs/gost94) | ![MSRV 1.41][msrv-1.41] | :yellow_heart: |
+| [Grøstl] (Groestl) | [`groestl`] | [![crates.io](https://img.shields.io/crates/v/groestl.svg)](https://crates.io/crates/groestl) | [![Documentation](https://docs.rs/groestl/badge.svg)](https://docs.rs/groestl) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [KangarooTwelve] | [`k12`] | [![crates.io](https://img.shields.io/crates/v/k12.svg)](https://crates.io/crates/k12) | [![Documentation](https://docs.rs/k12/badge.svg)](https://docs.rs/k12) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [MD2] | [`md2`] | [![crates.io](https://img.shields.io/crates/v/md2.svg)](https://crates.io/crates/md2) | [![Documentation](https://docs.rs/md2/badge.svg)](https://docs.rs/md2) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
+| [MD4] | [`md4`] | [![crates.io](https://img.shields.io/crates/v/md4.svg)](https://crates.io/crates/md4) | [![Documentation](https://docs.rs/md4/badge.svg)](https://docs.rs/md4) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
+| [MD5] | [`md-5`] [:exclamation:] | [![crates.io](https://img.shields.io/crates/v/md-5.svg)](https://crates.io/crates/md-5) | [![Documentation](https://docs.rs/md-5/badge.svg)](https://docs.rs/md-5) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
+| [RIPEMD] | [`ripemd`] | [![crates.io](https://img.shields.io/crates/v/ripemd.svg)](https://crates.io/crates/ripemd) | [![Documentation](https://docs.rs/ripemd/badge.svg)](https://docs.rs/ripemd) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [SHA-1] | [`sha-1`] [:exclamation:] | [![crates.io](https://img.shields.io/crates/v/sha-1.svg)](https://crates.io/crates/sha-1) | [![Documentation](https://docs.rs/sha-1/badge.svg)](https://docs.rs/sha-1) | ![MSRV 1.41][msrv-1.41] | :broken_heart: |
+| [SHA-2] | [`sha2`] | [![crates.io](https://img.shields.io/crates/v/sha2.svg)](https://crates.io/crates/sha2) | [![Documentation](https://docs.rs/sha2/badge.svg)](https://docs.rs/sha2) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [SHA-3] (Keccak) | [`sha3`] | [![crates.io](https://img.shields.io/crates/v/sha3.svg)](https://crates.io/crates/sha3) | [![Documentation](https://docs.rs/sha3/badge.svg)](https://docs.rs/sha3) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [SHABAL] | [`shabal`] | [![crates.io](https://img.shields.io/crates/v/shabal.svg)](https://crates.io/crates/shabal) | [![Documentation](https://docs.rs/shabal/badge.svg)](https://docs.rs/shabal) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [SM3] (OSCCA GM/T 0004-2012) | [`sm3`] | [![crates.io](https://img.shields.io/crates/v/sm3.svg)](https://crates.io/crates/sm3) | [![Documentation](https://docs.rs/sm3/badge.svg)](https://docs.rs/sm3) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [Streebog] (GOST R 34.11-2012) | [`streebog`] | [![crates.io](https://img.shields.io/crates/v/streebog.svg)](https://crates.io/crates/streebog) | [![Documentation](https://docs.rs/streebog/badge.svg)](https://docs.rs/streebog) | ![MSRV 1.41][msrv-1.41] | :yellow_heart: |
+| [Tiger] | [`tiger`] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) | [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+| [Whirlpool] | [`whirlpool`] | [![crates.io](https://img.shields.io/crates/v/whirlpool.svg)](https://crates.io/crates/whirlpool) | [![Documentation](https://docs.rs/whirlpool/badge.svg)](https://docs.rs/whirlpool) | ![MSRV 1.41][msrv-1.41] | :green_heart: |
+
+NOTE: the [BLAKE3 crate](https://github.com/BLAKE3-team/BLAKE3) implements the `digest` traits used by the rest of the hashes in this repository, but is maintained by the BLAKE3 team.
 
 [Security]: https://en.wikipedia.org/wiki/Hash_function_security_summary
 [:exclamation:]: #crate-names
 
-### Crate names
-Whenever possible crates are published under the the same name as the crate
-folder. Owners of `md5` and `sha1` crates declined
-([1](https://github.com/stainless-steel/md5/pull/2),
-[2](https://github.com/mitsuhiko/rust-sha1/issues/17)) to participate in this
-project. This is why crates marked by :exclamation: are published under
-`md-5` and `sha-1` names respectively.
+### Crate Names
+
+Whenever possible crates are published under the the same name as the crate folder.
+Owners of `md5` and `sha1` declined ([1](https://github.com/stainless-steel/md5/pull/2), [2](https://github.com/mitsuhiko/rust-sha1/issues/17)) to participate in this project.
+Those crates do not implement the [`digest`] traits, so they are not interoperable with the RustCrypto ecosystem.
+This is why crates marked by :exclamation: are published under `md-5` and `sha-1` names, but the libraries themselves are named as `md5` and `sha1`, i.e. inside `use` statements you should use `sha1`/`md5`, not `sha_1`/`md_5`.
 
 ### Security Level Legend
-The following describes the security level ratings associated with each
-hash function (i.e. algorithms, not the specific implementation):
+
+The following describes the security level ratings associated with each hash function (i.e. algorithms, not the specific implementation):
 
 | Heart          | Description |
-|----------------|-------------|
+|:--------------:|-------------|
 | :green_heart:  | No known successful attacks |
 | :yellow_heart: | Theoretical break: security lower than claimed |
 | :broken_heart: | Attack demonstrated in practice: avoid if at all possible |
 
 See the [Security] page on Wikipedia for more information.
 
-### Minimum Supported Rust Version (MSRV)
-All crates in this repository support Rust 1.21 or higher. In future
-minimally supported version of Rust can be changed, but it will be done with
-a minor version bump.
+### Minimum Supported Rust Version (MSRV) Policy
+
+MSRV bumps are considered breaking changes and will be performed only with minor version bump.
 
 ## Usage
-Let us demonstrate how to use crates in this repository using BLAKE2b as an
-example.
 
-First add `blake2` crate to your `Cargo.toml`:
+Let us demonstrate how to use crates in this repository using SHA-2 as an example.
+
+First add [`sha2`](https://docs.rs/sha2) crate to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-blake2 = "0.9"
+sha2 = "0.10"
 ```
 
-Note that crates in this repository have an enabled by default `std` feature.
+Note that all crates in this repository have an enabled by default `std` feature.
 So if you plan to use the crate in `no_std` environments, don't forget to disable it:
 
 ```toml
 [dependencies]
-blake2 = { version="0.9", default-features = false }
+sha2 = { version = "0.10", default-features = false }
 ```
 
-`blake2` and other crates re-export `digest` crate and `Digest` trait for
-convenience, so you don't have to add `digest` crate as an explicit dependency.
+[`sha2`](https://docs.rs/sha2) and the other hash implementation crates re-export the [`digest`] crate and the [`Digest`] trait for convenience, so you don't have to include it in your `Cargo.toml` it as an explicit dependency.
 
 Now you can write the following code:
 
-```Rust
-use blake2::{Blake2b, Digest};
+```rust
+use sha2::{Sha256, Digest};
 
-let mut hasher = Blake2b::new();
+let mut hasher = Sha256::new();
 let data = b"Hello world!";
 hasher.update(data);
 // `update` can be called repeatedly and is generic over `AsRef<[u8]>`
@@ -103,64 +95,56 @@ let hash = hasher.finalize();
 println!("Result: {:x}", hash);
 ```
 
-In this example `hash` has type [`GenericArray<u8, U64>`][2], which is a generic
-alternative to `[u8; 64]`.
+In this example `hash` has type `GenericArray<u8, U32>`, which is a generic alternative to `[u8; 32]` defined in the [`generic-array`] crate.
 
-Alternatively you can use chained approach, which is equivalent to the previous
-example:
+Alternatively, you can use chained approach, which is equivalent to the previous example:
 
-```Rust
-use blake2::{Blake2b, Digest};
+```rust
+use sha2::{Sha256, Digest};
 
-let hash = Blake2b::new()
+let hash = Sha256::new()
     .chain(b"Hello world!")
     .chain("String data")
     .finalize();
 println!("Result: {:x}", hash);
 ```
 
-If the whole message is available you also can use convenience `digest` method:
+If a complete message is available, then you also can use the convenience [`Digest::digest`] method:
 
-```Rust
-use blake2::{Blake2b, Digest};
+```rust
+use sha2::{Sha256, Digest};
 
-let hash = Blake2b::digest(b"my message");
+let hash = Sha256::digest(b"my message");
 println!("Result: {:x}", hash);
 ```
 
-### Hashing `Read`able objects
+### Hashing `Read`able Objects
 
-If you want to hash data from [`Read`][3] trait (e.g. from file) you can rely on
-implementation of [`Write`][4] trait (requires an enabled-by-default `std` feature):
+If you want to hash data from a type which imlements the [`Read`] trait, you can rely on implementation of the [`Write`] trait (requires enabled-by-default `std` feature):
 
-```Rust
-use blake2::{Blake2b, Digest};
+```rust
+use sha2::{Sha256, Digest};
 use std::{fs, io};
 
 let mut file = fs::File::open(&path)?;
-let mut hasher = Blake2b::new();
+let mut hasher = Sha256::new();
 let n = io::copy(&mut file, &mut hasher)?;
 let hash = hasher.finalize();
-println!("Path: {}", path);
+
 println!("Bytes processed: {}", n);
 println!("Hash value: {:x}", hash);
 ```
 
 ### Hash-based Message Authentication Code (HMAC)
 
-If you want to calculate [Hash-based Message Authentication Code][5] (HMAC),
-you can use generic implementation from [`hmac`](https://docs.rs/hmac) crate,
-which is a part of the [RustCrypto/MACs][6] repository.
+If you want to calculate [Hash-based Message Authentication Code][HMAC] (HMAC), you can use the generic implementation from [`hmac`] crate, which is a part of the [RustCrypto/MACs] repository.
 
-### Generic code
+### Generic Code
 
-You can write generic code over `Digest` (or other traits from `digest` crate)
-trait which will work over different hash functions:
+You can write generic code over the [`Digest`] trait (or other traits from the [`digest`] crate) which will work over different hash functions:
 
-```Rust
-use digest::Digest;
-use blake2::Blake2b;
-use sha2::Sha256;
+```rust
+use sha2::{Sha256, Sha512, Digest};
 
 // Toy example, do not use it in practice!
 // Instead use crates from: https://github.com/RustCrypto/password-hashing
@@ -172,15 +156,31 @@ fn hash_password<D: Digest>(password: &str, salt: &str, output: &mut [u8]) {
     output.copy_from_slice(&hasher.finalize())
 }
 
-let mut buf1 = [0u8; 64];
-hash_password::<Blake2b>("my_password", "abcd", &mut buf1);
+let mut buf1 = [0u8; 32];
+hash_password::<Sha256>("my_password", "abcd", &mut buf1);
 
-let mut buf2 = [0u8; 32];
-hash_password::<Sha256>("my_password", "abcd", &mut buf2);
+let mut buf2 = [0u8; 64];
+hash_password::<Sha512>("my_password", "abcd", &mut buf2);
 ```
 
-If you want to use hash functions with trait objects, use `digest::DynDigest`
-trait.
+If you want to use hash functions with trait objects, you can use the [`DynDigest`] trait:
+
+```rust
+use sha2::{Sha256, Sha512, digest::DynDigest};
+
+fn dyn_hash(hasher: &mut dyn DynDigest, data: &[u8]) -> Box<[u8]> {
+    hasher.update(data);
+    hasher.finalize_reset()
+}
+
+let mut sha256_hasher = Sha256::default();
+let mut sha512_hasher = Sha512::default();
+
+let res1 = dyn_hash(&mut sha256_hasher, b"foo");
+let res2 = dyn_hash(&mut sha256_hasher, b"bar");
+let res3 = dyn_hash(&mut sha512_hasher, b"foo");
+let res4 = dyn_hash(&mut sha512_hasher, b"bar");
+```
 
 ## License
 
@@ -193,25 +193,50 @@ at your option.
 
 ### Contribution
 
-Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
-dual licensed as above, without any additional terms or conditions.
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
 
 [//]: # (badges)
 
 [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
 [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
+[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
 [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg
 [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes
+[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg
+
+[//]: # (crates)
+
+[`blake2`]: ./blake2
+[`fsb`]: ./fsb
+[`gost94`]: ./gost94
+[`groestl`]: ./groestl
+[`k12`]: ./k12
+[`md2`]: ./md2
+[`md4`]: ./md4
+[`md-5`]: ./md-5
+[`ripemd`]: ./ripemd
+[`sha-1`]: ./sha1
+[`sha2`]: ./sha2
+[`sha3`]: ./sha3
+[`shabal`]: ./shabal
+[`sm3`]: ./sm3
+[`streebog`]: ./streebog
+[`tiger`]: ./tiger
+[`whirlpool`]: ./whirlpool
 
 [//]: # (footnotes)
 
 [1]: https://en.wikipedia.org/wiki/Cryptographic_hash_function
-[2]: https://docs.rs/generic-array
-[3]: https://doc.rust-lang.org/std/io/trait.Read.html
-[4]: https://doc.rust-lang.org/std/io/trait.Write.html
-[5]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
-[6]: https://github.com/RustCrypto/MACs
+[`digest`]: https://docs.rs/digest
+[`Digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html
+[`Digest::digest`]: https://docs.rs/digest/0.10.0/digest/trait.Digest.html#tymethod.digest
+[`DynDigest`]: https://docs.rs/digest/0.10.0/digest/trait.DynDigest.html
+[`generic-array`]: https://docs.rs/generic-array
+[HMAC]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
+[`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
+[`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
+[`hmac`]: https://docs.rs/hmac
+[RustCrypto/MACs]: https://github.com/RustCrypto/MACs
 
 [//]: # (algorithms)
 
@@ -223,9 +248,7 @@ dual licensed as above, without any additional terms or conditions.
 [MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography)
 [MD4]: https://en.wikipedia.org/wiki/MD4
 [MD5]: https://en.wikipedia.org/wiki/MD5
-[RIPEMD-160]: https://en.wikipedia.org/wiki/RIPEMD
-[RIPEMD-256]: https://en.wikipedia.org/wiki/RIPEMD
-[RIPEMD-320]: https://en.wikipedia.org/wiki/RIPEMD
+[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD
 [SHA-1]: https://en.wikipedia.org/wiki/SHA-1
 [SHA-2]: https://en.wikipedia.org/wiki/SHA-2
 [SHA-3]: https://en.wikipedia.org/wiki/SHA-3
diff --git a/blake2/CHANGELOG.md b/blake2/CHANGELOG.md
index 60bf2e2dd..c85a8f023 100644
--- a/blake2/CHANGELOG.md
+++ b/blake2/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 and remove dependency on `crypto-mac` ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.2 (2021-08-25)
 ### Fixed
 - Building with `simd_opt` on recent nightlies ([#301]) 
diff --git a/blake2/Cargo.toml b/blake2/Cargo.toml
index 6d25e15a7..2757eff0f 100644
--- a/blake2/Cargo.toml
+++ b/blake2/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
-name = "blake2"
-version = "0.9.2"
+name = "blake2" # Also update html_root_url in lib.rs when bumping this
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "BLAKE2 hash functions"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,18 +12,16 @@ keywords = ["crypto", "blake2", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-crypto-mac = "0.8"
-opaque-debug = "0.3"
+digest = { version = "0.10", features = ["mac"] }
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
-crypto-mac = { version = "0.8", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
 default = ["std"]
-std = ["digest/std", "crypto-mac/std"]
+std = ["digest/std"]
+reset = [] # Enable reset functionality
 simd = []
 simd_opt = ["simd"]
 simd_asm = ["simd_opt"]
diff --git a/blake2/benches/blake2b.rs b/blake2/benches/blake2b.rs
deleted file mode 100644
index 5c3f4e7e7..000000000
--- a/blake2/benches/blake2b.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(blake2::Blake2b);
diff --git a/blake2/benches/blake2s.rs b/blake2/benches/blake2s.rs
deleted file mode 100644
index dba70e921..000000000
--- a/blake2/benches/blake2s.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(blake2::Blake2s);
diff --git a/blake2/benches/mod.rs b/blake2/benches/mod.rs
new file mode 100644
index 000000000..fdb086ff0
--- /dev/null
+++ b/blake2/benches/mod.rs
@@ -0,0 +1,22 @@
+#![feature(test)]
+extern crate test;
+
+use blake2::{Blake2b512, Blake2s256};
+use digest::bench_update;
+use test::Bencher;
+
+bench_update!(
+    Blake2b512::default();
+    blake2b512_10 10;
+    blake2b512_100 100;
+    blake2b512_1000 1000;
+    blake2b512_10000 10000;
+);
+
+bench_update!(
+    Blake2s256::default();
+    blake2s256_10 10;
+    blake2s256_100 100;
+    blake2s256_1000 1000;
+    blake2s256_10000 10000;
+);
diff --git a/blake2/examples/blake2b_sum.rs b/blake2/examples/blake2b_sum.rs
deleted file mode 100644
index 31e0ff11e..000000000
--- a/blake2/examples/blake2b_sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use blake2::{Blake2b, Digest};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Blake2b, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Blake2b, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/blake2/examples/blake2s_sum.rs b/blake2/examples/blake2s_sum.rs
deleted file mode 100644
index 38be51d03..000000000
--- a/blake2/examples/blake2s_sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use blake2::{Blake2s, Digest};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Blake2s, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Blake2s, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/blake2/src/blake2.rs b/blake2/src/blake2.rs
deleted file mode 100644
index 3255a30e9..000000000
--- a/blake2/src/blake2.rs
+++ /dev/null
@@ -1,414 +0,0 @@
-macro_rules! blake2_impl {
-    (
-        $state:ident, $fix_state:ident, $word:ident, $vec:ident, $bytes:ident,
-        $block_size:ident, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr,
-        $vardoc:expr, $doc:expr,
-    ) => {
-        use $crate::as_bytes::AsBytes;
-        use $crate::simd::{$vec, Vector4};
-
-        use core::{cmp, convert::TryInto, ops::Div};
-        use crypto_mac::{InvalidKeyLength, Mac, NewMac};
-        use digest::generic_array::typenum::{Unsigned, U4};
-        use digest::generic_array::GenericArray;
-        use digest::InvalidOutputSize;
-        use digest::{BlockInput, FixedOutputDirty, Reset, Update, VariableOutputDirty};
-
-        type Output = GenericArray<u8, $bytes>;
-
-        #[derive(Clone)]
-        #[doc=$vardoc]
-        pub struct $state {
-            m: [$word; 16],
-            h: [$vec; 2],
-            t: u64,
-            n: usize,
-
-            h0: [$vec; 2],
-            m0: [$word; 16],
-            t0: u64,
-        }
-
-        #[inline(always)]
-        fn iv0() -> $vec {
-            $vec::new($IV[0], $IV[1], $IV[2], $IV[3])
-        }
-        #[inline(always)]
-        fn iv1() -> $vec {
-            $vec::new($IV[4], $IV[5], $IV[6], $IV[7])
-        }
-
-        #[inline(always)]
-        fn quarter_round(v: &mut [$vec; 4], rd: u32, rb: u32, m: $vec) {
-            v[0] = v[0].wrapping_add(v[1]).wrapping_add(m.from_le());
-            v[3] = (v[3] ^ v[0]).rotate_right_const(rd);
-            v[2] = v[2].wrapping_add(v[3]);
-            v[1] = (v[1] ^ v[2]).rotate_right_const(rb);
-        }
-
-        #[inline(always)]
-        fn shuffle(v: &mut [$vec; 4]) {
-            v[1] = v[1].shuffle_left_1();
-            v[2] = v[2].shuffle_left_2();
-            v[3] = v[3].shuffle_left_3();
-        }
-
-        #[inline(always)]
-        fn unshuffle(v: &mut [$vec; 4]) {
-            v[1] = v[1].shuffle_right_1();
-            v[2] = v[2].shuffle_right_2();
-            v[3] = v[3].shuffle_right_3();
-        }
-
-        #[inline(always)]
-        fn round(v: &mut [$vec; 4], m: &[$word; 16], s: &[usize; 16]) {
-            quarter_round(v, $R1, $R2, $vec::gather(m, s[0], s[2], s[4], s[6]));
-            quarter_round(v, $R3, $R4, $vec::gather(m, s[1], s[3], s[5], s[7]));
-
-            shuffle(v);
-            quarter_round(v, $R1, $R2, $vec::gather(m, s[8], s[10], s[12], s[14]));
-            quarter_round(v, $R3, $R4, $vec::gather(m, s[9], s[11], s[13], s[15]));
-            unshuffle(v);
-        }
-
-        impl $state {
-            /// Creates a new hashing context with a key.
-            ///
-            /// **WARNING!** If you plan to use it for variable output MAC, then
-            /// make sure to compare codes in constant time! It can be done
-            /// for example by using `subtle` crate.
-            pub fn new_keyed(key: &[u8], output_size: usize) -> Self {
-                Self::with_params(key, &[], &[], output_size)
-            }
-
-            /// Creates a new hashing context with the full set of sequential-mode parameters.
-            pub fn with_params(
-                key: &[u8],
-                salt: &[u8],
-                persona: &[u8],
-                output_size: usize,
-            ) -> Self {
-                let kk = key.len();
-                assert!(kk <= $bytes::to_usize());
-                assert!(output_size <= $bytes::to_usize());
-
-                // The number of bytes needed to express two words.
-                let length = $bytes::to_usize() / 4;
-                assert!(salt.len() <= length);
-                assert!(persona.len() <= length);
-
-                // Build a parameter block
-                let mut p = [0 as $word; 8];
-                p[0] = 0x0101_0000 ^ ((kk as $word) << 8) ^ (output_size as $word);
-
-                // salt is two words long
-                if salt.len() < length {
-                    let mut padded_salt =
-                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
-                    for i in 0..salt.len() {
-                        padded_salt[i] = salt[i];
-                    }
-                    p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap());
-                    p[5] = $word::from_le_bytes(
-                        padded_salt[length / 2..padded_salt.len()]
-                            .try_into()
-                            .unwrap(),
-                    );
-                } else {
-                    p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap());
-                    p[5] =
-                        $word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap());
-                }
-
-                // persona is also two words long
-                if persona.len() < length {
-                    let mut padded_persona =
-                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
-                    for i in 0..persona.len() {
-                        padded_persona[i] = persona[i];
-                    }
-                    p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap());
-                    p[7] = $word::from_le_bytes(
-                        padded_persona[length / 2..padded_persona.len()]
-                            .try_into()
-                            .unwrap(),
-                    );
-                } else {
-                    p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap());
-                    p[7] = $word::from_le_bytes(
-                        persona[length / 2..persona.len()].try_into().unwrap(),
-                    );
-                }
-
-                let mut state = Self::with_parameter_block(&p);
-
-                if kk > 0 {
-                    copy(key, state.m.as_mut_bytes());
-                    state.t = 2 * $bytes::to_u64();
-                }
-
-                state.t0 = state.t;
-                state.m0 = state.m;
-                state
-            }
-
-            #[doc(hidden)]
-            pub fn with_parameter_block(p: &[$word; 8]) -> Self {
-                let nn = p[0] as u8 as usize;
-                let kk = (p[0] >> 8) as u8 as usize;
-                assert!(nn >= 1 && nn <= $bytes::to_usize());
-                assert!(kk <= $bytes::to_usize());
-
-                let h0 = [
-                    iv0() ^ $vec::new(p[0], p[1], p[2], p[3]),
-                    iv1() ^ $vec::new(p[4], p[5], p[6], p[7]),
-                ];
-
-                $state {
-                    m: [0; 16],
-                    h: h0,
-                    t: 0,
-                    n: nn,
-
-                    t0: 0,
-                    m0: [0; 16],
-                    h0,
-                }
-            }
-
-            /// Updates the hashing context with more data.
-            fn update(&mut self, data: &[u8]) {
-                let mut rest = data;
-
-                let block = 2 * $bytes::to_usize();
-
-                let off = self.t as usize % block;
-                if off != 0 || self.t == 0 {
-                    let len = cmp::min(block - off, rest.len());
-
-                    let part = &rest[..len];
-                    rest = &rest[part.len()..];
-
-                    copy(part, &mut self.m.as_mut_bytes()[off..]);
-                    self.t = self
-                        .t
-                        .checked_add(part.len() as u64)
-                        .expect("hash data length overflow");
-                }
-
-                while rest.len() >= block {
-                    self.compress(0, 0);
-
-                    let part = &rest[..block];
-                    rest = &rest[part.len()..];
-
-                    copy(part, &mut self.m.as_mut_bytes());
-                    self.t = self
-                        .t
-                        .checked_add(part.len() as u64)
-                        .expect("hash data length overflow");
-                }
-
-                let n = rest.len();
-                if n > 0 {
-                    self.compress(0, 0);
-
-                    copy(rest, &mut self.m.as_mut_bytes());
-                    self.t = self
-                        .t
-                        .checked_add(rest.len() as u64)
-                        .expect("hash data length overflow");
-                }
-            }
-
-            #[doc(hidden)]
-            pub fn finalize_last_node(mut self) -> Output {
-                self.finalize_with_flag(!0)
-            }
-
-            fn finalize_with_flag(&mut self, f1: $word) -> Output {
-                let off = self.t as usize % (2 * $bytes::to_usize());
-                if off != 0 {
-                    self.m.as_mut_bytes()[off..].iter_mut().for_each(|b| *b = 0);
-                }
-
-                self.compress(!0, f1);
-
-                let buf = [self.h[0].to_le(), self.h[1].to_le()];
-
-                let mut out = GenericArray::default();
-                copy(buf.as_bytes(), &mut out);
-                out
-            }
-
-            fn compress(&mut self, f0: $word, f1: $word) {
-                use $crate::consts::SIGMA;
-
-                let m = &self.m;
-                let h = &mut self.h;
-
-                let t0 = self.t as $word;
-                let t1 = match $bytes::to_u8() {
-                    64 => 0,
-                    32 => (self.t >> 32) as $word,
-                    _ => unreachable!(),
-                };
-
-                let mut v = [h[0], h[1], iv0(), iv1() ^ $vec::new(t0, t1, f0, f1)];
-
-                round(&mut v, m, &SIGMA[0]);
-                round(&mut v, m, &SIGMA[1]);
-                round(&mut v, m, &SIGMA[2]);
-                round(&mut v, m, &SIGMA[3]);
-                round(&mut v, m, &SIGMA[4]);
-                round(&mut v, m, &SIGMA[5]);
-                round(&mut v, m, &SIGMA[6]);
-                round(&mut v, m, &SIGMA[7]);
-                round(&mut v, m, &SIGMA[8]);
-                round(&mut v, m, &SIGMA[9]);
-                if $bytes::to_u8() == 64 {
-                    round(&mut v, m, &SIGMA[0]);
-                    round(&mut v, m, &SIGMA[1]);
-                }
-
-                h[0] = h[0] ^ (v[0] ^ v[2]);
-                h[1] = h[1] ^ (v[1] ^ v[3]);
-            }
-        }
-
-        impl Default for $state {
-            fn default() -> Self {
-                Self::new_keyed(&[], $bytes::to_usize())
-            }
-        }
-
-        impl BlockInput for $state {
-            type BlockSize = $block_size;
-        }
-
-        impl Update for $state {
-            fn update(&mut self, data: impl AsRef<[u8]>) {
-                self.update(data.as_ref());
-            }
-        }
-
-        impl VariableOutputDirty for $state {
-            fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
-                if output_size == 0 || output_size > $bytes::to_usize() {
-                    return Err(InvalidOutputSize);
-                }
-                Ok(Self::new_keyed(&[], output_size))
-            }
-
-            fn output_size(&self) -> usize {
-                self.n
-            }
-
-            fn finalize_variable_dirty(&mut self, f: impl FnOnce(&[u8])) {
-                let n = self.n;
-                let res = self.finalize_with_flag(0);
-                f(&res[..n]);
-            }
-        }
-
-        impl Reset for $state {
-            fn reset(&mut self) {
-                self.t = self.t0;
-                self.m = self.m0;
-                self.h = self.h0;
-            }
-        }
-
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
-
-        #[derive(Clone)]
-        #[doc=$doc]
-        pub struct $fix_state {
-            state: $state,
-        }
-
-        impl $fix_state {
-            /// Creates a new hashing context with the full set of sequential-mode parameters.
-            pub fn with_params(key: &[u8], salt: &[u8], persona: &[u8]) -> Self {
-                let state = $state::with_params(key, salt, persona, $bytes::to_usize());
-                Self { state }
-            }
-        }
-
-        impl Default for $fix_state {
-            fn default() -> Self {
-                let state = $state::new_keyed(&[], $bytes::to_usize());
-                Self { state }
-            }
-        }
-
-        impl BlockInput for $fix_state {
-            type BlockSize = $block_size;
-        }
-
-        impl Update for $fix_state {
-            fn update(&mut self, data: impl AsRef<[u8]>) {
-                self.state.update(data.as_ref());
-            }
-        }
-
-        impl FixedOutputDirty for $fix_state {
-            type OutputSize = $bytes;
-
-            fn finalize_into_dirty(&mut self, out: &mut Output) {
-                out.copy_from_slice(&self.state.finalize_with_flag(0));
-            }
-        }
-
-        impl Reset for $fix_state {
-            fn reset(&mut self) {
-                self.state.reset()
-            }
-        }
-
-        impl NewMac for $fix_state {
-            type KeySize = $bytes;
-
-            fn new(key: &GenericArray<u8, $bytes>) -> Self {
-                let state = $state::new_keyed(key, $bytes::to_usize());
-                Self { state }
-            }
-
-            fn new_varkey(key: &[u8]) -> Result<Self, InvalidKeyLength> {
-                if key.len() > $bytes::to_usize() {
-                    Err(InvalidKeyLength)
-                } else {
-                    let state = $state::new_keyed(key, $bytes::to_usize());
-                    Ok(Self { state })
-                }
-            }
-        }
-
-        impl Mac for $fix_state {
-            type OutputSize = $bytes;
-
-            fn update(&mut self, data: &[u8]) {
-                self.state.update(data);
-            }
-
-            fn reset(&mut self) {
-                <Self as Reset>::reset(self)
-            }
-
-            fn finalize(mut self) -> crypto_mac::Output<Self> {
-                crypto_mac::Output::new(self.state.finalize_with_flag(0))
-            }
-        }
-
-        opaque_debug::implement!($fix_state);
-        digest::impl_write!($fix_state);
-
-        fn copy(src: &[u8], dst: &mut [u8]) {
-            assert!(dst.len() >= src.len());
-            unsafe {
-                core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len());
-            }
-        }
-    };
-}
diff --git a/blake2/src/blake2b.rs b/blake2/src/blake2b.rs
deleted file mode 100644
index 3afc18d84..000000000
--- a/blake2/src/blake2b.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::consts::BLAKE2B_IV;
-use digest::generic_array::typenum::{U128, U64};
-
-blake2_impl!(
-    VarBlake2b,
-    Blake2b,
-    u64,
-    u64x4,
-    U64,
-    U128,
-    32,
-    24,
-    16,
-    63,
-    BLAKE2B_IV,
-    "Blake2b instance with a variable output.",
-    "Blake2b instance with a fixed output.",
-);
diff --git a/blake2/src/blake2s.rs b/blake2/src/blake2s.rs
deleted file mode 100644
index 5306ed992..000000000
--- a/blake2/src/blake2s.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::consts::BLAKE2S_IV;
-use digest::generic_array::typenum::{U32, U64};
-
-blake2_impl!(
-    VarBlake2s,
-    Blake2s,
-    u32,
-    u32x4,
-    U32,
-    U64,
-    16,
-    12,
-    8,
-    7,
-    BLAKE2S_IV,
-    "Blake2s instance with a variable output.",
-    "Blake2s instance with a fixed output.",
-);
diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs
index b74c7b5bf..f4925b033 100644
--- a/blake2/src/lib.rs
+++ b/blake2/src/lib.rs
@@ -2,14 +2,14 @@
 //!
 //! # Usage
 //!
-//! `Blake2b` can be used in the following way:
+//! [`Blake2b512`] and [`Blake2s256`] can be used in the following way:
 //!
 //! ```rust
-//! use blake2::{Blake2b, Blake2s, Digest};
+//! use blake2::{Blake2b512, Blake2s256, Digest};
 //! use hex_literal::hex;
 //!
-//! // create a Blake2b object
-//! let mut hasher = Blake2b::new();
+//! // create a Blake2b512 object
+//! let mut hasher = Blake2b512::new();
 //!
 //! // write input message
 //! hasher.update(b"hello world");
@@ -21,8 +21,8 @@
 //!     c05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0
 //! ")[..]);
 //!
-//! // same example for `Blake2s`:
-//! let mut hasher = Blake2s::new();
+//! // same example for Blake2s256:
+//! let mut hasher = Blake2s256::new();
 //! hasher.update(b"hello world");
 //! let res = hasher.finalize();
 //! assert_eq!(res[..], hex!("
@@ -34,45 +34,32 @@
 //!
 //! ## Variable output size
 //!
-//! If you need variable sized output you can use `VarBlake2b` and `VarBlake2s`
-//! which support variable output sizes through `VariableOutput` trait. `Update`
-//! trait has to be imported as well.
+//! This implementation supports run and compile time variable sizes.
 //!
+//! Run time variable output example:
 //! ```rust
-//! use blake2::VarBlake2b;
+//! use blake2::Blake2bVar;
 //! use blake2::digest::{Update, VariableOutput};
+//! use hex_literal::hex;
 //!
-//! let mut hasher = VarBlake2b::new(10).unwrap();
+//! let mut hasher = Blake2bVar::new(10).unwrap();
 //! hasher.update(b"my_input");
-//! hasher.finalize_variable(|res| {
-//!     assert_eq!(res, [44, 197, 92, 132, 228, 22, 146, 78, 100, 0])
-//! })
+//! let mut buf = [0u8; 10];
+//! hasher.finalize_variable(&mut buf).unwrap();
+//! assert_eq!(buf, hex!("2cc55c84e416924e6400"));
 //! ```
 //!
-//! ## Message Authentication Code (MAC)
-//!
-//! BLAKE2 can be used as a MAC without any additional constructs:
-//!
+//! Compile time variable output example:
 //! ```rust
-//! use blake2::Blake2b;
-//! use blake2::crypto_mac::{Mac, NewMac};
-//!
-//! let mut hasher = Blake2b::new_varkey(b"my key").unwrap();
-//! hasher.update(b"hello world");
+//! use blake2::{Blake2b, Digest, digest::consts::U10};
+//! use hex_literal::hex;
 //!
-//! // `result` has type `crypto_mac::Output` which is a thin wrapper around
-//! // a byte array and provides a constant time equality check
-//! let result = hasher.finalize();
-//! // To get underlying array use the `into_bytes` method, but be careful,
-//! // since incorrect use of the code value may permit timing attacks which
-//! // defeat the security provided by the `crypto_mac::Output`
-//! let code_bytes = result.into_bytes();
+//! type Blake2b80 = Blake2b<U10>;
 //!
-//! // To verify the message it's recommended to use `verify` method
-//! let mut hasher = Blake2b::new_varkey(b"my key").unwrap();
-//! hasher.update(b"hello world");
-//! // `verify` return `Ok(())` if code is correct, `Err(MacError)` otherwise
-//! hasher.verify(&code_bytes).unwrap();
+//! let mut hasher = Blake2b80::new();
+//! hasher.update(b"my_input");
+//! let res = hasher.finalize();
+//! assert_eq!(res[..], hex!("2cc55c84e416924e6400")[..]);
 //! ```
 //!
 //! # Acknowledgment
@@ -83,8 +70,9 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/blake2/0.10.0"
 )]
 #![warn(missing_docs, rust_2018_idioms)]
 #![cfg_attr(feature = "simd", feature(platform_intrinsics, repr_simd))]
@@ -94,19 +82,95 @@
 #[cfg(feature = "std")]
 extern crate std;
 
+pub use digest::{self, Digest};
+
+use core::{convert::TryInto, fmt, marker::PhantomData, ops::Div};
+use digest::{
+    block_buffer::{Lazy, LazyBuffer},
+    consts::{U128, U32, U4, U64},
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
+        CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore,
+        VariableOutputCore,
+    },
+    crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser},
+    generic_array::{
+        typenum::{IsLessOrEqual, LeEq, NonZero, Unsigned},
+        ArrayLength, GenericArray,
+    },
+    FixedOutput, HashMarker, InvalidOutputSize, MacMarker, Output, Update,
+};
+#[cfg(feature = "reset")]
+use digest::{FixedOutputReset, Reset};
+
 mod as_bytes;
 mod consts;
 
 mod simd;
 
 #[macro_use]
-mod blake2;
+mod macros;
 
-mod blake2b;
-mod blake2s;
+use as_bytes::AsBytes;
+use consts::{BLAKE2B_IV, BLAKE2S_IV};
+use simd::{u32x4, u64x4, Vector4};
 
-pub use crypto_mac;
-pub use digest::{self, Digest};
+blake2_impl!(
+    Blake2bVarCore,
+    "Blake2b",
+    u64,
+    u64x4,
+    U64,
+    U128,
+    32,
+    24,
+    16,
+    63,
+    BLAKE2B_IV,
+    "Blake2b instance with a variable output.",
+    "Blake2b instance with a fixed output.",
+);
+
+/// BLAKE2b which allows to choose output size at runtime.
+pub type Blake2bVar = RtVariableCoreWrapper<Blake2bVarCore>;
+/// Core hasher state of BLAKE2b generic over output size.
+pub type Blake2bCore<OutSize> = CtVariableCoreWrapper<Blake2bVarCore, OutSize>;
+/// BLAKE2b generic over output size.
+pub type Blake2b<OutSize> = CoreWrapper<Blake2bCore<OutSize>>;
+/// BLAKE2b-512 hasher state.
+pub type Blake2b512 = Blake2b<U64>;
+
+blake2_mac_impl!(Blake2bMac, Blake2bVarCore, U64, "Blake2b MAC function");
+
+/// BLAKE2b-512 MAC state.
+pub type Blake2bMac512 = Blake2bMac<U64>;
+
+blake2_impl!(
+    Blake2sVarCore,
+    "Blake2s",
+    u32,
+    u32x4,
+    U32,
+    U64,
+    16,
+    12,
+    8,
+    7,
+    BLAKE2S_IV,
+    "Blake2s instance with a variable output.",
+    "Blake2s instance with a fixed output.",
+);
+
+/// BLAKE2s which allows to choose output size at runtime.
+pub type Blake2sVar = RtVariableCoreWrapper<Blake2sVarCore>;
+/// Core hasher state of BLAKE2s generic over output size.
+pub type Blake2sCore<OutSize> = CtVariableCoreWrapper<Blake2sVarCore, OutSize>;
+/// BLAKE2s generic over output size.
+pub type Blake2s<OutSize> = CoreWrapper<Blake2sCore<OutSize>>;
+/// BLAKE2s-256 hasher state.
+pub type Blake2s256 = Blake2s<U32>;
+
+blake2_mac_impl!(Blake2sMac, Blake2sVarCore, U32, "Blake2s MAC function");
 
-pub use crate::blake2b::{Blake2b, VarBlake2b};
-pub use crate::blake2s::{Blake2s, VarBlake2s};
+/// BLAKE2s-256 MAC state.
+pub type Blake2sMac256 = Blake2sMac<U32>;
diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs
new file mode 100644
index 000000000..393c1fe1f
--- /dev/null
+++ b/blake2/src/macros.rs
@@ -0,0 +1,430 @@
+macro_rules! blake2_impl {
+    (
+        $name:ident, $alg_name:expr, $word:ident, $vec:ident, $bytes:ident,
+        $block_size:ident, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr,
+        $vardoc:expr, $doc:expr,
+    ) => {
+        #[derive(Clone)]
+        #[doc=$vardoc]
+        pub struct $name {
+            h: [$vec; 2],
+            t: u64,
+            #[cfg(feature = "reset")]
+            h0: [$vec; 2],
+        }
+
+        impl $name {
+            #[inline(always)]
+            fn iv0() -> $vec {
+                $vec::new($IV[0], $IV[1], $IV[2], $IV[3])
+            }
+            #[inline(always)]
+            fn iv1() -> $vec {
+                $vec::new($IV[4], $IV[5], $IV[6], $IV[7])
+            }
+
+            /// Creates a new context with the full set of sequential-mode parameters.
+            pub fn new_with_params(
+                salt: &[u8],
+                persona: &[u8],
+                key_size: usize,
+                output_size: usize,
+            ) -> Self {
+                assert!(key_size <= $bytes::to_usize());
+                assert!(output_size <= $bytes::to_usize());
+
+                // The number of bytes needed to express two words.
+                let length = $bytes::to_usize() / 4;
+                assert!(salt.len() <= length);
+                assert!(persona.len() <= length);
+
+                // Build a parameter block
+                let mut p = [0 as $word; 8];
+                p[0] = 0x0101_0000 ^ ((key_size as $word) << 8) ^ (output_size as $word);
+
+                // salt is two words long
+                if salt.len() < length {
+                    let mut padded_salt =
+                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
+                    for i in 0..salt.len() {
+                        padded_salt[i] = salt[i];
+                    }
+                    p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap());
+                    p[5] = $word::from_le_bytes(
+                        padded_salt[length / 2..padded_salt.len()]
+                            .try_into()
+                            .unwrap(),
+                    );
+                } else {
+                    p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap());
+                    p[5] =
+                        $word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap());
+                }
+
+                // persona is also two words long
+                if persona.len() < length {
+                    let mut padded_persona =
+                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
+                    for i in 0..persona.len() {
+                        padded_persona[i] = persona[i];
+                    }
+                    p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap());
+                    p[7] = $word::from_le_bytes(
+                        padded_persona[length / 2..padded_persona.len()]
+                            .try_into()
+                            .unwrap(),
+                    );
+                } else {
+                    p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap());
+                    p[7] = $word::from_le_bytes(
+                        persona[length / 2..persona.len()].try_into().unwrap(),
+                    );
+                }
+
+                let h = [
+                    Self::iv0() ^ $vec::new(p[0], p[1], p[2], p[3]),
+                    Self::iv1() ^ $vec::new(p[4], p[5], p[6], p[7]),
+                ];
+                $name {
+                    #[cfg(feature = "reset")]
+                    h0: h.clone(),
+                    h,
+                    t: 0,
+                }
+            }
+
+            fn finalize_with_flag(
+                &mut self,
+                final_block: &GenericArray<u8, $block_size>,
+                flag: $word,
+                out: &mut Output<Self>,
+            ) {
+                self.compress(final_block, !0, flag);
+                let buf = [self.h[0].to_le(), self.h[1].to_le()];
+                out.copy_from_slice(buf.as_bytes())
+            }
+
+            fn compress(&mut self, block: &Block<Self>, f0: $word, f1: $word) {
+                use $crate::consts::SIGMA;
+
+                #[inline(always)]
+                fn quarter_round(v: &mut [$vec; 4], rd: u32, rb: u32, m: $vec) {
+                    v[0] = v[0].wrapping_add(v[1]).wrapping_add(m.from_le());
+                    v[3] = (v[3] ^ v[0]).rotate_right_const(rd);
+                    v[2] = v[2].wrapping_add(v[3]);
+                    v[1] = (v[1] ^ v[2]).rotate_right_const(rb);
+                }
+
+                #[inline(always)]
+                fn shuffle(v: &mut [$vec; 4]) {
+                    v[1] = v[1].shuffle_left_1();
+                    v[2] = v[2].shuffle_left_2();
+                    v[3] = v[3].shuffle_left_3();
+                }
+
+                #[inline(always)]
+                fn unshuffle(v: &mut [$vec; 4]) {
+                    v[1] = v[1].shuffle_right_1();
+                    v[2] = v[2].shuffle_right_2();
+                    v[3] = v[3].shuffle_right_3();
+                }
+
+                #[inline(always)]
+                fn round(v: &mut [$vec; 4], m: &[$word; 16], s: &[usize; 16]) {
+                    quarter_round(v, $R1, $R2, $vec::gather(m, s[0], s[2], s[4], s[6]));
+                    quarter_round(v, $R3, $R4, $vec::gather(m, s[1], s[3], s[5], s[7]));
+
+                    shuffle(v);
+                    quarter_round(v, $R1, $R2, $vec::gather(m, s[8], s[10], s[12], s[14]));
+                    quarter_round(v, $R3, $R4, $vec::gather(m, s[9], s[11], s[13], s[15]));
+                    unshuffle(v);
+                }
+
+                let mut m: [$word; 16] = Default::default();
+                let n = core::mem::size_of::<$word>();
+                for (v, chunk) in m.iter_mut().zip(block.chunks_exact(n)) {
+                    *v = $word::from_le_bytes(chunk.try_into().unwrap());
+                }
+                let h = &mut self.h;
+
+                let t0 = self.t as $word;
+                let t1 = match $bytes::to_u8() {
+                    64 => 0,
+                    32 => (self.t >> 32) as $word,
+                    _ => unreachable!(),
+                };
+
+                let mut v = [
+                    h[0],
+                    h[1],
+                    Self::iv0(),
+                    Self::iv1() ^ $vec::new(t0, t1, f0, f1),
+                ];
+
+                round(&mut v, &m, &SIGMA[0]);
+                round(&mut v, &m, &SIGMA[1]);
+                round(&mut v, &m, &SIGMA[2]);
+                round(&mut v, &m, &SIGMA[3]);
+                round(&mut v, &m, &SIGMA[4]);
+                round(&mut v, &m, &SIGMA[5]);
+                round(&mut v, &m, &SIGMA[6]);
+                round(&mut v, &m, &SIGMA[7]);
+                round(&mut v, &m, &SIGMA[8]);
+                round(&mut v, &m, &SIGMA[9]);
+                if $bytes::to_u8() == 64 {
+                    round(&mut v, &m, &SIGMA[0]);
+                    round(&mut v, &m, &SIGMA[1]);
+                }
+
+                h[0] = h[0] ^ (v[0] ^ v[2]);
+                h[1] = h[1] ^ (v[1] ^ v[3]);
+            }
+        }
+
+        impl HashMarker for $name {}
+
+        impl BlockSizeUser for $name {
+            type BlockSize = $block_size;
+        }
+
+        impl BufferKindUser for $name {
+            type BufferKind = Lazy;
+        }
+
+        impl UpdateCore for $name {
+            #[inline]
+            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+                for block in blocks {
+                    self.t += block.len() as u64;
+                    self.compress(block, 0, 0);
+                }
+            }
+        }
+
+        impl OutputSizeUser for $name {
+            type OutputSize = $bytes;
+        }
+
+        impl VariableOutputCore for $name {
+            const TRUNC_SIDE: TruncSide = TruncSide::Left;
+
+            #[inline]
+            fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+                if output_size > Self::OutputSize::USIZE {
+                    return Err(InvalidOutputSize);
+                }
+                Ok(Self::new_with_params(&[], &[], 0, output_size))
+            }
+
+            #[inline]
+            fn finalize_variable_core(
+                &mut self,
+                buffer: &mut Buffer<Self>,
+                out: &mut Output<Self>,
+            ) {
+                self.t += buffer.get_pos() as u64;
+                let block = buffer.pad_with_zeros();
+                self.finalize_with_flag(block, 0, out);
+            }
+        }
+
+        #[cfg(feature = "reset")]
+        impl Reset for $name {
+            fn reset(&mut self) {
+                self.h = self.h0;
+                self.t = 0;
+            }
+        }
+
+        impl AlgorithmName for $name {
+            #[inline]
+            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str($alg_name)
+            }
+        }
+
+        impl fmt::Debug for $name {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!(stringify!($name), " { ... }"))
+            }
+        }
+    };
+}
+
+macro_rules! blake2_mac_impl {
+    (
+        $name:ident, $hash:ty, $max_size:ty, $doc:expr
+    ) => {
+        #[derive(Clone)]
+        #[doc=$doc]
+        pub struct $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            core: $hash,
+            buffer: LazyBuffer<<$hash as BlockSizeUser>::BlockSize>,
+            #[cfg(feature = "reset")]
+            key_block: Block<$hash>,
+            _out: PhantomData<OutSize>,
+        }
+
+        impl<OutSize> $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            /// Create new instance using provided key, salt, and persona.
+            ///
+            /// Key length should not be bigger than block size, salt and persona
+            /// length should not be bigger than quarter of block size. If any
+            /// of those conditions is false the method will return an error.
+            #[inline]
+            pub fn new_with_salt_and_personal(
+                key: &[u8],
+                salt: &[u8],
+                persona: &[u8],
+            ) -> Result<Self, InvalidLength> {
+                let kl = key.len();
+                let bs = <$hash as BlockSizeUser>::BlockSize::USIZE;
+                let qbs = bs / 4;
+                if kl > bs || salt.len() > qbs || persona.len() > qbs {
+                    return Err(InvalidLength);
+                }
+                let mut key_block = Block::<$hash>::default();
+                key_block[..kl].copy_from_slice(key);
+                let buffer = LazyBuffer::new(&key_block);
+                Ok(Self {
+                    core: <$hash>::new_with_params(salt, persona, key.len(), OutSize::USIZE),
+                    buffer,
+                    #[cfg(feature = "reset")]
+                    key_block,
+                    _out: PhantomData,
+                })
+            }
+        }
+
+        impl<OutSize> KeySizeUser for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            type KeySize = <$hash as BlockSizeUser>::BlockSize;
+        }
+
+        impl<OutSize> KeyInit for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            fn new(key: &Key<Self>) -> Self {
+                Self {
+                    core: <$hash>::new_with_params(key, &[], key.len(), OutSize::USIZE),
+                    buffer: LazyBuffer::new(key),
+                    #[cfg(feature = "reset")]
+                    key_block: key.clone(),
+                    _out: PhantomData,
+                }
+            }
+
+            fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
+                let kl = key.len();
+                if kl > <$hash as BlockSizeUser>::BlockSize::USIZE {
+                    return Err(InvalidLength);
+                }
+                let mut key_block = Block::<$hash>::default();
+                key_block[..kl].copy_from_slice(key);
+                Ok(Self {
+                    core: <$hash>::new_with_params(&[], &[], key.len(), OutSize::USIZE),
+                    buffer: LazyBuffer::new(&key_block),
+                    #[cfg(feature = "reset")]
+                    key_block,
+                    _out: PhantomData,
+                })
+            }
+        }
+
+        impl<OutSize> Update for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            #[inline]
+            fn update(&mut self, input: &[u8]) {
+                let Self { core, buffer, .. } = self;
+                buffer.digest_blocks(input, |blocks| core.update_blocks(blocks));
+            }
+        }
+
+        impl<OutSize> OutputSizeUser for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            type OutputSize = OutSize;
+        }
+
+        impl<OutSize> FixedOutput for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            #[inline]
+            fn finalize_into(mut self, out: &mut Output<Self>) {
+                let Self { core, buffer, .. } = &mut self;
+                let mut full_res = Default::default();
+                core.finalize_variable_core(buffer, &mut full_res);
+                out.copy_from_slice(&full_res[..OutSize::USIZE]);
+            }
+        }
+
+        #[cfg(feature = "reset")]
+        impl<OutSize> Reset for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            fn reset(&mut self) {
+                self.core.reset();
+                self.buffer = LazyBuffer::new(&self.key_block);
+            }
+        }
+
+        #[cfg(feature = "reset")]
+        impl<OutSize> FixedOutputReset for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            #[inline]
+            fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
+                let Self {
+                    core,
+                    buffer,
+                    key_block,
+                    ..
+                } = self;
+                core.finalize_variable_core(buffer, out);
+                core.reset();
+                *buffer = LazyBuffer::new(key_block);
+            }
+        }
+
+        impl<OutSize> MacMarker for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+        }
+
+        impl<OutSize> fmt::Debug for $name<OutSize>
+        where
+            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                write!(f, "{}{} {{ ... }}", stringify!($name), OutSize::USIZE)
+            }
+        }
+    };
+}
diff --git a/fsb/CHANGELOG.md b/fsb/CHANGELOG.md
index bea8879e4..964415353 100644
--- a/fsb/CHANGELOG.md
+++ b/fsb/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.1.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.0.2 (2020-07-21)
 - Fixed `Reset` implementation bug. Reduce crate size by using binary dump
 of `PI` ([#300])
diff --git a/fsb/Cargo.toml b/fsb/Cargo.toml
index 9eb9fd6fd..96ffe6042 100644
--- a/fsb/Cargo.toml
+++ b/fsb/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "fsb"
 description = "FSB hash function"
-version = "0.0.2"
+version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
 readme = "README.md"
@@ -11,15 +11,13 @@ keywords = ["crypto", "fsb", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-whirlpool = { version = "0.9", path = "../whirlpool", default-features = false }
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
+whirlpool = { version = "0.10", path = "../whirlpool", default-features = false }
 
 [dev-dependencies]
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
-digest = { version = "0.9", features = ["dev"] }
 
 [features]
-asm = ["whirlpool/asm"]
-std = ["whirlpool/std"]
+default = ["std"]
+std = ["digest/std"]
diff --git a/ripemd160/LICENSE-APACHE b/fsb/LICENSE-APACHE
similarity index 100%
rename from ripemd160/LICENSE-APACHE
rename to fsb/LICENSE-APACHE
diff --git a/ripemd160/LICENSE-MIT b/fsb/LICENSE-MIT
similarity index 89%
rename from ripemd160/LICENSE-MIT
rename to fsb/LICENSE-MIT
index 66cf75563..c869ada57 100644
--- a/ripemd160/LICENSE-MIT
+++ b/fsb/LICENSE-MIT
@@ -1,6 +1,4 @@
-Copyright (c) 2006-2009 Graydon Hoare
-Copyright (c) 2009-2013 Mozilla Foundation
-Copyright (c) 2016 Artyom Pavlov
+Copyright (c) 2021 The RustCrypto Project Developers
 
 Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated
diff --git a/fsb/README.md b/fsb/README.md
index 53319e926..bff04069c 100644
--- a/fsb/README.md
+++ b/fsb/README.md
@@ -13,7 +13,7 @@ Pure Rust implementation of the [FSB hash function][1] family.
 
 ## Minimum Supported Rust Version
 
-Rust **1.47** or higher.
+Rust **1.41** or higher.
 
 Minimum supported Rust version can be changed in the future, but it will be
 done with a minor version bump.
diff --git a/fsb/benches/mod.rs b/fsb/benches/mod.rs
new file mode 100644
index 000000000..380a97488
--- /dev/null
+++ b/fsb/benches/mod.rs
@@ -0,0 +1,46 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use fsb::{Fsb160, Fsb224, Fsb256, Fsb384, Fsb512};
+use test::Bencher;
+
+bench_update!(
+    Fsb160::default();
+    fsb160_10 10;
+    fsb160_100 100;
+    fsb160_1000 1000;
+    fsb160_10000 10000;
+);
+
+bench_update!(
+    Fsb224::default();
+    fsb224_10 10;
+    fsb224_100 100;
+    fsb224_1000 1000;
+    fsb224_10000 10000;
+);
+
+bench_update!(
+    Fsb256::default();
+    fsb256_10 10;
+    fsb256_100 100;
+    fsb256_1000 1000;
+    fsb256_10000 10000;
+);
+
+bench_update!(
+    Fsb384::default();
+    fsb384_10 10;
+    fsb384_100 100;
+    fsb384_1000 1000;
+    fsb384_10000 10000;
+);
+
+bench_update!(
+    Fsb512::default();
+    fsb512_10 10;
+    fsb512_100 100;
+    fsb512_1000 1000;
+    fsb512_10000 10000;
+);
diff --git a/fsb/src/lib.rs b/fsb/src/lib.rs
index a8b69eabd..619f2df0d 100644
--- a/fsb/src/lib.rs
+++ b/fsb/src/lib.rs
@@ -39,38 +39,36 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/fsb/0.1.0"
 )]
-#![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 #![allow(non_snake_case)]
 
-extern crate alloc;
-
-#[cfg(feature = "std")]
-extern crate std;
-
-use alloc::vec;
-
 #[macro_use]
 mod macros;
 
+use core::fmt;
 pub use digest::{self, Digest};
-use whirlpool::Whirlpool;
-
-use core::convert::TryInto;
 
 // Double check this contains all values in the reference implementation
 static PI: &[u8; 272384] = include_bytes!("pi.bin");
 
-use block_buffer::BlockBuffer;
-use digest::generic_array::GenericArray;
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::{typenum::Unsigned, GenericArray},
+    HashMarker, Output,
+};
 
 // FSB-160
 fsb_impl!(
     Fsb160,
+    Fsb160Core,
     160,
     U60,
     U20,
@@ -79,12 +77,14 @@ fsb_impl!(
     640,
     653,
     1120,
-    "FSB-160 hash function."
+    "FSB-160 hasher state",
+    "Core FSB-160 hasher state",
 );
 
 // FSB-224
 fsb_impl!(
     Fsb224,
+    Fsb224Core,
     224,
     U84,
     U28,
@@ -93,12 +93,14 @@ fsb_impl!(
     896,
     907,
     1568,
-    "FSB-224 hash function."
+    "FSB-224 hasher state",
+    "Core FSB-224 hasher state",
 );
 
 // FSB-256
 fsb_impl!(
     Fsb256,
+    Fsb256Core,
     256,
     U96,
     U32,
@@ -107,12 +109,14 @@ fsb_impl!(
     1024,
     1061,
     1792,
-    "FSB-256 hash function."
+    "FSB-256 hasher state",
+    "Core FSB-256 hasher state",
 );
 
 // FSB-384
 fsb_impl!(
     Fsb384,
+    Fsb384Core,
     384,
     U115,
     U48,
@@ -121,12 +125,14 @@ fsb_impl!(
     1472,
     1483,
     2392,
-    "FSB-384 hash function."
+    "FSB-384 hasher state",
+    "Core FSB-384 hasher state",
 );
 
 // FSB-512
 fsb_impl!(
     Fsb512,
+    Fsb512Core,
     512,
     U155,
     U64,
@@ -135,5 +141,6 @@ fsb_impl!(
     1984,
     1987,
     3224,
-    "FSB-512 hash function."
+    "FSB-512 hasher state",
+    "Core FSB-512 hasher state",
 );
diff --git a/fsb/src/macros.rs b/fsb/src/macros.rs
index 8400722a4..620cb6d2b 100644
--- a/fsb/src/macros.rs
+++ b/fsb/src/macros.rs
@@ -1,57 +1,94 @@
 macro_rules! fsb_impl {
     (
-        $state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr,
-        $r:expr, $p:expr, $s:expr, $doc:expr
+        $full_state:ident, $state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr,
+        $r:expr, $p:expr, $s:expr, $full_doc:expr, $doc:expr,
     ) => {
         use digest::consts::{$blocksize, $outputsize};
 
         #[derive(Clone)]
         #[doc=$doc]
         pub struct $state {
-            /// bit size of the message till the current moment (the bit size is represented by a 64 bit
-            /// number)
-            bit_length: u64,
-            /// size of the message being processed
-            buffer: BlockBuffer<$blocksize>,
-            /// value of the input vector
-            hash: [u8; $r / 8],
+            blocks_len: u64,
+            state: [u8; $r / 8],
         }
 
+        impl HashMarker for $state {}
+
+        impl BlockSizeUser for $state {
+            type BlockSize = $blocksize;
+        }
+
+        impl OutputSizeUser for $state {
+            type OutputSize = $outputsize;
+        }
+
+        impl BufferKindUser for $state {
+            type BufferKind = Eager;
+        }
+
+        impl UpdateCore for $state {
+            #[inline]
+            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+                self.blocks_len += blocks.len() as u64;
+                for block in blocks {
+                    Self::compress(&mut self.state, Self::convert(block));
+                }
+            }
+        }
+
+        impl FixedOutputCore for $state {
+            #[inline]
+            fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+                let block_bytes = self.blocks_len * Self::BlockSize::U64;
+                let bit_len = 8 * (block_bytes + buffer.get_pos() as u64);
+                let mut h = self.state;
+                buffer.len64_padding_be(bit_len, |b| Self::compress(&mut h, Self::convert(b)));
+
+                let res = whirlpool::Whirlpool::digest(&h[..]);
+                let n = out.len();
+                out.copy_from_slice(&res[..n]);
+            }
+        }
+
+        impl Default for $state {
+            #[inline]
+            fn default() -> Self {
+                Self {
+                    blocks_len: 0u64,
+                    state: [0u8; $r / 8],
+                }
+            }
+        }
+
+        impl Reset for $state {
+            #[inline]
+            fn reset(&mut self) {
+                *self = Default::default();
+            }
+        }
+
+        impl AlgorithmName for $state {
+            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(stringify!($full_state))
+            }
+        }
+
+        impl fmt::Debug for $state {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!(stringify!($state), " { ... }"))
+            }
+        }
+
+        #[doc=$full_doc]
+        pub type $full_state = CoreWrapper<$state>;
+
         impl $state {
-            // constants
             const SIZE_OUTPUT_COMPRESS: usize = $r / 8;
             const SIZE_INPUT_COMPRESS: usize = $s / 8;
-            const HASH_OUTPUT_SIZE: usize = $state_num / 8;
             const SIZE_MSG_CHUNKS: usize = Self::SIZE_INPUT_COMPRESS - Self::SIZE_OUTPUT_COMPRESS;
             const SIZE_VECTORS: usize = $p / 8 + 1;
             const SHIFT: u8 = 8 - ($p % 8) as u8;
 
-            fn update_len(&mut self, len: u64) {
-                self.bit_length += len * 8;
-            }
-
-            fn finalize_inner(&mut self) {
-                let hash = &mut self.hash;
-                let pos = self.buffer.position();
-                if pos < Self::SIZE_MSG_CHUNKS - 8 {
-                    let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos - 8];
-                    padding[0] = 128u8;
-                    padding.extend_from_slice(&Self::helper_transform_usize(self.bit_length));
-                    self.buffer
-                        .input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
-                } else {
-                    let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos];
-                    padding[0] = 128u8;
-                    self.buffer
-                        .input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
-                    let mut second_padding = vec![0; Self::SIZE_MSG_CHUNKS - 8];
-                    second_padding
-                        .extend_from_slice(&Self::helper_transform_usize(self.bit_length));
-                    self.buffer
-                        .input_block(&second_padding, |b| Self::compress(hash, Self::convert(b)));
-                }
-            }
-
             fn define_iv(index: usize) -> [u8; Self::SIZE_VECTORS] {
                 let mut subset_pi: [u8; Self::SIZE_VECTORS] = [0u8; Self::SIZE_VECTORS];
                 subset_pi.copy_from_slice(
@@ -72,21 +109,21 @@ macro_rules! fsb_impl {
             /// $(W_i)_{i\in[0;w-1]}$ between $0$ and $n - 1$. The value of each $W_i$ is computed
             /// from the inputs bits like this:
             /// $W_i = i \times (n / w) + IV_i + M_i \times 2^{r / w}.
-            fn computing_W_indices(
+            fn computing_w_indices(
                 input_vector: &[u8; Self::SIZE_OUTPUT_COMPRESS],
                 message: &[u8; Self::SIZE_MSG_CHUNKS],
             ) -> [u32; $w] {
-                let mut W_indices: [u32; $w] = [0; $w];
+                let mut wind: [u32; $w] = [0; $w];
                 let divided_message: [u8; $w] = Self::dividing_bits(message, ($s - $r) / $w);
                 for i in 0..($w) {
                     let message_i = divided_message[i] as u32;
 
-                    W_indices[i] = (i * $n / $w) as u32
+                    wind[i] = (i * $n / $w) as u32
                         + input_vector[i] as u32
                         + (message_i << ($r / $w) as u8);
                 }
 
-                W_indices
+                wind
             }
 
             /// This function servers the purpose presented in table 3, of breaking a bit array into
@@ -133,7 +170,7 @@ macro_rules! fsb_impl {
             ) {
                 let mut initial_vector = [0u8; Self::SIZE_OUTPUT_COMPRESS];
 
-                let w_indices = Self::computing_W_indices(hash, message_block);
+                let w_indices = Self::computing_w_indices(hash, message_block);
                 for w_index in w_indices.iter() {
                     let chosen_vec = w_index / $r as u32;
                     let shift_value = w_index % $r as u32;
@@ -149,18 +186,6 @@ macro_rules! fsb_impl {
                 *hash = initial_vector;
             }
 
-            fn final_compression(
-                initial_vector: [u8; Self::SIZE_OUTPUT_COMPRESS],
-            ) -> [u8; Self::HASH_OUTPUT_SIZE] {
-                // Now we use Whirpool
-                let mut result = [0u8; Self::HASH_OUTPUT_SIZE];
-                let mut hasher = Whirlpool::new();
-
-                Update::update(&mut hasher, &initial_vector);
-                result.copy_from_slice(&hasher.finalize()[..Self::HASH_OUTPUT_SIZE]);
-                result
-            }
-
             fn shift_and_truncate(
                 array: &mut [u8; Self::SIZE_VECTORS],
                 shift_value: u32,
@@ -170,9 +195,7 @@ macro_rules! fsb_impl {
                 let mut truncated = [0u8; Self::SIZE_OUTPUT_COMPRESS];
 
                 if shift_value == 0 {
-                    array[..Self::SIZE_OUTPUT_COMPRESS]
-                        .try_into()
-                        .expect("SIZE_VECTORS is always bigger than SIZE_OUTPUT_COMPRESS")
+                    truncated.copy_from_slice(&array[..Self::SIZE_OUTPUT_COMPRESS]);
                 } else if shift_value <= (bits_in_cue as u32) {
                     let bytes_to_shift = 1;
                     let starting_byte = (array_len - bytes_to_shift) as usize;
@@ -183,8 +206,6 @@ macro_rules! fsb_impl {
                         truncated[position] ^= array[position - 1] >> (8 - shift_value);
                         truncated[position] ^= array[position] << shift_value;
                     }
-
-                    truncated
                 } else {
                     // First we need to decide which is the last byte and bit that will go to the first position.
                     // Then, we build our truncated array from there. Recall that the last byte is not complete,
@@ -246,8 +267,6 @@ macro_rules! fsb_impl {
                                 }
                             }
                         }
-
-                        truncated
                     } else {
                         truncated[..bytes_to_shift].clone_from_slice(
                             &array[starting_byte..(starting_byte + bytes_to_shift)],
@@ -261,76 +280,14 @@ macro_rules! fsb_impl {
                             truncated[position] ^= array[index] << (8 - bits_in_cue);
                             truncated[position] ^= array[index + 1] >> bits_in_cue;
                         }
-                        truncated
                     }
                 }
-            }
-
-            // I'm trying to avoid use unsafe code for this transformation. We are certain that the bit
-            // size of the buffer can be represented in 8 bytes.
-            fn helper_transform_usize(x: u64) -> [u8; 8] {
-                let b1: u8 = ((x >> 56) & 0xff) as u8;
-                let b2: u8 = ((x >> 48) & 0xff) as u8;
-                let b3: u8 = ((x >> 40) & 0xff) as u8;
-                let b4: u8 = ((x >> 32) & 0xff) as u8;
-                let b5: u8 = ((x >> 24) & 0xff) as u8;
-                let b6: u8 = ((x >> 16) & 0xff) as u8;
-                let b7: u8 = ((x >> 8) & 0xff) as u8;
-                let b8: u8 = (x & 0xff) as u8;
-                [b1, b2, b3, b4, b5, b6, b7, b8]
+                truncated
             }
 
             fn convert(block: &GenericArray<u8, $blocksize>) -> &[u8; Self::SIZE_MSG_CHUNKS] {
-                #[allow(unsafe_code)]
-                unsafe {
-                    &*(block.as_ptr() as *const [u8; Self::SIZE_MSG_CHUNKS])
-                }
-            }
-        }
-
-        impl Default for $state {
-            fn default() -> Self {
-                Self {
-                    bit_length: 0u64,
-                    buffer: BlockBuffer::default(),
-                    hash: [0u8; $r / 8],
-                }
-            }
-        }
-
-        impl BlockInput for $state {
-            type BlockSize = $blocksize;
-        }
-
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                let input = input.as_ref();
-                self.update_len(input.len() as u64);
-
-                let hash = &mut self.hash;
-                self.buffer
-                    .input_block(input, |b| $state::compress(hash, $state::convert(b)));
-            }
-        }
-
-        impl FixedOutputDirty for $state {
-            type OutputSize = $outputsize;
-
-            fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, Self::OutputSize>) {
-                self.finalize_inner();
-                let final_whirpool = $state::final_compression(self.hash);
-                out.copy_from_slice(&final_whirpool)
-            }
-        }
-
-        impl Reset for $state {
-            fn reset(&mut self) {
-                self.buffer.reset();
-                self.hash = [0u8; $r / 8];
-                self.bit_length = 0;
+                unsafe { &*(block.as_ptr() as *const [u8; Self::SIZE_MSG_CHUNKS]) }
             }
         }
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
     };
 }
diff --git a/gost94/CHANGELOG.md b/gost94/CHANGELOG.md
index 120bd45e7..6c6217903 100644
--- a/gost94/CHANGELOG.md
+++ b/gost94/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.1 (2020-11-03)
 ### Fixed
 - Arithmetic overflow bug. ([#193])
diff --git a/gost94/Cargo.toml b/gost94/Cargo.toml
index 9de6ffbfb..057e92a04 100644
--- a/gost94/Cargo.toml
+++ b/gost94/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "gost94"
-version = "0.9.1"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "GOST R 34.11-94 hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "gost94", "gost", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/gost94/benches/lib.rs b/gost94/benches/lib.rs
deleted file mode 100644
index 0043902bb..000000000
--- a/gost94/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(gost94::Gost94Test);
diff --git a/gost94/benches/mod.rs b/gost94/benches/mod.rs
new file mode 100644
index 000000000..4aa1f148b
--- /dev/null
+++ b/gost94/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use gost94::Gost94Test;
+use test::Bencher;
+
+bench_update!(
+    Gost94Test::default();
+    md2_10 10;
+    md2_100 100;
+    md2_1000 1000;
+    md2_10000 10000;
+);
diff --git a/gost94/examples/gost94_cryptopro_sum.rs b/gost94/examples/gost94_cryptopro_sum.rs
deleted file mode 100644
index c6bee4d74..000000000
--- a/gost94/examples/gost94_cryptopro_sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use gost94::{Digest, Gost94CryptoPro};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Gost94CryptoPro, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Gost94CryptoPro, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/gost94/src/cryptopro.rs b/gost94/src/cryptopro.rs
deleted file mode 100644
index e290472ca..000000000
--- a/gost94/src/cryptopro.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-const S_CRYPTO_PRO: SBox = [
-    [10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15],
-    [5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8],
-    [7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13],
-    [4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3],
-    [7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5],
-    [7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3],
-    [13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11],
-    [1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12],
-];
-
-gost94_impl!(Gost94CryptoPro, S_CRYPTO_PRO);
diff --git a/gost94/src/gost94.rs b/gost94/src/gost94_core.rs
similarity index 63%
rename from gost94/src/gost94.rs
rename to gost94/src/gost94_core.rs
index 8bbb0fe5a..569789a0b 100644
--- a/gost94/src/gost94.rs
+++ b/gost94/src/gost94_core.rs
@@ -1,19 +1,23 @@
 #![allow(clippy::many_single_char_names)]
-use block_buffer::block_padding::ZeroPadding;
-use block_buffer::BlockBuffer;
-use core::convert::TryInto;
-use digest::{consts::U32, generic_array::GenericArray};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-pub(crate) type Block = [u8; 32];
+use core::{convert::TryInto, fmt};
+use digest::{
+    block_buffer::Eager,
+    consts::U32,
+    core_api::{
+        AlgorithmName, Block as TBlock, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::{typenum::Unsigned, GenericArray},
+    HashMarker, Output,
+};
+
+use crate::params::{Block, Gost94Params, SBox};
 
 const C: Block = [
     0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00,
     0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff,
 ];
 
-pub type SBox = [[u8; 16]; 8];
-
 fn sbox(a: u32, s: &SBox) -> u32 {
     let mut v = 0;
 
@@ -114,15 +118,23 @@ fn psi(block: &mut Block) {
     block.copy_from_slice(&out);
 }
 
+#[inline(always)]
+fn adc(a: &mut u64, b: u64, carry: &mut u64) {
+    let ret = (*a as u128) + (b as u128) + (*carry as u128);
+    *a = ret as u64;
+    *carry = (ret >> 64) as u64;
+}
+
+/// Core GOST94 algorithm generic over parameters.
 #[derive(Clone)]
-struct Gost94State {
-    s: SBox,
+pub struct Gost94Core<P: Gost94Params> {
     h: Block,
     n: [u64; 4],
     sigma: [u64; 4],
+    _m: core::marker::PhantomData<P>,
 }
 
-impl Gost94State {
+impl<P: Gost94Params> Gost94Core<P> {
     fn shuffle(&mut self, m: &Block, s: &Block) {
         let mut res = Block::default();
         res.copy_from_slice(s);
@@ -141,23 +153,23 @@ impl Gost94State {
         let mut s = Block::default();
         s.copy_from_slice(&self.h);
         let k = p(x(&self.h, m));
-        encrypt(&mut s[0..8], k, &self.s);
+        encrypt(&mut s[0..8], k, &P::S_BOX);
 
         let u = a(self.h);
         let v = a(a(*m));
         let k = p(x(&u, &v));
-        encrypt(&mut s[8..16], k, &self.s);
+        encrypt(&mut s[8..16], k, &P::S_BOX);
 
         let mut u = a(u);
         x_mut(&mut u, &C);
         let v = a(a(v));
         let k = p(x(&u, &v));
-        encrypt(&mut s[16..24], k, &self.s);
+        encrypt(&mut s[16..24], k, &P::S_BOX);
 
         let u = a(u);
         let v = a(a(v));
         let k = p(x(&u, &v));
-        encrypt(&mut s[24..32], k, &self.s);
+        encrypt(&mut s[24..32], k, &P::S_BOX);
 
         self.shuffle(m, &s);
     }
@@ -178,92 +190,88 @@ impl Gost94State {
         adc(&mut self.n[3], 0, &mut carry);
     }
 
-    fn process_block(&mut self, block: &GenericArray<u8, U32>) {
+    #[inline(always)]
+    fn compress(&mut self, block: &GenericArray<u8, U32>) {
         let block = unsafe { &*(block.as_ptr() as *const [u8; 32]) };
         self.f(block);
         self.update_sigma(block);
     }
 }
 
-/// GOST94
-#[derive(Clone)]
-pub struct Gost94 {
-    buffer: BlockBuffer<U32>,
-    state: Gost94State,
-    h0: Block,
-}
-
-impl Gost94 {
-    /// Create new [`Gost94`] instance with given S-Box and IV
-    pub fn new(s: SBox, h: Block) -> Self {
-        let n = Default::default();
-        let sigma = Default::default();
-        Gost94 {
-            buffer: Default::default(),
-            h0: h,
-            state: Gost94State { s, h, n, sigma },
-        }
-    }
-}
+impl<P: Gost94Params> HashMarker for Gost94Core<P> {}
 
-impl BlockInput for Gost94 {
+impl<P: Gost94Params> BlockSizeUser for Gost94Core<P> {
     type BlockSize = U32;
 }
 
-impl Update for Gost94 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        let s = &mut self.state;
-        s.update_n(input.len());
-        self.buffer.input_block(input, |d| s.process_block(d));
-    }
+impl<P: Gost94Params> BufferKindUser for Gost94Core<P> {
+    type BufferKind = Eager;
 }
 
-impl FixedOutputDirty for Gost94 {
+impl<P: Gost94Params> OutputSizeUser for Gost94Core<P> {
     type OutputSize = U32;
+}
 
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, U32>) {
-        let self_state = &mut self.state;
-
-        if self.buffer.position() != 0 {
-            let block = self
-                .buffer
-                .pad_with::<ZeroPadding>()
-                .expect("we never use input_lazy");
+impl<P: Gost94Params> UpdateCore for Gost94Core<P> {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[TBlock<Self>]) {
+        let len = Self::BlockSize::USIZE * blocks.len();
+        self.update_n(len);
+        blocks.iter().for_each(|b| self.compress(b));
+    }
+}
 
-            self_state.process_block(block);
+impl<P: Gost94Params> FixedOutputCore for Gost94Core<P> {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        if buffer.get_pos() != 0 {
+            self.update_n(buffer.get_pos());
+            self.compress(buffer.pad_with_zeros());
         }
 
         let mut buf = Block::default();
-        for (o, v) in buf.chunks_exact_mut(8).zip(self_state.n.iter()) {
+        for (o, v) in buf.chunks_exact_mut(8).zip(self.n.iter()) {
             o.copy_from_slice(&v.to_le_bytes());
         }
-        self_state.f(&buf);
+        self.f(&buf);
 
-        for (o, v) in buf.chunks_exact_mut(8).zip(self_state.sigma.iter()) {
+        for (o, v) in buf.chunks_exact_mut(8).zip(self.sigma.iter()) {
             o.copy_from_slice(&v.to_le_bytes());
         }
-        self_state.f(&buf);
+        self.f(&buf);
 
-        out.copy_from_slice(&self.state.h);
+        out.copy_from_slice(&self.h);
+    }
+}
+
+impl<P: Gost94Params> Default for Gost94Core<P> {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            h: P::H0,
+            n: Default::default(),
+            sigma: Default::default(),
+            _m: Default::default(),
+        }
     }
 }
 
-impl Reset for Gost94 {
+impl<P: Gost94Params> Reset for Gost94Core<P> {
+    #[inline]
     fn reset(&mut self) {
-        self.buffer.reset();
-        self.state.n = Default::default();
-        self.state.h = self.h0;
-        self.state.sigma = Default::default();
+        *self = Default::default();
     }
 }
 
-#[inline(always)]
-fn adc(a: &mut u64, b: u64, carry: &mut u64) {
-    let ret = (*a as u128) + (b as u128) + (*carry as u128);
-    *a = ret as u64;
-    *carry = (ret >> 64) as u64;
+impl<P: Gost94Params> AlgorithmName for Gost94Core<P> {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(P::NAME)
+    }
 }
 
-opaque_debug::implement!(Gost94);
-digest::impl_write!(Gost94);
+impl<P: Gost94Params> fmt::Debug for Gost94Core<P> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        f.write_str(P::NAME)?;
+        f.write_str("Core { .. }")
+    }
+}
diff --git a/gost94/src/lib.rs b/gost94/src/lib.rs
index 7754c3c49..8427b2c55 100644
--- a/gost94/src/lib.rs
+++ b/gost94/src/lib.rs
@@ -1,22 +1,21 @@
 //! An implementation of the [GOST R 34.11-94][1] cryptographic hash algorithm.
 //!
 //! # Usage
-//!
 //! ```rust
-//! use gost94::{Gost94Test, Digest};
+//! use gost94::{Gost94CryptoPro, Digest};
 //! use hex_literal::hex;
 //!
-//! // create a Gost94 hasher instance with test S-box
-//! let mut hasher = Gost94Test::new();
+//! // create Gost94 hasher instance with CryptoPro params
+//! let mut hasher = Gost94CryptoPro::new();
 //!
 //! // process input message
-//! hasher.update(b"hello world");
+//! hasher.update("The quick brown fox jumps over the lazy dog");
 //!
 //! // acquire hash digest in the form of GenericArray,
 //! // which in this case is equivalent to [u8; 32]
 //! let result = hasher.finalize();
 //! assert_eq!(result[..], hex!("
-//!     1bb6ce69d2e895a78489c87a0712a2f40258d1fae3a4666c23f8f487bef0e22a
+//!     9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76
 //! "));
 //! ```
 //!
@@ -27,25 +26,28 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/gost94/0.10.0"
 )]
 #![warn(missing_docs, rust_2018_idioms)]
 
 #[cfg(feature = "std")]
 extern crate std;
 
-#[macro_use]
-mod macros;
+use digest::core_api::CoreWrapper;
 
-mod cryptopro;
-mod gost94;
-mod s2015;
-mod test_param;
+mod gost94_core;
+/// GOST94 parameters.
+pub mod params;
 
 pub use digest::{self, Digest};
 
-pub use crate::cryptopro::Gost94CryptoPro;
-pub use crate::gost94::Gost94;
-pub use crate::s2015::Gost94s2015;
-pub use crate::test_param::Gost94Test;
+pub use gost94_core::Gost94Core;
+
+/// GOST94 hash function with CryptoPro parameters.
+pub type Gost94CryptoPro = CoreWrapper<Gost94Core<params::CryptoProParam>>;
+/// GOST94 hash function with S-box defined in GOST R 34.12-2015.
+pub type Gost94s2015 = CoreWrapper<Gost94Core<params::S2015Param>>;
+/// GOST94 hash function with test parameters.
+pub type Gost94Test = CoreWrapper<Gost94Core<params::TestParam>>;
diff --git a/gost94/src/macros.rs b/gost94/src/macros.rs
deleted file mode 100644
index cd78628e7..000000000
--- a/gost94/src/macros.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-macro_rules! gost94_impl {
-    ($state:ident, $sbox:expr) => {
-        use digest::{consts::U32, BlockInput, FixedOutputDirty, Reset, Update};
-        use $crate::gost94::{Block, Gost94, SBox};
-
-        /// GOST94 state
-        #[derive(Clone)]
-        pub struct $state {
-            sh: Gost94,
-        }
-
-        impl Default for $state {
-            fn default() -> Self {
-                $state {
-                    sh: Gost94::new($sbox, Block::default()),
-                }
-            }
-        }
-
-        impl BlockInput for $state {
-            type BlockSize = U32;
-        }
-
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                let input = input.as_ref();
-                self.sh.update(input);
-            }
-        }
-
-        impl FixedOutputDirty for $state {
-            type OutputSize = U32;
-
-            fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-                self.sh.finalize_into_dirty(out)
-            }
-        }
-
-        impl Reset for $state {
-            fn reset(&mut self) {
-                self.sh.reset()
-            }
-        }
-
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
-    };
-}
diff --git a/gost94/src/params.rs b/gost94/src/params.rs
new file mode 100644
index 000000000..c7bf9459a
--- /dev/null
+++ b/gost94/src/params.rs
@@ -0,0 +1,70 @@
+pub(crate) type Block = [u8; 32];
+pub(crate) type SBox = [[u8; 16]; 8];
+
+/// Trait for storing parameter constants.
+// TODO: replace with const generics
+pub trait Gost94Params {
+    /// S-box value.
+    const S_BOX: SBox;
+    /// Initialization vector value.
+    const H0: Block;
+    /// Algorithm name
+    const NAME: &'static str;
+}
+
+/// CryptoPro parameters.
+#[derive(Copy, Clone, Default)]
+pub struct CryptoProParam;
+
+impl Gost94Params for CryptoProParam {
+    const S_BOX: SBox = [
+        [10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15],
+        [5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8],
+        [7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13],
+        [4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3],
+        [7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5],
+        [7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3],
+        [13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11],
+        [1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12],
+    ];
+    const H0: Block = [0; 32];
+    const NAME: &'static str = "Gost94CryptoPro";
+}
+
+/// S-Box defined in GOST R 34.12-2015.
+#[derive(Copy, Clone, Default)]
+pub struct S2015Param;
+
+impl Gost94Params for S2015Param {
+    const S_BOX: SBox = [
+        [12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1],
+        [6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15],
+        [11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0],
+        [12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11],
+        [7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12],
+        [5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0],
+        [8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7],
+        [1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2],
+    ];
+    const H0: Block = [0; 32];
+    const NAME: &'static str = "Gost94s2015";
+}
+
+/// Test parameters.
+#[derive(Copy, Clone, Default)]
+pub struct TestParam;
+
+impl Gost94Params for TestParam {
+    const S_BOX: SBox = [
+        [4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
+        [14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
+        [5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
+        [7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
+        [6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
+        [4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
+        [13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
+        [1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12],
+    ];
+    const H0: Block = [0; 32];
+    const NAME: &'static str = "Gost94Test";
+}
diff --git a/gost94/src/s2015.rs b/gost94/src/s2015.rs
deleted file mode 100644
index ac01a5c29..000000000
--- a/gost94/src/s2015.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-const S2015: SBox = [
-    [12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1],
-    [6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15],
-    [11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0],
-    [12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11],
-    [7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12],
-    [5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0],
-    [8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7],
-    [1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2],
-];
-
-gost94_impl!(Gost94s2015, S2015);
diff --git a/groestl/CHANGELOG.md b/groestl/CHANGELOG.md
index 08c48c032..4a1a089c8 100644
--- a/groestl/CHANGELOG.md
+++ b/groestl/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 and significantly improve performance ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.0 (2020-06-12)
 ### Changed
 - Bump `opaque-debug` to v0.3.0 ([#168])
diff --git a/groestl/Cargo.toml b/groestl/Cargo.toml
index 1408b7f66..307111bdd 100644
--- a/groestl/Cargo.toml
+++ b/groestl/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "groestl"
-version = "0.9.0"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "Grøstl hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "groestl", "grostl", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/groestl/LICENSE-MIT b/groestl/LICENSE-MIT
index 3d35f5c64..f39f9ff82 100644
--- a/groestl/LICENSE-MIT
+++ b/groestl/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Gulshan Singh
+Copyright (c) 2020 RustCrypto Developers
 
 Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated
diff --git a/groestl/benches/groestl256.rs b/groestl/benches/groestl256.rs
deleted file mode 100755
index eafc403b9..000000000
--- a/groestl/benches/groestl256.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(groestl::Groestl256);
diff --git a/groestl/benches/groestl512.rs b/groestl/benches/groestl512.rs
deleted file mode 100755
index 47c95d64e..000000000
--- a/groestl/benches/groestl512.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(groestl::Groestl512);
diff --git a/groestl/benches/mod.rs b/groestl/benches/mod.rs
new file mode 100644
index 000000000..1cae786b4
--- /dev/null
+++ b/groestl/benches/mod.rs
@@ -0,0 +1,22 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use groestl::{Groestl256, Groestl512};
+use test::Bencher;
+
+bench_update!(
+    Groestl256::default();
+    groestl256_10 10;
+    groestl256_100 100;
+    groestl256_1000 1000;
+    groestl256_10000 10000;
+);
+
+bench_update!(
+    Groestl512::default();
+    groestl512_10 10;
+    groestl512_100 100;
+    groestl512_1000 1000;
+    groestl512_10000 10000;
+);
diff --git a/groestl/src/compress1024.rs b/groestl/src/compress1024.rs
new file mode 100644
index 000000000..ebfd080fc
--- /dev/null
+++ b/groestl/src/compress1024.rs
@@ -0,0 +1,99 @@
+#![allow(clippy::needless_range_loop)]
+use crate::table::TABLE;
+use core::{convert::TryInto, u64};
+
+pub(crate) const COLS: usize = 16;
+const ROUNDS: u64 = 14;
+type Block = super::Block<super::GroestlLongVarCore>;
+
+#[inline(always)]
+fn column(x: &[u64; COLS], c: [usize; 8]) -> u64 {
+    let mut t = 0;
+    for i in 0..8 {
+        let sl = 8 * (7 - i);
+        let idx = ((x[c[i]] >> sl) & 0xFF) as usize;
+        t ^= TABLE[i][idx];
+    }
+    t
+}
+
+#[inline(always)]
+fn rndq(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
+    for i in 0..COLS {
+        x[i] ^= u64::MAX.wrapping_sub((i as u64) << 4) ^ r;
+    }
+    [
+        column(&x, [1, 3, 5, 11, 0, 2, 4, 6]),
+        column(&x, [2, 4, 6, 12, 1, 3, 5, 7]),
+        column(&x, [3, 5, 7, 13, 2, 4, 6, 8]),
+        column(&x, [4, 6, 8, 14, 3, 5, 7, 9]),
+        column(&x, [5, 7, 9, 15, 4, 6, 8, 10]),
+        column(&x, [6, 8, 10, 0, 5, 7, 9, 11]),
+        column(&x, [7, 9, 11, 1, 6, 8, 10, 12]),
+        column(&x, [8, 10, 12, 2, 7, 9, 11, 13]),
+        column(&x, [9, 11, 13, 3, 8, 10, 12, 14]),
+        column(&x, [10, 12, 14, 4, 9, 11, 13, 15]),
+        column(&x, [11, 13, 15, 5, 10, 12, 14, 0]),
+        column(&x, [12, 14, 0, 6, 11, 13, 15, 1]),
+        column(&x, [13, 15, 1, 7, 12, 14, 0, 2]),
+        column(&x, [14, 0, 2, 8, 13, 15, 1, 3]),
+        column(&x, [15, 1, 3, 9, 14, 0, 2, 4]),
+        column(&x, [0, 2, 4, 10, 15, 1, 3, 5]),
+    ]
+}
+
+#[inline(always)]
+fn rndp(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
+    for i in 0..COLS {
+        x[i] ^= ((i as u64) << 60) ^ r;
+    }
+    [
+        column(&x, [0, 1, 2, 3, 4, 5, 6, 11]),
+        column(&x, [1, 2, 3, 4, 5, 6, 7, 12]),
+        column(&x, [2, 3, 4, 5, 6, 7, 8, 13]),
+        column(&x, [3, 4, 5, 6, 7, 8, 9, 14]),
+        column(&x, [4, 5, 6, 7, 8, 9, 10, 15]),
+        column(&x, [5, 6, 7, 8, 9, 10, 11, 0]),
+        column(&x, [6, 7, 8, 9, 10, 11, 12, 1]),
+        column(&x, [7, 8, 9, 10, 11, 12, 13, 2]),
+        column(&x, [8, 9, 10, 11, 12, 13, 14, 3]),
+        column(&x, [9, 10, 11, 12, 13, 14, 15, 4]),
+        column(&x, [10, 11, 12, 13, 14, 15, 0, 5]),
+        column(&x, [11, 12, 13, 14, 15, 0, 1, 6]),
+        column(&x, [12, 13, 14, 15, 0, 1, 2, 7]),
+        column(&x, [13, 14, 15, 0, 1, 2, 3, 8]),
+        column(&x, [14, 15, 0, 1, 2, 3, 4, 9]),
+        column(&x, [15, 0, 1, 2, 3, 4, 5, 10]),
+    ]
+}
+
+pub(crate) fn compress(h: &mut [u64; COLS], block: &Block) {
+    let mut q = [0u64; COLS];
+    for (chunk, v) in block.chunks_exact(8).zip(q.iter_mut()) {
+        *v = u64::from_be_bytes(chunk.try_into().unwrap());
+    }
+    let mut p = [0u64; COLS];
+    for i in 0..COLS {
+        p[i] = h[i] ^ q[i];
+    }
+    for i in 0..ROUNDS {
+        q = rndq(q, i);
+    }
+    for i in 0..ROUNDS {
+        p = rndp(p, i << 56);
+    }
+    for i in 0..COLS {
+        h[i] ^= q[i] ^ p[i];
+    }
+}
+
+pub(crate) fn p(h: &[u64; COLS]) -> [u64; COLS] {
+    let mut p = *h;
+    for i in 0..ROUNDS {
+        p = rndp(p, i << 56);
+    }
+    for i in 0..COLS {
+        p[i] ^= h[i];
+    }
+    p
+}
diff --git a/groestl/src/compress512.rs b/groestl/src/compress512.rs
new file mode 100644
index 000000000..1db8d4091
--- /dev/null
+++ b/groestl/src/compress512.rs
@@ -0,0 +1,83 @@
+#![allow(clippy::needless_range_loop)]
+use crate::table::TABLE;
+use core::{convert::TryInto, u64};
+type Block = super::Block<super::GroestlShortVarCore>;
+
+pub(crate) const COLS: usize = 8;
+const ROUNDS: u64 = 10;
+
+#[inline(always)]
+fn column(x: &[u64; COLS], c: [usize; 8]) -> u64 {
+    let mut t = 0;
+    for i in 0..8 {
+        let sl = 8 * (7 - i);
+        let idx = ((x[c[i]] >> sl) & 0xFF) as usize;
+        t ^= TABLE[i][idx];
+    }
+    t
+}
+
+#[inline(always)]
+fn rndq(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
+    for i in 0..COLS {
+        x[i] ^= u64::MAX.wrapping_sub((i as u64) << 4) ^ r;
+    }
+    [
+        column(&x, [1, 3, 5, 7, 0, 2, 4, 6]),
+        column(&x, [2, 4, 6, 0, 1, 3, 5, 7]),
+        column(&x, [3, 5, 7, 1, 2, 4, 6, 0]),
+        column(&x, [4, 6, 0, 2, 3, 5, 7, 1]),
+        column(&x, [5, 7, 1, 3, 4, 6, 0, 2]),
+        column(&x, [6, 0, 2, 4, 5, 7, 1, 3]),
+        column(&x, [7, 1, 3, 5, 6, 0, 2, 4]),
+        column(&x, [0, 2, 4, 6, 7, 1, 3, 5]),
+    ]
+}
+
+#[inline(always)]
+fn rndp(mut x: [u64; COLS], r: u64) -> [u64; COLS] {
+    for i in 0..COLS {
+        x[i] ^= ((i as u64) << 60) ^ r;
+    }
+    [
+        column(&x, [0, 1, 2, 3, 4, 5, 6, 7]),
+        column(&x, [1, 2, 3, 4, 5, 6, 7, 0]),
+        column(&x, [2, 3, 4, 5, 6, 7, 0, 1]),
+        column(&x, [3, 4, 5, 6, 7, 0, 1, 2]),
+        column(&x, [4, 5, 6, 7, 0, 1, 2, 3]),
+        column(&x, [5, 6, 7, 0, 1, 2, 3, 4]),
+        column(&x, [6, 7, 0, 1, 2, 3, 4, 5]),
+        column(&x, [7, 0, 1, 2, 3, 4, 5, 6]),
+    ]
+}
+
+pub(crate) fn compress(h: &mut [u64; COLS], block: &Block) {
+    let mut q = [0u64; COLS];
+    for (chunk, v) in block.chunks_exact(8).zip(q.iter_mut()) {
+        *v = u64::from_be_bytes(chunk.try_into().unwrap());
+    }
+    let mut p = [0u64; COLS];
+    for i in 0..COLS {
+        p[i] = h[i] ^ q[i];
+    }
+    for i in 0..ROUNDS {
+        q = rndq(q, i);
+    }
+    for i in 0..ROUNDS {
+        p = rndp(p, i << 56);
+    }
+    for i in 0..COLS {
+        h[i] ^= q[i] ^ p[i];
+    }
+}
+
+pub(crate) fn p(h: &[u64; COLS]) -> [u64; COLS] {
+    let mut p = *h;
+    for i in 0..ROUNDS {
+        p = rndp(p, i << 56);
+    }
+    for i in 0..COLS {
+        p[i] ^= h[i];
+    }
+    p
+}
diff --git a/groestl/src/consts.rs b/groestl/src/consts.rs
deleted file mode 100644
index 6b09c6a25..000000000
--- a/groestl/src/consts.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-pub const SBOX: [u8; 256] = [
-    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
-    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
-    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
-    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
-    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
-    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
-    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
-    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
-    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
-    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
-    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
-    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
-    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
-    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
-    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
-    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
-];
-
-pub const C_P: [u8; 128] = [
-    0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-];
-pub const C_Q: [u8; 128] = [
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xef, 0xdf, 0xcf, 0xbf, 0xaf, 0x9f, 0x8f, 0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f,
-];
-
-pub const B: [[u8; 8]; 8] = [
-    [2, 2, 3, 4, 5, 3, 5, 7],
-    [7, 2, 2, 3, 4, 5, 3, 5],
-    [5, 7, 2, 2, 3, 4, 5, 3],
-    [3, 5, 7, 2, 2, 3, 4, 5],
-    [5, 3, 5, 7, 2, 2, 3, 4],
-    [4, 5, 3, 5, 7, 2, 2, 3],
-    [3, 4, 5, 3, 5, 7, 2, 2],
-    [2, 3, 4, 5, 3, 5, 7, 2],
-];
-
-pub const SHIFTS_P: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
-pub const SHIFTS_Q: [u8; 8] = [1, 3, 5, 7, 0, 2, 4, 6];
-pub const SHIFTS_P_WIDE: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 11];
-pub const SHIFTS_Q_WIDE: [u8; 8] = [1, 3, 5, 11, 0, 2, 4, 6];
diff --git a/groestl/src/groestl.rs b/groestl/src/groestl.rs
deleted file mode 100644
index 1690de283..000000000
--- a/groestl/src/groestl.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use block_buffer::BlockBuffer;
-use core::ops::Div;
-use digest::generic_array::typenum::{Quot, U8};
-use digest::generic_array::{ArrayLength, GenericArray};
-
-use crate::state::{xor_generic_array, GroestlState};
-
-#[derive(Clone)]
-pub struct Groestl<BlockSize>
-where
-    BlockSize: ArrayLength<u8> + Div<U8> + Default,
-    BlockSize::ArrayType: Copy,
-    Quot<BlockSize, U8>: ArrayLength<u8>,
-{
-    buffer: BlockBuffer<BlockSize>,
-    state: GroestlState<BlockSize>,
-    pub output_size: usize,
-}
-
-impl<BlockSize> Groestl<BlockSize>
-where
-    BlockSize: ArrayLength<u8> + Div<U8> + Default,
-    BlockSize::ArrayType: Copy,
-    Quot<BlockSize, U8>: ArrayLength<u8>,
-    Self: Clone,
-{
-    pub fn new(output_size: usize) -> Result<Self, digest::InvalidOutputSize> {
-        match BlockSize::to_usize() {
-            128 => {
-                if output_size <= 32 || output_size > 64 {
-                    return Err(digest::InvalidOutputSize);
-                }
-            }
-            64 => {
-                if output_size == 0 || output_size > 32 {
-                    return Err(digest::InvalidOutputSize);
-                }
-            }
-            _ => unreachable!(),
-        };
-
-        let state = GroestlState::new(output_size);
-        Ok(Groestl {
-            buffer: Default::default(),
-            state,
-            output_size,
-        })
-    }
-
-    pub fn process(&mut self, input: &[u8]) {
-        let s = &mut self.state;
-        self.buffer.input_block(input, |b| s.compress(b));
-    }
-
-    pub fn finalize(&mut self) -> GenericArray<u8, BlockSize> {
-        let res = {
-            let state = &mut self.state;
-            let l = if self.buffer.remaining() <= 8 {
-                state.num_blocks + 2
-            } else {
-                state.num_blocks + 1
-            };
-            self.buffer.len64_padding_be(l, |b| state.compress(b));
-            xor_generic_array(&state.p(&state.state), &state.state)
-        };
-
-        self.buffer = Default::default();
-        self.state = GroestlState::new(self.output_size);
-        res
-    }
-
-    pub fn reset(&mut self) {
-        self.state = GroestlState::new(self.output_size);
-        self.buffer.reset();
-    }
-}
diff --git a/groestl/src/lib.rs b/groestl/src/lib.rs
old mode 100755
new mode 100644
index d4c8155ec..8e5085a0b
--- a/groestl/src/lib.rs
+++ b/groestl/src/lib.rs
@@ -2,14 +2,7 @@
 //!
 //! # Usage
 //!
-//! Groestl can produce a digest of any size between 1 and 64 bytes inclusive.
-//! This crate defines the common digest sizes (`Groestl224`, `Groestl256`,
-//! `Groestl384`, and `Groestl512`), but allows you to specify a custom size
-//! with the `GroestlSmall` and `GroestlBig` structs. `GroestlSmall` allows you
-//! to specify a digest size between 1 and 32 inclusive, and `GroestlBig` allows
-//! you to specify a digest size between 33 and 64 inclusive.
-//!
-//! ```rust
+//! ```
 //! use groestl::{Digest, Groestl256};
 //! use hex_literal::hex;
 //!
@@ -34,33 +27,199 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/groestl/0.10.0"
 )]
 #![deny(unsafe_code)]
 #![warn(rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
-
 pub use digest::{self, Digest};
 
-mod consts;
-mod groestl;
-mod matrix;
-mod state;
-#[macro_use]
-mod macros;
-
-use crate::groestl::Groestl;
-use digest::consts::{U128, U28, U32, U48, U64};
-use digest::generic_array::typenum::Unsigned;
-use digest::{BlockInput, FixedOutputDirty, InvalidOutputSize, Reset, Update, VariableOutputDirty};
-
-impl_groestl!(Groestl512, U64, U128);
-impl_groestl!(Groestl384, U48, U128);
-impl_groestl!(Groestl256, U32, U64);
-impl_groestl!(Groestl224, U28, U64);
-
-impl_variable_groestl!(GroestlBig, U128, 32, 64);
-impl_variable_groestl!(GroestlSmall, U64, 0, 32);
+use core::fmt;
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
+        CtVariableCoreWrapper, OutputSizeUser, RtVariableCoreWrapper, TruncSide, UpdateCore,
+        VariableOutputCore,
+    },
+    generic_array::typenum::{Unsigned, U128, U28, U32, U48, U64},
+    HashMarker, InvalidOutputSize, Output,
+};
+
+mod compress1024;
+mod compress512;
+mod table;
+
+/// Lowest-level core hasher state of the short Groestl variant.
+#[derive(Clone)]
+pub struct GroestlShortVarCore {
+    state: [u64; compress512::COLS],
+    blocks_len: u64,
+}
+
+impl HashMarker for GroestlShortVarCore {}
+
+impl BlockSizeUser for GroestlShortVarCore {
+    type BlockSize = U64;
+}
+
+impl BufferKindUser for GroestlShortVarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for GroestlShortVarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.blocks_len += blocks.len() as u64;
+        for block in blocks {
+            compress512::compress(&mut self.state, block);
+        }
+    }
+}
+
+impl OutputSizeUser for GroestlShortVarCore {
+    type OutputSize = U32;
+}
+
+impl VariableOutputCore for GroestlShortVarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Right;
+
+    #[inline]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        if output_size > Self::OutputSize::USIZE {
+            return Err(InvalidOutputSize);
+        }
+        let mut state = [0; compress512::COLS];
+        state[compress512::COLS - 1] = 8 * output_size as u64;
+        let blocks_len = 0;
+        Ok(Self { state, blocks_len })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let blocks_len = if buffer.remaining() <= 8 {
+            self.blocks_len + 2
+        } else {
+            self.blocks_len + 1
+        };
+        buffer.len64_padding_be(blocks_len, |b| compress512::compress(&mut self.state, b));
+        let res = compress512::p(&self.state);
+        let n = compress512::COLS / 2;
+        for (chunk, v) in out.chunks_exact_mut(8).zip(res[n..].iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
+        }
+    }
+}
+
+impl AlgorithmName for GroestlShortVarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("GroestlShort")
+    }
+}
+
+impl fmt::Debug for GroestlShortVarCore {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("GroestlShortVarCore { ... }")
+    }
+}
+
+/// Short Groestl variant which allows to choose output size at runtime.
+pub type GroestlShortVar = RtVariableCoreWrapper<GroestlShortVarCore>;
+/// Core hasher state of the short Groestl variant generic over output size.
+pub type GroestlShortCore<OutSize> = CtVariableCoreWrapper<GroestlShortVarCore, OutSize>;
+/// Hasher state of the short Groestl variant generic over output size.
+pub type GroestlShort<OutSize> = CoreWrapper<GroestlShortCore<OutSize>>;
+
+/// Groestl-224 hasher state.
+pub type Groestl224 = CoreWrapper<GroestlShortCore<U28>>;
+/// Groestl-256 hasher state.
+pub type Groestl256 = CoreWrapper<GroestlShortCore<U32>>;
+
+/// Lowest-level core hasher state of the long Groestl variant.
+#[derive(Clone)]
+pub struct GroestlLongVarCore {
+    state: [u64; compress1024::COLS],
+    blocks_len: u64,
+}
+
+impl HashMarker for GroestlLongVarCore {}
+
+impl BlockSizeUser for GroestlLongVarCore {
+    type BlockSize = U128;
+}
+
+impl BufferKindUser for GroestlLongVarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for GroestlLongVarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.blocks_len += blocks.len() as u64;
+        for block in blocks {
+            compress1024::compress(&mut self.state, block);
+        }
+    }
+}
+
+impl OutputSizeUser for GroestlLongVarCore {
+    type OutputSize = U64;
+}
+
+impl VariableOutputCore for GroestlLongVarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Right;
+
+    #[inline]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        if output_size > Self::OutputSize::USIZE {
+            return Err(InvalidOutputSize);
+        }
+        let mut state = [0; compress1024::COLS];
+        state[compress1024::COLS - 1] = 8 * output_size as u64;
+        let blocks_len = 0;
+        Ok(Self { state, blocks_len })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let blocks_len = if buffer.remaining() <= 8 {
+            self.blocks_len + 2
+        } else {
+            self.blocks_len + 1
+        };
+        buffer.len64_padding_be(blocks_len, |b| compress1024::compress(&mut self.state, b));
+        let res = compress1024::p(&self.state);
+        let n = compress1024::COLS / 2;
+        for (chunk, v) in out.chunks_exact_mut(8).zip(res[n..].iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
+        }
+    }
+}
+
+impl AlgorithmName for GroestlLongVarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("GroestlLong")
+    }
+}
+
+impl fmt::Debug for GroestlLongVarCore {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("GroestlLongVarCore { ... }")
+    }
+}
+
+/// Long Groestl variant which allows to choose output size at runtime.
+pub type GroestlLongVar = RtVariableCoreWrapper<GroestlLongVarCore>;
+/// Core hasher state of the long Groestl variant generic over output size.
+pub type GroestlLongCore<OutSize> = CtVariableCoreWrapper<GroestlLongVarCore, OutSize>;
+/// Hasher state of the long Groestl variant generic over output size.
+pub type GroestlLong<OutSize> = CoreWrapper<GroestlLongCore<OutSize>>;
+
+/// Groestl-384 hasher state.
+pub type Groestl384 = CoreWrapper<GroestlLongCore<U48>>;
+/// Groestl-512 hasher state.
+pub type Groestl512 = CoreWrapper<GroestlLongCore<U64>>;
diff --git a/groestl/src/macros.rs b/groestl/src/macros.rs
deleted file mode 100644
index 00a54e338..000000000
--- a/groestl/src/macros.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-macro_rules! impl_groestl {
-    ($state:ident, $output:ident, $block:ident) => {
-        #[derive(Clone)]
-        pub struct $state {
-            groestl: Groestl<$block>,
-        }
-
-        impl Default for $state {
-            fn default() -> Self {
-                $state {
-                    groestl: Groestl::new($output::to_usize()).unwrap(),
-                }
-            }
-        }
-
-        impl BlockInput for $state {
-            type BlockSize = $block;
-        }
-
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                let input = input.as_ref();
-                self.groestl.process(input);
-            }
-        }
-
-        impl FixedOutputDirty for $state {
-            type OutputSize = $output;
-
-            fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-                let block = self.groestl.finalize();
-                let n = block.len() - Self::OutputSize::to_usize();
-                out.copy_from_slice(&block[n..])
-            }
-        }
-
-        impl Reset for $state {
-            fn reset(&mut self) {
-                self.groestl.reset()
-            }
-        }
-
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
-    };
-}
-
-macro_rules! impl_variable_groestl {
-    ($state:ident, $block:ident, $min:expr, $max:expr) => {
-        #[derive(Clone)]
-        pub struct $state {
-            groestl: Groestl<$block>,
-        }
-
-        impl BlockInput for $state {
-            type BlockSize = $block;
-        }
-
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                self.groestl.process(input.as_ref());
-            }
-        }
-
-        impl VariableOutputDirty for $state {
-            fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
-                if output_size == $min || output_size > $max {
-                    return Err(InvalidOutputSize);
-                }
-                Ok($state {
-                    groestl: Groestl::new(output_size).unwrap(),
-                })
-            }
-
-            fn output_size(&self) -> usize {
-                self.groestl.output_size
-            }
-
-            fn finalize_variable_dirty(&mut self, f: impl FnOnce(&[u8])) {
-                let block = self.groestl.finalize();
-                let n = block.len() - self.groestl.output_size;
-                f(&block[n..]);
-            }
-        }
-
-        impl Reset for $state {
-            fn reset(&mut self) {
-                self.groestl.reset()
-            }
-        }
-
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
-    };
-}
diff --git a/groestl/src/matrix.rs b/groestl/src/matrix.rs
deleted file mode 100644
index 433ddb66f..000000000
--- a/groestl/src/matrix.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use core::ops::{Index, IndexMut};
-use digest::generic_array::{ArrayLength, GenericArray};
-
-#[derive(Debug, Eq, PartialEq)]
-pub struct Matrix<R: ArrayLength<GenericArray<u8, C>>, C: ArrayLength<u8>> {
-    pub state: GenericArray<GenericArray<u8, C>, R>,
-}
-
-impl<R, C> Default for Matrix<R, C>
-where
-    R: ArrayLength<GenericArray<u8, C>>,
-    C: ArrayLength<u8>,
-{
-    fn default() -> Self {
-        Matrix {
-            state: GenericArray::default(),
-        }
-    }
-}
-
-impl<R, C> Index<usize> for Matrix<R, C>
-where
-    R: ArrayLength<GenericArray<u8, C>>,
-    C: ArrayLength<u8>,
-{
-    type Output = GenericArray<u8, C>;
-
-    fn index(&self, index: usize) -> &Self::Output {
-        &self.state[index]
-    }
-}
-
-impl<R, C> IndexMut<usize> for Matrix<R, C>
-where
-    R: ArrayLength<GenericArray<u8, C>>,
-    C: ArrayLength<u8>,
-{
-    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
-        &mut self.state[index]
-    }
-}
-
-fn poly_mul(a: u8, b: usize) -> usize {
-    let mut val = match a {
-        2 => b << 1,
-        3 => b ^ poly_mul(2, b),
-        4 => b << 2,
-        5 => b ^ poly_mul(4, b),
-        7 => b ^ poly_mul(2, b) ^ poly_mul(4, b),
-        _ => unreachable!(),
-    };
-
-    if val >= 512 {
-        val ^= 0x11b << 1;
-    }
-    if val >= 256 {
-        val ^= 0x11b;
-    }
-    val
-}
-
-impl<R, C> Matrix<R, C>
-where
-    R: ArrayLength<GenericArray<u8, C>>,
-    C: ArrayLength<u8>,
-{
-    pub fn rows(&self) -> usize {
-        R::to_usize()
-    }
-
-    pub fn cols(&self) -> usize {
-        C::to_usize()
-    }
-
-    pub fn mul_array(&self, a: &[[u8; 8]; 8]) -> Self {
-        let mut res = Matrix::default();
-        for i in 0..8 {
-            for j in 0..self.cols() {
-                for k in 0..8 {
-                    res[i][j] ^= poly_mul(a[i][k], self[k][j] as usize) as u8;
-                }
-            }
-        }
-
-        res
-    }
-}
diff --git a/groestl/src/state.rs b/groestl/src/state.rs
deleted file mode 100644
index f1e4dd424..000000000
--- a/groestl/src/state.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-use core::ops::Div;
-
-use crate::consts::{B, C_P, C_Q, SBOX, SHIFTS_P, SHIFTS_P_WIDE, SHIFTS_Q, SHIFTS_Q_WIDE};
-use crate::matrix::Matrix;
-use digest::generic_array::typenum::{Quot, U8};
-use digest::generic_array::{ArrayLength, GenericArray};
-
-#[derive(Copy, Clone)]
-pub struct GroestlState<BlockSize>
-where
-    BlockSize: ArrayLength<u8> + Div<U8>,
-    BlockSize::ArrayType: Copy,
-    Quot<BlockSize, U8>: ArrayLength<u8>,
-{
-    pub state: GenericArray<u8, BlockSize>,
-    rounds: u8,
-    pub num_blocks: u64,
-}
-
-pub fn xor_generic_array<L: ArrayLength<u8>>(
-    a1: &GenericArray<u8, L>,
-    a2: &GenericArray<u8, L>,
-) -> GenericArray<u8, L> {
-    let mut res = GenericArray::default();
-    for i in 0..L::to_usize() {
-        res[i] = a1[i] ^ a2[i];
-    }
-    res
-}
-
-fn gcd(a: usize, b: usize) -> usize {
-    if b == 0 {
-        return a;
-    }
-    gcd(b, a % b)
-}
-
-impl<BlockSize> GroestlState<BlockSize>
-where
-    BlockSize: ArrayLength<u8> + Div<U8>,
-    BlockSize::ArrayType: Copy,
-    Quot<BlockSize, U8>: ArrayLength<u8>,
-{
-    pub fn new(output_size: usize) -> Self {
-        let block_bytes = BlockSize::to_usize();
-        let output_bits = output_size * 8;
-
-        let mut state = GenericArray::default();
-        let n = output_bits as u64;
-        state[block_bytes - 8..].copy_from_slice(&n.to_be_bytes());
-        let rounds = match block_bytes {
-            128 => 14,
-            64 => 10,
-            _ => unreachable!(),
-        };
-
-        GroestlState {
-            state,
-            rounds,
-            num_blocks: 0,
-        }
-    }
-
-    fn wide(&self) -> bool {
-        match BlockSize::to_usize() {
-            128 => true,
-            64 => false,
-            _ => unreachable!(),
-        }
-    }
-
-    pub fn compress(&mut self, input_block: &GenericArray<u8, BlockSize>) {
-        self.state = xor_generic_array(
-            &xor_generic_array(
-                &self.p(&xor_generic_array(&self.state, input_block)),
-                &self.q(input_block),
-            ),
-            &self.state,
-        );
-        self.num_blocks += 1;
-    }
-
-    fn block_to_matrix(
-        &self,
-        block: &GenericArray<u8, BlockSize>,
-    ) -> Matrix<U8, Quot<BlockSize, U8>> {
-        let mut matrix = Matrix::<U8, Quot<BlockSize, U8>>::default();
-
-        let rows = matrix.rows();
-        for i in 0..matrix.cols() {
-            for j in 0..rows {
-                matrix[j][i] = block[i * rows + j];
-            }
-        }
-
-        matrix
-    }
-
-    fn matrix_to_block(
-        &self,
-        matrix: &Matrix<U8, Quot<BlockSize, U8>>,
-    ) -> GenericArray<u8, BlockSize> {
-        let mut block = GenericArray::default();
-
-        let rows = matrix.rows();
-        for i in 0..matrix.cols() {
-            for j in 0..rows {
-                block[i * rows + j] = matrix[j][i];
-            }
-        }
-
-        block
-    }
-
-    pub fn p(&self, block: &GenericArray<u8, BlockSize>) -> GenericArray<u8, BlockSize> {
-        let shifts = if self.wide() { SHIFTS_P_WIDE } else { SHIFTS_P };
-        let mut matrix = self.block_to_matrix(block);
-        for round in 0..self.rounds {
-            self.add_round_constant(&mut matrix, C_P, round);
-            self.sub_bytes(&mut matrix);
-            self.shift_bytes(&mut matrix, shifts);
-            matrix = matrix.mul_array(&B);
-        }
-        self.matrix_to_block(&matrix)
-    }
-
-    fn q(&self, block: &GenericArray<u8, BlockSize>) -> GenericArray<u8, BlockSize> {
-        let shifts = if self.wide() { SHIFTS_Q_WIDE } else { SHIFTS_Q };
-        let mut matrix = self.block_to_matrix(block);
-        for round in 0..self.rounds {
-            self.add_round_constant(&mut matrix, C_Q, round);
-            self.sub_bytes(&mut matrix);
-            self.shift_bytes(&mut matrix, shifts);
-            matrix = matrix.mul_array(&B);
-        }
-        self.matrix_to_block(&matrix)
-    }
-
-    fn add_round_constant(
-        &self,
-        matrix: &mut Matrix<U8, Quot<BlockSize, U8>>,
-        c: [u8; 128],
-        round: u8,
-    ) {
-        for i in 0..matrix.rows() {
-            for j in 0..matrix.cols() {
-                matrix[i][j] ^= c[i * 16 + j];
-
-                if (c[0] == 0x00 && i == 0) || (c[0] == 0xff && i == 7) {
-                    matrix[i][j] ^= round;
-                }
-            }
-        }
-    }
-
-    fn sub_bytes(&self, matrix: &mut Matrix<U8, Quot<BlockSize, U8>>) {
-        for i in 0..matrix.rows() {
-            for j in 0..matrix.cols() {
-                matrix[i][j] = SBOX[matrix[i][j] as usize];
-            }
-        }
-    }
-
-    fn shift_bytes(&self, matrix: &mut Matrix<U8, Quot<BlockSize, U8>>, shifts: [u8; 8]) {
-        let cols = matrix.cols();
-        for i in 0..matrix.rows() {
-            let shift = shifts[i] as usize;
-            if shift == 0 {
-                continue;
-            }
-            let d = gcd(shift, cols);
-            for j in 0..d {
-                let mut k = j;
-                let tmp = matrix[i][k];
-                loop {
-                    let pos = k.wrapping_add(shift) % cols;
-                    if pos == j {
-                        break;
-                    }
-                    matrix[i][k] = matrix[i][pos];
-                    k = pos;
-                }
-                matrix[i][k] = tmp;
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::{xor_generic_array, GroestlState};
-    use crate::consts::{C_P, C_Q, SHIFTS_P};
-    use digest::generic_array::typenum::U64;
-    use digest::generic_array::GenericArray;
-
-    fn get_padding_block() -> GenericArray<u8, U64> {
-        let padding_block: [u8; 64] = [
-            128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 0, 1,
-        ];
-
-        GenericArray::clone_from_slice(&padding_block)
-    }
-
-    #[test]
-    fn test_shift_bytes() {
-        let s = GroestlState::<U64>::new(32);
-        let mut block = GenericArray::default();
-        for i in 0..64 {
-            block[i] = i as u8;
-        }
-        let mut matrix = s.block_to_matrix(&block);
-        s.shift_bytes(&mut matrix, SHIFTS_P);
-        let block = s.matrix_to_block(&matrix);
-        let expected = [
-            0, 9, 18, 27, 36, 45, 54, 63, 8, 17, 26, 35, 44, 53, 62, 7, 16, 25, 34, 43, 52, 61, 6,
-            15, 24, 33, 42, 51, 60, 5, 14, 23, 32, 41, 50, 59, 4, 13, 22, 31, 40, 49, 58, 3, 12,
-            21, 30, 39, 48, 57, 2, 11, 20, 29, 38, 47, 56, 1, 10, 19, 28, 37, 46, 55,
-        ];
-        assert_eq!(&block[..], &expected[..]);
-    }
-
-    #[test]
-    fn test_p() {
-        let padding_chunk = get_padding_block();
-        let s = GroestlState::<U64>::new(32);
-        let block = xor_generic_array(&s.state, GenericArray::from_slice(&padding_chunk));
-
-        let p_block = s.p(&block);
-        let expected = [
-            247, 236, 141, 217, 73, 225, 112, 216, 1, 155, 85, 192, 152, 168, 174, 72, 112, 253,
-            159, 53, 7, 6, 8, 115, 58, 242, 7, 115, 148, 150, 157, 25, 18, 220, 11, 5, 178, 10,
-            110, 94, 44, 56, 110, 67, 107, 234, 102, 163, 243, 212, 49, 25, 46, 17, 170, 84, 5, 76,
-            239, 51, 4, 107, 94, 20,
-        ];
-        assert_eq!(&p_block[..], &expected[..]);
-    }
-
-    #[test]
-    fn test_q() {
-        let padding_chunk = get_padding_block();
-        let s = GroestlState::<U64>::new(32);
-        let q_block = s.q(GenericArray::from_slice(&padding_chunk));
-        let expected = [
-            189, 183, 105, 133, 208, 106, 34, 36, 82, 37, 180, 250, 229, 59, 230, 223, 215, 245,
-            53, 117, 167, 139, 150, 186, 210, 17, 220, 57, 116, 134, 209, 51, 124, 108, 84, 91, 79,
-            103, 148, 27, 135, 183, 144, 226, 59, 242, 87, 81, 109, 211, 84, 185, 192, 172, 88,
-            210, 8, 121, 31, 242, 158, 227, 207, 13,
-        ];
-        assert_eq!(&q_block[..], &expected[..]);
-    }
-
-    #[test]
-    fn test_block_to_matrix() {
-        let s = GroestlState::<U64>::new(32);
-        let mut block1 = GenericArray::default();
-        for i in 0..block1.len() {
-            block1[i] = i as u8;
-        }
-        let m = s.block_to_matrix(&block1);
-        let block2 = s.matrix_to_block(&m);
-        assert_eq!(block1, block2);
-    }
-
-    #[test]
-    fn test_add_round_constant() {
-        let padding_chunk = get_padding_block();
-        let s = GroestlState::<U64>::new(32);
-
-        let mut m = s.block_to_matrix(GenericArray::from_slice(&padding_chunk));
-        s.add_round_constant(&mut m, C_P, 0);
-        let b = s.matrix_to_block(&m);
-        let expected = [
-            128, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0,
-            0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0,
-            0, 112, 0, 0, 0, 0, 0, 0, 1,
-        ];
-        assert_eq!(&b[..], &expected[..]);
-
-        let mut m = s.block_to_matrix(GenericArray::from_slice(&padding_chunk));
-        s.add_round_constant(&mut m, C_Q, 0);
-        let b = s.matrix_to_block(&m);
-        let expected = [
-            0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e,
-        ];
-        assert_eq!(&b[..], &expected[..]);
-    }
-}
diff --git a/groestl/src/table.rs b/groestl/src/table.rs
new file mode 100644
index 000000000..fe1e729dc
--- /dev/null
+++ b/groestl/src/table.rs
@@ -0,0 +1,11 @@
+#[rustfmt::skip]
+pub(crate) const TABLE: [[u64; 256]; 8] = [
+    [0xc632f4a5f497a5c6, 0xf86f978497eb84f8, 0xee5eb099b0c799ee, 0xf67a8c8d8cf78df6, 0xffe8170d17e50dff, 0xd60adcbddcb7bdd6, 0xde16c8b1c8a7b1de, 0x916dfc54fc395491, 0x6090f050f0c05060, 0x0207050305040302, 0xce2ee0a9e087a9ce, 0x56d1877d87ac7d56, 0xe7cc2b192bd519e7, 0xb513a662a67162b5, 0x4d7c31e6319ae64d, 0xec59b59ab5c39aec, 0x8f40cf45cf05458f, 0x1fa3bc9dbc3e9d1f, 0x8949c040c0094089, 0xfa68928792ef87fa, 0xefd03f153fc515ef, 0xb29426eb267febb2, 0x8ece40c94007c98e, 0xfbe61d0b1ded0bfb, 0x416e2fec2f82ec41, 0xb31aa967a97d67b3, 0x5f431cfd1cbefd5f, 0x456025ea258aea45, 0x23f9dabfda46bf23, 0x535102f702a6f753, 0xe445a196a1d396e4, 0x9b76ed5bed2d5b9b, 0x75285dc25deac275, 0xe1c5241c24d91ce1, 0x3dd4e9aee97aae3d, 0x4cf2be6abe986a4c, 0x6c82ee5aeed85a6c, 0x7ebdc341c3fc417e, 0xf5f3060206f102f5, 0x8352d14fd11d4f83, 0x688ce45ce4d05c68, 0x515607f407a2f451, 0xd18d5c345cb934d1, 0xf9e1180818e908f9, 0xe24cae93aedf93e2, 0xab3e9573954d73ab, 0x6297f553f5c45362, 0x2a6b413f41543f2a, 0x081c140c14100c08, 0x9563f652f6315295, 0x46e9af65af8c6546, 0x9d7fe25ee2215e9d, 0x3048782878602830, 0x37cff8a1f86ea137, 0x0a1b110f11140f0a, 0x2febc4b5c45eb52f, 0x0e151b091b1c090e, 0x247e5a365a483624, 0x1badb69bb6369b1b, 0xdf98473d47a53ddf, 0xcda76a266a8126cd, 0x4ef5bb69bb9c694e, 0x7f334ccd4cfecd7f, 0xea50ba9fbacf9fea, 0x123f2d1b2d241b12, 0x1da4b99eb93a9e1d, 0x58c49c749cb07458, 0x3446722e72682e34, 0x3641772d776c2d36, 0xdc11cdb2cda3b2dc, 0xb49d29ee2973eeb4, 0x5b4d16fb16b6fb5b, 0xa4a501f60153f6a4, 0x76a1d74dd7ec4d76, 0xb714a361a37561b7, 0x7d3449ce49face7d, 0x52df8d7b8da47b52, 0xdd9f423e42a13edd, 0x5ecd937193bc715e, 0x13b1a297a2269713, 0xa6a204f50457f5a6, 0xb901b868b86968b9, 0x0000000000000000, 0xc1b5742c74992cc1, 0x40e0a060a0806040, 0xe3c2211f21dd1fe3, 0x793a43c843f2c879, 0xb69a2ced2c77edb6, 0xd40dd9bed9b3bed4, 0x8d47ca46ca01468d, 0x671770d970ced967, 0x72afdd4bdde44b72, 0x94ed79de7933de94, 0x98ff67d4672bd498, 0xb09323e8237be8b0, 0x855bde4ade114a85, 0xbb06bd6bbd6d6bbb, 0xc5bb7e2a7e912ac5, 0x4f7b34e5349ee54f, 0xedd73a163ac116ed, 0x86d254c55417c586, 0x9af862d7622fd79a, 0x6699ff55ffcc5566, 0x11b6a794a7229411, 0x8ac04acf4a0fcf8a, 0xe9d9301030c910e9, 0x040e0a060a080604, 0xfe66988198e781fe, 0xa0ab0bf00b5bf0a0, 0x78b4cc44ccf04478, 0x25f0d5bad54aba25, 0x4b753ee33e96e34b, 0xa2ac0ef30e5ff3a2, 0x5d4419fe19bafe5d, 0x80db5bc05b1bc080, 0x0580858a850a8a05, 0x3fd3ecadec7ead3f, 0x21fedfbcdf42bc21, 0x70a8d848d8e04870, 0xf1fd0c040cf904f1, 0x63197adf7ac6df63, 0x772f58c158eec177, 0xaf309f759f4575af, 0x42e7a563a5846342, 0x2070503050403020, 0xe5cb2e1a2ed11ae5, 0xfdef120e12e10efd, 0xbf08b76db7656dbf, 0x8155d44cd4194c81, 0x18243c143c301418, 0x26795f355f4c3526, 0xc3b2712f719d2fc3, 0xbe8638e13867e1be, 0x35c8fda2fd6aa235, 0x88c74fcc4f0bcc88, 0x2e654b394b5c392e, 0x936af957f93d5793, 0x55580df20daaf255, 0xfc619d829de382fc, 0x7ab3c947c9f4477a, 0xc827efacef8bacc8, 0xba8832e7326fe7ba, 0x324f7d2b7d642b32, 0xe642a495a4d795e6, 0xc03bfba0fb9ba0c0, 0x19aab398b3329819, 0x9ef668d16827d19e, 0xa322817f815d7fa3, 0x44eeaa66aa886644, 0x54d6827e82a87e54, 0x3bdde6abe676ab3b, 0x0b959e839e16830b, 0x8cc945ca4503ca8c, 0xc7bc7b297b9529c7, 0x6b056ed36ed6d36b, 0x286c443c44503c28, 0xa72c8b798b5579a7, 0xbc813de23d63e2bc, 0x1631271d272c1d16, 0xad379a769a4176ad, 0xdb964d3b4dad3bdb, 0x649efa56fac85664, 0x74a6d24ed2e84e74, 0x1436221e22281e14, 0x92e476db763fdb92, 0x0c121e0a1e180a0c, 0x48fcb46cb4906c48, 0xb88f37e4376be4b8, 0x9f78e75de7255d9f, 0xbd0fb26eb2616ebd, 0x43692aef2a86ef43, 0xc435f1a6f193a6c4, 0x39dae3a8e372a839, 0x31c6f7a4f762a431, 0xd38a593759bd37d3, 0xf274868b86ff8bf2, 0xd583563256b132d5, 0x8b4ec543c50d438b, 0x6e85eb59ebdc596e, 0xda18c2b7c2afb7da, 0x018e8f8c8f028c01, 0xb11dac64ac7964b1, 0x9cf16dd26d23d29c, 0x49723be03b92e049, 0xd81fc7b4c7abb4d8, 0xacb915fa1543faac, 0xf3fa090709fd07f3, 0xcfa06f256f8525cf, 0xca20eaafea8fafca, 0xf47d898e89f38ef4, 0x476720e9208ee947, 0x1038281828201810, 0x6f0b64d564ded56f, 0xf073838883fb88f0, 0x4afbb16fb1946f4a, 0x5cca967296b8725c, 0x38546c246c702438, 0x575f08f108aef157, 0x732152c752e6c773, 0x9764f351f3355197, 0xcbae6523658d23cb, 0xa125847c84597ca1, 0xe857bf9cbfcb9ce8, 0x3e5d6321637c213e, 0x96ea7cdd7c37dd96, 0x611e7fdc7fc2dc61, 0x0d9c9186911a860d, 0x0f9b9485941e850f, 0xe04bab90abdb90e0, 0x7cbac642c6f8427c, 0x712657c457e2c471, 0xcc29e5aae583aacc, 0x90e373d8733bd890, 0x06090f050f0c0506, 0xf7f4030103f501f7, 0x1c2a36123638121c, 0xc23cfea3fe9fa3c2, 0x6a8be15fe1d45f6a, 0xaebe10f91047f9ae, 0x69026bd06bd2d069, 0x17bfa891a82e9117, 0x9971e858e8295899, 0x3a5369276974273a, 0x27f7d0b9d04eb927, 0xd991483848a938d9, 0xebde351335cd13eb, 0x2be5ceb3ce56b32b, 0x2277553355443322, 0xd204d6bbd6bfbbd2, 0xa9399070904970a9, 0x07878089800e8907, 0x33c1f2a7f266a733, 0x2decc1b6c15ab62d, 0x3c5a66226678223c, 0x15b8ad92ad2a9215, 0xc9a96020608920c9, 0x875cdb49db154987, 0xaab01aff1a4fffaa, 0x50d8887888a07850, 0xa52b8e7a8e517aa5, 0x03898a8f8a068f03, 0x594a13f813b2f859, 0x09929b809b128009, 0x1a2339173934171a, 0x651075da75cada65, 0xd784533153b531d7, 0x84d551c65113c684, 0xd003d3b8d3bbb8d0, 0x82dc5ec35e1fc382, 0x29e2cbb0cb52b029, 0x5ac3997799b4775a, 0x1e2d3311333c111e, 0x7b3d46cb46f6cb7b, 0xa8b71ffc1f4bfca8, 0x6d0c61d661dad66d, 0x2c624e3a4e583a2c],
+    [0xc6c632f4a5f497a5, 0xf8f86f978497eb84, 0xeeee5eb099b0c799, 0xf6f67a8c8d8cf78d, 0xffffe8170d17e50d, 0xd6d60adcbddcb7bd, 0xdede16c8b1c8a7b1, 0x91916dfc54fc3954, 0x606090f050f0c050, 0x0202070503050403, 0xcece2ee0a9e087a9, 0x5656d1877d87ac7d, 0xe7e7cc2b192bd519, 0xb5b513a662a67162, 0x4d4d7c31e6319ae6, 0xecec59b59ab5c39a, 0x8f8f40cf45cf0545, 0x1f1fa3bc9dbc3e9d, 0x898949c040c00940, 0xfafa68928792ef87, 0xefefd03f153fc515, 0xb2b29426eb267feb, 0x8e8ece40c94007c9, 0xfbfbe61d0b1ded0b, 0x41416e2fec2f82ec, 0xb3b31aa967a97d67, 0x5f5f431cfd1cbefd, 0x45456025ea258aea, 0x2323f9dabfda46bf, 0x53535102f702a6f7, 0xe4e445a196a1d396, 0x9b9b76ed5bed2d5b, 0x7575285dc25deac2, 0xe1e1c5241c24d91c, 0x3d3dd4e9aee97aae, 0x4c4cf2be6abe986a, 0x6c6c82ee5aeed85a, 0x7e7ebdc341c3fc41, 0xf5f5f3060206f102, 0x838352d14fd11d4f, 0x68688ce45ce4d05c, 0x51515607f407a2f4, 0xd1d18d5c345cb934, 0xf9f9e1180818e908, 0xe2e24cae93aedf93, 0xabab3e9573954d73, 0x626297f553f5c453, 0x2a2a6b413f41543f, 0x08081c140c14100c, 0x959563f652f63152, 0x4646e9af65af8c65, 0x9d9d7fe25ee2215e, 0x3030487828786028, 0x3737cff8a1f86ea1, 0x0a0a1b110f11140f, 0x2f2febc4b5c45eb5, 0x0e0e151b091b1c09, 0x24247e5a365a4836, 0x1b1badb69bb6369b, 0xdfdf98473d47a53d, 0xcdcda76a266a8126, 0x4e4ef5bb69bb9c69, 0x7f7f334ccd4cfecd, 0xeaea50ba9fbacf9f, 0x12123f2d1b2d241b, 0x1d1da4b99eb93a9e, 0x5858c49c749cb074, 0x343446722e72682e, 0x363641772d776c2d, 0xdcdc11cdb2cda3b2, 0xb4b49d29ee2973ee, 0x5b5b4d16fb16b6fb, 0xa4a4a501f60153f6, 0x7676a1d74dd7ec4d, 0xb7b714a361a37561, 0x7d7d3449ce49face, 0x5252df8d7b8da47b, 0xdddd9f423e42a13e, 0x5e5ecd937193bc71, 0x1313b1a297a22697, 0xa6a6a204f50457f5, 0xb9b901b868b86968, 0x0000000000000000, 0xc1c1b5742c74992c, 0x4040e0a060a08060, 0xe3e3c2211f21dd1f, 0x79793a43c843f2c8, 0xb6b69a2ced2c77ed, 0xd4d40dd9bed9b3be, 0x8d8d47ca46ca0146, 0x67671770d970ced9, 0x7272afdd4bdde44b, 0x9494ed79de7933de, 0x9898ff67d4672bd4, 0xb0b09323e8237be8, 0x85855bde4ade114a, 0xbbbb06bd6bbd6d6b, 0xc5c5bb7e2a7e912a, 0x4f4f7b34e5349ee5, 0xededd73a163ac116, 0x8686d254c55417c5, 0x9a9af862d7622fd7, 0x666699ff55ffcc55, 0x1111b6a794a72294, 0x8a8ac04acf4a0fcf, 0xe9e9d9301030c910, 0x04040e0a060a0806, 0xfefe66988198e781, 0xa0a0ab0bf00b5bf0, 0x7878b4cc44ccf044, 0x2525f0d5bad54aba, 0x4b4b753ee33e96e3, 0xa2a2ac0ef30e5ff3, 0x5d5d4419fe19bafe, 0x8080db5bc05b1bc0, 0x050580858a850a8a, 0x3f3fd3ecadec7ead, 0x2121fedfbcdf42bc, 0x7070a8d848d8e048, 0xf1f1fd0c040cf904, 0x6363197adf7ac6df, 0x77772f58c158eec1, 0xafaf309f759f4575, 0x4242e7a563a58463, 0x2020705030504030, 0xe5e5cb2e1a2ed11a, 0xfdfdef120e12e10e, 0xbfbf08b76db7656d, 0x818155d44cd4194c, 0x1818243c143c3014, 0x2626795f355f4c35, 0xc3c3b2712f719d2f, 0xbebe8638e13867e1, 0x3535c8fda2fd6aa2, 0x8888c74fcc4f0bcc, 0x2e2e654b394b5c39, 0x93936af957f93d57, 0x5555580df20daaf2, 0xfcfc619d829de382, 0x7a7ab3c947c9f447, 0xc8c827efacef8bac, 0xbaba8832e7326fe7, 0x32324f7d2b7d642b, 0xe6e642a495a4d795, 0xc0c03bfba0fb9ba0, 0x1919aab398b33298, 0x9e9ef668d16827d1, 0xa3a322817f815d7f, 0x4444eeaa66aa8866, 0x5454d6827e82a87e, 0x3b3bdde6abe676ab, 0x0b0b959e839e1683, 0x8c8cc945ca4503ca, 0xc7c7bc7b297b9529, 0x6b6b056ed36ed6d3, 0x28286c443c44503c, 0xa7a72c8b798b5579, 0xbcbc813de23d63e2, 0x161631271d272c1d, 0xadad379a769a4176, 0xdbdb964d3b4dad3b, 0x64649efa56fac856, 0x7474a6d24ed2e84e, 0x141436221e22281e, 0x9292e476db763fdb, 0x0c0c121e0a1e180a, 0x4848fcb46cb4906c, 0xb8b88f37e4376be4, 0x9f9f78e75de7255d, 0xbdbd0fb26eb2616e, 0x4343692aef2a86ef, 0xc4c435f1a6f193a6, 0x3939dae3a8e372a8, 0x3131c6f7a4f762a4, 0xd3d38a593759bd37, 0xf2f274868b86ff8b, 0xd5d583563256b132, 0x8b8b4ec543c50d43, 0x6e6e85eb59ebdc59, 0xdada18c2b7c2afb7, 0x01018e8f8c8f028c, 0xb1b11dac64ac7964, 0x9c9cf16dd26d23d2, 0x4949723be03b92e0, 0xd8d81fc7b4c7abb4, 0xacacb915fa1543fa, 0xf3f3fa090709fd07, 0xcfcfa06f256f8525, 0xcaca20eaafea8faf, 0xf4f47d898e89f38e, 0x47476720e9208ee9, 0x1010382818282018, 0x6f6f0b64d564ded5, 0xf0f073838883fb88, 0x4a4afbb16fb1946f, 0x5c5cca967296b872, 0x3838546c246c7024, 0x57575f08f108aef1, 0x73732152c752e6c7, 0x979764f351f33551, 0xcbcbae6523658d23, 0xa1a125847c84597c, 0xe8e857bf9cbfcb9c, 0x3e3e5d6321637c21, 0x9696ea7cdd7c37dd, 0x61611e7fdc7fc2dc, 0x0d0d9c9186911a86, 0x0f0f9b9485941e85, 0xe0e04bab90abdb90, 0x7c7cbac642c6f842, 0x71712657c457e2c4, 0xcccc29e5aae583aa, 0x9090e373d8733bd8, 0x0606090f050f0c05, 0xf7f7f4030103f501, 0x1c1c2a3612363812, 0xc2c23cfea3fe9fa3, 0x6a6a8be15fe1d45f, 0xaeaebe10f91047f9, 0x6969026bd06bd2d0, 0x1717bfa891a82e91, 0x999971e858e82958, 0x3a3a536927697427, 0x2727f7d0b9d04eb9, 0xd9d991483848a938, 0xebebde351335cd13, 0x2b2be5ceb3ce56b3, 0x2222775533554433, 0xd2d204d6bbd6bfbb, 0xa9a9399070904970, 0x0707878089800e89, 0x3333c1f2a7f266a7, 0x2d2decc1b6c15ab6, 0x3c3c5a6622667822, 0x1515b8ad92ad2a92, 0xc9c9a96020608920, 0x87875cdb49db1549, 0xaaaab01aff1a4fff, 0x5050d8887888a078, 0xa5a52b8e7a8e517a, 0x0303898a8f8a068f, 0x59594a13f813b2f8, 0x0909929b809b1280, 0x1a1a233917393417, 0x65651075da75cada, 0xd7d784533153b531, 0x8484d551c65113c6, 0xd0d003d3b8d3bbb8, 0x8282dc5ec35e1fc3, 0x2929e2cbb0cb52b0, 0x5a5ac3997799b477, 0x1e1e2d3311333c11, 0x7b7b3d46cb46f6cb, 0xa8a8b71ffc1f4bfc, 0x6d6d0c61d661dad6, 0x2c2c624e3a4e583a],
+    [0xa5c6c632f4a5f497, 0x84f8f86f978497eb, 0x99eeee5eb099b0c7, 0x8df6f67a8c8d8cf7, 0x0dffffe8170d17e5, 0xbdd6d60adcbddcb7, 0xb1dede16c8b1c8a7, 0x5491916dfc54fc39, 0x50606090f050f0c0, 0x0302020705030504, 0xa9cece2ee0a9e087, 0x7d5656d1877d87ac, 0x19e7e7cc2b192bd5, 0x62b5b513a662a671, 0xe64d4d7c31e6319a, 0x9aecec59b59ab5c3, 0x458f8f40cf45cf05, 0x9d1f1fa3bc9dbc3e, 0x40898949c040c009, 0x87fafa68928792ef, 0x15efefd03f153fc5, 0xebb2b29426eb267f, 0xc98e8ece40c94007, 0x0bfbfbe61d0b1ded, 0xec41416e2fec2f82, 0x67b3b31aa967a97d, 0xfd5f5f431cfd1cbe, 0xea45456025ea258a, 0xbf2323f9dabfda46, 0xf753535102f702a6, 0x96e4e445a196a1d3, 0x5b9b9b76ed5bed2d, 0xc27575285dc25dea, 0x1ce1e1c5241c24d9, 0xae3d3dd4e9aee97a, 0x6a4c4cf2be6abe98, 0x5a6c6c82ee5aeed8, 0x417e7ebdc341c3fc, 0x02f5f5f3060206f1, 0x4f838352d14fd11d, 0x5c68688ce45ce4d0, 0xf451515607f407a2, 0x34d1d18d5c345cb9, 0x08f9f9e1180818e9, 0x93e2e24cae93aedf, 0x73abab3e9573954d, 0x53626297f553f5c4, 0x3f2a2a6b413f4154, 0x0c08081c140c1410, 0x52959563f652f631, 0x654646e9af65af8c, 0x5e9d9d7fe25ee221, 0x2830304878287860, 0xa13737cff8a1f86e, 0x0f0a0a1b110f1114, 0xb52f2febc4b5c45e, 0x090e0e151b091b1c, 0x3624247e5a365a48, 0x9b1b1badb69bb636, 0x3ddfdf98473d47a5, 0x26cdcda76a266a81, 0x694e4ef5bb69bb9c, 0xcd7f7f334ccd4cfe, 0x9feaea50ba9fbacf, 0x1b12123f2d1b2d24, 0x9e1d1da4b99eb93a, 0x745858c49c749cb0, 0x2e343446722e7268, 0x2d363641772d776c, 0xb2dcdc11cdb2cda3, 0xeeb4b49d29ee2973, 0xfb5b5b4d16fb16b6, 0xf6a4a4a501f60153, 0x4d7676a1d74dd7ec, 0x61b7b714a361a375, 0xce7d7d3449ce49fa, 0x7b5252df8d7b8da4, 0x3edddd9f423e42a1, 0x715e5ecd937193bc, 0x971313b1a297a226, 0xf5a6a6a204f50457, 0x68b9b901b868b869, 0x0000000000000000, 0x2cc1c1b5742c7499, 0x604040e0a060a080, 0x1fe3e3c2211f21dd, 0xc879793a43c843f2, 0xedb6b69a2ced2c77, 0xbed4d40dd9bed9b3, 0x468d8d47ca46ca01, 0xd967671770d970ce, 0x4b7272afdd4bdde4, 0xde9494ed79de7933, 0xd49898ff67d4672b, 0xe8b0b09323e8237b, 0x4a85855bde4ade11, 0x6bbbbb06bd6bbd6d, 0x2ac5c5bb7e2a7e91, 0xe54f4f7b34e5349e, 0x16ededd73a163ac1, 0xc58686d254c55417, 0xd79a9af862d7622f, 0x55666699ff55ffcc, 0x941111b6a794a722, 0xcf8a8ac04acf4a0f, 0x10e9e9d9301030c9, 0x0604040e0a060a08, 0x81fefe66988198e7, 0xf0a0a0ab0bf00b5b, 0x447878b4cc44ccf0, 0xba2525f0d5bad54a, 0xe34b4b753ee33e96, 0xf3a2a2ac0ef30e5f, 0xfe5d5d4419fe19ba, 0xc08080db5bc05b1b, 0x8a050580858a850a, 0xad3f3fd3ecadec7e, 0xbc2121fedfbcdf42, 0x487070a8d848d8e0, 0x04f1f1fd0c040cf9, 0xdf6363197adf7ac6, 0xc177772f58c158ee, 0x75afaf309f759f45, 0x634242e7a563a584, 0x3020207050305040, 0x1ae5e5cb2e1a2ed1, 0x0efdfdef120e12e1, 0x6dbfbf08b76db765, 0x4c818155d44cd419, 0x141818243c143c30, 0x352626795f355f4c, 0x2fc3c3b2712f719d, 0xe1bebe8638e13867, 0xa23535c8fda2fd6a, 0xcc8888c74fcc4f0b, 0x392e2e654b394b5c, 0x5793936af957f93d, 0xf25555580df20daa, 0x82fcfc619d829de3, 0x477a7ab3c947c9f4, 0xacc8c827efacef8b, 0xe7baba8832e7326f, 0x2b32324f7d2b7d64, 0x95e6e642a495a4d7, 0xa0c0c03bfba0fb9b, 0x981919aab398b332, 0xd19e9ef668d16827, 0x7fa3a322817f815d, 0x664444eeaa66aa88, 0x7e5454d6827e82a8, 0xab3b3bdde6abe676, 0x830b0b959e839e16, 0xca8c8cc945ca4503, 0x29c7c7bc7b297b95, 0xd36b6b056ed36ed6, 0x3c28286c443c4450, 0x79a7a72c8b798b55, 0xe2bcbc813de23d63, 0x1d161631271d272c, 0x76adad379a769a41, 0x3bdbdb964d3b4dad, 0x5664649efa56fac8, 0x4e7474a6d24ed2e8, 0x1e141436221e2228, 0xdb9292e476db763f, 0x0a0c0c121e0a1e18, 0x6c4848fcb46cb490, 0xe4b8b88f37e4376b, 0x5d9f9f78e75de725, 0x6ebdbd0fb26eb261, 0xef4343692aef2a86, 0xa6c4c435f1a6f193, 0xa83939dae3a8e372, 0xa43131c6f7a4f762, 0x37d3d38a593759bd, 0x8bf2f274868b86ff, 0x32d5d583563256b1, 0x438b8b4ec543c50d, 0x596e6e85eb59ebdc, 0xb7dada18c2b7c2af, 0x8c01018e8f8c8f02, 0x64b1b11dac64ac79, 0xd29c9cf16dd26d23, 0xe04949723be03b92, 0xb4d8d81fc7b4c7ab, 0xfaacacb915fa1543, 0x07f3f3fa090709fd, 0x25cfcfa06f256f85, 0xafcaca20eaafea8f, 0x8ef4f47d898e89f3, 0xe947476720e9208e, 0x1810103828182820, 0xd56f6f0b64d564de, 0x88f0f073838883fb, 0x6f4a4afbb16fb194, 0x725c5cca967296b8, 0x243838546c246c70, 0xf157575f08f108ae, 0xc773732152c752e6, 0x51979764f351f335, 0x23cbcbae6523658d, 0x7ca1a125847c8459, 0x9ce8e857bf9cbfcb, 0x213e3e5d6321637c, 0xdd9696ea7cdd7c37, 0xdc61611e7fdc7fc2, 0x860d0d9c9186911a, 0x850f0f9b9485941e, 0x90e0e04bab90abdb, 0x427c7cbac642c6f8, 0xc471712657c457e2, 0xaacccc29e5aae583, 0xd89090e373d8733b, 0x050606090f050f0c, 0x01f7f7f4030103f5, 0x121c1c2a36123638, 0xa3c2c23cfea3fe9f, 0x5f6a6a8be15fe1d4, 0xf9aeaebe10f91047, 0xd06969026bd06bd2, 0x911717bfa891a82e, 0x58999971e858e829, 0x273a3a5369276974, 0xb92727f7d0b9d04e, 0x38d9d991483848a9, 0x13ebebde351335cd, 0xb32b2be5ceb3ce56, 0x3322227755335544, 0xbbd2d204d6bbd6bf, 0x70a9a93990709049, 0x890707878089800e, 0xa73333c1f2a7f266, 0xb62d2decc1b6c15a, 0x223c3c5a66226678, 0x921515b8ad92ad2a, 0x20c9c9a960206089, 0x4987875cdb49db15, 0xffaaaab01aff1a4f, 0x785050d8887888a0, 0x7aa5a52b8e7a8e51, 0x8f0303898a8f8a06, 0xf859594a13f813b2, 0x800909929b809b12, 0x171a1a2339173934, 0xda65651075da75ca, 0x31d7d784533153b5, 0xc68484d551c65113, 0xb8d0d003d3b8d3bb, 0xc38282dc5ec35e1f, 0xb02929e2cbb0cb52, 0x775a5ac3997799b4, 0x111e1e2d3311333c, 0xcb7b7b3d46cb46f6, 0xfca8a8b71ffc1f4b, 0xd66d6d0c61d661da, 0x3a2c2c624e3a4e58],
+    [0x97a5c6c632f4a5f4, 0xeb84f8f86f978497, 0xc799eeee5eb099b0, 0xf78df6f67a8c8d8c, 0xe50dffffe8170d17, 0xb7bdd6d60adcbddc, 0xa7b1dede16c8b1c8, 0x395491916dfc54fc, 0xc050606090f050f0, 0x0403020207050305, 0x87a9cece2ee0a9e0, 0xac7d5656d1877d87, 0xd519e7e7cc2b192b, 0x7162b5b513a662a6, 0x9ae64d4d7c31e631, 0xc39aecec59b59ab5, 0x05458f8f40cf45cf, 0x3e9d1f1fa3bc9dbc, 0x0940898949c040c0, 0xef87fafa68928792, 0xc515efefd03f153f, 0x7febb2b29426eb26, 0x07c98e8ece40c940, 0xed0bfbfbe61d0b1d, 0x82ec41416e2fec2f, 0x7d67b3b31aa967a9, 0xbefd5f5f431cfd1c, 0x8aea45456025ea25, 0x46bf2323f9dabfda, 0xa6f753535102f702, 0xd396e4e445a196a1, 0x2d5b9b9b76ed5bed, 0xeac27575285dc25d, 0xd91ce1e1c5241c24, 0x7aae3d3dd4e9aee9, 0x986a4c4cf2be6abe, 0xd85a6c6c82ee5aee, 0xfc417e7ebdc341c3, 0xf102f5f5f3060206, 0x1d4f838352d14fd1, 0xd05c68688ce45ce4, 0xa2f451515607f407, 0xb934d1d18d5c345c, 0xe908f9f9e1180818, 0xdf93e2e24cae93ae, 0x4d73abab3e957395, 0xc453626297f553f5, 0x543f2a2a6b413f41, 0x100c08081c140c14, 0x3152959563f652f6, 0x8c654646e9af65af, 0x215e9d9d7fe25ee2, 0x6028303048782878, 0x6ea13737cff8a1f8, 0x140f0a0a1b110f11, 0x5eb52f2febc4b5c4, 0x1c090e0e151b091b, 0x483624247e5a365a, 0x369b1b1badb69bb6, 0xa53ddfdf98473d47, 0x8126cdcda76a266a, 0x9c694e4ef5bb69bb, 0xfecd7f7f334ccd4c, 0xcf9feaea50ba9fba, 0x241b12123f2d1b2d, 0x3a9e1d1da4b99eb9, 0xb0745858c49c749c, 0x682e343446722e72, 0x6c2d363641772d77, 0xa3b2dcdc11cdb2cd, 0x73eeb4b49d29ee29, 0xb6fb5b5b4d16fb16, 0x53f6a4a4a501f601, 0xec4d7676a1d74dd7, 0x7561b7b714a361a3, 0xface7d7d3449ce49, 0xa47b5252df8d7b8d, 0xa13edddd9f423e42, 0xbc715e5ecd937193, 0x26971313b1a297a2, 0x57f5a6a6a204f504, 0x6968b9b901b868b8, 0x0000000000000000, 0x992cc1c1b5742c74, 0x80604040e0a060a0, 0xdd1fe3e3c2211f21, 0xf2c879793a43c843, 0x77edb6b69a2ced2c, 0xb3bed4d40dd9bed9, 0x01468d8d47ca46ca, 0xced967671770d970, 0xe44b7272afdd4bdd, 0x33de9494ed79de79, 0x2bd49898ff67d467, 0x7be8b0b09323e823, 0x114a85855bde4ade, 0x6d6bbbbb06bd6bbd, 0x912ac5c5bb7e2a7e, 0x9ee54f4f7b34e534, 0xc116ededd73a163a, 0x17c58686d254c554, 0x2fd79a9af862d762, 0xcc55666699ff55ff, 0x22941111b6a794a7, 0x0fcf8a8ac04acf4a, 0xc910e9e9d9301030, 0x080604040e0a060a, 0xe781fefe66988198, 0x5bf0a0a0ab0bf00b, 0xf0447878b4cc44cc, 0x4aba2525f0d5bad5, 0x96e34b4b753ee33e, 0x5ff3a2a2ac0ef30e, 0xbafe5d5d4419fe19, 0x1bc08080db5bc05b, 0x0a8a050580858a85, 0x7ead3f3fd3ecadec, 0x42bc2121fedfbcdf, 0xe0487070a8d848d8, 0xf904f1f1fd0c040c, 0xc6df6363197adf7a, 0xeec177772f58c158, 0x4575afaf309f759f, 0x84634242e7a563a5, 0x4030202070503050, 0xd11ae5e5cb2e1a2e, 0xe10efdfdef120e12, 0x656dbfbf08b76db7, 0x194c818155d44cd4, 0x30141818243c143c, 0x4c352626795f355f, 0x9d2fc3c3b2712f71, 0x67e1bebe8638e138, 0x6aa23535c8fda2fd, 0x0bcc8888c74fcc4f, 0x5c392e2e654b394b, 0x3d5793936af957f9, 0xaaf25555580df20d, 0xe382fcfc619d829d, 0xf4477a7ab3c947c9, 0x8bacc8c827efacef, 0x6fe7baba8832e732, 0x642b32324f7d2b7d, 0xd795e6e642a495a4, 0x9ba0c0c03bfba0fb, 0x32981919aab398b3, 0x27d19e9ef668d168, 0x5d7fa3a322817f81, 0x88664444eeaa66aa, 0xa87e5454d6827e82, 0x76ab3b3bdde6abe6, 0x16830b0b959e839e, 0x03ca8c8cc945ca45, 0x9529c7c7bc7b297b, 0xd6d36b6b056ed36e, 0x503c28286c443c44, 0x5579a7a72c8b798b, 0x63e2bcbc813de23d, 0x2c1d161631271d27, 0x4176adad379a769a, 0xad3bdbdb964d3b4d, 0xc85664649efa56fa, 0xe84e7474a6d24ed2, 0x281e141436221e22, 0x3fdb9292e476db76, 0x180a0c0c121e0a1e, 0x906c4848fcb46cb4, 0x6be4b8b88f37e437, 0x255d9f9f78e75de7, 0x616ebdbd0fb26eb2, 0x86ef4343692aef2a, 0x93a6c4c435f1a6f1, 0x72a83939dae3a8e3, 0x62a43131c6f7a4f7, 0xbd37d3d38a593759, 0xff8bf2f274868b86, 0xb132d5d583563256, 0x0d438b8b4ec543c5, 0xdc596e6e85eb59eb, 0xafb7dada18c2b7c2, 0x028c01018e8f8c8f, 0x7964b1b11dac64ac, 0x23d29c9cf16dd26d, 0x92e04949723be03b, 0xabb4d8d81fc7b4c7, 0x43faacacb915fa15, 0xfd07f3f3fa090709, 0x8525cfcfa06f256f, 0x8fafcaca20eaafea, 0xf38ef4f47d898e89, 0x8ee947476720e920, 0x2018101038281828, 0xded56f6f0b64d564, 0xfb88f0f073838883, 0x946f4a4afbb16fb1, 0xb8725c5cca967296, 0x70243838546c246c, 0xaef157575f08f108, 0xe6c773732152c752, 0x3551979764f351f3, 0x8d23cbcbae652365, 0x597ca1a125847c84, 0xcb9ce8e857bf9cbf, 0x7c213e3e5d632163, 0x37dd9696ea7cdd7c, 0xc2dc61611e7fdc7f, 0x1a860d0d9c918691, 0x1e850f0f9b948594, 0xdb90e0e04bab90ab, 0xf8427c7cbac642c6, 0xe2c471712657c457, 0x83aacccc29e5aae5, 0x3bd89090e373d873, 0x0c050606090f050f, 0xf501f7f7f4030103, 0x38121c1c2a361236, 0x9fa3c2c23cfea3fe, 0xd45f6a6a8be15fe1, 0x47f9aeaebe10f910, 0xd2d06969026bd06b, 0x2e911717bfa891a8, 0x2958999971e858e8, 0x74273a3a53692769, 0x4eb92727f7d0b9d0, 0xa938d9d991483848, 0xcd13ebebde351335, 0x56b32b2be5ceb3ce, 0x4433222277553355, 0xbfbbd2d204d6bbd6, 0x4970a9a939907090, 0x0e89070787808980, 0x66a73333c1f2a7f2, 0x5ab62d2decc1b6c1, 0x78223c3c5a662266, 0x2a921515b8ad92ad, 0x8920c9c9a9602060, 0x154987875cdb49db, 0x4fffaaaab01aff1a, 0xa0785050d8887888, 0x517aa5a52b8e7a8e, 0x068f0303898a8f8a, 0xb2f859594a13f813, 0x12800909929b809b, 0x34171a1a23391739, 0xcada65651075da75, 0xb531d7d784533153, 0x13c68484d551c651, 0xbbb8d0d003d3b8d3, 0x1fc38282dc5ec35e, 0x52b02929e2cbb0cb, 0xb4775a5ac3997799, 0x3c111e1e2d331133, 0xf6cb7b7b3d46cb46, 0x4bfca8a8b71ffc1f, 0xdad66d6d0c61d661, 0x583a2c2c624e3a4e],
+    [0xf497a5c6c632f4a5, 0x97eb84f8f86f9784, 0xb0c799eeee5eb099, 0x8cf78df6f67a8c8d, 0x17e50dffffe8170d, 0xdcb7bdd6d60adcbd, 0xc8a7b1dede16c8b1, 0xfc395491916dfc54, 0xf0c050606090f050, 0x0504030202070503, 0xe087a9cece2ee0a9, 0x87ac7d5656d1877d, 0x2bd519e7e7cc2b19, 0xa67162b5b513a662, 0x319ae64d4d7c31e6, 0xb5c39aecec59b59a, 0xcf05458f8f40cf45, 0xbc3e9d1f1fa3bc9d, 0xc00940898949c040, 0x92ef87fafa689287, 0x3fc515efefd03f15, 0x267febb2b29426eb, 0x4007c98e8ece40c9, 0x1ded0bfbfbe61d0b, 0x2f82ec41416e2fec, 0xa97d67b3b31aa967, 0x1cbefd5f5f431cfd, 0x258aea45456025ea, 0xda46bf2323f9dabf, 0x02a6f753535102f7, 0xa1d396e4e445a196, 0xed2d5b9b9b76ed5b, 0x5deac27575285dc2, 0x24d91ce1e1c5241c, 0xe97aae3d3dd4e9ae, 0xbe986a4c4cf2be6a, 0xeed85a6c6c82ee5a, 0xc3fc417e7ebdc341, 0x06f102f5f5f30602, 0xd11d4f838352d14f, 0xe4d05c68688ce45c, 0x07a2f451515607f4, 0x5cb934d1d18d5c34, 0x18e908f9f9e11808, 0xaedf93e2e24cae93, 0x954d73abab3e9573, 0xf5c453626297f553, 0x41543f2a2a6b413f, 0x14100c08081c140c, 0xf63152959563f652, 0xaf8c654646e9af65, 0xe2215e9d9d7fe25e, 0x7860283030487828, 0xf86ea13737cff8a1, 0x11140f0a0a1b110f, 0xc45eb52f2febc4b5, 0x1b1c090e0e151b09, 0x5a483624247e5a36, 0xb6369b1b1badb69b, 0x47a53ddfdf98473d, 0x6a8126cdcda76a26, 0xbb9c694e4ef5bb69, 0x4cfecd7f7f334ccd, 0xbacf9feaea50ba9f, 0x2d241b12123f2d1b, 0xb93a9e1d1da4b99e, 0x9cb0745858c49c74, 0x72682e343446722e, 0x776c2d363641772d, 0xcda3b2dcdc11cdb2, 0x2973eeb4b49d29ee, 0x16b6fb5b5b4d16fb, 0x0153f6a4a4a501f6, 0xd7ec4d7676a1d74d, 0xa37561b7b714a361, 0x49face7d7d3449ce, 0x8da47b5252df8d7b, 0x42a13edddd9f423e, 0x93bc715e5ecd9371, 0xa226971313b1a297, 0x0457f5a6a6a204f5, 0xb86968b9b901b868, 0x0000000000000000, 0x74992cc1c1b5742c, 0xa080604040e0a060, 0x21dd1fe3e3c2211f, 0x43f2c879793a43c8, 0x2c77edb6b69a2ced, 0xd9b3bed4d40dd9be, 0xca01468d8d47ca46, 0x70ced967671770d9, 0xdde44b7272afdd4b, 0x7933de9494ed79de, 0x672bd49898ff67d4, 0x237be8b0b09323e8, 0xde114a85855bde4a, 0xbd6d6bbbbb06bd6b, 0x7e912ac5c5bb7e2a, 0x349ee54f4f7b34e5, 0x3ac116ededd73a16, 0x5417c58686d254c5, 0x622fd79a9af862d7, 0xffcc55666699ff55, 0xa722941111b6a794, 0x4a0fcf8a8ac04acf, 0x30c910e9e9d93010, 0x0a080604040e0a06, 0x98e781fefe669881, 0x0b5bf0a0a0ab0bf0, 0xccf0447878b4cc44, 0xd54aba2525f0d5ba, 0x3e96e34b4b753ee3, 0x0e5ff3a2a2ac0ef3, 0x19bafe5d5d4419fe, 0x5b1bc08080db5bc0, 0x850a8a050580858a, 0xec7ead3f3fd3ecad, 0xdf42bc2121fedfbc, 0xd8e0487070a8d848, 0x0cf904f1f1fd0c04, 0x7ac6df6363197adf, 0x58eec177772f58c1, 0x9f4575afaf309f75, 0xa584634242e7a563, 0x5040302020705030, 0x2ed11ae5e5cb2e1a, 0x12e10efdfdef120e, 0xb7656dbfbf08b76d, 0xd4194c818155d44c, 0x3c30141818243c14, 0x5f4c352626795f35, 0x719d2fc3c3b2712f, 0x3867e1bebe8638e1, 0xfd6aa23535c8fda2, 0x4f0bcc8888c74fcc, 0x4b5c392e2e654b39, 0xf93d5793936af957, 0x0daaf25555580df2, 0x9de382fcfc619d82, 0xc9f4477a7ab3c947, 0xef8bacc8c827efac, 0x326fe7baba8832e7, 0x7d642b32324f7d2b, 0xa4d795e6e642a495, 0xfb9ba0c0c03bfba0, 0xb332981919aab398, 0x6827d19e9ef668d1, 0x815d7fa3a322817f, 0xaa88664444eeaa66, 0x82a87e5454d6827e, 0xe676ab3b3bdde6ab, 0x9e16830b0b959e83, 0x4503ca8c8cc945ca, 0x7b9529c7c7bc7b29, 0x6ed6d36b6b056ed3, 0x44503c28286c443c, 0x8b5579a7a72c8b79, 0x3d63e2bcbc813de2, 0x272c1d161631271d, 0x9a4176adad379a76, 0x4dad3bdbdb964d3b, 0xfac85664649efa56, 0xd2e84e7474a6d24e, 0x22281e141436221e, 0x763fdb9292e476db, 0x1e180a0c0c121e0a, 0xb4906c4848fcb46c, 0x376be4b8b88f37e4, 0xe7255d9f9f78e75d, 0xb2616ebdbd0fb26e, 0x2a86ef4343692aef, 0xf193a6c4c435f1a6, 0xe372a83939dae3a8, 0xf762a43131c6f7a4, 0x59bd37d3d38a5937, 0x86ff8bf2f274868b, 0x56b132d5d5835632, 0xc50d438b8b4ec543, 0xebdc596e6e85eb59, 0xc2afb7dada18c2b7, 0x8f028c01018e8f8c, 0xac7964b1b11dac64, 0x6d23d29c9cf16dd2, 0x3b92e04949723be0, 0xc7abb4d8d81fc7b4, 0x1543faacacb915fa, 0x09fd07f3f3fa0907, 0x6f8525cfcfa06f25, 0xea8fafcaca20eaaf, 0x89f38ef4f47d898e, 0x208ee947476720e9, 0x2820181010382818, 0x64ded56f6f0b64d5, 0x83fb88f0f0738388, 0xb1946f4a4afbb16f, 0x96b8725c5cca9672, 0x6c70243838546c24, 0x08aef157575f08f1, 0x52e6c773732152c7, 0xf33551979764f351, 0x658d23cbcbae6523, 0x84597ca1a125847c, 0xbfcb9ce8e857bf9c, 0x637c213e3e5d6321, 0x7c37dd9696ea7cdd, 0x7fc2dc61611e7fdc, 0x911a860d0d9c9186, 0x941e850f0f9b9485, 0xabdb90e0e04bab90, 0xc6f8427c7cbac642, 0x57e2c471712657c4, 0xe583aacccc29e5aa, 0x733bd89090e373d8, 0x0f0c050606090f05, 0x03f501f7f7f40301, 0x3638121c1c2a3612, 0xfe9fa3c2c23cfea3, 0xe1d45f6a6a8be15f, 0x1047f9aeaebe10f9, 0x6bd2d06969026bd0, 0xa82e911717bfa891, 0xe82958999971e858, 0x6974273a3a536927, 0xd04eb92727f7d0b9, 0x48a938d9d9914838, 0x35cd13ebebde3513, 0xce56b32b2be5ceb3, 0x5544332222775533, 0xd6bfbbd2d204d6bb, 0x904970a9a9399070, 0x800e890707878089, 0xf266a73333c1f2a7, 0xc15ab62d2decc1b6, 0x6678223c3c5a6622, 0xad2a921515b8ad92, 0x608920c9c9a96020, 0xdb154987875cdb49, 0x1a4fffaaaab01aff, 0x88a0785050d88878, 0x8e517aa5a52b8e7a, 0x8a068f0303898a8f, 0x13b2f859594a13f8, 0x9b12800909929b80, 0x3934171a1a233917, 0x75cada65651075da, 0x53b531d7d7845331, 0x5113c68484d551c6, 0xd3bbb8d0d003d3b8, 0x5e1fc38282dc5ec3, 0xcb52b02929e2cbb0, 0x99b4775a5ac39977, 0x333c111e1e2d3311, 0x46f6cb7b7b3d46cb, 0x1f4bfca8a8b71ffc, 0x61dad66d6d0c61d6, 0x4e583a2c2c624e3a],
+    [0xa5f497a5c6c632f4, 0x8497eb84f8f86f97, 0x99b0c799eeee5eb0, 0x8d8cf78df6f67a8c, 0x0d17e50dffffe817, 0xbddcb7bdd6d60adc, 0xb1c8a7b1dede16c8, 0x54fc395491916dfc, 0x50f0c050606090f0, 0x0305040302020705, 0xa9e087a9cece2ee0, 0x7d87ac7d5656d187, 0x192bd519e7e7cc2b, 0x62a67162b5b513a6, 0xe6319ae64d4d7c31, 0x9ab5c39aecec59b5, 0x45cf05458f8f40cf, 0x9dbc3e9d1f1fa3bc, 0x40c00940898949c0, 0x8792ef87fafa6892, 0x153fc515efefd03f, 0xeb267febb2b29426, 0xc94007c98e8ece40, 0x0b1ded0bfbfbe61d, 0xec2f82ec41416e2f, 0x67a97d67b3b31aa9, 0xfd1cbefd5f5f431c, 0xea258aea45456025, 0xbfda46bf2323f9da, 0xf702a6f753535102, 0x96a1d396e4e445a1, 0x5bed2d5b9b9b76ed, 0xc25deac27575285d, 0x1c24d91ce1e1c524, 0xaee97aae3d3dd4e9, 0x6abe986a4c4cf2be, 0x5aeed85a6c6c82ee, 0x41c3fc417e7ebdc3, 0x0206f102f5f5f306, 0x4fd11d4f838352d1, 0x5ce4d05c68688ce4, 0xf407a2f451515607, 0x345cb934d1d18d5c, 0x0818e908f9f9e118, 0x93aedf93e2e24cae, 0x73954d73abab3e95, 0x53f5c453626297f5, 0x3f41543f2a2a6b41, 0x0c14100c08081c14, 0x52f63152959563f6, 0x65af8c654646e9af, 0x5ee2215e9d9d7fe2, 0x2878602830304878, 0xa1f86ea13737cff8, 0x0f11140f0a0a1b11, 0xb5c45eb52f2febc4, 0x091b1c090e0e151b, 0x365a483624247e5a, 0x9bb6369b1b1badb6, 0x3d47a53ddfdf9847, 0x266a8126cdcda76a, 0x69bb9c694e4ef5bb, 0xcd4cfecd7f7f334c, 0x9fbacf9feaea50ba, 0x1b2d241b12123f2d, 0x9eb93a9e1d1da4b9, 0x749cb0745858c49c, 0x2e72682e34344672, 0x2d776c2d36364177, 0xb2cda3b2dcdc11cd, 0xee2973eeb4b49d29, 0xfb16b6fb5b5b4d16, 0xf60153f6a4a4a501, 0x4dd7ec4d7676a1d7, 0x61a37561b7b714a3, 0xce49face7d7d3449, 0x7b8da47b5252df8d, 0x3e42a13edddd9f42, 0x7193bc715e5ecd93, 0x97a226971313b1a2, 0xf50457f5a6a6a204, 0x68b86968b9b901b8, 0x0000000000000000, 0x2c74992cc1c1b574, 0x60a080604040e0a0, 0x1f21dd1fe3e3c221, 0xc843f2c879793a43, 0xed2c77edb6b69a2c, 0xbed9b3bed4d40dd9, 0x46ca01468d8d47ca, 0xd970ced967671770, 0x4bdde44b7272afdd, 0xde7933de9494ed79, 0xd4672bd49898ff67, 0xe8237be8b0b09323, 0x4ade114a85855bde, 0x6bbd6d6bbbbb06bd, 0x2a7e912ac5c5bb7e, 0xe5349ee54f4f7b34, 0x163ac116ededd73a, 0xc55417c58686d254, 0xd7622fd79a9af862, 0x55ffcc55666699ff, 0x94a722941111b6a7, 0xcf4a0fcf8a8ac04a, 0x1030c910e9e9d930, 0x060a080604040e0a, 0x8198e781fefe6698, 0xf00b5bf0a0a0ab0b, 0x44ccf0447878b4cc, 0xbad54aba2525f0d5, 0xe33e96e34b4b753e, 0xf30e5ff3a2a2ac0e, 0xfe19bafe5d5d4419, 0xc05b1bc08080db5b, 0x8a850a8a05058085, 0xadec7ead3f3fd3ec, 0xbcdf42bc2121fedf, 0x48d8e0487070a8d8, 0x040cf904f1f1fd0c, 0xdf7ac6df6363197a, 0xc158eec177772f58, 0x759f4575afaf309f, 0x63a584634242e7a5, 0x3050403020207050, 0x1a2ed11ae5e5cb2e, 0x0e12e10efdfdef12, 0x6db7656dbfbf08b7, 0x4cd4194c818155d4, 0x143c30141818243c, 0x355f4c352626795f, 0x2f719d2fc3c3b271, 0xe13867e1bebe8638, 0xa2fd6aa23535c8fd, 0xcc4f0bcc8888c74f, 0x394b5c392e2e654b, 0x57f93d5793936af9, 0xf20daaf25555580d, 0x829de382fcfc619d, 0x47c9f4477a7ab3c9, 0xacef8bacc8c827ef, 0xe7326fe7baba8832, 0x2b7d642b32324f7d, 0x95a4d795e6e642a4, 0xa0fb9ba0c0c03bfb, 0x98b332981919aab3, 0xd16827d19e9ef668, 0x7f815d7fa3a32281, 0x66aa88664444eeaa, 0x7e82a87e5454d682, 0xabe676ab3b3bdde6, 0x839e16830b0b959e, 0xca4503ca8c8cc945, 0x297b9529c7c7bc7b, 0xd36ed6d36b6b056e, 0x3c44503c28286c44, 0x798b5579a7a72c8b, 0xe23d63e2bcbc813d, 0x1d272c1d16163127, 0x769a4176adad379a, 0x3b4dad3bdbdb964d, 0x56fac85664649efa, 0x4ed2e84e7474a6d2, 0x1e22281e14143622, 0xdb763fdb9292e476, 0x0a1e180a0c0c121e, 0x6cb4906c4848fcb4, 0xe4376be4b8b88f37, 0x5de7255d9f9f78e7, 0x6eb2616ebdbd0fb2, 0xef2a86ef4343692a, 0xa6f193a6c4c435f1, 0xa8e372a83939dae3, 0xa4f762a43131c6f7, 0x3759bd37d3d38a59, 0x8b86ff8bf2f27486, 0x3256b132d5d58356, 0x43c50d438b8b4ec5, 0x59ebdc596e6e85eb, 0xb7c2afb7dada18c2, 0x8c8f028c01018e8f, 0x64ac7964b1b11dac, 0xd26d23d29c9cf16d, 0xe03b92e04949723b, 0xb4c7abb4d8d81fc7, 0xfa1543faacacb915, 0x0709fd07f3f3fa09, 0x256f8525cfcfa06f, 0xafea8fafcaca20ea, 0x8e89f38ef4f47d89, 0xe9208ee947476720, 0x1828201810103828, 0xd564ded56f6f0b64, 0x8883fb88f0f07383, 0x6fb1946f4a4afbb1, 0x7296b8725c5cca96, 0x246c70243838546c, 0xf108aef157575f08, 0xc752e6c773732152, 0x51f33551979764f3, 0x23658d23cbcbae65, 0x7c84597ca1a12584, 0x9cbfcb9ce8e857bf, 0x21637c213e3e5d63, 0xdd7c37dd9696ea7c, 0xdc7fc2dc61611e7f, 0x86911a860d0d9c91, 0x85941e850f0f9b94, 0x90abdb90e0e04bab, 0x42c6f8427c7cbac6, 0xc457e2c471712657, 0xaae583aacccc29e5, 0xd8733bd89090e373, 0x050f0c050606090f, 0x0103f501f7f7f403, 0x123638121c1c2a36, 0xa3fe9fa3c2c23cfe, 0x5fe1d45f6a6a8be1, 0xf91047f9aeaebe10, 0xd06bd2d06969026b, 0x91a82e911717bfa8, 0x58e82958999971e8, 0x276974273a3a5369, 0xb9d04eb92727f7d0, 0x3848a938d9d99148, 0x1335cd13ebebde35, 0xb3ce56b32b2be5ce, 0x3355443322227755, 0xbbd6bfbbd2d204d6, 0x70904970a9a93990, 0x89800e8907078780, 0xa7f266a73333c1f2, 0xb6c15ab62d2decc1, 0x226678223c3c5a66, 0x92ad2a921515b8ad, 0x20608920c9c9a960, 0x49db154987875cdb, 0xff1a4fffaaaab01a, 0x7888a0785050d888, 0x7a8e517aa5a52b8e, 0x8f8a068f0303898a, 0xf813b2f859594a13, 0x809b12800909929b, 0x173934171a1a2339, 0xda75cada65651075, 0x3153b531d7d78453, 0xc65113c68484d551, 0xb8d3bbb8d0d003d3, 0xc35e1fc38282dc5e, 0xb0cb52b02929e2cb, 0x7799b4775a5ac399, 0x11333c111e1e2d33, 0xcb46f6cb7b7b3d46, 0xfc1f4bfca8a8b71f, 0xd661dad66d6d0c61, 0x3a4e583a2c2c624e],
+    [0xf4a5f497a5c6c632, 0x978497eb84f8f86f, 0xb099b0c799eeee5e, 0x8c8d8cf78df6f67a, 0x170d17e50dffffe8, 0xdcbddcb7bdd6d60a, 0xc8b1c8a7b1dede16, 0xfc54fc395491916d, 0xf050f0c050606090, 0x0503050403020207, 0xe0a9e087a9cece2e, 0x877d87ac7d5656d1, 0x2b192bd519e7e7cc, 0xa662a67162b5b513, 0x31e6319ae64d4d7c, 0xb59ab5c39aecec59, 0xcf45cf05458f8f40, 0xbc9dbc3e9d1f1fa3, 0xc040c00940898949, 0x928792ef87fafa68, 0x3f153fc515efefd0, 0x26eb267febb2b294, 0x40c94007c98e8ece, 0x1d0b1ded0bfbfbe6, 0x2fec2f82ec41416e, 0xa967a97d67b3b31a, 0x1cfd1cbefd5f5f43, 0x25ea258aea454560, 0xdabfda46bf2323f9, 0x02f702a6f7535351, 0xa196a1d396e4e445, 0xed5bed2d5b9b9b76, 0x5dc25deac2757528, 0x241c24d91ce1e1c5, 0xe9aee97aae3d3dd4, 0xbe6abe986a4c4cf2, 0xee5aeed85a6c6c82, 0xc341c3fc417e7ebd, 0x060206f102f5f5f3, 0xd14fd11d4f838352, 0xe45ce4d05c68688c, 0x07f407a2f4515156, 0x5c345cb934d1d18d, 0x180818e908f9f9e1, 0xae93aedf93e2e24c, 0x9573954d73abab3e, 0xf553f5c453626297, 0x413f41543f2a2a6b, 0x140c14100c08081c, 0xf652f63152959563, 0xaf65af8c654646e9, 0xe25ee2215e9d9d7f, 0x7828786028303048, 0xf8a1f86ea13737cf, 0x110f11140f0a0a1b, 0xc4b5c45eb52f2feb, 0x1b091b1c090e0e15, 0x5a365a483624247e, 0xb69bb6369b1b1bad, 0x473d47a53ddfdf98, 0x6a266a8126cdcda7, 0xbb69bb9c694e4ef5, 0x4ccd4cfecd7f7f33, 0xba9fbacf9feaea50, 0x2d1b2d241b12123f, 0xb99eb93a9e1d1da4, 0x9c749cb0745858c4, 0x722e72682e343446, 0x772d776c2d363641, 0xcdb2cda3b2dcdc11, 0x29ee2973eeb4b49d, 0x16fb16b6fb5b5b4d, 0x01f60153f6a4a4a5, 0xd74dd7ec4d7676a1, 0xa361a37561b7b714, 0x49ce49face7d7d34, 0x8d7b8da47b5252df, 0x423e42a13edddd9f, 0x937193bc715e5ecd, 0xa297a226971313b1, 0x04f50457f5a6a6a2, 0xb868b86968b9b901, 0x0000000000000000, 0x742c74992cc1c1b5, 0xa060a080604040e0, 0x211f21dd1fe3e3c2, 0x43c843f2c879793a, 0x2ced2c77edb6b69a, 0xd9bed9b3bed4d40d, 0xca46ca01468d8d47, 0x70d970ced9676717, 0xdd4bdde44b7272af, 0x79de7933de9494ed, 0x67d4672bd49898ff, 0x23e8237be8b0b093, 0xde4ade114a85855b, 0xbd6bbd6d6bbbbb06, 0x7e2a7e912ac5c5bb, 0x34e5349ee54f4f7b, 0x3a163ac116ededd7, 0x54c55417c58686d2, 0x62d7622fd79a9af8, 0xff55ffcc55666699, 0xa794a722941111b6, 0x4acf4a0fcf8a8ac0, 0x301030c910e9e9d9, 0x0a060a080604040e, 0x988198e781fefe66, 0x0bf00b5bf0a0a0ab, 0xcc44ccf0447878b4, 0xd5bad54aba2525f0, 0x3ee33e96e34b4b75, 0x0ef30e5ff3a2a2ac, 0x19fe19bafe5d5d44, 0x5bc05b1bc08080db, 0x858a850a8a050580, 0xecadec7ead3f3fd3, 0xdfbcdf42bc2121fe, 0xd848d8e0487070a8, 0x0c040cf904f1f1fd, 0x7adf7ac6df636319, 0x58c158eec177772f, 0x9f759f4575afaf30, 0xa563a584634242e7, 0x5030504030202070, 0x2e1a2ed11ae5e5cb, 0x120e12e10efdfdef, 0xb76db7656dbfbf08, 0xd44cd4194c818155, 0x3c143c3014181824, 0x5f355f4c35262679, 0x712f719d2fc3c3b2, 0x38e13867e1bebe86, 0xfda2fd6aa23535c8, 0x4fcc4f0bcc8888c7, 0x4b394b5c392e2e65, 0xf957f93d5793936a, 0x0df20daaf2555558, 0x9d829de382fcfc61, 0xc947c9f4477a7ab3, 0xefacef8bacc8c827, 0x32e7326fe7baba88, 0x7d2b7d642b32324f, 0xa495a4d795e6e642, 0xfba0fb9ba0c0c03b, 0xb398b332981919aa, 0x68d16827d19e9ef6, 0x817f815d7fa3a322, 0xaa66aa88664444ee, 0x827e82a87e5454d6, 0xe6abe676ab3b3bdd, 0x9e839e16830b0b95, 0x45ca4503ca8c8cc9, 0x7b297b9529c7c7bc, 0x6ed36ed6d36b6b05, 0x443c44503c28286c, 0x8b798b5579a7a72c, 0x3de23d63e2bcbc81, 0x271d272c1d161631, 0x9a769a4176adad37, 0x4d3b4dad3bdbdb96, 0xfa56fac85664649e, 0xd24ed2e84e7474a6, 0x221e22281e141436, 0x76db763fdb9292e4, 0x1e0a1e180a0c0c12, 0xb46cb4906c4848fc, 0x37e4376be4b8b88f, 0xe75de7255d9f9f78, 0xb26eb2616ebdbd0f, 0x2aef2a86ef434369, 0xf1a6f193a6c4c435, 0xe3a8e372a83939da, 0xf7a4f762a43131c6, 0x593759bd37d3d38a, 0x868b86ff8bf2f274, 0x563256b132d5d583, 0xc543c50d438b8b4e, 0xeb59ebdc596e6e85, 0xc2b7c2afb7dada18, 0x8f8c8f028c01018e, 0xac64ac7964b1b11d, 0x6dd26d23d29c9cf1, 0x3be03b92e0494972, 0xc7b4c7abb4d8d81f, 0x15fa1543faacacb9, 0x090709fd07f3f3fa, 0x6f256f8525cfcfa0, 0xeaafea8fafcaca20, 0x898e89f38ef4f47d, 0x20e9208ee9474767, 0x2818282018101038, 0x64d564ded56f6f0b, 0x838883fb88f0f073, 0xb16fb1946f4a4afb, 0x967296b8725c5cca, 0x6c246c7024383854, 0x08f108aef157575f, 0x52c752e6c7737321, 0xf351f33551979764, 0x6523658d23cbcbae, 0x847c84597ca1a125, 0xbf9cbfcb9ce8e857, 0x6321637c213e3e5d, 0x7cdd7c37dd9696ea, 0x7fdc7fc2dc61611e, 0x9186911a860d0d9c, 0x9485941e850f0f9b, 0xab90abdb90e0e04b, 0xc642c6f8427c7cba, 0x57c457e2c4717126, 0xe5aae583aacccc29, 0x73d8733bd89090e3, 0x0f050f0c05060609, 0x030103f501f7f7f4, 0x36123638121c1c2a, 0xfea3fe9fa3c2c23c, 0xe15fe1d45f6a6a8b, 0x10f91047f9aeaebe, 0x6bd06bd2d0696902, 0xa891a82e911717bf, 0xe858e82958999971, 0x69276974273a3a53, 0xd0b9d04eb92727f7, 0x483848a938d9d991, 0x351335cd13ebebde, 0xceb3ce56b32b2be5, 0x5533554433222277, 0xd6bbd6bfbbd2d204, 0x9070904970a9a939, 0x8089800e89070787, 0xf2a7f266a73333c1, 0xc1b6c15ab62d2dec, 0x66226678223c3c5a, 0xad92ad2a921515b8, 0x6020608920c9c9a9, 0xdb49db154987875c, 0x1aff1a4fffaaaab0, 0x887888a0785050d8, 0x8e7a8e517aa5a52b, 0x8a8f8a068f030389, 0x13f813b2f859594a, 0x9b809b1280090992, 0x39173934171a1a23, 0x75da75cada656510, 0x533153b531d7d784, 0x51c65113c68484d5, 0xd3b8d3bbb8d0d003, 0x5ec35e1fc38282dc, 0xcbb0cb52b02929e2, 0x997799b4775a5ac3, 0x3311333c111e1e2d, 0x46cb46f6cb7b7b3d, 0x1ffc1f4bfca8a8b7, 0x61d661dad66d6d0c, 0x4e3a4e583a2c2c62],
+    [0x32f4a5f497a5c6c6, 0x6f978497eb84f8f8, 0x5eb099b0c799eeee, 0x7a8c8d8cf78df6f6, 0xe8170d17e50dffff, 0x0adcbddcb7bdd6d6, 0x16c8b1c8a7b1dede, 0x6dfc54fc39549191, 0x90f050f0c0506060, 0x0705030504030202, 0x2ee0a9e087a9cece, 0xd1877d87ac7d5656, 0xcc2b192bd519e7e7, 0x13a662a67162b5b5, 0x7c31e6319ae64d4d, 0x59b59ab5c39aecec, 0x40cf45cf05458f8f, 0xa3bc9dbc3e9d1f1f, 0x49c040c009408989, 0x68928792ef87fafa, 0xd03f153fc515efef, 0x9426eb267febb2b2, 0xce40c94007c98e8e, 0xe61d0b1ded0bfbfb, 0x6e2fec2f82ec4141, 0x1aa967a97d67b3b3, 0x431cfd1cbefd5f5f, 0x6025ea258aea4545, 0xf9dabfda46bf2323, 0x5102f702a6f75353, 0x45a196a1d396e4e4, 0x76ed5bed2d5b9b9b, 0x285dc25deac27575, 0xc5241c24d91ce1e1, 0xd4e9aee97aae3d3d, 0xf2be6abe986a4c4c, 0x82ee5aeed85a6c6c, 0xbdc341c3fc417e7e, 0xf3060206f102f5f5, 0x52d14fd11d4f8383, 0x8ce45ce4d05c6868, 0x5607f407a2f45151, 0x8d5c345cb934d1d1, 0xe1180818e908f9f9, 0x4cae93aedf93e2e2, 0x3e9573954d73abab, 0x97f553f5c4536262, 0x6b413f41543f2a2a, 0x1c140c14100c0808, 0x63f652f631529595, 0xe9af65af8c654646, 0x7fe25ee2215e9d9d, 0x4878287860283030, 0xcff8a1f86ea13737, 0x1b110f11140f0a0a, 0xebc4b5c45eb52f2f, 0x151b091b1c090e0e, 0x7e5a365a48362424, 0xadb69bb6369b1b1b, 0x98473d47a53ddfdf, 0xa76a266a8126cdcd, 0xf5bb69bb9c694e4e, 0x334ccd4cfecd7f7f, 0x50ba9fbacf9feaea, 0x3f2d1b2d241b1212, 0xa4b99eb93a9e1d1d, 0xc49c749cb0745858, 0x46722e72682e3434, 0x41772d776c2d3636, 0x11cdb2cda3b2dcdc, 0x9d29ee2973eeb4b4, 0x4d16fb16b6fb5b5b, 0xa501f60153f6a4a4, 0xa1d74dd7ec4d7676, 0x14a361a37561b7b7, 0x3449ce49face7d7d, 0xdf8d7b8da47b5252, 0x9f423e42a13edddd, 0xcd937193bc715e5e, 0xb1a297a226971313, 0xa204f50457f5a6a6, 0x01b868b86968b9b9, 0x0000000000000000, 0xb5742c74992cc1c1, 0xe0a060a080604040, 0xc2211f21dd1fe3e3, 0x3a43c843f2c87979, 0x9a2ced2c77edb6b6, 0x0dd9bed9b3bed4d4, 0x47ca46ca01468d8d, 0x1770d970ced96767, 0xafdd4bdde44b7272, 0xed79de7933de9494, 0xff67d4672bd49898, 0x9323e8237be8b0b0, 0x5bde4ade114a8585, 0x06bd6bbd6d6bbbbb, 0xbb7e2a7e912ac5c5, 0x7b34e5349ee54f4f, 0xd73a163ac116eded, 0xd254c55417c58686, 0xf862d7622fd79a9a, 0x99ff55ffcc556666, 0xb6a794a722941111, 0xc04acf4a0fcf8a8a, 0xd9301030c910e9e9, 0x0e0a060a08060404, 0x66988198e781fefe, 0xab0bf00b5bf0a0a0, 0xb4cc44ccf0447878, 0xf0d5bad54aba2525, 0x753ee33e96e34b4b, 0xac0ef30e5ff3a2a2, 0x4419fe19bafe5d5d, 0xdb5bc05b1bc08080, 0x80858a850a8a0505, 0xd3ecadec7ead3f3f, 0xfedfbcdf42bc2121, 0xa8d848d8e0487070, 0xfd0c040cf904f1f1, 0x197adf7ac6df6363, 0x2f58c158eec17777, 0x309f759f4575afaf, 0xe7a563a584634242, 0x7050305040302020, 0xcb2e1a2ed11ae5e5, 0xef120e12e10efdfd, 0x08b76db7656dbfbf, 0x55d44cd4194c8181, 0x243c143c30141818, 0x795f355f4c352626, 0xb2712f719d2fc3c3, 0x8638e13867e1bebe, 0xc8fda2fd6aa23535, 0xc74fcc4f0bcc8888, 0x654b394b5c392e2e, 0x6af957f93d579393, 0x580df20daaf25555, 0x619d829de382fcfc, 0xb3c947c9f4477a7a, 0x27efacef8bacc8c8, 0x8832e7326fe7baba, 0x4f7d2b7d642b3232, 0x42a495a4d795e6e6, 0x3bfba0fb9ba0c0c0, 0xaab398b332981919, 0xf668d16827d19e9e, 0x22817f815d7fa3a3, 0xeeaa66aa88664444, 0xd6827e82a87e5454, 0xdde6abe676ab3b3b, 0x959e839e16830b0b, 0xc945ca4503ca8c8c, 0xbc7b297b9529c7c7, 0x056ed36ed6d36b6b, 0x6c443c44503c2828, 0x2c8b798b5579a7a7, 0x813de23d63e2bcbc, 0x31271d272c1d1616, 0x379a769a4176adad, 0x964d3b4dad3bdbdb, 0x9efa56fac8566464, 0xa6d24ed2e84e7474, 0x36221e22281e1414, 0xe476db763fdb9292, 0x121e0a1e180a0c0c, 0xfcb46cb4906c4848, 0x8f37e4376be4b8b8, 0x78e75de7255d9f9f, 0x0fb26eb2616ebdbd, 0x692aef2a86ef4343, 0x35f1a6f193a6c4c4, 0xdae3a8e372a83939, 0xc6f7a4f762a43131, 0x8a593759bd37d3d3, 0x74868b86ff8bf2f2, 0x83563256b132d5d5, 0x4ec543c50d438b8b, 0x85eb59ebdc596e6e, 0x18c2b7c2afb7dada, 0x8e8f8c8f028c0101, 0x1dac64ac7964b1b1, 0xf16dd26d23d29c9c, 0x723be03b92e04949, 0x1fc7b4c7abb4d8d8, 0xb915fa1543faacac, 0xfa090709fd07f3f3, 0xa06f256f8525cfcf, 0x20eaafea8fafcaca, 0x7d898e89f38ef4f4, 0x6720e9208ee94747, 0x3828182820181010, 0x0b64d564ded56f6f, 0x73838883fb88f0f0, 0xfbb16fb1946f4a4a, 0xca967296b8725c5c, 0x546c246c70243838, 0x5f08f108aef15757, 0x2152c752e6c77373, 0x64f351f335519797, 0xae6523658d23cbcb, 0x25847c84597ca1a1, 0x57bf9cbfcb9ce8e8, 0x5d6321637c213e3e, 0xea7cdd7c37dd9696, 0x1e7fdc7fc2dc6161, 0x9c9186911a860d0d, 0x9b9485941e850f0f, 0x4bab90abdb90e0e0, 0xbac642c6f8427c7c, 0x2657c457e2c47171, 0x29e5aae583aacccc, 0xe373d8733bd89090, 0x090f050f0c050606, 0xf4030103f501f7f7, 0x2a36123638121c1c, 0x3cfea3fe9fa3c2c2, 0x8be15fe1d45f6a6a, 0xbe10f91047f9aeae, 0x026bd06bd2d06969, 0xbfa891a82e911717, 0x71e858e829589999, 0x5369276974273a3a, 0xf7d0b9d04eb92727, 0x91483848a938d9d9, 0xde351335cd13ebeb, 0xe5ceb3ce56b32b2b, 0x7755335544332222, 0x04d6bbd6bfbbd2d2, 0x399070904970a9a9, 0x878089800e890707, 0xc1f2a7f266a73333, 0xecc1b6c15ab62d2d, 0x5a66226678223c3c, 0xb8ad92ad2a921515, 0xa96020608920c9c9, 0x5cdb49db15498787, 0xb01aff1a4fffaaaa, 0xd8887888a0785050, 0x2b8e7a8e517aa5a5, 0x898a8f8a068f0303, 0x4a13f813b2f85959, 0x929b809b12800909, 0x2339173934171a1a, 0x1075da75cada6565, 0x84533153b531d7d7, 0xd551c65113c68484, 0x03d3b8d3bbb8d0d0, 0xdc5ec35e1fc38282, 0xe2cbb0cb52b02929, 0xc3997799b4775a5a, 0x2d3311333c111e1e, 0x3d46cb46f6cb7b7b, 0xb71ffc1f4bfca8a8, 0x0c61d661dad66d6d, 0x624e3a4e583a2c2c],
+];
diff --git a/k12/CHANGELOG.md b/k12/CHANGELOG.md
index da46bb91a..af466a1d8 100644
--- a/k12/CHANGELOG.md
+++ b/k12/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.2.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.1.0 (2020-06-09)
 ### Changed
 - Update to `digest` v0.9 release; MSRV 1.41+ ([#155])
diff --git a/k12/Cargo.toml b/k12/Cargo.toml
index 7b227d4dd..d736e0827 100644
--- a/k12/Cargo.toml
+++ b/k12/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "k12"
-version = "0.1.0"
+version = "0.2.0" # Also update html_root_url in lib.rs when bumping this
 description = "Experimental pure Rust implementation of the KangarooTwelve hash function"
 authors = ["Diggory Hardy <github1@dhardy.name>"]
 license = "Apache-2.0 OR MIT"
@@ -12,10 +12,10 @@ keywords = ["crypto", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = { version = "0.9", features = ["alloc"] }
+digest = { version = "0.10", features = ["alloc"] }
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["alloc", "dev"] }
+digest = { version = "0.10", features = ["alloc", "dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/k12/benches/lib.rs b/k12/benches/lib.rs
deleted file mode 100644
index 9ea5ab9b4..000000000
--- a/k12/benches/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-extern crate test;
-
-use digest::Update;
-use test::Bencher;
-
-digest::bench!(bench1_10, k12::KangarooTwelve, 10);
-digest::bench!(bench2_100, k12::KangarooTwelve, 100);
-digest::bench!(bench3_1000, k12::KangarooTwelve, 1000);
-digest::bench!(bench4_10000, k12::KangarooTwelve, 10000);
diff --git a/k12/benches/mod.rs b/k12/benches/mod.rs
new file mode 100644
index 000000000..0074e2a22
--- /dev/null
+++ b/k12/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use test::Bencher;
+
+bench_update!(
+    k12::KangarooTwelve::default();
+    k12_10 10;
+    k12_100 100;
+    // the bigger sizes result in OOM
+    // k12_1000 1000;
+    // k12_10000 10000;
+);
diff --git a/k12/src/lib.rs b/k12/src/lib.rs
index 8c467d869..73607228d 100644
--- a/k12/src/lib.rs
+++ b/k12/src/lib.rs
@@ -10,8 +10,9 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/k12/0.2.0"
 )]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
@@ -28,7 +29,7 @@ mod lanes;
 // TODO(tarcieri): eliminate usage of `Vec`
 use alloc::vec::Vec;
 use core::{cmp::min, convert::TryInto, mem};
-use digest::{ExtendableOutputDirty, Reset, Update, XofReader};
+use digest::{ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update, XofReader};
 
 /// The KangarooTwelve extendable-output function (XOF).
 #[derive(Debug, Default)]
@@ -57,17 +58,28 @@ impl KangarooTwelve {
     }
 }
 
+impl HashMarker for KangarooTwelve {}
+
 impl Update for KangarooTwelve {
-    /// Input data into the hash function
-    fn update(&mut self, bytes: impl AsRef<[u8]>) {
-        self.buffer.extend_from_slice(bytes.as_ref());
+    fn update(&mut self, bytes: &[u8]) {
+        self.buffer.extend_from_slice(bytes);
     }
 }
 
-impl ExtendableOutputDirty for KangarooTwelve {
+impl ExtendableOutput for KangarooTwelve {
     type Reader = Reader;
 
-    fn finalize_xof_dirty(&mut self) -> Self::Reader {
+    fn finalize_xof(self) -> Self::Reader {
+        Reader {
+            buffer: self.buffer,
+            customization: self.customization,
+            finished: false,
+        }
+    }
+}
+
+impl ExtendableOutputReset for KangarooTwelve {
+    fn finalize_xof_reset(&mut self) -> Self::Reader {
         let mut buffer = vec![];
         let mut customization = vec![];
 
diff --git a/md2/CHANGELOG.md b/md2/CHANGELOG.md
index 2319dae63..641a7eb53 100644
--- a/md2/CHANGELOG.md
+++ b/md2/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.0 (2020-06-12)
 ### Changed
 - Bump `opaque-debug` to v0.3.0 ([#168])
diff --git a/md2/Cargo.toml b/md2/Cargo.toml
index a025061d8..33b7833fc 100644
--- a/md2/Cargo.toml
+++ b/md2/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "md2"
-version = "0.9.0"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 license = "MIT OR Apache-2.0"
 authors = ["RustCrypto Developers"]
 description = "MD2 hash function"
@@ -12,12 +12,10 @@ keywords = ["crypto", "md2", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/md2/benches/lib.rs b/md2/benches/lib.rs
deleted file mode 100644
index b1f617601..000000000
--- a/md2/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(md2::Md2);
diff --git a/md2/benches/mod.rs b/md2/benches/mod.rs
new file mode 100644
index 000000000..ea06422de
--- /dev/null
+++ b/md2/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use md2::Md2;
+use test::Bencher;
+
+bench_update!(
+    Md2::default();
+    md2_10 10;
+    md2_100 100;
+    md2_1000 1000;
+    md2_10000 10000;
+);
diff --git a/md2/examples/md2sum.rs b/md2/examples/md2sum.rs
deleted file mode 100644
index 14a94ff78..000000000
--- a/md2/examples/md2sum.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use md2::{Digest, Md2};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<R: Read>(reader: &mut R, name: &str) {
-    let mut sh = Md2::new();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    println!("{:x}\t{}", &sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process(&mut file, &path);
-            }
-        }
-    } else {
-        process(&mut io::stdin(), "-");
-    }
-}
diff --git a/md2/src/lib.rs b/md2/src/lib.rs
index 07ca1e68c..d41a1178e 100644
--- a/md2/src/lib.rs
+++ b/md2/src/lib.rs
@@ -25,52 +25,40 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/md2/0.10.0"
 )]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
-
 pub use digest::{self, Digest};
 
-use block_buffer::{block_padding::Pkcs7, BlockBuffer};
-use digest::{consts::U16, generic_array::GenericArray};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
+use core::fmt;
+use digest::{
+    block_buffer::Eager,
+    consts::U16,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    HashMarker, Output,
+};
 
 mod consts;
 
-type Block = GenericArray<u8, U16>;
-
+/// Core MD2 hasher state.
 #[derive(Clone)]
-struct Md2State {
+pub struct Md2Core {
     x: [u8; 48],
-    checksum: Block,
-}
-
-impl Default for Md2State {
-    fn default() -> Self {
-        Self {
-            x: [0; 48],
-            checksum: Default::default(),
-        }
-    }
-}
-
-/// The MD2 hasher
-#[derive(Clone, Default)]
-pub struct Md2 {
-    buffer: BlockBuffer<U16>,
-    state: Md2State,
+    checksum: Block<Self>,
 }
 
-impl Md2State {
-    fn process_block(&mut self, input: &Block) {
+impl Md2Core {
+    fn compress(&mut self, block: &Block<Self>) {
         // Update state
         for j in 0..16 {
-            self.x[16 + j] = input[j];
+            self.x[16 + j] = block[j];
             self.x[32 + j] = self.x[16 + j] ^ self.x[j];
         }
 
@@ -86,48 +74,78 @@ impl Md2State {
         // Update checksum
         let mut l = self.checksum[15];
         for j in 0..16 {
-            self.checksum[j] ^= consts::S[(input[j] ^ l) as usize];
+            self.checksum[j] ^= consts::S[(block[j] ^ l) as usize];
             l = self.checksum[j];
         }
     }
 }
 
-impl BlockInput for Md2 {
+impl HashMarker for Md2Core {}
+
+impl BlockSizeUser for Md2Core {
     type BlockSize = U16;
 }
 
-impl Update for Md2 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        let s = &mut self.state;
-        self.buffer.input_block(input, |d| s.process_block(d));
-    }
+impl BufferKindUser for Md2Core {
+    type BufferKind = Eager;
 }
 
-impl FixedOutputDirty for Md2 {
+impl OutputSizeUser for Md2Core {
     type OutputSize = U16;
+}
 
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let buf = self
-            .buffer
-            .pad_with::<Pkcs7>()
-            .expect("we never use input_lazy");
-
-        self.state.process_block(buf);
+impl UpdateCore for Md2Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        for block in blocks {
+            self.compress(block)
+        }
+    }
+}
 
-        let checksum = self.state.checksum;
-        self.state.process_block(&checksum);
+impl FixedOutputCore for Md2Core {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let pos = buffer.get_pos();
+        let rem = buffer.remaining() as u8;
+        let block = buffer.pad_with_zeros();
+        block[pos..].iter_mut().for_each(|b| *b = rem);
+
+        self.compress(block);
+        let checksum = self.checksum;
+        self.compress(&checksum);
+        out.copy_from_slice(&self.x[0..16]);
+    }
+}
 
-        out.copy_from_slice(&self.state.x[0..16]);
+impl Default for Md2Core {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            x: [0; 48],
+            checksum: Default::default(),
+        }
     }
 }
 
-impl Reset for Md2 {
+impl Reset for Md2Core {
+    #[inline]
     fn reset(&mut self) {
-        self.state = Default::default();
-        self.buffer.reset();
+        *self = Default::default();
+    }
+}
+
+impl AlgorithmName for Md2Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md2")
+    }
+}
+
+impl fmt::Debug for Md2Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md2Core { ... }")
     }
 }
 
-opaque_debug::implement!(Md2);
-digest::impl_write!(Md2);
+/// MD2 hasher state.
+pub type Md2 = CoreWrapper<Md2Core>;
diff --git a/md4/CHANGELOG.md b/md4/CHANGELOG.md
index b4728c816..92a5f63c0 100644
--- a/md4/CHANGELOG.md
+++ b/md4/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.0 (2020-06-12)
 ### Changed
 - Bump `opaque-debug` to v0.3.0 ([#168])
diff --git a/md4/Cargo.toml b/md4/Cargo.toml
index c2a53b5c0..23c0b6ace 100644
--- a/md4/Cargo.toml
+++ b/md4/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "md4"
-version = "0.9.0"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "MD4 hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "md4", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/md4/benches/lib.rs b/md4/benches/lib.rs
deleted file mode 100644
index 19f62705b..000000000
--- a/md4/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(md4::Md4);
diff --git a/md4/benches/mod.rs b/md4/benches/mod.rs
new file mode 100644
index 000000000..0f32fc224
--- /dev/null
+++ b/md4/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use md4::Md4;
+use test::Bencher;
+
+bench_update!(
+    Md4::default();
+    md4_10 10;
+    md4_100 100;
+    md4_1000 1000;
+    md4_10000 10000;
+);
diff --git a/md4/examples/md4sum.rs b/md4/examples/md4sum.rs
deleted file mode 100644
index 4f4e67073..000000000
--- a/md4/examples/md4sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use md4::{Digest, Md4};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh: D = Default::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Md4, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Md4, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/md4/src/lib.rs b/md4/src/lib.rs
index f2360fbf8..afbf0c31a 100644
--- a/md4/src/lib.rs
+++ b/md4/src/lib.rs
@@ -25,166 +25,175 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/md4/0.10.0"
 )]
 #![deny(unsafe_code)]
 #![warn(rust_2018_idioms)]
 #![allow(clippy::many_single_char_names)]
 
-#[cfg(feature = "std")]
-extern crate std;
-
-use core::convert::TryInto;
 pub use digest::{self, Digest};
 
-use block_buffer::BlockBuffer;
+use core::{convert::TryInto, fmt};
 use digest::{
-    consts::{U16, U64},
-    generic_array::GenericArray,
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U16, U64},
+    HashMarker, Output,
 };
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
 
-// initial values for Md4State
-const S: [u32; 4] = [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476];
+#[derive(Clone)]
+pub struct Md4Core {
+    block_len: u64,
+    state: [u32; 4],
+}
 
-type Block = GenericArray<u8, U64>;
+impl HashMarker for Md4Core {}
 
-#[derive(Copy, Clone)]
-struct Md4State {
-    s: [u32; 4],
+impl BlockSizeUser for Md4Core {
+    type BlockSize = U64;
 }
 
-/// The MD4 hasher
-#[derive(Clone, Default)]
-pub struct Md4 {
-    length_bytes: u64,
-    buffer: BlockBuffer<U64>,
-    state: Md4State,
+impl BufferKindUser for Md4Core {
+    type BufferKind = Eager;
 }
 
-impl Md4State {
-    fn process_block(&mut self, input: &Block) {
-        fn f(x: u32, y: u32, z: u32) -> u32 {
-            (x & y) | (!x & z)
-        }
+impl OutputSizeUser for Md4Core {
+    type OutputSize = U16;
+}
 
-        fn g(x: u32, y: u32, z: u32) -> u32 {
-            (x & y) | (x & z) | (y & z)
+impl UpdateCore for Md4Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len = self.block_len.wrapping_add(blocks.len() as u64);
+        for block in blocks {
+            compress(&mut self.state, block);
         }
+    }
+}
 
-        fn h(x: u32, y: u32, z: u32) -> u32 {
-            x ^ y ^ z
+impl FixedOutputCore for Md4Core {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bit_len = self
+            .block_len
+            .wrapping_mul(Self::BlockSize::U64)
+            .wrapping_add(buffer.get_pos() as u64)
+            .wrapping_mul(8);
+        let mut state = self.state;
+        buffer.len64_padding_le(bit_len, |block| compress(&mut state, block));
+
+        for (chunk, v) in out.chunks_exact_mut(4).zip(state.iter()) {
+            chunk.copy_from_slice(&v.to_le_bytes());
         }
+    }
+}
 
-        fn op1(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
-            a.wrapping_add(f(b, c, d)).wrapping_add(k).rotate_left(s)
+impl Default for Md4Core {
+    #[inline]
+    fn default() -> Self {
+        let state = [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476];
+        Self {
+            state,
+            block_len: 0,
         }
+    }
+}
 
-        fn op2(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
-            a.wrapping_add(g(b, c, d))
-                .wrapping_add(k)
-                .wrapping_add(0x5A82_7999)
-                .rotate_left(s)
-        }
+impl Reset for Md4Core {
+    #[inline]
+    fn reset(&mut self) {
+        *self = Default::default();
+    }
+}
 
-        fn op3(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
-            a.wrapping_add(h(b, c, d))
-                .wrapping_add(k)
-                .wrapping_add(0x6ED9_EBA1)
-                .rotate_left(s)
-        }
+impl AlgorithmName for Md4Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md4")
+    }
+}
 
-        let mut a = self.s[0];
-        let mut b = self.s[1];
-        let mut c = self.s[2];
-        let mut d = self.s[3];
+impl fmt::Debug for Md4Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md4Core { ... }")
+    }
+}
 
-        // load block to data
-        let mut data = [0u32; 16];
-        for (o, chunk) in data.iter_mut().zip(input.chunks_exact(4)) {
-            *o = u32::from_le_bytes(chunk.try_into().unwrap());
-        }
+/// MD4 hasher state.
+pub type Md4 = CoreWrapper<Md4Core>;
 
-        // round 1
-        for &i in &[0, 4, 8, 12] {
-            a = op1(a, b, c, d, data[i], 3);
-            d = op1(d, a, b, c, data[i + 1], 7);
-            c = op1(c, d, a, b, data[i + 2], 11);
-            b = op1(b, c, d, a, data[i + 3], 19);
-        }
+fn compress(state: &mut [u32; 4], input: &Block<Md4Core>) {
+    fn f(x: u32, y: u32, z: u32) -> u32 {
+        (x & y) | (!x & z)
+    }
 
-        // round 2
-        for i in 0..4 {
-            a = op2(a, b, c, d, data[i], 3);
-            d = op2(d, a, b, c, data[i + 4], 5);
-            c = op2(c, d, a, b, data[i + 8], 9);
-            b = op2(b, c, d, a, data[i + 12], 13);
-        }
+    fn g(x: u32, y: u32, z: u32) -> u32 {
+        (x & y) | (x & z) | (y & z)
+    }
 
-        // round 3
-        for &i in &[0, 2, 1, 3] {
-            a = op3(a, b, c, d, data[i], 3);
-            d = op3(d, a, b, c, data[i + 8], 9);
-            c = op3(c, d, a, b, data[i + 4], 11);
-            b = op3(b, c, d, a, data[i + 12], 15);
-        }
+    fn h(x: u32, y: u32, z: u32) -> u32 {
+        x ^ y ^ z
+    }
 
-        self.s[0] = self.s[0].wrapping_add(a);
-        self.s[1] = self.s[1].wrapping_add(b);
-        self.s[2] = self.s[2].wrapping_add(c);
-        self.s[3] = self.s[3].wrapping_add(d);
+    fn op1(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
+        a.wrapping_add(f(b, c, d)).wrapping_add(k).rotate_left(s)
     }
-}
 
-impl Default for Md4State {
-    fn default() -> Self {
-        Md4State { s: S }
+    fn op2(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
+        a.wrapping_add(g(b, c, d))
+            .wrapping_add(k)
+            .wrapping_add(0x5A82_7999)
+            .rotate_left(s)
     }
-}
 
-impl Md4 {
-    fn finalize_inner(&mut self) {
-        let state = &mut self.state;
-        let l = (self.length_bytes << 3) as u64;
-        self.buffer.len64_padding_le(l, |d| state.process_block(d))
+    fn op3(a: u32, b: u32, c: u32, d: u32, k: u32, s: u32) -> u32 {
+        a.wrapping_add(h(b, c, d))
+            .wrapping_add(k)
+            .wrapping_add(0x6ED9_EBA1)
+            .rotate_left(s)
     }
-}
 
-impl BlockInput for Md4 {
-    type BlockSize = U64;
-}
+    let mut a = state[0];
+    let mut b = state[1];
+    let mut c = state[2];
+    let mut d = state[3];
 
-impl Update for Md4 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Unlike Sha1 and Sha2, the length value in MD4 is defined as
-        // the length of the message mod 2^64 - ie: integer overflow is OK.
-        self.length_bytes = self.length_bytes.wrapping_add(input.len() as u64);
-        let s = &mut self.state;
-        self.buffer.input_block(input, |d| s.process_block(d));
+    // load block to data
+    let mut data = [0u32; 16];
+    for (o, chunk) in data.iter_mut().zip(input.chunks_exact(4)) {
+        *o = u32::from_le_bytes(chunk.try_into().unwrap());
     }
-}
-
-impl FixedOutputDirty for Md4 {
-    type OutputSize = U16;
 
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.finalize_inner();
+    // round 1
+    for &i in &[0, 4, 8, 12] {
+        a = op1(a, b, c, d, data[i], 3);
+        d = op1(d, a, b, c, data[i + 1], 7);
+        c = op1(c, d, a, b, data[i + 2], 11);
+        b = op1(b, c, d, a, data[i + 3], 19);
+    }
 
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.state.s.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
+    // round 2
+    for i in 0..4 {
+        a = op2(a, b, c, d, data[i], 3);
+        d = op2(d, a, b, c, data[i + 4], 5);
+        c = op2(c, d, a, b, data[i + 8], 9);
+        b = op2(b, c, d, a, data[i + 12], 13);
     }
-}
 
-impl Reset for Md4 {
-    fn reset(&mut self) {
-        self.state = Default::default();
-        self.length_bytes = 0;
-        self.buffer.reset();
+    // round 3
+    for &i in &[0, 2, 1, 3] {
+        a = op3(a, b, c, d, data[i], 3);
+        d = op3(d, a, b, c, data[i + 8], 9);
+        c = op3(c, d, a, b, data[i + 4], 11);
+        b = op3(b, c, d, a, data[i + 12], 15);
     }
-}
 
-opaque_debug::implement!(Md4);
-digest::impl_write!(Md4);
+    state[0] = state[0].wrapping_add(a);
+    state[1] = state[1].wrapping_add(b);
+    state[2] = state[2].wrapping_add(c);
+    state[3] = state[3].wrapping_add(d);
+}
diff --git a/md5/CHANGELOG.md b/md5/CHANGELOG.md
index 5b573e207..394eaa3d9 100644
--- a/md5/CHANGELOG.md
+++ b/md5/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.1 (2020-06-28)
 ### Changed
 - Update to `block-buffer` v0.9 ([#164])
diff --git a/md5/Cargo.toml b/md5/Cargo.toml
index bf3292926..1c578e8f8 100644
--- a/md5/Cargo.toml
+++ b/md5/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "md-5"
-version = "0.9.1"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "MD5 hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -15,13 +15,11 @@ categories = ["cryptography", "no-std"]
 name = "md5"
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-md5-asm = { version = "0.4", optional = true}
-opaque-debug = "0.3"
+digest = "0.10"
+md5-asm = { version = "0.5", optional = true }
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/md5/benches/lib.rs b/md5/benches/lib.rs
deleted file mode 100644
index 1ebd6d9f0..000000000
--- a/md5/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(md5::Md5);
diff --git a/md5/benches/mod.rs b/md5/benches/mod.rs
new file mode 100644
index 000000000..7729eb708
--- /dev/null
+++ b/md5/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use md5::Md5;
+use test::Bencher;
+
+bench_update!(
+    Md5::default();
+    md5_10 10;
+    md5_100 100;
+    md5_1000 1000;
+    md5_10000 10000;
+);
diff --git a/md5/examples/md5sum.rs b/md5/examples/md5sum.rs
deleted file mode 100644
index baa91fcd3..000000000
--- a/md5/examples/md5sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use md5::{Digest, Md5};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Md5, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Md5, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/md5/src/utils.rs b/md5/src/compress.rs
similarity index 79%
rename from md5/src/utils.rs
rename to md5/src/compress.rs
index 9cd0259bc..46857038e 100644
--- a/md5/src/utils.rs
+++ b/md5/src/compress.rs
@@ -1,7 +1,21 @@
-#![allow(clippy::many_single_char_names)]
-use crate::consts::RC;
+#![allow(clippy::many_single_char_names, clippy::unreadable_literal)]
 use core::convert::TryInto;
 
+const RC: [u32; 64] = [
+    // round 1
+    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+    // round 2
+    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+    // round 3
+    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+    // round 4
+    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+];
+
 #[inline(always)]
 fn op_f(w: u32, x: u32, y: u32, z: u32, m: u32, c: u32, s: u32) -> u32 {
     ((x & y) | (!x & z))
@@ -42,7 +56,7 @@ fn op_i(w: u32, x: u32, y: u32, z: u32, m: u32, c: u32, s: u32) -> u32 {
 }
 
 #[inline]
-pub fn compress(state: &mut [u32; 4], input: &[u8; 64]) {
+pub fn compress_block(state: &mut [u32; 4], input: &[u8; 64]) {
     let mut a = state[0];
     let mut b = state[1];
     let mut c = state[2];
@@ -142,3 +156,10 @@ pub fn compress(state: &mut [u32; 4], input: &[u8; 64]) {
     state[2] = state[2].wrapping_add(c);
     state[3] = state[3].wrapping_add(d);
 }
+
+#[inline]
+pub fn compress(state: &mut [u32; 4], blocks: &[[u8; 64]]) {
+    for block in blocks {
+        compress_block(state, block)
+    }
+}
diff --git a/md5/src/consts.rs b/md5/src/consts.rs
deleted file mode 100644
index e9b94edb7..000000000
--- a/md5/src/consts.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-#![allow(clippy::unreadable_literal)]
-
-/// Round constants
-#[cfg(not(feature = "asm"))]
-pub static RC: [u32; 64] = [
-    // round 1
-    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
-    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
-    // round 2
-    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
-    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
-    // round 3
-    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
-    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
-    // round 4
-    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
-    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
-];
-
-/// Init state
-pub const S0: [u32; 4] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
diff --git a/md5/src/lib.rs b/md5/src/lib.rs
index 2bb9913d5..fadaf5192 100644
--- a/md5/src/lib.rs
+++ b/md5/src/lib.rs
@@ -25,102 +25,115 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/md-5/0.10.0"
 )]
-#![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 #[cfg(feature = "asm")]
-extern crate md5_asm as utils;
-
-#[cfg(feature = "std")]
-extern crate std;
+extern crate md5_asm as compress;
 
 #[cfg(not(feature = "asm"))]
-mod utils;
+mod compress;
 
 pub use digest::{self, Digest};
 
-use crate::utils::compress;
+use compress::compress;
+
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U16, U64},
+    HashMarker, Output,
+};
+/// Core MD5 hasher state.
+#[derive(Clone)]
+pub struct Md5Core {
+    block_len: u64,
+    state: [u32; 4],
+}
 
-use block_buffer::BlockBuffer;
-use digest::generic_array::typenum::{U16, U64};
-use digest::generic_array::GenericArray;
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
+impl HashMarker for Md5Core {}
 
-mod consts;
+impl BlockSizeUser for Md5Core {
+    type BlockSize = U64;
+}
 
-/// The MD5 hasher
-#[derive(Clone)]
-pub struct Md5 {
-    length_bytes: u64,
-    buffer: BlockBuffer<U64>,
-    state: [u32; 4],
+impl BufferKindUser for Md5Core {
+    type BufferKind = Eager;
 }
 
-impl Default for Md5 {
-    fn default() -> Self {
-        Md5 {
-            length_bytes: 0,
-            buffer: Default::default(),
-            state: consts::S0,
-        }
-    }
+impl OutputSizeUser for Md5Core {
+    type OutputSize = U16;
 }
 
-#[inline(always)]
-fn convert(d: &GenericArray<u8, U64>) -> &[u8; 64] {
-    #[allow(unsafe_code)]
-    unsafe {
-        &*(d.as_ptr() as *const [u8; 64])
+impl UpdateCore for Md5Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len = self.block_len.wrapping_add(blocks.len() as u64);
+        compress(&mut self.state, convert(blocks))
     }
 }
 
-impl Md5 {
+impl FixedOutputCore for Md5Core {
     #[inline]
-    fn finalize_inner(&mut self) {
-        let s = &mut self.state;
-        let l = (self.length_bytes << 3) as u64;
-        self.buffer.len64_padding_le(l, |d| compress(s, convert(d)));
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bit_len = self
+            .block_len
+            .wrapping_mul(Self::BlockSize::U64)
+            .wrapping_add(buffer.get_pos() as u64)
+            .wrapping_mul(8);
+        let mut s = self.state;
+        buffer.len64_padding_le(bit_len, |b| compress(&mut s, convert(from_ref(b))));
+        for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
+            chunk.copy_from_slice(&v.to_le_bytes());
+        }
     }
 }
 
-impl BlockInput for Md5 {
-    type BlockSize = U64;
+impl Default for Md5Core {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            block_len: 0,
+            state: [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476],
+        }
+    }
 }
 
-impl Update for Md5 {
+impl Reset for Md5Core {
     #[inline]
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Unlike Sha1 and Sha2, the length value in MD5 is defined as
-        // the length of the message mod 2^64 - ie: integer overflow is OK.
-        self.length_bytes = self.length_bytes.wrapping_add(input.len() as u64);
-        let s = &mut self.state;
-        self.buffer.input_block(input, |d| compress(s, convert(d)));
+    fn reset(&mut self) {
+        *self = Default::default();
     }
 }
 
-impl FixedOutputDirty for Md5 {
-    type OutputSize = U16;
-
-    #[inline]
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, U16>) {
-        self.finalize_inner();
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.state.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
+impl AlgorithmName for Md5Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md5")
     }
 }
 
-impl Reset for Md5 {
-    fn reset(&mut self) {
-        self.state = consts::S0;
-        self.length_bytes = 0;
-        self.buffer.reset();
+impl fmt::Debug for Md5Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Md5Core { ... }")
     }
 }
 
-opaque_debug::implement!(Md5);
-digest::impl_write!(Md5);
+/// MD5 hasher state.
+pub type Md5 = CoreWrapper<Md5Core>;
+
+const BLOCK_SIZE: usize = <Md5Core as BlockSizeUser>::BlockSize::USIZE;
+
+#[inline(always)]
+fn convert(blocks: &[Block<Md5Core>]) -> &[[u8; BLOCK_SIZE]] {
+    // SAFETY: GenericArray<u8, U64> and [u8; 64] have
+    // exactly the same memory layout
+    let p = blocks.as_ptr() as *const [u8; BLOCK_SIZE];
+    unsafe { core::slice::from_raw_parts(p, blocks.len()) }
+}
diff --git a/ripemd/CHANGELOG.md b/ripemd/CHANGELOG.md
new file mode 100644
index 000000000..24fbb48f7
--- /dev/null
+++ b/ripemd/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## 0.1.0 (2021-12-07)
+- Initial release of merged `ripemd160` and `ripemd320` crates updated
+to `digest` v0.10. ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
diff --git a/ripemd320/Cargo.toml b/ripemd/Cargo.toml
similarity index 56%
rename from ripemd320/Cargo.toml
rename to ripemd/Cargo.toml
index 6083fe0ef..9487f7fb9 100644
--- a/ripemd320/Cargo.toml
+++ b/ripemd/Cargo.toml
@@ -1,23 +1,21 @@
 [package]
-name = "ripemd320"
-version = "0.9.0"
-description = "RIPEMD-320 hash function"
+name = "ripemd"
+version = "0.1.0" # Also update html_root_url in lib.rs when bumping this
+description = "Pure Rust implementation of the RIPEMD hash functions"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
 readme = "README.md"
 edition = "2018"
-documentation = "https://docs.rs/ripemd320"
+documentation = "https://docs.rs/ripemd"
 repository = "https://github.com/RustCrypto/hashes"
-keywords = ["crypto", "ripemd320", "hash", "digest"]
+keywords = ["crypto", "ripemd", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/ripemd256/LICENSE-APACHE b/ripemd/LICENSE-APACHE
similarity index 100%
rename from ripemd256/LICENSE-APACHE
rename to ripemd/LICENSE-APACHE
diff --git a/ripemd256/LICENSE-MIT b/ripemd/LICENSE-MIT
similarity index 88%
rename from ripemd256/LICENSE-MIT
rename to ripemd/LICENSE-MIT
index a239acf63..efb0b5f8b 100644
--- a/ripemd256/LICENSE-MIT
+++ b/ripemd/LICENSE-MIT
@@ -1,6 +1,4 @@
-Copyright (c) 2006-2009 Graydon Hoare
-Copyright (c) 2009-2013 Mozilla Foundation
-Copyright (c) 2016 The RustCrypto Project Developers
+Copyright (c) 2021 RustCrypto Developers
 
 Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated
diff --git a/ripemd160/README.md b/ripemd/README.md
similarity index 93%
rename from ripemd160/README.md
rename to ripemd/README.md
index 29a18e4a0..64f72e493 100644
--- a/ripemd160/README.md
+++ b/ripemd/README.md
@@ -1,4 +1,4 @@
-# RustCrypto: RIPEMD160
+# RustCrypto: RIPEMD
 
 [![crate][crate-image]][crate-link]
 [![Docs][docs-image]][docs-link]
@@ -7,7 +7,7 @@
 [![Project Chat][chat-image]][chat-link]
 [![Build Status][build-image]][build-link]
 
-Pure Rust implementation of the [RIPEMD160 hash function][1].
+Pure Rust implementation of the [RIPEMD] hash functions.
 
 [Documentation][docs-link]
 
@@ -53,4 +53,4 @@ dual licensed as above, without any additional terms or conditions.
 
 [//]: # (general links)
 
-[1]: https://en.wikipedia.org/wiki/RIPEMD
+[RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD
diff --git a/ripemd/benches/mod.rs b/ripemd/benches/mod.rs
new file mode 100644
index 000000000..67eaa716f
--- /dev/null
+++ b/ripemd/benches/mod.rs
@@ -0,0 +1,30 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use ripemd::{Ripemd160, Ripemd256, Ripemd320};
+use test::Bencher;
+
+bench_update!(
+    Ripemd160::default();
+    ripemd160_10 10;
+    ripemd160_100 100;
+    ripemd160_1000 1000;
+    ripemd160_10000 10000;
+);
+
+bench_update!(
+    Ripemd256::default();
+    ripemd256_10 10;
+    ripemd256_100 100;
+    ripemd256_1000 1000;
+    ripemd256_10000 10000;
+);
+
+bench_update!(
+    Ripemd320::default();
+    ripemd320_10 10;
+    ripemd320_100 100;
+    ripemd320_1000 1000;
+    ripemd320_10000 10000;
+);
diff --git a/ripemd160/src/block.rs b/ripemd/src/c160.rs
similarity index 98%
rename from ripemd160/src/block.rs
rename to ripemd/src/c160.rs
index fccf1ab60..2163e848f 100644
--- a/ripemd160/src/block.rs
+++ b/ripemd/src/c160.rs
@@ -1,6 +1,5 @@
+use super::{Block, Ripemd160Core};
 use core::convert::TryInto;
-use digest::generic_array::typenum::U64;
-use digest::generic_array::GenericArray;
 
 pub const DIGEST_BUF_LEN: usize = 5;
 pub const WORK_BUF_LEN: usize = 16;
@@ -13,8 +12,6 @@ pub const H0: [u32; DIGEST_BUF_LEN] = [
     0xc3d2_e1f0,
 ];
 
-type Block = GenericArray<u8, U64>;
-
 macro_rules! round(
     ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr,
      $x:expr, $bits:expr, $add:expr, $round:expr) => ({
@@ -115,7 +112,7 @@ macro_rules! process_block(
     });
 );
 
-pub fn process_msg_block(h: &mut [u32; DIGEST_BUF_LEN], data: &Block) {
+pub fn compress(h: &mut [u32; DIGEST_BUF_LEN], data: &Block<Ripemd160Core>) {
     let mut w = [0u32; WORK_BUF_LEN];
     for (o, chunk) in w.iter_mut().zip(data.chunks_exact(4)) {
         *o = u32::from_le_bytes(chunk.try_into().unwrap());
diff --git a/ripemd256/src/block.rs b/ripemd/src/c256.rs
similarity index 98%
rename from ripemd256/src/block.rs
rename to ripemd/src/c256.rs
index de085350c..a7d6e51a9 100644
--- a/ripemd256/src/block.rs
+++ b/ripemd/src/c256.rs
@@ -1,6 +1,5 @@
+use super::{Block, Ripemd256Core};
 use core::{convert::TryInto, mem::swap};
-use digest::generic_array::typenum::U64;
-use digest::generic_array::GenericArray;
 
 pub const DIGEST_BUF_LEN: usize = 8;
 pub const HALF_DIGEST_BUF_LEN: usize = DIGEST_BUF_LEN / 2;
@@ -17,8 +16,6 @@ pub const H0: [u32; DIGEST_BUF_LEN] = [
     0x0123_4567,
 ];
 
-type Block = GenericArray<u8, U64>;
-
 macro_rules! round(
     ($a:expr, $b:expr, $c:expr, $d:expr,
      $x:expr, $bits:expr, $add:expr, $round:expr) => ({
@@ -122,7 +119,7 @@ macro_rules! process_block(
     });
 );
 
-pub fn process_msg_block(h: &mut [u32; DIGEST_BUF_LEN], data: &Block) {
+pub fn compress(h: &mut [u32; DIGEST_BUF_LEN], data: &Block<Ripemd256Core>) {
     let mut w = [0u32; WORK_BUF_LEN];
     for (o, chunk) in w.iter_mut().zip(data.chunks_exact(4)) {
         *o = u32::from_le_bytes(chunk.try_into().unwrap());
diff --git a/ripemd320/src/block.rs b/ripemd/src/c320.rs
similarity index 98%
rename from ripemd320/src/block.rs
rename to ripemd/src/c320.rs
index 4834ead90..89b174066 100644
--- a/ripemd320/src/block.rs
+++ b/ripemd/src/c320.rs
@@ -1,6 +1,5 @@
+use super::{Block, Ripemd320Core};
 use core::{convert::TryInto, mem::swap};
-use digest::generic_array::typenum::U64;
-use digest::generic_array::GenericArray;
 
 pub const HALF_DIGEST_BUF_LEN: usize = 5;
 pub const DIGEST_BUF_LEN: usize = 10;
@@ -19,8 +18,6 @@ pub const H0: [u32; DIGEST_BUF_LEN] = [
     0x3c2d_1e0f,
 ];
 
-type Block = GenericArray<u8, U64>;
-
 macro_rules! round(
     ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr,
      $x:expr, $bits:expr, $add:expr, $round:expr) => ({
@@ -145,7 +142,7 @@ macro_rules! process_block(
     });
 );
 
-pub fn process_msg_block(h: &mut [u32; DIGEST_BUF_LEN], data: &Block) {
+pub fn compress(h: &mut [u32; DIGEST_BUF_LEN], data: &Block<Ripemd320Core>) {
     let mut w = [0u32; WORK_BUF_LEN];
     for (o, chunk) in w.iter_mut().zip(data.chunks_exact(4)) {
         *o = u32::from_le_bytes(chunk.try_into().unwrap());
diff --git a/ripemd/src/lib.rs b/ripemd/src/lib.rs
new file mode 100644
index 000000000..18a0a3b5f
--- /dev/null
+++ b/ripemd/src/lib.rs
@@ -0,0 +1,159 @@
+//! An implementation of the [RIPEMD] cryptographic hash.
+//!
+//! This crate implements only the modified 1996 versions, not the original
+//! one from 1992.
+//!
+//! Note that RIPEMD-256 provides only the same security as RIPEMD-128,
+//! and RIPEMD-320 provides only the same security as RIPEMD-160.
+//!
+//! # Usage
+//!
+//! ```rust
+//! use hex_literal::hex;
+//! use ripemd::{Ripemd160, Ripemd320, Digest};
+//!
+//! // create a RIPEMD-160 hasher instance
+//! let mut hasher = Ripemd160::new();
+//!
+//! // process input message
+//! hasher.update(b"Hello world!");
+//!
+//! // acquire hash digest in the form of GenericArray,
+//! // which in this case is equivalent to [u8; 20]
+//! let result = hasher.finalize();
+//! assert_eq!(result[..], hex!("7f772647d88750add82d8e1a7a3e5c0902a346a3"));
+//!
+//! // same for RIPEMD-320
+//! let mut hasher = Ripemd320::new();
+//! hasher.update(b"Hello world!");
+//! let result = hasher.finalize();
+//! assert_eq!(&result[..], &hex!("
+//!     f1c1c231d301abcf2d7daae0269ff3e7bc68e623
+//!     ad723aa068d316b056d26b7d1bb6f0cc0f28336d
+//! ")[..]);
+//! ```
+//!
+//! Also see [RustCrypto/hashes] readme.
+//!
+//! [RIPEMD]: https://en.wikipedia.org/wiki/RIPEMD
+//! [RustCrypto/hashes]: https://github.com/RustCrypto/hashes
+
+#![no_std]
+#![doc(
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/ripemd/0.1.0"
+)]
+#![deny(unsafe_code)]
+#![warn(missing_docs, rust_2018_idioms)]
+
+pub use digest::{self, Digest};
+
+use core::fmt;
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U20, U32, U40, U64},
+    HashMarker, Output,
+};
+
+mod c160;
+mod c256;
+mod c320;
+
+macro_rules! impl_ripemd {
+    (
+        $name:ident, $wrapped_name:ident, $mod:ident,
+        $alg_width:expr, $doc_name:expr, $output_size:ty $(,)?
+    ) => {
+        #[doc = "Core block-level"]
+        #[doc = $doc_name]
+        #[doc = " hasher state."]
+        #[derive(Clone)]
+        pub struct $name {
+            h: [u32; $mod::DIGEST_BUF_LEN],
+            block_len: u64,
+        }
+
+        impl HashMarker for $name {}
+
+        impl BlockSizeUser for $name {
+            type BlockSize = U64;
+        }
+
+        impl BufferKindUser for $name {
+            type BufferKind = Eager;
+        }
+
+        impl OutputSizeUser for $name {
+            type OutputSize = $output_size;
+        }
+
+        impl UpdateCore for $name {
+            #[inline]
+            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+                // Assumes that `block_len` does not overflow
+                self.block_len += blocks.len() as u64;
+                for block in blocks {
+                    $mod::compress(&mut self.h, block);
+                }
+            }
+        }
+
+        impl FixedOutputCore for $name {
+            #[inline]
+            fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+                let bs = Self::BlockSize::U64;
+                let bit_len = 8 * (buffer.get_pos() as u64 + bs * self.block_len);
+                let mut h = self.h;
+                buffer.len64_padding_le(bit_len, |block| $mod::compress(&mut h, block));
+
+                for (chunk, v) in out.chunks_exact_mut(4).zip(h.iter()) {
+                    chunk.copy_from_slice(&v.to_le_bytes());
+                }
+            }
+        }
+
+        impl Default for $name {
+            #[inline]
+            fn default() -> Self {
+                Self {
+                    h: $mod::H0,
+                    block_len: 0,
+                }
+            }
+        }
+
+        impl Reset for $name {
+            #[inline]
+            fn reset(&mut self) {
+                *self = Default::default();
+            }
+        }
+
+        impl AlgorithmName for $name {
+            #[inline]
+            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!("Ripemd", $alg_width))
+            }
+        }
+
+        impl fmt::Debug for $name {
+            #[inline]
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!("Ripemd", $alg_width, "Core { ... }"))
+            }
+        }
+
+        #[doc = $doc_name]
+        #[doc = " hasher."]
+        pub type $wrapped_name = CoreWrapper<$name>;
+    };
+}
+
+impl_ripemd!(Ripemd160Core, Ripemd160, c160, "160", "RIPEMD-160", U20);
+impl_ripemd!(Ripemd256Core, Ripemd256, c256, "256", "RIPEMD-256", U32);
+impl_ripemd!(Ripemd320Core, Ripemd320, c320, "320", "RIPEMD-320", U40);
diff --git a/ripemd160/CHANGELOG.md b/ripemd160/CHANGELOG.md
deleted file mode 100644
index 22305f2b8..000000000
--- a/ripemd160/CHANGELOG.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## 0.9.1 (2020-06-28)
-### Changed
-- Update to `block-buffer` v0.9 ([#164])
-- Update to `opaque-debug` v0.3 ([#168])
-
-[#164]: https://github.com/RustCrypto/hashes/pull/164
-[#168]: https://github.com/RustCrypto/hashes/pull/168
-
-## 0.9.0 (2020-06-10)
-### Changed
-- Update to `digest` v0.9 release ([#155])
-- Use new `*Dirty` traits from the `digest` crate ([#153])
-- Bump `block-buffer` to v0.8 release ([#151])
-- Rename `*result*` to `finalize` ([#148])
-- Upgrade to Rust 2018 edition ([#129])
-
-[#155]: https://github.com/RustCrypto/hashes/pull/155
-[#153]: https://github.com/RustCrypto/hashes/pull/153
-[#151]: https://github.com/RustCrypto/hashes/pull/151
-[#148]: https://github.com/RustCrypto/hashes/pull/148
-[#129]: https://github.com/RustCrypto/hashes/pull/129
-
-## 0.8.0 (2018-10-02)
-
-## 0.7.0 (2017-11-15)
-
-## 0.6.0 (2017-06-12)
-
-## 0.5.2 (2017-06-04)
-
-## 0.5.1 (2017-05-02)
-
-## 0.5.0 (2017-04-06)
-
-## 0.4.1 (2017-01-20)
-
-## 0.4.0 (2016-12-25)
-
-## 0.3.0 (2016-11-17)
-
-## 0.2.0 (2016-10-14)
-
-## 0.1.0 (2016-10-06)
diff --git a/ripemd160/Cargo.toml b/ripemd160/Cargo.toml
deleted file mode 100644
index 9f1861621..000000000
--- a/ripemd160/Cargo.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[package]
-name = "ripemd160"
-version = "0.9.1"
-description = "RIPEMD-160 hash function"
-authors = ["RustCrypto Developers"]
-license = "MIT OR Apache-2.0"
-readme = "README.md"
-edition = "2018"
-documentation = "https://docs.rs/ripemd160"
-repository = "https://github.com/RustCrypto/hashes"
-keywords = ["crypto", "ripemd160", "hash", "digest"]
-categories = ["cryptography", "no-std"]
-
-[dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
-
-[dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
-hex-literal = "0.2"
-
-[features]
-default = ["std"]
-std = ["digest/std"]
diff --git a/ripemd160/benches/lib.rs b/ripemd160/benches/lib.rs
deleted file mode 100644
index c665c0793..000000000
--- a/ripemd160/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(ripemd160::Ripemd160);
diff --git a/ripemd160/examples/ripemd160sum.rs b/ripemd160/examples/ripemd160sum.rs
deleted file mode 100644
index 4f71a12af..000000000
--- a/ripemd160/examples/ripemd160sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use ripemd160::{Digest, Ripemd160};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Ripemd160, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Ripemd160, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/ripemd160/src/lib.rs b/ripemd160/src/lib.rs
deleted file mode 100644
index 544d4ae2e..000000000
--- a/ripemd160/src/lib.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-//! An implementation of the [RIPEMD-160][1] cryptographic hash.
-//!
-//! # Usage
-//!
-//! ```rust
-//! use hex_literal::hex;
-//! use ripemd160::{Ripemd160, Digest};
-//!
-//! // create a RIPEMD-160 hasher instance
-//! let mut hasher = Ripemd160::new();
-//!
-//! // process input message
-//! hasher.update(b"Hello world!");
-//!
-//! // acquire hash digest in the form of GenericArray,
-//! // which in this case is equivalent to [u8; 20]
-//! let result = hasher.finalize();
-//! assert_eq!(result[..], hex!("7f772647d88750add82d8e1a7a3e5c0902a346a3"));
-//! ```
-//!
-//! Also see [RustCrypto/hashes][2] readme.
-//!
-//! [1]: https://en.wikipedia.org/wiki/RIPEMD
-//! [2]: https://github.com/RustCrypto/hashes
-
-#![no_std]
-#![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
-)]
-#![deny(unsafe_code)]
-#![warn(missing_docs, rust_2018_idioms)]
-
-#[cfg(feature = "std")]
-extern crate std;
-
-mod block;
-
-pub use digest::{self, Digest};
-
-use crate::block::{process_msg_block, DIGEST_BUF_LEN, H0};
-use block_buffer::BlockBuffer;
-use digest::consts::{U20, U64};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-/// Structure representing the state of a Ripemd160 computation
-#[derive(Clone)]
-pub struct Ripemd160 {
-    h: [u32; DIGEST_BUF_LEN],
-    len: u64,
-    buffer: BlockBuffer<U64>,
-}
-
-impl Default for Ripemd160 {
-    fn default() -> Self {
-        Ripemd160 {
-            h: H0,
-            len: 0,
-            buffer: Default::default(),
-        }
-    }
-}
-
-impl BlockInput for Ripemd160 {
-    type BlockSize = U64;
-}
-
-impl Update for Ripemd160 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Assumes that input.len() can be converted to u64 without overflow
-        self.len += input.len() as u64;
-        let h = &mut self.h;
-        self.buffer.input_block(input, |b| process_msg_block(h, b));
-    }
-}
-
-impl FixedOutputDirty for Ripemd160 {
-    type OutputSize = U20;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let h = &mut self.h;
-        let l = self.len << 3;
-        self.buffer.len64_padding_le(l, |b| process_msg_block(h, b));
-
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.h.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Ripemd160 {
-    fn reset(&mut self) {
-        self.buffer.reset();
-        self.len = 0;
-        self.h = H0;
-    }
-}
-
-opaque_debug::implement!(Ripemd160);
-digest::impl_write!(Ripemd160);
diff --git a/ripemd256/.gitignore b/ripemd256/.gitignore
deleted file mode 100644
index 96ef6c0b9..000000000
--- a/ripemd256/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/target
-Cargo.lock
diff --git a/ripemd256/Cargo.toml b/ripemd256/Cargo.toml
deleted file mode 100644
index 67fe29899..000000000
--- a/ripemd256/Cargo.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[package]
-name = "ripemd256"
-version = "0.1.0"
-authors = ["RustCrypto Developers"]
-license = "MIT OR Apache-2.0"
-edition = "2018"
-readme = "README.md"
-keywords = ["crypto", "ripemd256", "hash", "digest"]
-categories = ["cryptography", "no-std"]
-description = "RIPEMD-256 hash function"
-repository = "https://github.com/RustCrypto/hashes"
-
-
-[dependencies]
-block-buffer = "0.9"
-digest = "0.9"
-opaque-debug = "0.3"
-
-[dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
-hex-literal = "0.2"
-
-[features]
-default = ["std"]
-std = ["digest/std"]
diff --git a/ripemd256/README.md b/ripemd256/README.md
deleted file mode 100644
index b0bab35e7..000000000
--- a/ripemd256/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# RustCrypto: RIPEMD256
-Pure Rust implementation of the [RIPEMD256 hash function][1].
-
-## Minimum Supported Rust Version
-
-Rust **1.41** or higher.
-
-Minimum supported Rust version can be changed in the future, but it will be
-done with a minor version bump.
-
-## SemVer Policy
-
-- All on-by-default features of this library are covered by SemVer
-- MSRV is considered exempt from SemVer as noted above
-
-## License
-
-Licensed under either of:
-
- * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
- * [MIT license](http://opensource.org/licenses/MIT)
-
-at your option.
-
-### Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
-dual licensed as above, without any additional terms or conditions
-
-[1]: https://en.wikipedia.org/wiki/RIPEMD
diff --git a/ripemd256/examples/ripemd256sum.rs b/ripemd256/examples/ripemd256sum.rs
deleted file mode 100644
index 6a6ad1a5c..000000000
--- a/ripemd256/examples/ripemd256sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use ripemd256::{Digest, Ripemd256};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Ripemd256, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Ripemd256, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/ripemd256/src/lib.rs b/ripemd256/src/lib.rs
deleted file mode 100644
index 70b6344a7..000000000
--- a/ripemd256/src/lib.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-//! An implementation of the [RIPEMD-256][1] cryptographic hash.
-//!
-//! # Usage
-//!
-//! ```rust
-//! use hex_literal::hex;
-//! use ripemd256::{Ripemd256, Digest};
-//!
-//! // create a RIPEMD-256 hasher instance
-//! let mut hasher = Ripemd256::new();
-//!
-//! // process input message
-//! hasher.update(b"Hello world!");
-//!
-//! // acquire hash digest in the form of GenericArray,
-//! // which in this case is equivalent to [u8; 32]
-//! let expected = hex!("2700f1122c7bd5df165b0615efbbbc54f551aef2401738811a5aea19ccb9233a");
-//! let result = hasher.finalize();
-//! assert_eq!(&result[..], &expected[..]);
-//! ```
-//!
-//! [1]: https://en.wikipedia.org/wiki/RIPEMD
-
-#![no_std]
-#![deny(unsafe_code)]
-#![warn(missing_docs, rust_2018_idioms)]
-
-#[cfg(feature = "std")]
-extern crate std;
-
-mod block;
-
-pub use digest::{self, Digest};
-
-use block::{process_msg_block, DIGEST_BUF_LEN, H0};
-use block_buffer::BlockBuffer;
-use digest::{
-    consts::{U32, U64},
-    BlockInput, FixedOutputDirty, Reset, Update,
-};
-
-/// Structure representing the state of a Ripemd256 computation
-#[derive(Clone)]
-pub struct Ripemd256 {
-    h: [u32; DIGEST_BUF_LEN],
-    len: u64,
-    buffer: BlockBuffer<U64>,
-}
-
-impl Default for Ripemd256 {
-    fn default() -> Self {
-        Ripemd256 {
-            h: H0,
-            len: 0,
-            buffer: Default::default(),
-        }
-    }
-}
-
-impl BlockInput for Ripemd256 {
-    type BlockSize = U64;
-}
-
-impl Update for Ripemd256 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Assumes that input.len() can be converted to u64 without overflow
-        self.len += input.len() as u64;
-        let h = &mut self.h;
-        self.buffer.input_block(input, |b| process_msg_block(h, b));
-    }
-}
-
-impl FixedOutputDirty for Ripemd256 {
-    type OutputSize = U32;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let h = &mut self.h;
-        let l = self.len << 3;
-        self.buffer.len64_padding_le(l, |b| process_msg_block(h, b));
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.h.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Ripemd256 {
-    fn reset(&mut self) {
-        self.buffer.reset();
-        self.len = 0;
-        self.h = H0;
-    }
-}
-
-opaque_debug::implement!(Ripemd256);
-digest::impl_write!(Ripemd256);
diff --git a/ripemd320/CHANGELOG.md b/ripemd320/CHANGELOG.md
deleted file mode 100644
index 58dfc426a..000000000
--- a/ripemd320/CHANGELOG.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## 0.9.0 (2020-06-12)
-### Changed
-- Bump `opaque-debug` to v0.3.0 ([#168])
-- Bump `digest` to v0.9 release; MSRV 1.41 ([#155])
-- Use new `*Dirty` traits from the `digest` crate ([#153])
-- Bump `block-buffer` to v0.8 release ([#151])
-- Rename `*result*` to `finalize` ([#148])
-- Upgrade to Rust 2018 edition ([#130])
-
-[#168]: https://github.com/RustCrypto/hashes/pull/168
-[#155]: https://github.com/RustCrypto/hashes/pull/155
-[#153]: https://github.com/RustCrypto/hashes/pull/153
-[#151]: https://github.com/RustCrypto/hashes/pull/151
-[#148]: https://github.com/RustCrypto/hashes/pull/148
-[#130]: https://github.com/RustCrypto/hashes/pull/130
-
-## 0.8.0 (2019-02-11)
-- Initial release
diff --git a/ripemd320/LICENSE-APACHE b/ripemd320/LICENSE-APACHE
deleted file mode 100644
index 78173fa2e..000000000
--- a/ripemd320/LICENSE-APACHE
+++ /dev/null
@@ -1,201 +0,0 @@
-                              Apache License
-                        Version 2.0, January 2004
-                     http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-   "License" shall mean the terms and conditions for use, reproduction,
-   and distribution as defined by Sections 1 through 9 of this document.
-
-   "Licensor" shall mean the copyright owner or entity authorized by
-   the copyright owner that is granting the License.
-
-   "Legal Entity" shall mean the union of the acting entity and all
-   other entities that control, are controlled by, or are under common
-   control with that entity. For the purposes of this definition,
-   "control" means (i) the power, direct or indirect, to cause the
-   direction or management of such entity, whether by contract or
-   otherwise, or (ii) ownership of fifty percent (50%) or more of the
-   outstanding shares, or (iii) beneficial ownership of such entity.
-
-   "You" (or "Your") shall mean an individual or Legal Entity
-   exercising permissions granted by this License.
-
-   "Source" form shall mean the preferred form for making modifications,
-   including but not limited to software source code, documentation
-   source, and configuration files.
-
-   "Object" form shall mean any form resulting from mechanical
-   transformation or translation of a Source form, including but
-   not limited to compiled object code, generated documentation,
-   and conversions to other media types.
-
-   "Work" shall mean the work of authorship, whether in Source or
-   Object form, made available under the License, as indicated by a
-   copyright notice that is included in or attached to the work
-   (an example is provided in the Appendix below).
-
-   "Derivative Works" shall mean any work, whether in Source or Object
-   form, that is based on (or derived from) the Work and for which the
-   editorial revisions, annotations, elaborations, or other modifications
-   represent, as a whole, an original work of authorship. For the purposes
-   of this License, Derivative Works shall not include works that remain
-   separable from, or merely link (or bind by name) to the interfaces of,
-   the Work and Derivative Works thereof.
-
-   "Contribution" shall mean any work of authorship, including
-   the original version of the Work and any modifications or additions
-   to that Work or Derivative Works thereof, that is intentionally
-   submitted to Licensor for inclusion in the Work by the copyright owner
-   or by an individual or Legal Entity authorized to submit on behalf of
-   the copyright owner. For the purposes of this definition, "submitted"
-   means any form of electronic, verbal, or written communication sent
-   to the Licensor or its representatives, including but not limited to
-   communication on electronic mailing lists, source code control systems,
-   and issue tracking systems that are managed by, or on behalf of, the
-   Licensor for the purpose of discussing and improving the Work, but
-   excluding communication that is conspicuously marked or otherwise
-   designated in writing by the copyright owner as "Not a Contribution."
-
-   "Contributor" shall mean Licensor and any individual or Legal Entity
-   on behalf of whom a Contribution has been received by Licensor and
-   subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   copyright license to reproduce, prepare Derivative Works of,
-   publicly display, publicly perform, sublicense, and distribute the
-   Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
-   this License, each Contributor hereby grants to You a perpetual,
-   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-   (except as stated in this section) patent license to make, have made,
-   use, offer to sell, sell, import, and otherwise transfer the Work,
-   where such license applies only to those patent claims licensable
-   by such Contributor that are necessarily infringed by their
-   Contribution(s) alone or by combination of their Contribution(s)
-   with the Work to which such Contribution(s) was submitted. If You
-   institute patent litigation against any entity (including a
-   cross-claim or counterclaim in a lawsuit) alleging that the Work
-   or a Contribution incorporated within the Work constitutes direct
-   or contributory patent infringement, then any patent licenses
-   granted to You under this License for that Work shall terminate
-   as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
-   Work or Derivative Works thereof in any medium, with or without
-   modifications, and in Source or Object form, provided that You
-   meet the following conditions:
-
-   (a) You must give any other recipients of the Work or
-       Derivative Works a copy of this License; and
-
-   (b) You must cause any modified files to carry prominent notices
-       stating that You changed the files; and
-
-   (c) You must retain, in the Source form of any Derivative Works
-       that You distribute, all copyright, patent, trademark, and
-       attribution notices from the Source form of the Work,
-       excluding those notices that do not pertain to any part of
-       the Derivative Works; and
-
-   (d) If the Work includes a "NOTICE" text file as part of its
-       distribution, then any Derivative Works that You distribute must
-       include a readable copy of the attribution notices contained
-       within such NOTICE file, excluding those notices that do not
-       pertain to any part of the Derivative Works, in at least one
-       of the following places: within a NOTICE text file distributed
-       as part of the Derivative Works; within the Source form or
-       documentation, if provided along with the Derivative Works; or,
-       within a display generated by the Derivative Works, if and
-       wherever such third-party notices normally appear. The contents
-       of the NOTICE file are for informational purposes only and
-       do not modify the License. You may add Your own attribution
-       notices within Derivative Works that You distribute, alongside
-       or as an addendum to the NOTICE text from the Work, provided
-       that such additional attribution notices cannot be construed
-       as modifying the License.
-
-   You may add Your own copyright statement to Your modifications and
-   may provide additional or different license terms and conditions
-   for use, reproduction, or distribution of Your modifications, or
-   for any such Derivative Works as a whole, provided Your use,
-   reproduction, and distribution of the Work otherwise complies with
-   the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
-   any Contribution intentionally submitted for inclusion in the Work
-   by You to the Licensor shall be under the terms and conditions of
-   this License, without any additional terms or conditions.
-   Notwithstanding the above, nothing herein shall supersede or modify
-   the terms of any separate license agreement you may have executed
-   with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
-   names, trademarks, service marks, or product names of the Licensor,
-   except as required for reasonable and customary use in describing the
-   origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
-   agreed to in writing, Licensor provides the Work (and each
-   Contributor provides its Contributions) on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-   implied, including, without limitation, any warranties or conditions
-   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-   PARTICULAR PURPOSE. You are solely responsible for determining the
-   appropriateness of using or redistributing the Work and assume any
-   risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
-   whether in tort (including negligence), contract, or otherwise,
-   unless required by applicable law (such as deliberate and grossly
-   negligent acts) or agreed to in writing, shall any Contributor be
-   liable to You for damages, including any direct, indirect, special,
-   incidental, or consequential damages of any character arising as a
-   result of this License or out of the use or inability to use the
-   Work (including but not limited to damages for loss of goodwill,
-   work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses), even if such Contributor
-   has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
-   the Work or Derivative Works thereof, You may choose to offer,
-   and charge a fee for, acceptance of support, warranty, indemnity,
-   or other liability obligations and/or rights consistent with this
-   License. However, in accepting such obligations, You may act only
-   on Your own behalf and on Your sole responsibility, not on behalf
-   of any other Contributor, and only if You agree to indemnify,
-   defend, and hold each Contributor harmless for any liability
-   incurred by, or claims asserted against, such Contributor by reason
-   of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/ripemd320/LICENSE-MIT b/ripemd320/LICENSE-MIT
deleted file mode 100644
index a239acf63..000000000
--- a/ripemd320/LICENSE-MIT
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2006-2009 Graydon Hoare
-Copyright (c) 2009-2013 Mozilla Foundation
-Copyright (c) 2016 The RustCrypto Project Developers
-
-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.
diff --git a/ripemd320/README.md b/ripemd320/README.md
deleted file mode 100644
index ba34c4c46..000000000
--- a/ripemd320/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# RustCrypto: RIPEMD320
-
-[![crate][crate-image]][crate-link]
-[![Docs][docs-image]][docs-link]
-![Apache2/MIT licensed][license-image]
-![Rust Version][rustc-image]
-[![Project Chat][chat-image]][chat-link]
-[![Build Status][build-image]][build-link]
-
-Pure Rust implementation of the [RIPEMD320 hash function][1].
-
-[Documentation][docs-link]
-
-## Minimum Supported Rust Version
-
-Rust **1.41** or higher.
-
-Minimum supported Rust version can be changed in the future, but it will be
-done with a minor version bump.
-
-## SemVer Policy
-
-- All on-by-default features of this library are covered by SemVer
-- MSRV is considered exempt from SemVer as noted above
-
-## License
-
-Licensed under either of:
-
- * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
- * [MIT license](http://opensource.org/licenses/MIT)
-
-at your option.
-
-### Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
-dual licensed as above, without any additional terms or conditions.
-
-[//]: # (badges)
-
-[crate-image]: https://img.shields.io/crates/v/ripemd320.svg
-[crate-link]: https://crates.io/crates/ripemd320
-[docs-image]: https://docs.rs/ripemd320/badge.svg
-[docs-link]: https://docs.rs/ripemd320/
-[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
-[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg
-[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
-[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
-[build-image]: https://github.com/RustCrypto/hashes/workflows/ripemd320/badge.svg?branch=master
-[build-link]: https://github.com/RustCrypto/hashes/actions?query=workflow%3Aripemd320
-
-[//]: # (general links)
-
-[1]: https://en.wikipedia.org/wiki/RIPEMD
diff --git a/ripemd320/benches/lib.rs b/ripemd320/benches/lib.rs
deleted file mode 100644
index c8e49596b..000000000
--- a/ripemd320/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(ripemd320::Ripemd320);
diff --git a/ripemd320/examples/ripemd320sum.rs b/ripemd320/examples/ripemd320sum.rs
deleted file mode 100644
index 0b1f49fa3..000000000
--- a/ripemd320/examples/ripemd320sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use ripemd320::{Digest, Ripemd320};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Ripemd320, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Ripemd320, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/ripemd320/src/lib.rs b/ripemd320/src/lib.rs
deleted file mode 100644
index ba6f32ce0..000000000
--- a/ripemd320/src/lib.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-//! An implementation of the [RIPEMD-320][1] cryptographic hash.
-//!
-//! # Usage
-//!
-//! ```rust
-//! use hex_literal::hex;
-//! use ripemd320::{Ripemd320, Digest};
-//!
-//! // create a RIPEMD-320 hasher instance
-//! let mut hasher = Ripemd320::new();
-//!
-//! // process input message
-//! hasher.update(b"Hello world!");
-//!
-//! // acquire hash digest in the form of GenericArray,
-//! // which in this case is equivalent to [u8; 40]
-//! let expected = hex!("f1c1c231d301abcf2d7daae0269ff3e7bc68e623ad723aa068d316b056d26b7d1bb6f0cc0f28336d");
-//! let result = hasher.finalize();
-//! assert_eq!(&result[..], &expected[..]);
-//! ```
-//!
-//! Also see [RustCrypto/hashes][2] readme.
-//!
-//! [1]: https://en.wikipedia.org/wiki/RIPEMD
-//! [2]: https://github.com/RustCrypto/hashes
-
-#![no_std]
-#![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
-)]
-#![deny(unsafe_code)]
-#![warn(missing_docs, rust_2018_idioms)]
-
-#[cfg(feature = "std")]
-extern crate std;
-
-mod block;
-
-pub use digest::{self, Digest};
-
-use crate::block::{process_msg_block, DIGEST_BUF_LEN, H0};
-
-use block_buffer::BlockBuffer;
-use digest::consts::{U40, U64};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-/// Structure representing the state of a ripemd320 computation
-#[derive(Clone)]
-pub struct Ripemd320 {
-    h: [u32; DIGEST_BUF_LEN],
-    len: u64,
-    buffer: BlockBuffer<U64>,
-}
-
-impl Default for Ripemd320 {
-    fn default() -> Self {
-        Ripemd320 {
-            h: H0,
-            len: 0,
-            buffer: Default::default(),
-        }
-    }
-}
-
-impl BlockInput for Ripemd320 {
-    type BlockSize = U64;
-}
-
-impl Update for Ripemd320 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Assumes that input.len() can be converted to u64 without overflow
-        self.len += input.len() as u64;
-        let h = &mut self.h;
-        self.buffer.input_block(input, |b| process_msg_block(h, b));
-    }
-}
-
-impl FixedOutputDirty for Ripemd320 {
-    type OutputSize = U40;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let h = &mut self.h;
-        let l = self.len << 3;
-        self.buffer.len64_padding_le(l, |b| process_msg_block(h, b));
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.h.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Ripemd320 {
-    fn reset(&mut self) {
-        self.buffer.reset();
-        self.len = 0;
-        self.h = H0;
-    }
-}
-
-opaque_debug::implement!(Ripemd320);
-digest::impl_write!(Ripemd320);
diff --git a/sha1/CHANGELOG.md b/sha1/CHANGELOG.md
index 17cd4b9e9..3e99c8436 100644
--- a/sha1/CHANGELOG.md
+++ b/sha1/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.8 (2021-08-27)
 ### Changed
 - Bump `cpufeatures` dependency to 0.2 ([#306])
diff --git a/sha1/Cargo.toml b/sha1/Cargo.toml
index cbf261eca..3c8c14624 100644
--- a/sha1/Cargo.toml
+++ b/sha1/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sha-1"
-version = "0.9.8"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "SHA-1 hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -15,9 +15,7 @@ categories = ["cryptography", "no-std"]
 name = "sha1"
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 cfg-if = "1.0"
 sha1-asm = { version = "0.5", optional = true }
 
@@ -25,7 +23,7 @@ sha1-asm = { version = "0.5", optional = true }
 cpufeatures = "0.2"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
@@ -35,9 +33,6 @@ asm = ["sha1-asm"]
 compress = [] # Expose compress function
 force-soft = [] # Force software implementation
 
-# DEPRECATED: use `asm` instead
-asm-aarch64 = ["asm"]
-
 [package.metadata.docs.rs]
 all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
diff --git a/sha1/benches/lib.rs b/sha1/benches/lib.rs
deleted file mode 100644
index 130dc78b8..000000000
--- a/sha1/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sha1::Sha1);
diff --git a/sha1/benches/mod.rs b/sha1/benches/mod.rs
new file mode 100644
index 000000000..5c52e954c
--- /dev/null
+++ b/sha1/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use sha1::Sha1;
+use test::Bencher;
+
+bench_update!(
+    Sha1::default();
+    sha1_10 10;
+    sha1_100 100;
+    sha1_1000 1000;
+    sha1_10000 10000;
+);
diff --git a/sha1/examples/sha1sum.rs b/sha1/examples/sha1sum.rs
deleted file mode 100644
index 78d0cbf90..000000000
--- a/sha1/examples/sha1sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sha1::{Digest, Sha1};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sha1, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sha1, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sha1/src/compress.rs b/sha1/src/compress.rs
index b45f87bc2..feba6fd05 100644
--- a/sha1/src/compress.rs
+++ b/sha1/src/compress.rs
@@ -1,5 +1,5 @@
-use digest::consts::U64;
-use digest::generic_array::GenericArray;
+use crate::{Block, BlockSizeUser, Sha1Core};
+use digest::generic_array::typenum::Unsigned;
 
 cfg_if::cfg_if! {
     if #[cfg(feature = "force-soft")] {
@@ -14,9 +14,7 @@ cfg_if::cfg_if! {
         mod soft;
         #[cfg(feature = "asm")]
         mod soft {
-            pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
-                sha1_asm::compress(state, blocks)
-            }
+            pub use sha1_asm::compress;
         }
         mod x86;
         use x86::compress as compress_inner;
@@ -26,12 +24,15 @@ cfg_if::cfg_if! {
     }
 }
 
+const BLOCK_SIZE: usize = <Sha1Core as BlockSizeUser>::BlockSize::USIZE;
+
 /// SHA-1 compression function
 #[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
-pub fn compress(state: &mut [u32; 5], blocks: &[GenericArray<u8, U64>]) {
+pub fn compress(state: &mut [u32; 5], blocks: &[Block<Sha1Core>]) {
     // SAFETY: GenericArray<u8, U64> and [u8; 64] have
     // exactly the same memory layout
     #[allow(unsafe_code)]
-    let blocks: &[[u8; 64]] = unsafe { &*(blocks as *const _ as *const [[u8; 64]]) };
+    let blocks: &[[u8; BLOCK_SIZE]] =
+        unsafe { &*(blocks as *const _ as *const [[u8; BLOCK_SIZE]]) };
     compress_inner(state, blocks);
 }
diff --git a/sha1/src/compress/soft.rs b/sha1/src/compress/soft.rs
index 19366fb13..f4777e7cc 100644
--- a/sha1/src/compress/soft.rs
+++ b/sha1/src/compress/soft.rs
@@ -1,7 +1,9 @@
 #![allow(clippy::many_single_char_names)]
-use crate::consts::{BLOCK_LEN, K0, K1, K2, K3};
+use super::BLOCK_SIZE;
 use core::convert::TryInto;
 
+const K: [u32; 4] = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6];
+
 #[inline(always)]
 fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
     [
@@ -47,16 +49,11 @@ fn sha1_first_half(abcd: [u32; 4], msg: [u32; 4]) -> [u32; 4] {
 }
 
 fn sha1_digest_round_x4(abcd: [u32; 4], work: [u32; 4], i: i8) -> [u32; 4] {
-    const K0V: [u32; 4] = [K0, K0, K0, K0];
-    const K1V: [u32; 4] = [K1, K1, K1, K1];
-    const K2V: [u32; 4] = [K2, K2, K2, K2];
-    const K3V: [u32; 4] = [K3, K3, K3, K3];
-
     match i {
-        0 => sha1rnds4c(abcd, add(work, K0V)),
-        1 => sha1rnds4p(abcd, add(work, K1V)),
-        2 => sha1rnds4m(abcd, add(work, K2V)),
-        3 => sha1rnds4p(abcd, add(work, K3V)),
+        0 => sha1rnds4c(abcd, add(work, [K[0]; 4])),
+        1 => sha1rnds4p(abcd, add(work, [K[1]; 4])),
+        2 => sha1rnds4m(abcd, add(work, [K[2]; 4])),
+        3 => sha1rnds4p(abcd, add(work, [K[3]; 4])),
         _ => unreachable!("unknown icosaround index"),
     }
 }
@@ -247,8 +244,8 @@ fn sha1_digest_block_u32(state: &mut [u32; 5], block: &[u32; 16]) {
     state[4] = state[4].wrapping_add(e);
 }
 
-pub fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
-    let mut block_u32 = [0u32; BLOCK_LEN];
+pub fn compress(state: &mut [u32; 5], blocks: &[[u8; BLOCK_SIZE]]) {
+    let mut block_u32 = [0u32; BLOCK_SIZE / 4];
     // since LLVM can't properly use aliasing yet it will make
     // unnecessary state stores without this copy
     let mut state_cpy = *state;
diff --git a/sha1/src/consts.rs b/sha1/src/consts.rs
deleted file mode 100644
index c227df3a9..000000000
--- a/sha1/src/consts.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![allow(clippy::unreadable_literal, dead_code)]
-
-pub const STATE_LEN: usize = 5;
-
-pub const BLOCK_LEN: usize = 16;
-
-pub const K0: u32 = 0x5A827999u32;
-pub const K1: u32 = 0x6ED9EBA1u32;
-pub const K2: u32 = 0x8F1BBCDCu32;
-pub const K3: u32 = 0xCA62C1D6u32;
-
-pub const H: [u32; STATE_LEN] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
diff --git a/sha1/src/lib.rs b/sha1/src/lib.rs
index e1c8ac4a0..134108e77 100644
--- a/sha1/src/lib.rs
+++ b/sha1/src/lib.rs
@@ -36,81 +36,105 @@
 #![no_std]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/sha-1/0.10.0"
 )]
-#![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
+pub use digest::{self, Digest};
+
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U20, U64},
+    HashMarker, Output,
+};
 
 mod compress;
-mod consts;
 
 #[cfg(feature = "compress")]
-pub use crate::compress::compress;
+pub use compress::compress;
 #[cfg(not(feature = "compress"))]
-use crate::compress::compress;
-use crate::consts::{H, STATE_LEN};
-use block_buffer::BlockBuffer;
-use digest::consts::{U20, U64};
-pub use digest::{self, Digest};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
+use compress::compress;
+
+const STATE_LEN: usize = 5;
 
-/// Structure representing the state of a SHA-1 computation
+/// Core SHA-1 hasher state.
 #[derive(Clone)]
-pub struct Sha1 {
+pub struct Sha1Core {
     h: [u32; STATE_LEN],
-    len: u64,
-    buffer: BlockBuffer<U64>,
+    block_len: u64,
 }
 
-impl Default for Sha1 {
-    fn default() -> Self {
-        Sha1 {
-            h: H,
-            len: 0u64,
-            buffer: Default::default(),
-        }
-    }
-}
+impl HashMarker for Sha1Core {}
 
-impl BlockInput for Sha1 {
+impl BlockSizeUser for Sha1Core {
     type BlockSize = U64;
 }
 
-impl Update for Sha1 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        // Assumes that `length_bits<<3` will not overflow
-        self.len += input.len() as u64;
-        let state = &mut self.h;
-        self.buffer.input_blocks(input, |d| compress(state, d));
-    }
+impl BufferKindUser for Sha1Core {
+    type BufferKind = Eager;
 }
 
-impl FixedOutputDirty for Sha1 {
+impl OutputSizeUser for Sha1Core {
     type OutputSize = U20;
+}
+
+impl UpdateCore for Sha1Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u64;
+        compress(&mut self.h, blocks);
+    }
+}
 
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let s = &mut self.h;
-        let l = self.len << 3;
-        self.buffer
-            .len64_padding_be(l, |d| compress(s, core::slice::from_ref(d)));
-        for (chunk, v) in out.chunks_exact_mut(4).zip(self.h.iter()) {
+impl FixedOutputCore for Sha1Core {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64;
+        let bit_len = 8 * (buffer.get_pos() as u64 + bs * self.block_len);
+
+        let mut h = self.h;
+        buffer.len64_padding_be(bit_len, |b| compress(&mut h, from_ref(b)));
+        for (chunk, v) in out.chunks_exact_mut(4).zip(h.iter()) {
             chunk.copy_from_slice(&v.to_be_bytes());
         }
     }
 }
 
-impl Reset for Sha1 {
+impl Default for Sha1Core {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            h: [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0],
+            block_len: 0,
+        }
+    }
+}
+
+impl Reset for Sha1Core {
+    #[inline]
     fn reset(&mut self) {
-        self.h = H;
-        self.len = 0;
-        self.buffer.reset();
+        *self = Default::default();
+    }
+}
+
+impl AlgorithmName for Sha1Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha1")
+    }
+}
+
+impl fmt::Debug for Sha1Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha1Core { ... }")
     }
 }
 
-opaque_debug::implement!(Sha1);
-digest::impl_write!(Sha1);
+/// SHA-1 hasher state.
+pub type Sha1 = CoreWrapper<Sha1Core>;
diff --git a/sha2/CHANGELOG.md b/sha2/CHANGELOG.md
index 433465d0b..b59a388bd 100644
--- a/sha2/CHANGELOG.md
+++ b/sha2/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+- Rename `Sha512Trunc224` and `Sha512Trunc256` to `Sha512_224` and `Sha512_256` respectively. ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.8 (2021-09-09)
 ### Fixed
 - Bug in the AVX2 backend ([#314])
diff --git a/sha2/Cargo.toml b/sha2/Cargo.toml
index cf17feb8e..5a9984032 100644
--- a/sha2/Cargo.toml
+++ b/sha2/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sha2"
-version = "0.9.8"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = """
 Pure Rust implementation of the SHA-2 hash function family
 including SHA-224, SHA-256, SHA-384, and SHA-512.
@@ -15,9 +15,7 @@ keywords = ["crypto", "sha2", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 cfg-if = "1.0"
 sha2-asm = { version = "0.6.1", optional = true }
 
@@ -25,7 +23,7 @@ sha2-asm = { version = "0.6.1", optional = true }
 cpufeatures = "0.2"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/sha2/benches/mod.rs b/sha2/benches/mod.rs
new file mode 100644
index 000000000..8c60cccb9
--- /dev/null
+++ b/sha2/benches/mod.rs
@@ -0,0 +1,22 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use sha2::{Sha256, Sha512};
+use test::Bencher;
+
+bench_update!(
+    Sha256::default();
+    sha256_10 10;
+    sha256_100 100;
+    sha256_1000 1000;
+    sha256_10000 10000;
+);
+
+bench_update!(
+    Sha512::default();
+    sha512_10 10;
+    sha512_100 100;
+    sha512_1000 1000;
+    sha512_10000 10000;
+);
diff --git a/sha2/benches/sha256.rs b/sha2/benches/sha256.rs
deleted file mode 100644
index 181a68c40..000000000
--- a/sha2/benches/sha256.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sha2::Sha256);
diff --git a/sha2/benches/sha512.rs b/sha2/benches/sha512.rs
deleted file mode 100644
index 88708b383..000000000
--- a/sha2/benches/sha512.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sha2::Sha512);
diff --git a/sha2/examples/sha256sum.rs b/sha2/examples/sha256sum.rs
deleted file mode 100644
index 31ee97929..000000000
--- a/sha2/examples/sha256sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sha2::{Digest, Sha256};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sha256, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sha256, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sha2/examples/sha512sum.rs b/sha2/examples/sha512sum.rs
deleted file mode 100644
index 9d4785acd..000000000
--- a/sha2/examples/sha512sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sha2::{Digest, Sha512};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sha512, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sha512, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sha2/src/consts.rs b/sha2/src/consts.rs
index f126dc616..8c0bbab50 100644
--- a/sha2/src/consts.rs
+++ b/sha2/src/consts.rs
@@ -3,6 +3,9 @@
 pub const STATE_LEN: usize = 8;
 pub const BLOCK_LEN: usize = 16;
 
+pub type State256 = [u32; STATE_LEN];
+pub type State512 = [u64; STATE_LEN];
+
 /// Constants necessary for SHA-256 family of digests.
 pub const K32: [u32; 64] = [
     0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
@@ -37,180 +40,68 @@ pub const K32X4: [[u32; 4]; 16] = [
 
 /// Constants necessary for SHA-512 family of digests.
 pub const K64: [u64; 80] = [
-    0x428a2f98d728ae22,
-    0x7137449123ef65cd,
-    0xb5c0fbcfec4d3b2f,
-    0xe9b5dba58189dbbc,
-    0x3956c25bf348b538,
-    0x59f111f1b605d019,
-    0x923f82a4af194f9b,
-    0xab1c5ed5da6d8118,
-    0xd807aa98a3030242,
-    0x12835b0145706fbe,
-    0x243185be4ee4b28c,
-    0x550c7dc3d5ffb4e2,
-    0x72be5d74f27b896f,
-    0x80deb1fe3b1696b1,
-    0x9bdc06a725c71235,
-    0xc19bf174cf692694,
-    0xe49b69c19ef14ad2,
-    0xefbe4786384f25e3,
-    0x0fc19dc68b8cd5b5,
-    0x240ca1cc77ac9c65,
-    0x2de92c6f592b0275,
-    0x4a7484aa6ea6e483,
-    0x5cb0a9dcbd41fbd4,
-    0x76f988da831153b5,
-    0x983e5152ee66dfab,
-    0xa831c66d2db43210,
-    0xb00327c898fb213f,
-    0xbf597fc7beef0ee4,
-    0xc6e00bf33da88fc2,
-    0xd5a79147930aa725,
-    0x06ca6351e003826f,
-    0x142929670a0e6e70,
-    0x27b70a8546d22ffc,
-    0x2e1b21385c26c926,
-    0x4d2c6dfc5ac42aed,
-    0x53380d139d95b3df,
-    0x650a73548baf63de,
-    0x766a0abb3c77b2a8,
-    0x81c2c92e47edaee6,
-    0x92722c851482353b,
-    0xa2bfe8a14cf10364,
-    0xa81a664bbc423001,
-    0xc24b8b70d0f89791,
-    0xc76c51a30654be30,
-    0xd192e819d6ef5218,
-    0xd69906245565a910,
-    0xf40e35855771202a,
-    0x106aa07032bbd1b8,
-    0x19a4c116b8d2d0c8,
-    0x1e376c085141ab53,
-    0x2748774cdf8eeb99,
-    0x34b0bcb5e19b48a8,
-    0x391c0cb3c5c95a63,
-    0x4ed8aa4ae3418acb,
-    0x5b9cca4f7763e373,
-    0x682e6ff3d6b2b8a3,
-    0x748f82ee5defb2fc,
-    0x78a5636f43172f60,
-    0x84c87814a1f0ab72,
-    0x8cc702081a6439ec,
-    0x90befffa23631e28,
-    0xa4506cebde82bde9,
-    0xbef9a3f7b2c67915,
-    0xc67178f2e372532b,
-    0xca273eceea26619c,
-    0xd186b8c721c0c207,
-    0xeada7dd6cde0eb1e,
-    0xf57d4f7fee6ed178,
-    0x06f067aa72176fba,
-    0x0a637dc5a2c898a6,
-    0x113f9804bef90dae,
-    0x1b710b35131c471b,
-    0x28db77f523047d84,
-    0x32caab7b40c72493,
-    0x3c9ebe0a15c9bebc,
-    0x431d67c49c100d4c,
-    0x4cc5d4becb3e42b6,
-    0x597f299cfc657e2a,
-    0x5fcb6fab3ad6faec,
-    0x6c44198c4a475817,
+    0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+    0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+    0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+    0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+    0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+    0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+    0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+    0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+    0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+    0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+    0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+    0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+    0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+    0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+    0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+    0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+    0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+    0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+    0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+    0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
 ];
 
 /// Constants necessary for SHA-512 family of digests.
 pub const K64X2: [[u64; 2]; 40] = [
-    [K64[1], K64[0]],
-    [K64[3], K64[2]],
-    [K64[5], K64[4]],
-    [K64[7], K64[6]],
-    [K64[9], K64[8]],
-    [K64[11], K64[10]],
-    [K64[13], K64[12]],
-    [K64[15], K64[14]],
-    [K64[17], K64[16]],
-    [K64[19], K64[18]],
-    [K64[21], K64[20]],
-    [K64[23], K64[22]],
-    [K64[25], K64[24]],
-    [K64[27], K64[26]],
-    [K64[29], K64[28]],
-    [K64[31], K64[30]],
-    [K64[33], K64[32]],
-    [K64[35], K64[34]],
-    [K64[37], K64[36]],
-    [K64[39], K64[38]],
-    [K64[41], K64[40]],
-    [K64[43], K64[42]],
-    [K64[45], K64[44]],
-    [K64[47], K64[46]],
-    [K64[49], K64[48]],
-    [K64[51], K64[50]],
-    [K64[53], K64[52]],
-    [K64[55], K64[54]],
-    [K64[57], K64[56]],
-    [K64[59], K64[58]],
-    [K64[61], K64[60]],
-    [K64[63], K64[62]],
-    [K64[65], K64[64]],
-    [K64[67], K64[66]],
-    [K64[69], K64[68]],
-    [K64[71], K64[70]],
-    [K64[73], K64[72]],
-    [K64[75], K64[74]],
-    [K64[77], K64[76]],
-    [K64[79], K64[78]],
+    [K64[1],  K64[0]],  [K64[3],  K64[2]],  [K64[5],  K64[4]],  [K64[7],  K64[6]],
+    [K64[9],  K64[8]],  [K64[11], K64[10]], [K64[13], K64[12]], [K64[15], K64[14]],
+    [K64[17], K64[16]], [K64[19], K64[18]], [K64[21], K64[20]], [K64[23], K64[22]],
+    [K64[25], K64[24]], [K64[27], K64[26]], [K64[29], K64[28]], [K64[31], K64[30]],
+    [K64[33], K64[32]], [K64[35], K64[34]], [K64[37], K64[36]], [K64[39], K64[38]],
+    [K64[41], K64[40]], [K64[43], K64[42]], [K64[45], K64[44]], [K64[47], K64[46]],
+    [K64[49], K64[48]], [K64[51], K64[50]], [K64[53], K64[52]], [K64[55], K64[54]],
+    [K64[57], K64[56]], [K64[59], K64[58]], [K64[61], K64[60]], [K64[63], K64[62]],
+    [K64[65], K64[64]], [K64[67], K64[66]], [K64[69], K64[68]], [K64[71], K64[70]],
+    [K64[73], K64[72]], [K64[75], K64[74]], [K64[77], K64[76]], [K64[79], K64[78]],
 ];
 
-pub static H224: [u32; STATE_LEN] = [
-    0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,
+pub const H256_224: State256 = [
+    0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
+    0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,
 ];
 
-pub static H256: [u32; STATE_LEN] = [
-    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
+pub const H256_256: State256 = [
+    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+    0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
 ];
 
-pub static H384: [u64; STATE_LEN] = [
-    0xcbbb9d5dc1059ed8,
-    0x629a292a367cd507,
-    0x9159015a3070dd17,
-    0x152fecd8f70e5939,
-    0x67332667ffc00b31,
-    0x8eb44a8768581511,
-    0xdb0c2e0d64f98fa7,
-    0x47b5481dbefa4fa4,
+pub const H512_224: State512 = [
+    0x8c3d37c819544da2, 0x73e1996689dcd4d6, 0x1dfab7ae32ff9c82, 0x679dd514582f9fcf,
+    0x0f6d2b697bd44da8, 0x77e36f7304c48942, 0x3f9d85a86a1d36c8, 0x1112e6ad91d692a1,
 ];
 
-pub static H512: [u64; STATE_LEN] = [
-    0x6a09e667f3bcc908,
-    0xbb67ae8584caa73b,
-    0x3c6ef372fe94f82b,
-    0xa54ff53a5f1d36f1,
-    0x510e527fade682d1,
-    0x9b05688c2b3e6c1f,
-    0x1f83d9abfb41bd6b,
-    0x5be0cd19137e2179,
+pub const H512_256: State512 = [
+    0x22312194fc2bf72c, 0x9f555fa3c84c64c2, 0x2393b86b6f53b151, 0x963877195940eabd,
+    0x96283ee2a88effe3, 0xbe5e1e2553863992, 0x2b0199fc2c85b8aa, 0x0eb72ddc81c52ca2,
 ];
 
-pub static H512_TRUNC_224: [u64; STATE_LEN] = [
-    0x8c3d37c819544da2,
-    0x73e1996689dcd4d6,
-    0x1dfab7ae32ff9c82,
-    0x679dd514582f9fcf,
-    0x0f6d2b697bd44da8,
-    0x77e36f7304c48942,
-    0x3f9d85a86a1d36c8,
-    0x1112e6ad91d692a1,
+pub const H512_384: State512 = [
+    0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
+    0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4,
 ];
 
-pub static H512_TRUNC_256: [u64; STATE_LEN] = [
-    0x22312194fc2bf72c,
-    0x9f555fa3c84c64c2,
-    0x2393b86b6f53b151,
-    0x963877195940eabd,
-    0x96283ee2a88effe3,
-    0xbe5e1e2553863992,
-    0x2b0199fc2c85b8aa,
-    0x0eb72ddc81c52ca2,
+pub const H512_512: State512 = [
+    0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+    0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
 ];
diff --git a/sha2/src/core_api.rs b/sha2/src/core_api.rs
new file mode 100644
index 000000000..8484cca53
--- /dev/null
+++ b/sha2/src/core_api.rs
@@ -0,0 +1,157 @@
+use crate::{consts, sha256::compress256, sha512::compress512};
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide,
+        UpdateCore, VariableOutputCore,
+    },
+    generic_array::typenum::{Unsigned, U128, U32, U64},
+    HashMarker, InvalidOutputSize, Output,
+};
+
+/// Core block-level SHA-256 hasher with variable output size.
+///
+/// Supports initialization only for 28 and 32 byte output sizes,
+/// i.e. 224 and 256 bits respectively.
+#[derive(Clone)]
+pub struct Sha256VarCore {
+    state: consts::State256,
+    block_len: u64,
+}
+
+impl HashMarker for Sha256VarCore {}
+
+impl BlockSizeUser for Sha256VarCore {
+    type BlockSize = U64;
+}
+
+impl BufferKindUser for Sha256VarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for Sha256VarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u64;
+        compress256(&mut self.state, blocks);
+    }
+}
+
+impl OutputSizeUser for Sha256VarCore {
+    type OutputSize = U32;
+}
+
+impl VariableOutputCore for Sha256VarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Left;
+
+    #[inline]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        let state = match output_size {
+            28 => consts::H256_224,
+            32 => consts::H256_256,
+            _ => return Err(InvalidOutputSize),
+        };
+        let block_len = 0;
+        Ok(Self { state, block_len })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64;
+        let bit_len = 8 * (buffer.get_pos() as u64 + bs * self.block_len);
+        buffer.len64_padding_be(bit_len, |b| compress256(&mut self.state, from_ref(b)));
+
+        for (chunk, v) in out.chunks_exact_mut(4).zip(self.state.iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
+        }
+    }
+}
+
+impl AlgorithmName for Sha256VarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha256")
+    }
+}
+
+impl fmt::Debug for Sha256VarCore {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha256VarCore { ... }")
+    }
+}
+
+/// Core block-level SHA-512 hasher with variable output size.
+///
+/// Supports initialization only for 28, 32, 48, and 64 byte output sizes,
+/// i.e. 224, 256, 384, and 512 bits respectively.
+#[derive(Clone)]
+pub struct Sha512VarCore {
+    state: consts::State512,
+    block_len: u128,
+}
+
+impl HashMarker for Sha512VarCore {}
+
+impl BlockSizeUser for Sha512VarCore {
+    type BlockSize = U128;
+}
+
+impl BufferKindUser for Sha512VarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for Sha512VarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u128;
+        compress512(&mut self.state, blocks);
+    }
+}
+
+impl OutputSizeUser for Sha512VarCore {
+    type OutputSize = U64;
+}
+
+impl VariableOutputCore for Sha512VarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Left;
+
+    #[inline]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        let state = match output_size {
+            28 => consts::H512_224,
+            32 => consts::H512_256,
+            48 => consts::H512_384,
+            64 => consts::H512_512,
+            _ => return Err(InvalidOutputSize),
+        };
+        let block_len = 0;
+        Ok(Self { state, block_len })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64 as u128;
+        let bit_len = 8 * (buffer.get_pos() as u128 + bs * self.block_len);
+        buffer.len128_padding_be(bit_len, |b| compress512(&mut self.state, from_ref(b)));
+
+        for (chunk, v) in out.chunks_exact_mut(8).zip(self.state.iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
+        }
+    }
+}
+
+impl AlgorithmName for Sha512VarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha512")
+    }
+}
+
+impl fmt::Debug for Sha512VarCore {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sha512VarCore { ... }")
+    }
+}
diff --git a/sha2/src/lib.rs b/sha2/src/lib.rs
index 1927067e1..651bc6065 100644
--- a/sha2/src/lib.rs
+++ b/sha2/src/lib.rs
@@ -1,21 +1,12 @@
 //! An implementation of the [SHA-2][1] cryptographic hash algorithms.
 //!
-//! There are 6 standard algorithms specified in the SHA-2 standard:
+//! There are 6 standard algorithms specified in the SHA-2 standard: [`Sha224`],
+//! [`Sha256`], [`Sha512_224`], [`Sha512_256`], [`Sha384`], and [`Sha512`].
 //!
-//! * `Sha224`, which is the 8 x 32-bit `Sha256` algorithm with the result truncated
-//! to 224 bits.
-//! * `Sha256`, which is the 8 x 32-bit `Sha256` algorithm.
-//! * `Sha384`, which is the 8 x 64-bit `Sha512` algorithm with the result truncated
-//! to 384 bits.
-//! * `Sha512`, which is the 8 x 64-bit `Sha512` algorithm.
-//! * `Sha512Trunc224`, which is the 8 x 64-bit `Sha512` algorithm with the result
-//! truncated to 224 bits.
-//! * `Sha512Trunc256`, which is the 8 x 64-bit `Sha512` algorithm with the result
-//! truncated to 256 bits.
-//!
-//! Algorithmically, there are only 2 core algorithms: `Sha256` and `Sha512`.
+//! Algorithmically, there are only 2 core algorithms: SHA-256 and SHA-512.
 //! All other algorithms are just applications of these with different initial
-//! hash values, and truncated to different digest bit lengths.
+//! hash values, and truncated to different digest bit lengths. The first two
+//! algorithms in the list are based on SHA-256, while the last three on SHA-512.
 //!
 //! # Usage
 //!
@@ -55,22 +46,41 @@
 #![no_std]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/sha2/0.10.0"
 )]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
+pub use digest::{self, Digest};
+
+use digest::{
+    consts::{U28, U32, U48, U64},
+    core_api::{CoreWrapper, CtVariableCoreWrapper},
+};
 
+#[rustfmt::skip]
 mod consts;
+mod core_api;
 mod sha256;
 mod sha512;
 
-pub use digest::{self, Digest};
 #[cfg(feature = "compress")]
 pub use sha256::compress256;
-pub use sha256::{Sha224, Sha256};
 #[cfg(feature = "compress")]
 pub use sha512::compress512;
-pub use sha512::{Sha384, Sha512, Sha512Trunc224, Sha512Trunc256};
+
+pub use core_api::{Sha256VarCore, Sha512VarCore};
+
+/// SHA-224 hasher.
+pub type Sha224 = CoreWrapper<CtVariableCoreWrapper<Sha256VarCore, U28>>;
+/// SHA-256 hasher.
+pub type Sha256 = CoreWrapper<CtVariableCoreWrapper<Sha256VarCore, U32>>;
+/// SHA-512/224 hasher.
+pub type Sha512_224 = CoreWrapper<CtVariableCoreWrapper<Sha512VarCore, U28>>;
+/// SHA-512/256 hasher.
+pub type Sha512_256 = CoreWrapper<CtVariableCoreWrapper<Sha512VarCore, U32>>;
+/// SHA-384 hasher.
+pub type Sha384 = CoreWrapper<CtVariableCoreWrapper<Sha512VarCore, U48>>;
+/// SHA-512 hasher.
+pub type Sha512 = CoreWrapper<CtVariableCoreWrapper<Sha512VarCore, U64>>;
diff --git a/sha2/src/sha256.rs b/sha2/src/sha256.rs
index 5e5909657..2f493713b 100644
--- a/sha2/src/sha256.rs
+++ b/sha2/src/sha256.rs
@@ -1,142 +1,4 @@
-//! SHA-256
-use crate::consts::{H224, H256, STATE_LEN};
-use block_buffer::BlockBuffer;
-use core::slice::from_ref;
-use digest::consts::{U28, U32, U64};
-use digest::generic_array::GenericArray;
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-type BlockSize = U64;
-
-/// Structure that keeps state of the Sha-256 operation and
-/// contains the logic necessary to perform the final calculations.
-#[derive(Clone)]
-struct Engine256 {
-    len: u64,
-    buffer: BlockBuffer<BlockSize>,
-    state: [u32; 8],
-}
-
-impl Engine256 {
-    fn new(h: &[u32; STATE_LEN]) -> Engine256 {
-        Engine256 {
-            len: 0,
-            buffer: Default::default(),
-            state: *h,
-        }
-    }
-
-    fn update(&mut self, input: &[u8]) {
-        // Assumes that input.len() can be converted to u64 without overflow
-        self.len += (input.len() as u64) << 3;
-        let s = &mut self.state;
-        self.buffer.input_blocks(input, |b| compress256(s, b));
-    }
-
-    fn finish(&mut self) {
-        let s = &mut self.state;
-        let l = self.len;
-        self.buffer
-            .len64_padding_be(l, |b| compress256(s, from_ref(b)));
-    }
-
-    fn reset(&mut self, h: &[u32; STATE_LEN]) {
-        self.len = 0;
-        self.buffer.reset();
-        self.state = *h;
-    }
-}
-
-/// The SHA-256 hash algorithm with the SHA-256 initial hash value.
-#[derive(Clone)]
-pub struct Sha256 {
-    engine: Engine256,
-}
-
-impl Default for Sha256 {
-    fn default() -> Self {
-        Sha256 {
-            engine: Engine256::new(&H256),
-        }
-    }
-}
-
-impl BlockInput for Sha256 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha256 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha256 {
-    type OutputSize = U32;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = self.engine.state;
-        for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sha256 {
-    fn reset(&mut self) {
-        self.engine.reset(&H256);
-    }
-}
-
-/// The SHA-256 hash algorithm with the SHA-224 initial hash value. The result
-/// is truncated to 224 bits.
-#[derive(Clone)]
-pub struct Sha224 {
-    engine: Engine256,
-}
-
-impl Default for Sha224 {
-    fn default() -> Self {
-        Sha224 {
-            engine: Engine256::new(&H224),
-        }
-    }
-}
-
-impl BlockInput for Sha224 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha224 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha224 {
-    type OutputSize = U28;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = &self.engine.state[..7];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sha224 {
-    fn reset(&mut self) {
-        self.engine.reset(&H224);
-    }
-}
-
-opaque_debug::implement!(Sha224);
-opaque_debug::implement!(Sha256);
-
-digest::impl_write!(Sha224);
-digest::impl_write!(Sha256);
+use digest::generic_array::{typenum::U64, GenericArray};
 
 cfg_if::cfg_if! {
     if #[cfg(feature = "force-soft")] {
@@ -147,9 +9,7 @@ cfg_if::cfg_if! {
         mod soft;
         #[cfg(feature = "asm")]
         mod soft {
-            pub(crate) fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
-                sha2_asm::compress256(state, blocks);
-            }
+            pub(crate) use sha2_asm::compress256 as compress;
         }
         mod x86;
         use x86::compress;
@@ -171,7 +31,7 @@ cfg_if::cfg_if! {
 pub fn compress256(state: &mut [u32; 8], blocks: &[GenericArray<u8, U64>]) {
     // SAFETY: GenericArray<u8, U64> and [u8; 64] have
     // exactly the same memory layout
-    #[allow(unsafe_code)]
-    let blocks: &[[u8; 64]] = unsafe { &*(blocks as *const _ as *const [[u8; 64]]) };
+    let p = blocks.as_ptr() as *const [u8; 64];
+    let blocks = unsafe { core::slice::from_raw_parts(p, blocks.len()) };
     compress(state, blocks)
 }
diff --git a/sha2/src/sha512.rs b/sha2/src/sha512.rs
index 22f141e43..05d169631 100644
--- a/sha2/src/sha512.rs
+++ b/sha2/src/sha512.rs
@@ -1,231 +1,4 @@
-//! SHA-512
-use crate::consts::{H384, H512, H512_TRUNC_224, H512_TRUNC_256, STATE_LEN};
-use block_buffer::BlockBuffer;
-use core::slice::from_ref;
-use digest::consts::{U128, U28, U32, U48, U64};
-use digest::generic_array::GenericArray;
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-type BlockSize = U128;
-
-/// Structure that keeps state of the Sha-512 operation and
-/// contains the logic necessary to perform the final calculations.
-#[derive(Clone)]
-struct Engine512 {
-    len: u128,
-    buffer: BlockBuffer<BlockSize>,
-    state: [u64; 8],
-}
-
-impl Engine512 {
-    fn new(h: &[u64; STATE_LEN]) -> Engine512 {
-        Engine512 {
-            len: 0,
-            buffer: Default::default(),
-            state: *h,
-        }
-    }
-
-    fn update(&mut self, input: &[u8]) {
-        self.len += (input.len() as u128) << 3;
-        let s = &mut self.state;
-        self.buffer.input_blocks(input, |b| compress512(s, b));
-    }
-
-    fn finish(&mut self) {
-        let s = &mut self.state;
-        self.buffer
-            .len128_padding_be(self.len, |d| compress512(s, from_ref(d)));
-    }
-
-    fn reset(&mut self, h: &[u64; STATE_LEN]) {
-        self.len = 0;
-        self.buffer.reset();
-        self.state = *h;
-    }
-}
-
-/// The SHA-512 hash algorithm with the SHA-512 initial hash value.
-#[derive(Clone)]
-pub struct Sha512 {
-    engine: Engine512,
-}
-
-impl Default for Sha512 {
-    fn default() -> Self {
-        Sha512 {
-            engine: Engine512::new(&H512),
-        }
-    }
-}
-
-impl BlockInput for Sha512 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha512 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha512 {
-    type OutputSize = U64;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = self.engine.state;
-        for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sha512 {
-    fn reset(&mut self) {
-        self.engine.reset(&H512);
-    }
-}
-
-/// The SHA-512 hash algorithm with the SHA-384 initial hash value. The result
-/// is truncated to 384 bits.
-#[derive(Clone)]
-pub struct Sha384 {
-    engine: Engine512,
-}
-
-impl Default for Sha384 {
-    fn default() -> Self {
-        Sha384 {
-            engine: Engine512::new(&H384),
-        }
-    }
-}
-
-impl BlockInput for Sha384 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha384 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha384 {
-    type OutputSize = U48;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = &self.engine.state[..6];
-        for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sha384 {
-    fn reset(&mut self) {
-        self.engine.reset(&H384);
-    }
-}
-
-/// The SHA-512 hash algorithm with the SHA-512/256 initial hash value. The
-/// result is truncated to 256 bits.
-#[derive(Clone)]
-pub struct Sha512Trunc256 {
-    engine: Engine512,
-}
-
-impl Default for Sha512Trunc256 {
-    fn default() -> Self {
-        Sha512Trunc256 {
-            engine: Engine512::new(&H512_TRUNC_256),
-        }
-    }
-}
-
-impl BlockInput for Sha512Trunc256 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha512Trunc256 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha512Trunc256 {
-    type OutputSize = U32;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = &self.engine.state[..4];
-        for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sha512Trunc256 {
-    fn reset(&mut self) {
-        self.engine.reset(&H512_TRUNC_256);
-    }
-}
-
-/// The SHA-512 hash algorithm with the SHA-512/224 initial hash value.
-/// The result is truncated to 224 bits.
-#[derive(Clone)]
-pub struct Sha512Trunc224 {
-    engine: Engine512,
-}
-
-impl Default for Sha512Trunc224 {
-    fn default() -> Self {
-        Sha512Trunc224 {
-            engine: Engine512::new(&H512_TRUNC_224),
-        }
-    }
-}
-
-impl BlockInput for Sha512Trunc224 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Sha512Trunc224 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.update(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Sha512Trunc224 {
-    type OutputSize = U28;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let s = &self.engine.state;
-        for (chunk, v) in out.chunks_exact_mut(8).zip(s[..3].iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-        out[24..28].copy_from_slice(&s[3].to_be_bytes()[..4]);
-    }
-}
-
-impl Reset for Sha512Trunc224 {
-    fn reset(&mut self) {
-        self.engine.reset(&H512_TRUNC_224);
-    }
-}
-
-opaque_debug::implement!(Sha384);
-opaque_debug::implement!(Sha512);
-opaque_debug::implement!(Sha512Trunc224);
-opaque_debug::implement!(Sha512Trunc256);
-
-digest::impl_write!(Sha384);
-digest::impl_write!(Sha512);
-digest::impl_write!(Sha512Trunc224);
-digest::impl_write!(Sha512Trunc256);
+use digest::generic_array::{typenum::U128, GenericArray};
 
 cfg_if::cfg_if! {
     if #[cfg(feature = "force-soft")] {
@@ -254,9 +27,9 @@ cfg_if::cfg_if! {
 /// functionality of SHA-512.
 #[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
 pub fn compress512(state: &mut [u64; 8], blocks: &[GenericArray<u8, U128>]) {
-    // SAFETY: GenericArray<u8, U128> and [u8; 128] have
+    // SAFETY: GenericArray<u8, U64> and [u8; 64] have
     // exactly the same memory layout
-    #[allow(unsafe_code)]
-    let blocks: &[[u8; 128]] = unsafe { &*(blocks as *const _ as *const [[u8; 128]]) };
+    let p = blocks.as_ptr() as *const [u8; 128];
+    let blocks = unsafe { core::slice::from_raw_parts(p, blocks.len()) };
     compress(state, blocks)
 }
diff --git a/sha3/CHANGELOG.md b/sha3/CHANGELOG.md
index 6d2b6b7bc..c94b5e0c8 100644
--- a/sha3/CHANGELOG.md
+++ b/sha3/CHANGELOG.md
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
 
 ## 0.9.1 (2020-06-28)
 ### Changed
diff --git a/sha3/Cargo.toml b/sha3/Cargo.toml
index 57824552d..faae04c30 100644
--- a/sha3/Cargo.toml
+++ b/sha3/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sha3"
-version = "0.9.1"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "SHA-3 (Keccak) hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,13 +12,11 @@ keywords = ["crypto", "sha3", "keccak", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
 keccak = "0.1"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/sha3/benches/mod.rs b/sha3/benches/mod.rs
new file mode 100644
index 000000000..db2940516
--- /dev/null
+++ b/sha3/benches/mod.rs
@@ -0,0 +1,54 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
+use test::Bencher;
+
+bench_update!(
+    Sha3_224::default();
+    sha3_224_10 10;
+    sha3_224_100 100;
+    sha3_224_1000 1000;
+    sha3_224_10000 10000;
+);
+
+bench_update!(
+    Sha3_256::default();
+    sha3_256_10 10;
+    sha3_265_100 100;
+    sha3_256_1000 1000;
+    sha3_256_10000 10000;
+);
+
+bench_update!(
+    Sha3_384::default();
+    sha3_384_10 10;
+    sha3_384_100 100;
+    sha3_384_1000 1000;
+    sha3_384_10000 10000;
+);
+
+bench_update!(
+    Sha3_512::default();
+    sha3_512_10 10;
+    sha3_512_100 100;
+    sha3_512_1000 1000;
+    sha3_512_10000 10000;
+);
+
+bench_update!(
+    Shake128::default();
+    shake128_10 10;
+    shake128_100 100;
+    shake128_1000 1000;
+    shake128_10000 10000;
+);
+
+bench_update!(
+    Shake256::default();
+    shake256_10 10;
+    shake256_100 100;
+    shake256_1000 1000;
+    shake256_10000 10000;
+);
diff --git a/sha3/benches/sha3_256.rs b/sha3/benches/sha3_256.rs
deleted file mode 100644
index 56edb1498..000000000
--- a/sha3/benches/sha3_256.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sha3::Sha3_256);
diff --git a/sha3/benches/sha3_512.rs b/sha3/benches/sha3_512.rs
deleted file mode 100644
index dd7ad23ae..000000000
--- a/sha3/benches/sha3_512.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sha3::Sha3_512);
diff --git a/sha3/examples/sha3_256sum.rs b/sha3/examples/sha3_256sum.rs
deleted file mode 100644
index bc9da2e08..000000000
--- a/sha3/examples/sha3_256sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sha3::{Digest, Sha3_256};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sha3_256, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sha3_256, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sha3/examples/sha3_512sum.rs b/sha3/examples/sha3_512sum.rs
deleted file mode 100644
index 1bfbe3b89..000000000
--- a/sha3/examples/sha3_512sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sha3::{Digest, Sha3_512};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sha3_512, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sha3_512, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sha3/src/lib.rs b/sha3/src/lib.rs
index 831762955..b6dcee0b2 100644
--- a/sha3/src/lib.rs
+++ b/sha3/src/lib.rs
@@ -58,106 +58,72 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/sha3/0.10.0"
 )]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
-
 pub use digest::{self, Digest};
 
-use block_buffer::BlockBuffer;
-use digest::consts::{U104, U136, U144, U168, U200, U28, U32, U48, U64, U72};
-use digest::generic_array::typenum::Unsigned;
-use digest::{BlockInput, ExtendableOutputDirty, FixedOutputDirty, Reset, Update};
+use core::fmt;
+use digest::{
+    block_buffer::Eager,
+    consts::{U104, U136, U144, U168, U200, U28, U32, U48, U64, U72},
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper,
+        ExtendableOutputCore, FixedOutputCore, OutputSizeUser, Reset, UpdateCore, XofReaderCore,
+        XofReaderCoreWrapper,
+    },
+    HashMarker, Output,
+};
 
-mod paddings;
 #[macro_use]
 mod macros;
-mod reader;
 mod state;
 
-pub use crate::reader::Sha3XofReader;
 use crate::state::Sha3State;
 
-sha3_impl!(
-    Keccak224,
-    U28,
-    U144,
-    paddings::Keccak,
-    "Keccak-224 hash function."
-);
-sha3_impl!(
-    Keccak256,
-    U32,
-    U136,
-    paddings::Keccak,
-    "Keccak-256 hash function."
-);
-sha3_impl!(
-    Keccak384,
-    U48,
-    U104,
-    paddings::Keccak,
-    "Keccak-384 hash function."
-);
-sha3_impl!(
-    Keccak512,
-    U64,
-    U72,
-    paddings::Keccak,
-    "Keccak-512 hash function."
-);
+// Paddings
+const KECCAK: u8 = 0x01;
+const SHA3: u8 = 0x06;
+const SHAKE: u8 = 0x1f;
 
-sha3_impl!(
+impl_sha3!(Keccak224Core, Keccak224, U28, U144, KECCAK, "Keccak-224");
+impl_sha3!(Keccak256Core, Keccak256, U32, U136, KECCAK, "Keccak-256");
+impl_sha3!(Keccak384Core, Keccak384, U48, U104, KECCAK, "Keccak-384");
+impl_sha3!(Keccak512Core, Keccak512, U64, U72, KECCAK, "Keccak-512");
+
+impl_sha3!(
+    Keccak256FullCore,
     Keccak256Full,
     U200,
     U136,
-    paddings::Keccak,
-    "SHA-3 variant used in CryptoNight."
+    KECCAK,
+    "SHA-3 CryptoNight variant",
 );
 
-sha3_impl!(
-    Sha3_224,
-    U28,
-    U144,
-    paddings::Sha3,
-    "SHA-3-224 hash function."
-);
-sha3_impl!(
-    Sha3_256,
-    U32,
-    U136,
-    paddings::Sha3,
-    "SHA-3-256 hash function."
-);
-sha3_impl!(
-    Sha3_384,
-    U48,
-    U104,
-    paddings::Sha3,
-    "SHA-3-384 hash function."
-);
-sha3_impl!(
-    Sha3_512,
-    U64,
-    U72,
-    paddings::Sha3,
-    "SHA-3-512 hash function."
-);
+impl_sha3!(Sha3_224Core, Sha3_224, U28, U144, SHA3, "SHA-3-224");
+impl_sha3!(Sha3_256Core, Sha3_256, U32, U136, SHA3, "SHA-3-256");
+impl_sha3!(Sha3_384Core, Sha3_384, U48, U104, SHA3, "SHA-3-384");
+impl_sha3!(Sha3_512Core, Sha3_512, U64, U72, SHA3, "SHA-3-512");
 
-shake_impl!(
+impl_shake!(
+    Shake128Core,
     Shake128,
+    Shake128ReaderCore,
+    Shake128Reader,
     U168,
-    paddings::Shake,
-    "SHAKE128 extendable output (XOF) hash function"
+    SHAKE,
+    "SHAKE128",
 );
-shake_impl!(
+impl_shake!(
+    Shake256Core,
     Shake256,
+    Shake256ReaderCore,
+    Shake256Reader,
     U136,
-    paddings::Shake,
-    "SHAKE256 extendable output (XOF) hash function"
+    SHAKE,
+    "SHAKE256",
 );
diff --git a/sha3/src/macros.rs b/sha3/src/macros.rs
index b690124c8..1ec96367b 100644
--- a/sha3/src/macros.rs
+++ b/sha3/src/macros.rs
@@ -1,49 +1,50 @@
-macro_rules! impl_state {
-    ($state:ident, $rate:ident, $padding:ty, $doc:expr) => {
+macro_rules! impl_sha3 {
+    (
+        $name:ident, $full_name:ident, $output_size:ident,
+        $rate:ident, $pad:expr, $alg_name:expr $(,)?
+    ) => {
+        #[doc = "Core "]
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        #[derive(Clone)]
         #[allow(non_camel_case_types)]
-        #[derive(Clone, Default)]
-        #[doc=$doc]
-        pub struct $state {
+        pub struct $name {
             state: Sha3State,
-            buffer: BlockBuffer<$rate>,
         }
 
-        impl $state {
-            fn absorb(&mut self, input: &[u8]) {
-                let s = &mut self.state;
-                self.buffer.input_block(input, |b| s.absorb_block(b));
-            }
+        impl HashMarker for $name {}
 
-            fn apply_padding(&mut self) {
-                let buf = self
-                    .buffer
-                    .pad_with::<$padding>()
-                    .expect("we never use input_lazy");
-                self.state.absorb_block(buf);
-            }
+        impl BlockSizeUser for $name {
+            type BlockSize = $rate;
         }
-    };
-}
 
-macro_rules! sha3_impl {
-    ($state:ident, $output_size:ident, $rate:ident, $padding:ty, $doc:expr) => {
-        impl_state!($state, $rate, $padding, $doc);
+        impl BufferKindUser for $name {
+            type BufferKind = Eager;
+        }
 
-        impl BlockInput for $state {
-            type BlockSize = $rate;
+        impl OutputSizeUser for $name {
+            type OutputSize = $output_size;
         }
 
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                self.absorb(input.as_ref())
+        impl UpdateCore for $name {
+            #[inline]
+            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+                for block in blocks {
+                    self.state.absorb_block(block)
+                }
             }
         }
 
-        impl FixedOutputDirty for $state {
-            type OutputSize = $output_size;
+        impl FixedOutputCore for $name {
+            #[inline]
+            fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+                let pos = buffer.get_pos();
+                let block = buffer.pad_with_zeros();
+                block[pos] = $pad;
+                let n = block.len();
+                block[n - 1] |= 0x80;
 
-            fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-                self.apply_padding();
+                self.state.absorb_block(block);
 
                 let n = out.len();
                 self.state.as_bytes(|state| {
@@ -52,46 +53,151 @@ macro_rules! sha3_impl {
             }
         }
 
-        impl Reset for $state {
+        impl Default for $name {
+            #[inline]
+            fn default() -> Self {
+                Self {
+                    state: Default::default(),
+                }
+            }
+        }
+
+        impl Reset for $name {
+            #[inline]
             fn reset(&mut self) {
-                self.state = Default::default();
-                self.buffer.reset();
+                *self = Default::default();
             }
         }
 
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
+        impl AlgorithmName for $name {
+            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(stringify!($full_name))
+            }
+        }
+
+        impl fmt::Debug for $name {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!(stringify!($name), " { ... }"))
+            }
+        }
+
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
     };
 }
 
-macro_rules! shake_impl {
-    ($state:ident, $rate:ident, $padding:ty, $doc:expr) => {
-        impl_state!($state, $rate, $padding, $doc);
+macro_rules! impl_shake {
+    (
+        $name:ident, $full_name:ident, $reader:ident, $reader_full:ident,
+        $rate:ident, $pad:expr, $alg_name:expr,
+    ) => {
+        #[doc = "Core "]
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        #[derive(Clone)]
+        #[allow(non_camel_case_types)]
+        pub struct $name {
+            state: Sha3State,
+        }
+
+        impl HashMarker for $name {}
+
+        impl BlockSizeUser for $name {
+            type BlockSize = $rate;
+        }
+
+        impl BufferKindUser for $name {
+            type BufferKind = Eager;
+        }
 
-        impl Update for $state {
-            fn update(&mut self, input: impl AsRef<[u8]>) {
-                self.absorb(input.as_ref())
+        impl UpdateCore for $name {
+            #[inline]
+            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+                for block in blocks {
+                    self.state.absorb_block(block)
+                }
             }
         }
 
-        impl ExtendableOutputDirty for $state {
-            type Reader = Sha3XofReader;
+        impl ExtendableOutputCore for $name {
+            type ReaderCore = $reader;
+
+            #[inline]
+            fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
+                let pos = buffer.get_pos();
+                let block = buffer.pad_with_zeros();
+                block[pos] = $pad;
+                let n = block.len();
+                block[n - 1] |= 0x80;
+
+                self.state.absorb_block(block);
+                $reader {
+                    state: self.state.clone(),
+                }
+            }
+        }
 
-            fn finalize_xof_dirty(&mut self) -> Sha3XofReader {
-                self.apply_padding();
-                let r = $rate::to_usize();
-                Sha3XofReader::new(self.state.clone(), r)
+        impl Default for $name {
+            #[inline]
+            fn default() -> Self {
+                Self {
+                    state: Default::default(),
+                }
             }
         }
 
-        impl Reset for $state {
+        impl Reset for $name {
+            #[inline]
             fn reset(&mut self) {
-                self.state = Default::default();
-                self.buffer.reset();
+                *self = Default::default();
+            }
+        }
+
+        impl AlgorithmName for $name {
+            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(stringify!($full_name))
+            }
+        }
+
+        impl fmt::Debug for $name {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(concat!(stringify!($name), " { ... }"))
+            }
+        }
+
+        #[doc = "Core "]
+        #[doc = $alg_name]
+        #[doc = " reader state."]
+        #[derive(Clone)]
+        #[allow(non_camel_case_types)]
+        pub struct $reader {
+            state: Sha3State,
+        }
+
+        impl BlockSizeUser for $reader {
+            type BlockSize = $rate;
+        }
+
+        impl XofReaderCore for $reader {
+            #[inline]
+            fn read_block(&mut self) -> Block<Self> {
+                let mut block = Block::<Self>::default();
+                let n = block.len();
+                self.state.as_bytes(|state| {
+                    block.copy_from_slice(&state[..n]);
+                });
+                self.state.apply_f();
+                block
             }
         }
 
-        opaque_debug::implement!($state);
-        digest::impl_write!($state);
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
+
+        #[doc = $alg_name]
+        #[doc = " reader state."]
+        pub type $reader_full = XofReaderCoreWrapper<$name>;
     };
 }
diff --git a/sha3/src/paddings.rs b/sha3/src/paddings.rs
deleted file mode 100644
index d11c87f89..000000000
--- a/sha3/src/paddings.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use block_buffer::block_padding::{PadError, Padding, UnpadError};
-
-macro_rules! impl_padding {
-    ($name:ident, $pad:expr) => {
-        // it does not work with empty enum as it required to have Default impl
-        // for it for some unclear reason.
-        #[derive(Copy, Clone, Default)]
-        pub struct $name;
-
-        impl Padding for $name {
-            #[inline(always)]
-            fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> {
-                if pos >= block.len() {
-                    return Err(PadError);
-                }
-                block[pos] = $pad;
-                block[pos + 1..].iter_mut().for_each(|b| *b = 0);
-                let n = block.len();
-                block[n - 1] |= 0x80;
-                Ok(())
-            }
-
-            #[inline(always)]
-            fn unpad(_data: &[u8]) -> Result<&[u8], UnpadError> {
-                unimplemented!();
-            }
-        }
-    };
-}
-
-impl_padding!(Keccak, 0x01);
-impl_padding!(Sha3, 0x06);
-impl_padding!(Shake, 0x1f);
diff --git a/sha3/src/reader.rs b/sha3/src/reader.rs
deleted file mode 100644
index fbb2d716e..000000000
--- a/sha3/src/reader.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use crate::state::Sha3State;
-use digest::XofReader;
-#[cfg(feature = "std")]
-use std::io;
-
-/// Reader state for extracting extendable output.
-pub struct Sha3XofReader {
-    state: Sha3State,
-    rate: usize,
-    pos: usize,
-}
-
-impl Sha3XofReader {
-    pub(crate) fn new(state: Sha3State, rate: usize) -> Self {
-        Sha3XofReader {
-            state,
-            rate,
-            pos: 0,
-        }
-    }
-}
-
-impl XofReader for Sha3XofReader {
-    fn read(&mut self, mut buffer: &mut [u8]) {
-        let rem = self.rate - self.pos;
-        let n = buffer.len();
-        if n >= rem {
-            let (l, r) = { buffer }.split_at_mut(rem);
-            buffer = r;
-            self.state.as_bytes(|state| {
-                l.copy_from_slice(&state[self.pos..self.rate]);
-            });
-            self.state.apply_f();
-        } else {
-            self.state.as_bytes(|state| {
-                buffer.copy_from_slice(&state[self.pos..self.pos + n]);
-            });
-            self.pos += n;
-            return;
-        }
-
-        while buffer.len() >= self.rate {
-            let (l, r) = { buffer }.split_at_mut(self.rate);
-            buffer = r;
-
-            self.state.as_bytes(|state| {
-                l.copy_from_slice(&state[..self.rate]);
-            });
-            self.state.apply_f();
-        }
-
-        let n = buffer.len();
-        self.pos = n;
-        self.state.as_bytes(|state| {
-            buffer.copy_from_slice(&state[..n]);
-        });
-    }
-}
-
-#[cfg(feature = "std")]
-impl io::Read for Sha3XofReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        <Self as XofReader>::read(self, buf);
-        Ok(buf.len())
-    }
-}
diff --git a/shabal/CHANGELOG.md b/shabal/CHANGELOG.md
index d44eb49f9..0f16088c2 100644
--- a/shabal/CHANGELOG.md
+++ b/shabal/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.4.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.3.0 (2020-06-12)
 ### Changed
 - Bump `opaque-debug` to v0.3.0 ([#168])
diff --git a/shabal/Cargo.toml b/shabal/Cargo.toml
index a83f02704..e0f0edb9d 100644
--- a/shabal/Cargo.toml
+++ b/shabal/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "shabal"
-version = "0.3.0"
+version = "0.4.0" # Also update html_root_url in lib.rs when bumping this
 description = "Shabal hash functions"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "shabal", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/shabal/benches/mod.rs b/shabal/benches/mod.rs
new file mode 100644
index 000000000..c7007ca7f
--- /dev/null
+++ b/shabal/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use shabal::Shabal256;
+use test::Bencher;
+
+bench_update!(
+    Shabal256::default();
+    shabal256_10 10;
+    shabal256_100 100;
+    shabal256_1000 1000;
+    shabal256_10000 10000;
+);
diff --git a/shabal/benches/shabal.rs b/shabal/benches/shabal.rs
deleted file mode 100644
index ce4ee1202..000000000
--- a/shabal/benches/shabal.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(shabal::Shabal256);
diff --git a/shabal/examples/shabal256sum.rs b/shabal/examples/shabal256sum.rs
deleted file mode 100644
index 5a5186183..000000000
--- a/shabal/examples/shabal256sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use shabal::{Digest, Shabal256};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Shabal256, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Shabal256, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/shabal/examples/shabal512sum.rs b/shabal/examples/shabal512sum.rs
deleted file mode 100644
index b045f6581..000000000
--- a/shabal/examples/shabal512sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use shabal::{Digest, Shabal512};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Shabal512, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Shabal512, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/shabal/src/consts.rs b/shabal/src/consts.rs
index 27fddcdbe..ec4124160 100644
--- a/shabal/src/consts.rs
+++ b/shabal/src/consts.rs
@@ -1,264 +1,101 @@
-pub const A_INIT_512: [u32; 12] = [
-    0x2072_8DFD,
-    0x46C0_BD53,
-    0xE782_B699,
-    0x5530_4632,
-    0x71B4_EF90,
-    0x0EA9_E82C,
-    0xDBB9_30F1,
-    0xFAD0_6B8B,
-    0xBE0C_AE40,
-    0x8BD1_4410,
-    0x76D2_ADAC,
-    0x28AC_AB7F,
-];
+//! Shabal initialization constants.
 
-pub const B_INIT_512: [u32; 16] = [
-    0xC109_9CB7,
-    0x07B3_85F3,
-    0xE744_2C26,
-    0xCC8A_D640,
-    0xEB6F_56C7,
-    0x1EA8_1AA9,
-    0x73B9_D314,
-    0x1DE8_5D08,
-    0x4891_0A5A,
-    0x893B_22DB,
-    0xC5A0_DF44,
-    0xBBC4_324E,
-    0x72D2_F240,
-    0x7594_1D99,
-    0x6D8B_DE82,
-    0xA1A7_502B,
-];
+pub(crate) const INIT_192: ([u32; 12], [u32; 16], [u32; 16]) = (
+    [
+        0xFD74_9ED4, 0xB798_E530, 0x3390_4B6F, 0x46BD_A85E,
+        0x0769_34B4, 0x454B_4058, 0x77F7_4527, 0xFB4C_F465,
+        0x6293_1DA9, 0xE778_C8DB, 0x22B3_998E, 0xAC15_CFB9,
+    ],
+    [
+        0x58BC_BAC4, 0xEC47_A08E, 0xAEE9_33B2, 0xDFCB_C824,
+        0xA794_4804, 0xBF65_BDB0, 0x5A9D_4502, 0x5997_9AF7,
+        0xC5CE_A54E, 0x4B6B_8150, 0x16E7_1909, 0x7D63_2319,
+        0x9305_73A0, 0xF34C_63D1, 0xCAF9_14B4, 0xFDD6_612C,
+    ],
+    [
+        0x6155_0878, 0x89EF_2B75, 0xA166_0C46, 0x7EF3_855B,
+        0x7297_B58C, 0x1BC6_7793, 0x7FB1_C723, 0xB66F_C640,
+        0x1A48_B71C, 0xF097_6D17, 0x088C_E80A, 0xA454_EDF3,
+        0x1C09_6BF4, 0xAC76_224B, 0x5215_781C, 0xCD5D_2669,
+    ],
+);
 
-pub const C_INIT_512: [u32; 16] = [
-    0xD9BF_68D1,
-    0x58BA_D750,
-    0x5602_8CB2,
-    0x8134_F359,
-    0xB5D4_69D8,
-    0x941A_8CC2,
-    0x418B_2A6E,
-    0x0405_2780,
-    0x7F07_D787,
-    0x5194_358F,
-    0x3C60_D665,
-    0xBE97_D79A,
-    0x950C_3434,
-    0xAED9_A06D,
-    0x2537_DC8D,
-    0x7CDB_5969,
-];
+pub(crate) const INIT_224: ([u32; 12], [u32; 16], [u32; 16]) = (
+    [
+        0xA520_1467, 0xA9B8_D94A, 0xD4CE_D997, 0x6837_9D7B,
+        0xA7FC_73BA, 0xF1A2_546B, 0x6067_82BF, 0xE0BC_FD0F,
+        0x2F25_374E, 0x069A_149F, 0x5E2D_FF25, 0xFAEC_F061,
+    ],
+    [
+        0xEC99_05D8, 0xF218_50CF, 0xC0A7_46C8, 0x21DA_D498,
+        0x3515_6EEB, 0x088C_97F2, 0x2630_3E40, 0x8A2D_4FB5,
+        0xFEEE_44B6, 0x8A1E_9573, 0x7B81_111A, 0xCBC1_39F0,
+        0xA351_3861, 0x1D2C_362E, 0x918C_580E, 0xB58E_1B9C,
+    ],
+    [
+        0xE4B5_73A1, 0x4C1A_0880, 0x1E90_7C51, 0x0480_7EFD,
+        0x3AD8_CDE5, 0x16B2_1302, 0x0251_2C53, 0x2204_CB18,
+        0x9940_5F2D, 0xE5B6_48A1, 0x70AB_1D43, 0xA10C_25C2,
+        0x16F1_AC05, 0x38BB_EB56, 0x9B01_DC60, 0xB109_6D83,
+    ],
+);
 
-pub const A_INIT_384: [u32; 12] = [
-    0xC8FC_A331,
-    0xE55C_504E,
-    0x003E_BF26,
-    0xBB6B_8D83,
-    0x7B04_48C1,
-    0x41B8_2789,
-    0x0A7C_9601,
-    0x8D65_9CFF,
-    0xB6E2_673E,
-    0xCA54_C77B,
-    0x1460_FD7E,
-    0x3FCB_8F2D,
-];
+pub(crate) const INIT_256: ([u32; 12], [u32; 16], [u32; 16]) = (
+    [
+        0x52F8_4552, 0xE54B_7999, 0x2D8E_E3EC, 0xB964_5191,
+        0xE007_8B86, 0xBB7C_44C9, 0xD2B5_C1CA, 0xB0D2_EB8C,
+        0x14CE_5A45, 0x22AF_50DC, 0xEFFD_BC6B, 0xEB21_B74A,
+    ],
+    [
+        0xB555_C6EE, 0x3E71_0596, 0xA72A_652F, 0x9301_515F,
+        0xDA28_C1FA, 0x696F_D868, 0x9CB6_BF72, 0x0AFE_4002,
+        0xA6E0_3615, 0x5138_C1D4, 0xBE21_6306, 0xB38B_8890,
+        0x3EA8_B96B, 0x3299_ACE4, 0x3092_4DD4, 0x55CB_34A5,
+    ],
+    [
+        0xB405_F031, 0xC423_3EBA, 0xB373_3979, 0xC0DD_9D55,
+        0xC51C_28AE, 0xA327_B8E1, 0x56C5_6167, 0xED61_4433,
+        0x88B5_9D60, 0x60E2_CEBA, 0x758B_4B8B, 0x83E8_2A7F,
+        0xBC96_8828, 0xE6E0_0BF7, 0xBA83_9E55, 0x9B49_1C60,
+    ],
+);
 
-pub const B_INIT_384: [u32; 16] = [
-    0x5272_91FC,
-    0x2A16_455F,
-    0x78E6_27E5,
-    0x944F_169F,
-    0x1CA6_F016,
-    0xA854_EA25,
-    0x8DB9_8ABE,
-    0xF2C6_2641,
-    0x3011_7DCB,
-    0xCF5C_4309,
-    0x9371_1A25,
-    0xF9F6_71B8,
-    0xB01D_2116,
-    0x333F_4B89,
-    0xB285_D165,
-    0x8682_9B36,
-];
+pub(crate) const INIT_384: ([u32; 12], [u32; 16], [u32; 16]) = (
+    [
+        0xC8FC_A331, 0xE55C_504E, 0x003E_BF26, 0xBB6B_8D83,
+        0x7B04_48C1, 0x41B8_2789, 0x0A7C_9601, 0x8D65_9CFF,
+        0xB6E2_673E, 0xCA54_C77B, 0x1460_FD7E, 0x3FCB_8F2D,
+    ],
+    [
+        0x5272_91FC, 0x2A16_455F, 0x78E6_27E5, 0x944F_169F,
+        0x1CA6_F016, 0xA854_EA25, 0x8DB9_8ABE, 0xF2C6_2641,
+        0x3011_7DCB, 0xCF5C_4309, 0x9371_1A25, 0xF9F6_71B8,
+        0xB01D_2116, 0x333F_4B89, 0xB285_D165, 0x8682_9B36,
+    ],
+    [
+        0xF764_B11A, 0x7617_2146, 0xCEF6_934D, 0xC6D2_8399,
+        0xFE09_5F61, 0x5E60_18B4, 0x5048_ECF5, 0x5135_3261,
+        0x6E6E_36DC, 0x6313_0DAD, 0xA9C6_9BD6, 0x1E90_EA0C,
+        0x7C35_073B, 0x28D9_5E6D, 0xAA34_0E0D, 0xCB3D_EE70,
+    ],
+);
 
-pub const C_INIT_384: [u32; 16] = [
-    0xF764_B11A,
-    0x7617_2146,
-    0xCEF6_934D,
-    0xC6D2_8399,
-    0xFE09_5F61,
-    0x5E60_18B4,
-    0x5048_ECF5,
-    0x5135_3261,
-    0x6E6E_36DC,
-    0x6313_0DAD,
-    0xA9C6_9BD6,
-    0x1E90_EA0C,
-    0x7C35_073B,
-    0x28D9_5E6D,
-    0xAA34_0E0D,
-    0xCB3D_EE70,
-];
-
-pub const A_INIT_256: [u32; 12] = [
-    0x52F8_4552,
-    0xE54B_7999,
-    0x2D8E_E3EC,
-    0xB964_5191,
-    0xE007_8B86,
-    0xBB7C_44C9,
-    0xD2B5_C1CA,
-    0xB0D2_EB8C,
-    0x14CE_5A45,
-    0x22AF_50DC,
-    0xEFFD_BC6B,
-    0xEB21_B74A,
-];
-
-pub const B_INIT_256: [u32; 16] = [
-    0xB555_C6EE,
-    0x3E71_0596,
-    0xA72A_652F,
-    0x9301_515F,
-    0xDA28_C1FA,
-    0x696F_D868,
-    0x9CB6_BF72,
-    0x0AFE_4002,
-    0xA6E0_3615,
-    0x5138_C1D4,
-    0xBE21_6306,
-    0xB38B_8890,
-    0x3EA8_B96B,
-    0x3299_ACE4,
-    0x3092_4DD4,
-    0x55CB_34A5,
-];
-
-pub const C_INIT_256: [u32; 16] = [
-    0xB405_F031,
-    0xC423_3EBA,
-    0xB373_3979,
-    0xC0DD_9D55,
-    0xC51C_28AE,
-    0xA327_B8E1,
-    0x56C5_6167,
-    0xED61_4433,
-    0x88B5_9D60,
-    0x60E2_CEBA,
-    0x758B_4B8B,
-    0x83E8_2A7F,
-    0xBC96_8828,
-    0xE6E0_0BF7,
-    0xBA83_9E55,
-    0x9B49_1C60,
-];
-
-pub const A_INIT_224: [u32; 12] = [
-    0xA520_1467,
-    0xA9B8_D94A,
-    0xD4CE_D997,
-    0x6837_9D7B,
-    0xA7FC_73BA,
-    0xF1A2_546B,
-    0x6067_82BF,
-    0xE0BC_FD0F,
-    0x2F25_374E,
-    0x069A_149F,
-    0x5E2D_FF25,
-    0xFAEC_F061,
-];
-
-pub const B_INIT_224: [u32; 16] = [
-    0xEC99_05D8,
-    0xF218_50CF,
-    0xC0A7_46C8,
-    0x21DA_D498,
-    0x3515_6EEB,
-    0x088C_97F2,
-    0x2630_3E40,
-    0x8A2D_4FB5,
-    0xFEEE_44B6,
-    0x8A1E_9573,
-    0x7B81_111A,
-    0xCBC1_39F0,
-    0xA351_3861,
-    0x1D2C_362E,
-    0x918C_580E,
-    0xB58E_1B9C,
-];
-
-pub const C_INIT_224: [u32; 16] = [
-    0xE4B5_73A1,
-    0x4C1A_0880,
-    0x1E90_7C51,
-    0x0480_7EFD,
-    0x3AD8_CDE5,
-    0x16B2_1302,
-    0x0251_2C53,
-    0x2204_CB18,
-    0x9940_5F2D,
-    0xE5B6_48A1,
-    0x70AB_1D43,
-    0xA10C_25C2,
-    0x16F1_AC05,
-    0x38BB_EB56,
-    0x9B01_DC60,
-    0xB109_6D83,
-];
-
-pub const A_INIT_192: [u32; 12] = [
-    0xFD74_9ED4,
-    0xB798_E530,
-    0x3390_4B6F,
-    0x46BD_A85E,
-    0x0769_34B4,
-    0x454B_4058,
-    0x77F7_4527,
-    0xFB4C_F465,
-    0x6293_1DA9,
-    0xE778_C8DB,
-    0x22B3_998E,
-    0xAC15_CFB9,
-];
-
-pub const B_INIT_192: [u32; 16] = [
-    0x58BC_BAC4,
-    0xEC47_A08E,
-    0xAEE9_33B2,
-    0xDFCB_C824,
-    0xA794_4804,
-    0xBF65_BDB0,
-    0x5A9D_4502,
-    0x5997_9AF7,
-    0xC5CE_A54E,
-    0x4B6B_8150,
-    0x16E7_1909,
-    0x7D63_2319,
-    0x9305_73A0,
-    0xF34C_63D1,
-    0xCAF9_14B4,
-    0xFDD6_612C,
-];
-
-pub const C_INIT_192: [u32; 16] = [
-    0x6155_0878,
-    0x89EF_2B75,
-    0xA166_0C46,
-    0x7EF3_855B,
-    0x7297_B58C,
-    0x1BC6_7793,
-    0x7FB1_C723,
-    0xB66F_C640,
-    0x1A48_B71C,
-    0xF097_6D17,
-    0x088C_E80A,
-    0xA454_EDF3,
-    0x1C09_6BF4,
-    0xAC76_224B,
-    0x5215_781C,
-    0xCD5D_2669,
-];
+pub(crate) const INIT_512: ([u32; 12], [u32; 16], [u32; 16]) = (
+    [
+        0x2072_8DFD, 0x46C0_BD53, 0xE782_B699, 0x5530_4632,
+        0x71B4_EF90, 0x0EA9_E82C, 0xDBB9_30F1, 0xFAD0_6B8B,
+        0xBE0C_AE40, 0x8BD1_4410, 0x76D2_ADAC, 0x28AC_AB7F,
+    ],
+    [
+        0xC109_9CB7, 0x07B3_85F3, 0xE744_2C26, 0xCC8A_D640,
+        0xEB6F_56C7, 0x1EA8_1AA9, 0x73B9_D314, 0x1DE8_5D08,
+        0x4891_0A5A, 0x893B_22DB, 0xC5A0_DF44, 0xBBC4_324E,
+        0x72D2_F240, 0x7594_1D99, 0x6D8B_DE82, 0xA1A7_502B,
+    ],
+    [
+        0xD9BF_68D1, 0x58BA_D750, 0x5602_8CB2, 0x8134_F359,
+        0xB5D4_69D8, 0x941A_8CC2, 0x418B_2A6E, 0x0405_2780,
+        0x7F07_D787, 0x5194_358F, 0x3C60_D665, 0xBE97_D79A,
+        0x950C_3434, 0xAED9_A06D, 0x2537_DC8D, 0x7CDB_5969,
+    ],
+);
diff --git a/shabal/src/core_api.rs b/shabal/src/core_api.rs
new file mode 100644
index 000000000..561117522
--- /dev/null
+++ b/shabal/src/core_api.rs
@@ -0,0 +1,250 @@
+use crate::consts;
+use core::{convert::TryInto, fmt, mem, num::Wrapping};
+use digest::{
+    block_buffer::Eager,
+    consts::U64,
+    core_api::{
+        AlgorithmName, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser, TruncSide,
+        UpdateCore, VariableOutputCore,
+    },
+    generic_array::GenericArray,
+    HashMarker, InvalidOutputSize, Output,
+};
+
+type BlockSize = U64;
+type Block = GenericArray<u8, BlockSize>;
+type M = [Wrapping<u32>; 16];
+
+/// Inner state of Shabal hash functions.
+#[derive(Clone)]
+pub struct ShabalVarCore {
+    a: [Wrapping<u32>; 12],
+    b: M,
+    c: M,
+    w: Wrapping<u64>,
+}
+
+impl ShabalVarCore {
+    #[allow(clippy::needless_range_loop)]
+    fn add_m(&mut self, m: &M) {
+        for i in 0..16 {
+            self.b[i] += m[i];
+        }
+    }
+
+    #[allow(clippy::needless_range_loop)]
+    fn sub_m(&mut self, m: &M) {
+        for i in 0..16 {
+            self.c[i] -= m[i];
+        }
+    }
+
+    fn xor_w(&mut self) {
+        self.a[0].0 ^= self.w.0 as u32;
+        self.a[1].0 ^= (self.w.0 >> 32) as u32;
+    }
+
+    fn perm(&mut self, m: &M) {
+        self.b.iter_mut().for_each(|b| b.0 = b.0.rotate_left(17));
+        self.perm_blocks(m);
+
+        let a = &mut self.a;
+        let c = &self.c;
+        a[0] += c[11] + c[15] + c[3];
+        a[1] += c[12] + c[0] + c[4];
+        a[2] += c[13] + c[1] + c[5];
+        a[3] += c[14] + c[2] + c[6];
+        a[4] += c[15] + c[3] + c[7];
+        a[5] += c[0] + c[4] + c[8];
+        a[6] += c[1] + c[5] + c[9];
+        a[7] += c[2] + c[6] + c[10];
+        a[8] += c[3] + c[7] + c[11];
+        a[9] += c[4] + c[8] + c[12];
+        a[10] += c[5] + c[9] + c[13];
+        a[11] += c[6] + c[10] + c[14];
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn perm_elt(
+        &mut self,
+        xa0: usize,
+        xa1: usize,
+        xb0: usize,
+        xb1: usize,
+        xb2: usize,
+        xb3: usize,
+        xc0: usize,
+        xm: Wrapping<u32>,
+    ) {
+        let a = &mut self.a;
+        let b = &mut self.b;
+        let xc = self.c[xc0];
+
+        let t1 = Wrapping(a[xa1].0.rotate_left(15));
+        let t2 = t1 * Wrapping(5);
+        let t3 = (a[xa0] ^ t2 ^ xc) * Wrapping(3);
+        a[xa0] = t3 ^ b[xb1] ^ (b[xb2] & !b[xb3]) ^ xm;
+
+        let t = Wrapping(b[xb0].0.rotate_left(1));
+        b[xb0] = !(t ^ a[xa0]);
+    }
+
+    fn perm_blocks(&mut self, m: &M) {
+        self.perm_elt(0, 11, 0, 13, 9, 6, 8, m[0]);
+        self.perm_elt(1, 0, 1, 14, 10, 7, 7, m[1]);
+        self.perm_elt(2, 1, 2, 15, 11, 8, 6, m[2]);
+        self.perm_elt(3, 2, 3, 0, 12, 9, 5, m[3]);
+        self.perm_elt(4, 3, 4, 1, 13, 10, 4, m[4]);
+        self.perm_elt(5, 4, 5, 2, 14, 11, 3, m[5]);
+        self.perm_elt(6, 5, 6, 3, 15, 12, 2, m[6]);
+        self.perm_elt(7, 6, 7, 4, 0, 13, 1, m[7]);
+        self.perm_elt(8, 7, 8, 5, 1, 14, 0, m[8]);
+        self.perm_elt(9, 8, 9, 6, 2, 15, 15, m[9]);
+        self.perm_elt(10, 9, 10, 7, 3, 0, 14, m[10]);
+        self.perm_elt(11, 10, 11, 8, 4, 1, 13, m[11]);
+        self.perm_elt(0, 11, 12, 9, 5, 2, 12, m[12]);
+        self.perm_elt(1, 0, 13, 10, 6, 3, 11, m[13]);
+        self.perm_elt(2, 1, 14, 11, 7, 4, 10, m[14]);
+        self.perm_elt(3, 2, 15, 12, 8, 5, 9, m[15]);
+        self.perm_elt(4, 3, 0, 13, 9, 6, 8, m[0]);
+        self.perm_elt(5, 4, 1, 14, 10, 7, 7, m[1]);
+        self.perm_elt(6, 5, 2, 15, 11, 8, 6, m[2]);
+        self.perm_elt(7, 6, 3, 0, 12, 9, 5, m[3]);
+        self.perm_elt(8, 7, 4, 1, 13, 10, 4, m[4]);
+        self.perm_elt(9, 8, 5, 2, 14, 11, 3, m[5]);
+        self.perm_elt(10, 9, 6, 3, 15, 12, 2, m[6]);
+        self.perm_elt(11, 10, 7, 4, 0, 13, 1, m[7]);
+        self.perm_elt(0, 11, 8, 5, 1, 14, 0, m[8]);
+        self.perm_elt(1, 0, 9, 6, 2, 15, 15, m[9]);
+        self.perm_elt(2, 1, 10, 7, 3, 0, 14, m[10]);
+        self.perm_elt(3, 2, 11, 8, 4, 1, 13, m[11]);
+        self.perm_elt(4, 3, 12, 9, 5, 2, 12, m[12]);
+        self.perm_elt(5, 4, 13, 10, 6, 3, 11, m[13]);
+        self.perm_elt(6, 5, 14, 11, 7, 4, 10, m[14]);
+        self.perm_elt(7, 6, 15, 12, 8, 5, 9, m[15]);
+        self.perm_elt(8, 7, 0, 13, 9, 6, 8, m[0]);
+        self.perm_elt(9, 8, 1, 14, 10, 7, 7, m[1]);
+        self.perm_elt(10, 9, 2, 15, 11, 8, 6, m[2]);
+        self.perm_elt(11, 10, 3, 0, 12, 9, 5, m[3]);
+        self.perm_elt(0, 11, 4, 1, 13, 10, 4, m[4]);
+        self.perm_elt(1, 0, 5, 2, 14, 11, 3, m[5]);
+        self.perm_elt(2, 1, 6, 3, 15, 12, 2, m[6]);
+        self.perm_elt(3, 2, 7, 4, 0, 13, 1, m[7]);
+        self.perm_elt(4, 3, 8, 5, 1, 14, 0, m[8]);
+        self.perm_elt(5, 4, 9, 6, 2, 15, 15, m[9]);
+        self.perm_elt(6, 5, 10, 7, 3, 0, 14, m[10]);
+        self.perm_elt(7, 6, 11, 8, 4, 1, 13, m[11]);
+        self.perm_elt(8, 7, 12, 9, 5, 2, 12, m[12]);
+        self.perm_elt(9, 8, 13, 10, 6, 3, 11, m[13]);
+        self.perm_elt(10, 9, 14, 11, 7, 4, 10, m[14]);
+        self.perm_elt(11, 10, 15, 12, 8, 5, 9, m[15]);
+    }
+
+    fn swap_b_c(&mut self) {
+        mem::swap(&mut self.b, &mut self.c);
+    }
+}
+
+#[inline]
+fn read_m(input: &Block) -> M {
+    let mut m = [Wrapping(0); 16];
+    for (o, chunk) in m.iter_mut().zip(input.chunks_exact(4)) {
+        let a = chunk.try_into().unwrap();
+        *o = Wrapping(u32::from_le_bytes(a));
+    }
+    m
+}
+
+impl HashMarker for ShabalVarCore {}
+
+impl BlockSizeUser for ShabalVarCore {
+    type BlockSize = BlockSize;
+}
+
+impl BufferKindUser for ShabalVarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for ShabalVarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block]) {
+        for block in blocks {
+            let m = read_m(block);
+            self.add_m(&m);
+            self.xor_w();
+            self.perm(&m);
+            self.sub_m(&m);
+            self.swap_b_c();
+            self.w += Wrapping(1);
+        }
+    }
+}
+
+impl OutputSizeUser for ShabalVarCore {
+    type OutputSize = U64;
+}
+
+impl VariableOutputCore for ShabalVarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Right;
+
+    #[inline]
+    #[allow(clippy::needless_range_loop)]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        let init = match output_size {
+            24 => consts::INIT_192,
+            28 => consts::INIT_224,
+            32 => consts::INIT_256,
+            48 => consts::INIT_384,
+            64 => consts::INIT_512,
+            _ => return Err(InvalidOutputSize),
+        };
+        let w = Wrapping(1);
+        // TODO: use `array::map` on MSRV bump
+        let mut a = [Wrapping(0u32); 12];
+        let mut b = [Wrapping(0u32); 16];
+        let mut c = [Wrapping(0u32); 16];
+        for i in 0..12 {
+            a[i] = Wrapping(init.0[i]);
+        }
+        for i in 0..16 {
+            b[i] = Wrapping(init.1[i]);
+            c[i] = Wrapping(init.2[i]);
+        }
+        Ok(Self { a, b, c, w })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let pos = buffer.get_pos();
+        let block = buffer.pad_with_zeros();
+        block[pos] = 0x80;
+
+        let m = read_m(block);
+        self.add_m(&m);
+        self.xor_w();
+        self.perm(&m);
+        for _ in 0..3 {
+            self.swap_b_c();
+            self.xor_w();
+            self.perm(&m);
+        }
+
+        for (chunk, v) in out.chunks_exact_mut(4).zip(self.b.iter()) {
+            chunk.copy_from_slice(&v.0.to_le_bytes());
+        }
+    }
+}
+
+impl AlgorithmName for ShabalVarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Shabal")
+    }
+}
+
+impl fmt::Debug for ShabalVarCore {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("ShabalVarCore { ... }")
+    }
+}
diff --git a/shabal/src/lib.rs b/shabal/src/lib.rs
index ecb5e96e1..0997d2a7e 100644
--- a/shabal/src/lib.rs
+++ b/shabal/src/lib.rs
@@ -26,7 +26,9 @@
 //! // acquire hash digest in the form of GenericArray,
 //! // which in this case is equivalent to [u8; 32]
 //! let result = hasher.finalize();
-//! assert_eq!(result[..], hex!("d945dee21ffca23ac232763aa9cac6c15805f144db9d6c97395437e01c8595a8"));
+//! assert_eq!(result[..], hex!("
+//!     d945dee21ffca23ac232763aa9cac6c15805f144db9d6c97395437e01c8595a8
+//! ")[..]);
 //! ```
 //!
 //! Also see [RustCrypto/hashes][2] readme.
@@ -36,8 +38,9 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/shabal/0.4.0"
 )]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
@@ -45,8 +48,25 @@
 #[cfg(feature = "std")]
 extern crate std;
 
+#[rustfmt::skip]
 mod consts;
-mod shabal;
+mod core_api;
 
-pub use crate::shabal::{Shabal192, Shabal224, Shabal256, Shabal384, Shabal512};
+pub use core_api::ShabalVarCore;
 pub use digest::{self, Digest};
+
+use digest::{
+    consts::{U24, U28, U32, U48, U64},
+    core_api::{CoreWrapper, CtVariableCoreWrapper},
+};
+
+/// Shabal192 hasher.
+pub type Shabal192 = CoreWrapper<CtVariableCoreWrapper<ShabalVarCore, U24>>;
+/// Shabal224 hasher.
+pub type Shabal224 = CoreWrapper<CtVariableCoreWrapper<ShabalVarCore, U28>>;
+/// Shabal256 hasher.
+pub type Shabal256 = CoreWrapper<CtVariableCoreWrapper<ShabalVarCore, U32>>;
+/// Shabal384 hasher.
+pub type Shabal384 = CoreWrapper<CtVariableCoreWrapper<ShabalVarCore, U48>>;
+/// Shabal512 hasher.
+pub type Shabal512 = CoreWrapper<CtVariableCoreWrapper<ShabalVarCore, U64>>;
diff --git a/shabal/src/shabal.rs b/shabal/src/shabal.rs
deleted file mode 100644
index 09d3879be..000000000
--- a/shabal/src/shabal.rs
+++ /dev/null
@@ -1,513 +0,0 @@
-//! Shabal
-use block_buffer::block_padding::Iso7816;
-use block_buffer::BlockBuffer;
-use core::convert::TryInto;
-use digest::{
-    consts::{U24, U28, U32, U48, U64},
-    generic_array::GenericArray,
-};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-use crate::consts::{
-    A_INIT_192, A_INIT_224, A_INIT_256, A_INIT_384, A_INIT_512, B_INIT_192, B_INIT_224, B_INIT_256,
-    B_INIT_384, B_INIT_512, C_INIT_192, C_INIT_224, C_INIT_256, C_INIT_384, C_INIT_512,
-};
-
-type BlockSize = U64;
-type Block = GenericArray<u8, BlockSize>;
-
-/// A structure that represents that state of a digest computation for the
-/// Shabal family of digest functions
-#[derive(Clone)]
-struct EngineState {
-    a: [u32; 12],
-    b: [u32; 16],
-    c: [u32; 16],
-    whigh: u32,
-    wlow: u32,
-}
-
-impl EngineState {
-    fn new(a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) -> Self {
-        Self {
-            a: *a,
-            b: *b,
-            c: *c,
-            wlow: 1,
-            whigh: 0,
-        }
-    }
-
-    fn process_block(&mut self, block: &Block) {
-        #[allow(unsafe_code)]
-        let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) };
-        compress(self, block);
-    }
-
-    fn process_final_block(&mut self, block: &Block) {
-        #[allow(unsafe_code)]
-        let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) };
-        compress_final(self, block);
-    }
-
-    #[inline]
-    fn add_m(&mut self, m: &[u32; 16]) {
-        for (b, m) in self.b.iter_mut().zip(m) {
-            *b = b.wrapping_add(*m);
-        }
-    }
-
-    #[inline]
-    fn sub_m(&mut self, m: &[u32; 16]) {
-        for (c, m) in self.c.iter_mut().zip(m) {
-            *c = c.wrapping_sub(*m);
-        }
-    }
-
-    #[inline]
-    fn inc_w(&mut self) {
-        self.wlow = self.wlow.wrapping_add(1);
-        if self.wlow == 0 {
-            self.whigh = self.whigh.wrapping_add(1);
-        }
-    }
-
-    #[inline]
-    fn xor_w(&mut self) {
-        self.a[0] ^= self.wlow;
-        self.a[1] ^= self.whigh;
-    }
-
-    #[inline]
-    fn perm(&mut self, m: &[u32; 16]) {
-        for b in self.b.iter_mut() {
-            *b = b.wrapping_shl(17) | b.wrapping_shr(15);
-        }
-        self.perm_blocks(m);
-
-        let a = &mut self.a;
-        let c = &self.c;
-        a[0] = a[0]
-            .wrapping_add(c[11])
-            .wrapping_add(c[15])
-            .wrapping_add(c[3]);
-        a[1] = a[1]
-            .wrapping_add(c[12])
-            .wrapping_add(c[0])
-            .wrapping_add(c[4]);
-        a[2] = a[2]
-            .wrapping_add(c[13])
-            .wrapping_add(c[1])
-            .wrapping_add(c[5]);
-        a[3] = a[3]
-            .wrapping_add(c[14])
-            .wrapping_add(c[2])
-            .wrapping_add(c[6]);
-        a[4] = a[4]
-            .wrapping_add(c[15])
-            .wrapping_add(c[3])
-            .wrapping_add(c[7]);
-        a[5] = a[5]
-            .wrapping_add(c[0])
-            .wrapping_add(c[4])
-            .wrapping_add(c[8]);
-        a[6] = a[6]
-            .wrapping_add(c[1])
-            .wrapping_add(c[5])
-            .wrapping_add(c[9]);
-        a[7] = a[7]
-            .wrapping_add(c[2])
-            .wrapping_add(c[6])
-            .wrapping_add(c[10]);
-        a[8] = a[8]
-            .wrapping_add(c[3])
-            .wrapping_add(c[7])
-            .wrapping_add(c[11]);
-        a[9] = a[9]
-            .wrapping_add(c[4])
-            .wrapping_add(c[8])
-            .wrapping_add(c[12]);
-        a[10] = a[10]
-            .wrapping_add(c[5])
-            .wrapping_add(c[9])
-            .wrapping_add(c[13]);
-        a[11] = a[11]
-            .wrapping_add(c[6])
-            .wrapping_add(c[10])
-            .wrapping_add(c[14]);
-    }
-
-    #[inline]
-    #[allow(clippy::too_many_arguments)]
-    fn perm_elt(
-        &mut self,
-        xa0: usize,
-        xa1: usize,
-        xb0: usize,
-        xb1: usize,
-        xb2: usize,
-        xb3: usize,
-        xc0: usize,
-        xm: u32,
-    ) {
-        let a = &mut self.a;
-        let b = &mut self.b;
-        let xc = self.c[xc0];
-
-        a[xa0] = (a[xa0]
-            ^ ((a[xa1].wrapping_shl(15u32) | a[xa1].wrapping_shr(17u32)).wrapping_mul(5u32))
-            ^ xc)
-            .wrapping_mul(3u32)
-            ^ b[xb1]
-            ^ (b[xb2] & !b[xb3])
-            ^ xm;
-        b[xb0] = !((b[xb0].wrapping_shl(1) | b[xb0].wrapping_shr(31)) ^ a[xa0]);
-    }
-
-    #[inline]
-    fn perm_blocks(&mut self, m: &[u32; 16]) {
-        self.perm_elt(0, 11, 0, 13, 9, 6, 8, m[0]);
-        self.perm_elt(1, 0, 1, 14, 10, 7, 7, m[1]);
-        self.perm_elt(2, 1, 2, 15, 11, 8, 6, m[2]);
-        self.perm_elt(3, 2, 3, 0, 12, 9, 5, m[3]);
-        self.perm_elt(4, 3, 4, 1, 13, 10, 4, m[4]);
-        self.perm_elt(5, 4, 5, 2, 14, 11, 3, m[5]);
-        self.perm_elt(6, 5, 6, 3, 15, 12, 2, m[6]);
-        self.perm_elt(7, 6, 7, 4, 0, 13, 1, m[7]);
-        self.perm_elt(8, 7, 8, 5, 1, 14, 0, m[8]);
-        self.perm_elt(9, 8, 9, 6, 2, 15, 15, m[9]);
-        self.perm_elt(10, 9, 10, 7, 3, 0, 14, m[10]);
-        self.perm_elt(11, 10, 11, 8, 4, 1, 13, m[11]);
-        self.perm_elt(0, 11, 12, 9, 5, 2, 12, m[12]);
-        self.perm_elt(1, 0, 13, 10, 6, 3, 11, m[13]);
-        self.perm_elt(2, 1, 14, 11, 7, 4, 10, m[14]);
-        self.perm_elt(3, 2, 15, 12, 8, 5, 9, m[15]);
-        self.perm_elt(4, 3, 0, 13, 9, 6, 8, m[0]);
-        self.perm_elt(5, 4, 1, 14, 10, 7, 7, m[1]);
-        self.perm_elt(6, 5, 2, 15, 11, 8, 6, m[2]);
-        self.perm_elt(7, 6, 3, 0, 12, 9, 5, m[3]);
-        self.perm_elt(8, 7, 4, 1, 13, 10, 4, m[4]);
-        self.perm_elt(9, 8, 5, 2, 14, 11, 3, m[5]);
-        self.perm_elt(10, 9, 6, 3, 15, 12, 2, m[6]);
-        self.perm_elt(11, 10, 7, 4, 0, 13, 1, m[7]);
-        self.perm_elt(0, 11, 8, 5, 1, 14, 0, m[8]);
-        self.perm_elt(1, 0, 9, 6, 2, 15, 15, m[9]);
-        self.perm_elt(2, 1, 10, 7, 3, 0, 14, m[10]);
-        self.perm_elt(3, 2, 11, 8, 4, 1, 13, m[11]);
-        self.perm_elt(4, 3, 12, 9, 5, 2, 12, m[12]);
-        self.perm_elt(5, 4, 13, 10, 6, 3, 11, m[13]);
-        self.perm_elt(6, 5, 14, 11, 7, 4, 10, m[14]);
-        self.perm_elt(7, 6, 15, 12, 8, 5, 9, m[15]);
-        self.perm_elt(8, 7, 0, 13, 9, 6, 8, m[0]);
-        self.perm_elt(9, 8, 1, 14, 10, 7, 7, m[1]);
-        self.perm_elt(10, 9, 2, 15, 11, 8, 6, m[2]);
-        self.perm_elt(11, 10, 3, 0, 12, 9, 5, m[3]);
-        self.perm_elt(0, 11, 4, 1, 13, 10, 4, m[4]);
-        self.perm_elt(1, 0, 5, 2, 14, 11, 3, m[5]);
-        self.perm_elt(2, 1, 6, 3, 15, 12, 2, m[6]);
-        self.perm_elt(3, 2, 7, 4, 0, 13, 1, m[7]);
-        self.perm_elt(4, 3, 8, 5, 1, 14, 0, m[8]);
-        self.perm_elt(5, 4, 9, 6, 2, 15, 15, m[9]);
-        self.perm_elt(6, 5, 10, 7, 3, 0, 14, m[10]);
-        self.perm_elt(7, 6, 11, 8, 4, 1, 13, m[11]);
-        self.perm_elt(8, 7, 12, 9, 5, 2, 12, m[12]);
-        self.perm_elt(9, 8, 13, 10, 6, 3, 11, m[13]);
-        self.perm_elt(10, 9, 14, 11, 7, 4, 10, m[14]);
-        self.perm_elt(11, 10, 15, 12, 8, 5, 9, m[15]);
-    }
-
-    #[inline]
-    fn swap_b_c(&mut self) {
-        ::core::mem::swap(&mut self.b, &mut self.c);
-    }
-}
-
-/// A structure that keeps track of the state of the Shabal operation and
-/// contains the logic necessary to perform the final calculations.
-#[derive(Clone)]
-struct Engine256 {
-    buffer: BlockBuffer<BlockSize>,
-    state: EngineState,
-}
-
-impl Engine256 {
-    fn new(a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) -> Engine256 {
-        Engine256 {
-            buffer: Default::default(),
-            state: EngineState::new(a, b, c),
-        }
-    }
-
-    fn input(&mut self, input: &[u8]) {
-        let s = &mut self.state;
-        self.buffer.input_block(input, |b| s.process_block(b));
-    }
-
-    fn finish(&mut self) {
-        let state = &mut self.state;
-        let block = self.buffer.pad_with::<Iso7816>().unwrap();
-        state.process_final_block(block);
-    }
-
-    fn reset(&mut self, a: &[u32; 12], b: &[u32; 16], c: &[u32; 16]) {
-        self.state = EngineState::new(a, b, c);
-        self.buffer.reset();
-    }
-}
-
-/// The Shabal hash algorithm with the Shabal-512 initial hash value.
-#[derive(Clone)]
-pub struct Shabal512 {
-    engine: Engine256,
-}
-
-impl Default for Shabal512 {
-    fn default() -> Self {
-        Self {
-            engine: Engine256::new(&A_INIT_512, &B_INIT_512, &C_INIT_512),
-        }
-    }
-}
-
-impl BlockInput for Shabal512 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Shabal512 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.input(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Shabal512 {
-    type OutputSize = U64;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let b = &self.engine.state.b[0..16];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(b.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Shabal512 {
-    fn reset(&mut self) {
-        self.engine.reset(&A_INIT_512, &B_INIT_512, &C_INIT_512);
-    }
-}
-
-/// The Shabal hash algorithm with the Shabal-384 initial hash value. The result
-/// is truncated to 384 bits.
-#[derive(Clone)]
-pub struct Shabal384 {
-    engine: Engine256,
-}
-
-impl Default for Shabal384 {
-    fn default() -> Self {
-        Self {
-            engine: Engine256::new(&A_INIT_384, &B_INIT_384, &C_INIT_384),
-        }
-    }
-}
-
-impl BlockInput for Shabal384 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Shabal384 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.input(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Shabal384 {
-    type OutputSize = U48;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let b = &self.engine.state.b[4..16];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(b.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Shabal384 {
-    fn reset(&mut self) {
-        self.engine.reset(&A_INIT_384, &B_INIT_384, &C_INIT_384);
-    }
-}
-
-/// The Shabal hash algorithm with the Shabal-256 initial hash value. The result
-/// is truncated to 256 bits.
-#[derive(Clone)]
-pub struct Shabal256 {
-    engine: Engine256,
-}
-
-impl Default for Shabal256 {
-    fn default() -> Self {
-        Self {
-            engine: Engine256::new(&A_INIT_256, &B_INIT_256, &C_INIT_256),
-        }
-    }
-}
-
-impl BlockInput for Shabal256 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Shabal256 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.input(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Shabal256 {
-    type OutputSize = U32;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let b = &self.engine.state.b[8..16];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(b.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Shabal256 {
-    fn reset(&mut self) {
-        self.engine.reset(&A_INIT_256, &B_INIT_256, &C_INIT_256);
-    }
-}
-
-/// The Shabal hash algorithm with the Shabal-224 initial hash value. The result
-/// is truncated to 224 bits.
-#[derive(Clone)]
-pub struct Shabal224 {
-    engine: Engine256,
-}
-
-impl Default for Shabal224 {
-    fn default() -> Self {
-        Self {
-            engine: Engine256::new(&A_INIT_224, &B_INIT_224, &C_INIT_224),
-        }
-    }
-}
-
-impl BlockInput for Shabal224 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Shabal224 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.input(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Shabal224 {
-    type OutputSize = U28;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let b = &self.engine.state.b[9..16];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(b.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Shabal224 {
-    fn reset(&mut self) {
-        self.engine.reset(&A_INIT_224, &B_INIT_224, &C_INIT_224);
-    }
-}
-
-/// The Shabal hash algorithm with the Shabal-192 initial hash value. The result
-/// is truncated to 192 bits.
-#[derive(Clone)]
-pub struct Shabal192 {
-    engine: Engine256,
-}
-
-impl Default for Shabal192 {
-    fn default() -> Self {
-        Self {
-            engine: Engine256::new(&A_INIT_192, &B_INIT_192, &C_INIT_192),
-        }
-    }
-}
-
-impl BlockInput for Shabal192 {
-    type BlockSize = BlockSize;
-}
-
-impl Update for Shabal192 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.engine.input(input.as_ref());
-    }
-}
-
-impl FixedOutputDirty for Shabal192 {
-    type OutputSize = U24;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        self.engine.finish();
-        let b = &self.engine.state.b[10..16];
-        for (chunk, v) in out.chunks_exact_mut(4).zip(b.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
-        }
-    }
-}
-
-impl Reset for Shabal192 {
-    fn reset(&mut self) {
-        self.engine.reset(&A_INIT_192, &B_INIT_192, &C_INIT_192);
-    }
-}
-
-opaque_debug::implement!(Shabal512);
-opaque_debug::implement!(Shabal384);
-opaque_debug::implement!(Shabal256);
-opaque_debug::implement!(Shabal224);
-opaque_debug::implement!(Shabal192);
-
-digest::impl_write!(Shabal512);
-digest::impl_write!(Shabal384);
-digest::impl_write!(Shabal256);
-digest::impl_write!(Shabal224);
-digest::impl_write!(Shabal192);
-
-#[inline]
-fn read_m(input: &[u8; 64]) -> [u32; 16] {
-    let mut m = [0u32; 16];
-    for (o, chunk) in m.iter_mut().zip(input.chunks_exact(4)) {
-        *o = u32::from_le_bytes(chunk.try_into().unwrap());
-    }
-    m
-}
-
-fn compress(state: &mut EngineState, input: &[u8; 64]) {
-    let m = read_m(input);
-    state.add_m(&m);
-    state.xor_w();
-    state.perm(&m);
-    state.sub_m(&m);
-    state.swap_b_c();
-    state.inc_w();
-}
-
-fn compress_final(state: &mut EngineState, input: &[u8; 64]) {
-    let m = read_m(input);
-    state.add_m(&m);
-    state.xor_w();
-    state.perm(&m);
-    for _ in 0..3 {
-        state.swap_b_c();
-        state.xor_w();
-        state.perm(&m);
-    }
-}
diff --git a/sm3/CHANGELOG.md b/sm3/CHANGELOG.md
index 5f7b7bc8c..343b19f16 100644
--- a/sm3/CHANGELOG.md
+++ b/sm3/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.4.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.3.0 (2021-07-18)
 ### Changed
 - RustCrypto SM3 release ([#249])
diff --git a/sm3/Cargo.toml b/sm3/Cargo.toml
index 994a4f0cd..28fe163b2 100644
--- a/sm3/Cargo.toml
+++ b/sm3/Cargo.toml
@@ -1,8 +1,8 @@
 [package]
 name = "sm3"
-version = "0.3.0"
+version = "0.4.0" # Also update html_root_url in lib.rs when bumping this
 description = "SM3 (OSCCA GM/T 0004-2012) hash function"
-authors = ["Tianjia Zhang <tianjia.zhang@linux.alibaba.com>"]
+authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
 readme = "README.md"
 edition = "2018"
@@ -12,12 +12,10 @@ keywords = ["crypto", "sm3", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = "0.9"
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/sm3/LICENSE-MIT b/sm3/LICENSE-MIT
index 78d6d79a4..c869ada57 100644
--- a/sm3/LICENSE-MIT
+++ b/sm3/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2016 Artyom Pavlov
+Copyright (c) 2021 The RustCrypto Project Developers
 
 Permission is hereby granted, free of charge, to any
 person obtaining a copy of this software and associated
diff --git a/sm3/benches/mod.rs b/sm3/benches/mod.rs
new file mode 100644
index 000000000..afcf8ee0f
--- /dev/null
+++ b/sm3/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use sm3::Sm3;
+use test::Bencher;
+
+bench_update!(
+    Sm3::default();
+    sm3_10 10;
+    sm3_100 100;
+    sm3_1000 1000;
+    sm3_10000 10000;
+);
diff --git a/sm3/benches/sm3.rs b/sm3/benches/sm3.rs
deleted file mode 100644
index 1d9158388..000000000
--- a/sm3/benches/sm3.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(sm3::Sm3);
diff --git a/sm3/examples/sm3sum.rs b/sm3/examples/sm3sum.rs
deleted file mode 100644
index 94ec40810..000000000
--- a/sm3/examples/sm3sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use sm3::{Digest, Sm3};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Sm3, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Sm3, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/sm3/src/sm3.rs b/sm3/src/compress.rs
similarity index 81%
rename from sm3/src/sm3.rs
rename to sm3/src/compress.rs
index 42be5cf5c..731efb3c6 100644
--- a/sm3/src/sm3.rs
+++ b/sm3/src/compress.rs
@@ -1,67 +1,6 @@
-//! SM3
 #![allow(clippy::many_single_char_names, clippy::too_many_arguments)]
-use crate::consts::{SM3_IV, T32};
-use block_buffer::BlockBuffer;
+use crate::{consts::T32, Block, Sm3Core};
 use core::convert::TryInto;
-use core::slice::from_ref;
-use digest::consts::{U32, U64};
-use digest::generic_array::GenericArray;
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-/// The SM3 hash algorithm with the SM3 initial hash value.
-#[derive(Clone)]
-pub struct Sm3 {
-    len: u64,
-    buffer: BlockBuffer<U64>,
-    state: [u32; 8],
-}
-
-impl Default for Sm3 {
-    fn default() -> Self {
-        Sm3 {
-            len: 0,
-            buffer: Default::default(),
-            state: SM3_IV,
-        }
-    }
-}
-
-impl BlockInput for Sm3 {
-    type BlockSize = U64;
-}
-
-impl Update for Sm3 {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        self.len += (input.as_ref().len() as u64) << 3;
-        let s = &mut self.state;
-        self.buffer
-            .input_blocks(input.as_ref(), |b| sm3_compress(s, b));
-    }
-}
-
-impl FixedOutputDirty for Sm3 {
-    type OutputSize = U32;
-
-    fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
-        let s = &mut self.state;
-        let l = self.len;
-        self.buffer
-            .len64_padding_be(l, |b| sm3_compress(s, from_ref(b)));
-
-        let s = self.state;
-        for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
-    }
-}
-
-impl Reset for Sm3 {
-    fn reset(&mut self) {
-        self.len = 0;
-        self.buffer.reset();
-        self.state = SM3_IV;
-    }
-}
 
 #[inline(always)]
 fn ff1(x: u32, y: u32, z: u32) -> u32 {
@@ -209,7 +148,7 @@ macro_rules! R2 {
     }};
 }
 
-fn sm3_digest_block_u32(state: &mut [u32; 8], block: &[u32; 16]) {
+fn compress_u32(state: &mut [u32; 8], block: &[u32; 16]) {
     let mut x: [u32; 16] = *block;
 
     let mut a = state[0];
@@ -296,15 +235,12 @@ fn sm3_digest_block_u32(state: &mut [u32; 8], block: &[u32; 16]) {
     state[7] ^= h;
 }
 
-pub fn sm3_compress(state: &mut [u32; 8], blocks: &[GenericArray<u8, U64>]) {
-    #[allow(unsafe_code)]
-    let blocks = unsafe { &*(blocks as *const _ as *const [[u8; 64]]) };
-
-    let mut w = [0u32; 16];
+pub(crate) fn compress(state: &mut [u32; 8], blocks: &[Block<Sm3Core>]) {
     for block in blocks {
+        let mut w = [0u32; 16];
         for (o, chunk) in w.iter_mut().zip(block.chunks_exact(4)) {
             *o = u32::from_be_bytes(chunk.try_into().unwrap());
         }
-        sm3_digest_block_u32(state, &w);
+        compress_u32(state, &w);
     }
 }
diff --git a/sm3/src/consts.rs b/sm3/src/consts.rs
index 1e997c221..792bdfbea 100644
--- a/sm3/src/consts.rs
+++ b/sm3/src/consts.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::unreadable_literal)]
 
-pub const T32: [u32; 64] = [
+pub(crate) const T32: [u32; 64] = [
     0x79cc4519, 0xf3988a32, 0xe7311465, 0xce6228cb, 0x9cc45197, 0x3988a32f, 0x7311465e, 0xe6228cbc,
     0xcc451979, 0x988a32f3, 0x311465e7, 0x6228cbce, 0xc451979c, 0x88a32f39, 0x11465e73, 0x228cbce6,
     0x9d8a7a87, 0x3b14f50f, 0x7629ea1e, 0xec53d43c, 0xd8a7a879, 0xb14f50f3, 0x629ea1e7, 0xc53d43ce,
@@ -11,6 +11,6 @@ pub const T32: [u32; 64] = [
     0x8a7a879d, 0x14f50f3b, 0x29ea1e76, 0x53d43cec, 0xa7a879d8, 0x4f50f3b1, 0x9ea1e762, 0x3d43cec5,
 ];
 
-pub static SM3_IV: [u32; 8] = [
+pub(crate) static H0: [u32; 8] = [
     0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
 ];
diff --git a/sm3/src/lib.rs b/sm3/src/lib.rs
index ca9001d60..95062fa46 100644
--- a/sm3/src/lib.rs
+++ b/sm3/src/lib.rs
@@ -30,25 +30,102 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/sm3/0.4.0"
 )]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
+pub use digest::{self, Digest};
+
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U32, U64},
+    HashMarker, Output,
+};
 
+mod compress;
 mod consts;
-mod sm3;
 
-pub use digest::{self, Digest};
+use compress::compress;
+
+/// Core SM3 hasher state.
+#[derive(Clone)]
+pub struct Sm3Core {
+    block_len: u64,
+    h: [u32; 8],
+}
+
+impl HashMarker for Sm3Core {}
+
+impl BlockSizeUser for Sm3Core {
+    type BlockSize = U64;
+}
+
+impl BufferKindUser for Sm3Core {
+    type BufferKind = Eager;
+}
+
+impl OutputSizeUser for Sm3Core {
+    type OutputSize = U32;
+}
+
+impl UpdateCore for Sm3Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u64;
+        compress(&mut self.h, blocks);
+    }
+}
+
+impl FixedOutputCore for Sm3Core {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64;
+        let bit_len = 8 * (buffer.get_pos() as u64 + bs * self.block_len);
+
+        let mut h = self.h;
+        buffer.len64_padding_be(bit_len, |b| compress(&mut h, from_ref(b)));
+        for (chunk, v) in out.chunks_exact_mut(4).zip(h.iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
+        }
+    }
+}
+
+impl Default for Sm3Core {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            h: consts::H0,
+            block_len: 0,
+        }
+    }
+}
 
-#[cfg(feature = "std")]
-use digest::Update;
+impl Reset for Sm3Core {
+    #[inline]
+    fn reset(&mut self) {
+        *self = Default::default();
+    }
+}
 
-pub use crate::sm3::Sm3;
+impl AlgorithmName for Sm3Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sm3")
+    }
+}
 
-opaque_debug::implement!(Sm3);
+impl fmt::Debug for Sm3Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Sm3Core { ... }")
+    }
+}
 
-digest::impl_write!(Sm3);
+/// Sm3 hasher state.
+pub type Sm3 = CoreWrapper<Sm3Core>;
diff --git a/streebog/CHANGELOG.md b/streebog/CHANGELOG.md
index da35bcac0..b11a91b8c 100644
--- a/streebog/CHANGELOG.md
+++ b/streebog/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.2 (2020-08-13)
 ### Changed
 - Documentation update ([#185])
diff --git a/streebog/Cargo.toml b/streebog/Cargo.toml
index 7d8436b82..ade2b10d9 100644
--- a/streebog/Cargo.toml
+++ b/streebog/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "streebog"
-version = "0.9.2"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "Streebog (GOST R 34.11-2012) hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,12 +12,10 @@ keywords = ["crypto", "streebog", "gost", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/streebog/benches/mod.rs b/streebog/benches/mod.rs
new file mode 100644
index 000000000..1d61e4774
--- /dev/null
+++ b/streebog/benches/mod.rs
@@ -0,0 +1,22 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use streebog::{Streebog256, Streebog512};
+use test::Bencher;
+
+bench_update!(
+    Streebog256::default();
+    streebog256_10 10;
+    streebog256_100 100;
+    streebog256_1000 1000;
+    streebog256_10000 10000;
+);
+
+bench_update!(
+    Streebog512::default();
+    streebog512_10 10;
+    streebog512_100 100;
+    streebog512_1000 1000;
+    streebog512_10000 10000;
+);
diff --git a/streebog/benches/streebog256.rs b/streebog/benches/streebog256.rs
deleted file mode 100644
index b4b0df0a0..000000000
--- a/streebog/benches/streebog256.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(streebog::Streebog256);
diff --git a/streebog/benches/streebog512.rs b/streebog/benches/streebog512.rs
deleted file mode 100644
index c2216675d..000000000
--- a/streebog/benches/streebog512.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(streebog::Streebog512);
diff --git a/streebog/examples/streebog256sum.rs b/streebog/examples/streebog256sum.rs
deleted file mode 100644
index 4973d262a..000000000
--- a/streebog/examples/streebog256sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use streebog::{Digest, Streebog256};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Streebog256, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Streebog256, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/streebog/examples/streebog512sum.rs b/streebog/examples/streebog512sum.rs
deleted file mode 100644
index e2130ff63..000000000
--- a/streebog/examples/streebog512sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use streebog::{Digest, Streebog512};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Streebog512, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Streebog512, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/streebog/src/core_api.rs b/streebog/src/core_api.rs
new file mode 100644
index 000000000..f16da4a6c
--- /dev/null
+++ b/streebog/src/core_api.rs
@@ -0,0 +1,190 @@
+use core::{convert::TryInto, fmt};
+use digest::{
+    block_buffer::Eager,
+    consts::U64,
+    core_api::{
+        AlgorithmName, Block as GenBlock, BlockSizeUser, Buffer, BufferKindUser, OutputSizeUser,
+        TruncSide, UpdateCore, VariableOutputCore,
+    },
+    HashMarker, InvalidOutputSize, Output,
+};
+
+use crate::consts::{BLOCK_SIZE, C};
+use crate::table::SHUFFLED_LIN_TABLE;
+
+type Block = [u8; 64];
+
+/// Core block-level Streebog hasher with variable output size.
+///
+/// Supports initialization only for 32 and 64 byte output sizes,
+/// i.e. 256 and 512 bits respectively.
+#[derive(Clone)]
+pub struct StreebogVarCore {
+    h: Block,
+    n: [u64; 8],
+    sigma: [u64; 8],
+}
+
+#[inline(always)]
+fn lps(h: &mut Block, n: &Block) {
+    for i in 0..64 {
+        h[i] ^= n[i];
+    }
+
+    let mut buf = [0u64; 8];
+
+    for i in 0..4 {
+        for j in 0..8 {
+            let b = h[2 * i + 8 * j] as usize;
+            buf[2 * i] ^= SHUFFLED_LIN_TABLE[j][b];
+            let b = h[2 * i + 1 + 8 * j] as usize;
+            buf[2 * i + 1] ^= SHUFFLED_LIN_TABLE[j][b];
+        }
+    }
+
+    *h = to_bytes(&buf);
+}
+
+impl StreebogVarCore {
+    fn g(&mut self, n: &Block, m: &Block) {
+        let mut key = [0u8; 64];
+        let mut block = [0u8; 64];
+
+        key.copy_from_slice(&self.h);
+        block.copy_from_slice(m);
+
+        lps(&mut key, n);
+
+        #[allow(clippy::needless_range_loop)]
+        for i in 0..12 {
+            lps(&mut block, &key);
+            lps(&mut key, &C[i]);
+        }
+
+        for i in 0..64 {
+            self.h[i] ^= block[i] ^ key[i] ^ m[i];
+        }
+    }
+
+    fn update_sigma(&mut self, m: &Block) {
+        let t = from_bytes(m);
+        let mut carry = 0;
+        adc(&mut self.sigma[0], t[0], &mut carry);
+        adc(&mut self.sigma[1], t[1], &mut carry);
+        adc(&mut self.sigma[2], t[2], &mut carry);
+        adc(&mut self.sigma[3], t[3], &mut carry);
+        adc(&mut self.sigma[4], t[4], &mut carry);
+        adc(&mut self.sigma[5], t[5], &mut carry);
+        adc(&mut self.sigma[6], t[6], &mut carry);
+        adc(&mut self.sigma[7], t[7], &mut carry);
+    }
+
+    fn update_n(&mut self, len: u64) {
+        let mut carry = 0;
+        // note: `len` can not be bigger than block size,
+        // so `8*len` will never overflow
+        adc(&mut self.n[0], 8 * len, &mut carry);
+        adc(&mut self.n[1], 0, &mut carry);
+        adc(&mut self.n[2], 0, &mut carry);
+        adc(&mut self.n[3], 0, &mut carry);
+        adc(&mut self.n[4], 0, &mut carry);
+        adc(&mut self.n[5], 0, &mut carry);
+        adc(&mut self.n[6], 0, &mut carry);
+        adc(&mut self.n[7], 0, &mut carry);
+    }
+
+    fn compress(&mut self, block: &GenBlock<Self>, msg_len: u64) {
+        let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) };
+        self.g(&to_bytes(&self.n), block);
+        self.update_n(msg_len);
+        self.update_sigma(block);
+    }
+}
+
+impl HashMarker for StreebogVarCore {}
+
+impl BlockSizeUser for StreebogVarCore {
+    type BlockSize = U64;
+}
+
+impl BufferKindUser for StreebogVarCore {
+    type BufferKind = Eager;
+}
+
+impl UpdateCore for StreebogVarCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[GenBlock<Self>]) {
+        for block in blocks {
+            self.compress(block, BLOCK_SIZE as u64);
+        }
+    }
+}
+
+impl OutputSizeUser for StreebogVarCore {
+    type OutputSize = U64;
+}
+
+impl VariableOutputCore for StreebogVarCore {
+    const TRUNC_SIDE: TruncSide = TruncSide::Right;
+
+    #[inline]
+    fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
+        let h = match output_size {
+            32 => [1; 64],
+            64 => [0; 64],
+            _ => return Err(InvalidOutputSize),
+        };
+        let (n, sigma) = Default::default();
+        Ok(Self { h, n, sigma })
+    }
+
+    #[inline]
+    fn finalize_variable_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let pos = buffer.get_pos();
+        let block = buffer.pad_with_zeros();
+        block[pos] = 1;
+        self.compress(block, pos as u64);
+        self.g(&[0u8; 64], &to_bytes(&self.n));
+        self.g(&[0u8; 64], &to_bytes(&self.sigma));
+        out.copy_from_slice(&self.h);
+    }
+}
+
+impl AlgorithmName for StreebogVarCore {
+    #[inline]
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Streebog")
+    }
+}
+
+impl fmt::Debug for StreebogVarCore {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("StreebogVarCore { ... }")
+    }
+}
+
+#[inline(always)]
+fn adc(a: &mut u64, b: u64, carry: &mut u64) {
+    let ret = (*a as u128) + (b as u128) + (*carry as u128);
+    *a = ret as u64;
+    *carry = (ret >> 64) as u64;
+}
+
+#[inline(always)]
+fn to_bytes(b: &[u64; 8]) -> Block {
+    let mut t = [0; 64];
+    for (chunk, v) in t.chunks_exact_mut(8).zip(b.iter()) {
+        chunk.copy_from_slice(&v.to_le_bytes());
+    }
+    t
+}
+
+#[inline(always)]
+fn from_bytes(b: &Block) -> [u64; 8] {
+    let mut t = [0u64; 8];
+    for (v, chunk) in t.iter_mut().zip(b.chunks_exact(8)) {
+        *v = u64::from_le_bytes(chunk.try_into().unwrap());
+    }
+    t
+}
diff --git a/streebog/src/lib.rs b/streebog/src/lib.rs
index 53fc7eb94..9b2fca404 100644
--- a/streebog/src/lib.rs
+++ b/streebog/src/lib.rs
@@ -2,34 +2,29 @@
 //! in GOST R 34.11-2012.
 //!
 //! # Usage
-//! Hasher functionality is expressed via traits defined in the [`digest`]
-//! crate.
-//!
 //! ```rust
 //! use streebog::{Digest, Streebog256, Streebog512};
 //! use hex_literal::hex;
 //!
-//! // create a hasher object, to use it do not forget to import `Digest` trait
+//! // create Streebog256 hasher state
 //! let mut hasher = Streebog256::new();
 //! // write input message
-//! hasher.update(b"my");
-//! hasher.update(b" ");
-//! hasher.update(b"message");
+//! hasher.update("The quick brown fox jumps over the lazy dog");
 //! // read hash digest (it will consume hasher)
 //! let result = hasher.finalize();
 //!
 //! assert_eq!(result[..], hex!("
-//!     a47752ba9491bd1d52dd5dcea6d8c08e9b1ee70c42a2fc3e0d1a2852468c1329
+//!     3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4
 //! ")[..]);
 //!
 //! // same for Streebog512
 //! let mut hasher = Streebog512::new();
-//! hasher.update(b"my message");
+//! hasher.update("The quick brown fox jumps over the lazy dog.");
 //! let result = hasher.finalize();
 //!
 //! assert_eq!(result[..], hex!("
-//!     c40cc26c37a683c74459820d884b766d9c96697a8d168c0272db8f4ecca2935b
-//!     4164ede98fc9c8d2bafb1249b238676c81f5b97f98c393b99fdf2dc961391484
+//!     fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be8
+//!     2d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070
 //! ")[..]);
 //! ```
 //!
@@ -40,33 +35,28 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/streebog/0.10.0"
 )]
-#![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 #[cfg(feature = "std")]
 extern crate std;
 
+use digest::{
+    consts::{U32, U64},
+    core_api::{CoreWrapper, CtVariableCoreWrapper},
+};
+
 mod consts;
-mod streebog;
+mod core_api;
 mod table;
 
-use digest::consts::{U32, U64};
+pub use core_api::StreebogVarCore;
 pub use digest::{self, Digest};
 
-#[cfg(feature = "std")]
-use digest::Update;
-
-/// Streebog-256 cryptographic hash function
-pub type Streebog256 = streebog::Streebog<U32>;
-
-/// Streebog-512 cryptographic hash function
-pub type Streebog512 = streebog::Streebog<U64>;
-
-opaque_debug::implement!(Streebog512);
-opaque_debug::implement!(Streebog256);
-
-digest::impl_write!(Streebog512);
-digest::impl_write!(Streebog256);
+/// Streebog256 hasher.
+pub type Streebog256 = CoreWrapper<CtVariableCoreWrapper<StreebogVarCore, U32>>;
+/// Streebog512 hasher.
+pub type Streebog512 = CoreWrapper<CtVariableCoreWrapper<StreebogVarCore, U64>>;
diff --git a/streebog/src/streebog.rs b/streebog/src/streebog.rs
deleted file mode 100644
index 9856d6621..000000000
--- a/streebog/src/streebog.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-use block_buffer::{block_padding::ZeroPadding, BlockBuffer};
-use core::marker::PhantomData;
-use digest::consts::U64;
-use digest::generic_array::{ArrayLength, GenericArray};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
-
-use crate::consts::{BLOCK_SIZE, C};
-use crate::table::SHUFFLED_LIN_TABLE;
-
-type Block = [u8; 64];
-
-#[derive(Copy, Clone)]
-struct StreebogState {
-    h: Block,
-    n: Block,
-    sigma: Block,
-}
-
-#[inline(always)]
-fn lps(h: &mut Block, n: &Block) {
-    for i in 0..64 {
-        h[i] ^= n[i];
-    }
-
-    let mut buf = [0u64; 8];
-
-    for i in 0..4 {
-        for j in 0..8 {
-            let b = h[2 * i + 8 * j] as usize;
-            buf[2 * i] ^= SHUFFLED_LIN_TABLE[j][b];
-            let b = h[2 * i + 1 + 8 * j] as usize;
-            buf[2 * i + 1] ^= SHUFFLED_LIN_TABLE[j][b];
-        }
-    }
-
-    for (chunk, v) in h.chunks_exact_mut(8).zip(buf.iter()) {
-        chunk.copy_from_slice(&v.to_le_bytes());
-    }
-}
-
-impl StreebogState {
-    fn g(&mut self, n: &Block, m: &Block) {
-        let mut key = [0u8; 64];
-        let mut block = [0u8; 64];
-
-        key.copy_from_slice(&self.h);
-        block.copy_from_slice(m);
-
-        lps(&mut key, n);
-
-        #[allow(clippy::needless_range_loop)]
-        for i in 0..12 {
-            lps(&mut block, &key);
-            lps(&mut key, &C[i]);
-        }
-
-        for i in 0..64 {
-            self.h[i] ^= block[i] ^ key[i] ^ m[i];
-        }
-    }
-
-    fn update_sigma(&mut self, m: &Block) {
-        let mut carry = 0;
-        for (a, b) in self.sigma.iter_mut().zip(m.iter()) {
-            carry = (*a as u16) + (*b as u16) + (carry >> 8);
-            *a = (carry & 0xFF) as u8;
-        }
-    }
-
-    fn update_n(&mut self, mut l: u8) {
-        let res = u16::from(self.n[0]) + (u16::from(l) << 3);
-        self.n[0] = (res & 0xff) as u8;
-        l = (res >> 8) as u8;
-
-        for a in self.n.iter_mut().skip(1) {
-            let (res, over) = (*a).overflowing_add(l);
-            *a = res;
-            if over {
-                l = 1;
-            } else {
-                break;
-            }
-        }
-    }
-
-    fn process_block(&mut self, block: &GenericArray<u8, U64>, msg_len: u8) {
-        #[allow(unsafe_code)]
-        let block = unsafe { &*(block.as_ptr() as *const [u8; 64]) };
-        let n = self.n;
-        self.g(&n, block);
-        self.update_n(msg_len);
-        self.update_sigma(block);
-    }
-}
-
-#[derive(Clone)]
-pub struct Streebog<DigestSize: ArrayLength<u8> + Copy> {
-    buffer: BlockBuffer<U64>,
-    state: StreebogState,
-    // Phantom data to tie digest size to the struct
-    digest_size: PhantomData<DigestSize>,
-}
-
-impl<N> Default for Streebog<N>
-where
-    N: ArrayLength<u8> + Copy,
-{
-    fn default() -> Self {
-        let h = match N::to_usize() {
-            64 => [0u8; 64],
-            32 => [1u8; 64],
-            _ => unreachable!(),
-        };
-        Streebog {
-            buffer: Default::default(),
-            state: StreebogState {
-                h,
-                n: [0u8; 64],
-                sigma: [0u8; 64],
-            },
-            digest_size: Default::default(),
-        }
-    }
-}
-
-impl<N> BlockInput for Streebog<N>
-where
-    N: ArrayLength<u8> + Copy,
-{
-    type BlockSize = U64;
-}
-
-impl<N> Update for Streebog<N>
-where
-    N: ArrayLength<u8> + Copy,
-{
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let s = &mut self.state;
-        self.buffer
-            .input_block(input.as_ref(), |d| s.process_block(d, BLOCK_SIZE as u8));
-    }
-}
-
-impl<N> FixedOutputDirty for Streebog<N>
-where
-    N: ArrayLength<u8> + Copy,
-{
-    type OutputSize = N;
-
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, N>) {
-        let mut self_state = self.state;
-        let pos = self.buffer.position();
-
-        let block = self
-            .buffer
-            .pad_with::<ZeroPadding>()
-            .expect("we never use input_lazy");
-        block[pos] = 1;
-        self_state.process_block(block, pos as u8);
-
-        let n = self_state.n;
-        self_state.g(&[0u8; 64], &n);
-        let sigma = self_state.sigma;
-        self_state.g(&[0u8; 64], &sigma);
-
-        let n = BLOCK_SIZE - Self::OutputSize::to_usize();
-        out.copy_from_slice(&self_state.h[n..])
-    }
-}
-
-impl<N> Reset for Streebog<N>
-where
-    N: ArrayLength<u8> + Copy,
-{
-    fn reset(&mut self) {
-        self.buffer.reset();
-        self.state.h = match N::to_usize() {
-            64 => [0u8; 64],
-            32 => [1u8; 64],
-            _ => unreachable!(),
-        };
-        self.state.n = [0; 64];
-        self.state.sigma = [0; 64];
-    }
-}
diff --git a/tiger/CHANGELOG.md b/tiger/CHANGELOG.md
index ed74940b2..5f690082b 100644
--- a/tiger/CHANGELOG.md
+++ b/tiger/CHANGELOG.md
@@ -5,5 +5,14 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.2.0 (2021-12-07)
+### Added
+- `Tiger2` variant ([#217])
+
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.1.0 (2021-02-05)
 - Initial release
diff --git a/tiger/Cargo.toml b/tiger/Cargo.toml
index 144833559..3e3c7171d 100644
--- a/tiger/Cargo.toml
+++ b/tiger/Cargo.toml
@@ -1,8 +1,8 @@
 [package]
 name = "tiger"
-version = "0.1.0"
+version = "0.2.0" # Also update html_root_url in lib.rs when bumping this
 description = "Tiger hash function"
-authors = ["Ulrik Mikaelsson <ulrik.mikaelsson@gmail.com>", "RustCrypto Developers"]
+authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
 readme = "README.md"
 edition = "2018"
@@ -12,12 +12,10 @@ keywords = ["crypto", "hash", "tiger", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-byteorder = { version = "1", default-features = false }
+digest = "0.10"
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/tiger/LICENSE-MIT b/tiger/LICENSE-MIT
index d967eab37..d62c85c68 100644
--- a/tiger/LICENSE-MIT
+++ b/tiger/LICENSE-MIT
@@ -1,3 +1,4 @@
+Copyright (c) 2021 RustCrypto Developers
 Copyright (c) 2020 Ulrik Mikaelsson
 
 Permission is hereby granted, free of charge, to any
diff --git a/tiger/benches/lib.rs b/tiger/benches/lib.rs
deleted file mode 100644
index ee01c2a4b..000000000
--- a/tiger/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(tiger::Tiger);
diff --git a/tiger/benches/mod.rs b/tiger/benches/mod.rs
new file mode 100644
index 000000000..c9c7dbc73
--- /dev/null
+++ b/tiger/benches/mod.rs
@@ -0,0 +1,22 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use test::Bencher;
+use tiger::{Tiger, Tiger2};
+
+bench_update!(
+    Tiger::default();
+    tiger_10 10;
+    tiger_100 100;
+    tiger_1000 1000;
+    tiger_10000 10000;
+);
+
+bench_update!(
+    Tiger2::default();
+    tiger2_10 10;
+    tiger2_100 100;
+    tiger2_1000 1000;
+    tiger2_10000 10000;
+);
diff --git a/tiger/src/compress.rs b/tiger/src/compress.rs
new file mode 100644
index 000000000..20f7c56fc
--- /dev/null
+++ b/tiger/src/compress.rs
@@ -0,0 +1,72 @@
+use super::tables::{T1, T2, T3, T4};
+use super::State;
+use core::convert::TryInto;
+use digest::generic_array::{typenum::U64, GenericArray};
+
+#[inline(always)]
+fn round(a: &mut u64, b: &mut u64, c: &mut u64, x: &u64, mul: u8) {
+    *c ^= *x;
+    let c2: [u8; 8] = c.to_le_bytes();
+    let a2 = T1[usize::from(c2[0])]
+        ^ T2[usize::from(c2[2])]
+        ^ T3[usize::from(c2[4])]
+        ^ T4[usize::from(c2[6])];
+    let b2 = T4[usize::from(c2[1])]
+        ^ T3[usize::from(c2[3])]
+        ^ T2[usize::from(c2[5])]
+        ^ T1[usize::from(c2[7])];
+    *a = a.wrapping_sub(a2);
+    *b = b.wrapping_add(b2).wrapping_mul(u64::from(mul));
+}
+
+#[inline(always)]
+fn pass(a: &mut u64, b: &mut u64, c: &mut u64, x: &[u64; 8], mul: u8) {
+    round(a, b, c, &x[0], mul);
+    round(b, c, a, &x[1], mul);
+    round(c, a, b, &x[2], mul);
+    round(a, b, c, &x[3], mul);
+    round(b, c, a, &x[4], mul);
+    round(c, a, b, &x[5], mul);
+    round(a, b, c, &x[6], mul);
+    round(b, c, a, &x[7], mul);
+}
+
+#[inline(always)]
+fn key_schedule(x: &mut [u64; 8]) {
+    x[0] = x[0].wrapping_sub(x[7] ^ 0xA5A5_A5A5_A5A5_A5A5);
+    x[1] ^= x[0];
+    x[2] = x[2].wrapping_add(x[1]);
+    x[3] = x[3].wrapping_sub(x[2] ^ ((!x[1]) << 19));
+    x[4] ^= x[3];
+    x[5] = x[5].wrapping_add(x[4]);
+    x[6] = x[6].wrapping_sub(x[5] ^ ((!x[4]) >> 23));
+    x[7] ^= x[6];
+    x[0] = x[0].wrapping_add(x[7]);
+    x[1] = x[1].wrapping_sub(x[0] ^ ((!x[7]) << 19));
+    x[2] ^= x[1];
+    x[3] = x[3].wrapping_add(x[2]);
+    x[4] = x[4].wrapping_sub(x[3] ^ ((!x[2]) >> 23));
+    x[5] ^= x[4];
+    x[6] = x[6].wrapping_add(x[5]);
+    x[7] = x[7].wrapping_sub(x[6] ^ 0x0123_4567_89AB_CDEF);
+}
+
+pub(crate) fn compress(state: &mut State, blocks: &[GenericArray<u8, U64>]) {
+    let mut block: [u64; 8] = Default::default();
+    for raw_block in blocks {
+        for (o, chunk) in block.iter_mut().zip(raw_block.chunks_exact(8)) {
+            *o = u64::from_le_bytes(chunk.try_into().unwrap());
+        }
+        let [mut a, mut b, mut c] = *state;
+
+        pass(&mut a, &mut b, &mut c, &block, 5);
+        key_schedule(&mut block);
+        pass(&mut c, &mut a, &mut b, &block, 7);
+        key_schedule(&mut block);
+        pass(&mut b, &mut c, &mut a, &block, 9);
+
+        state[0] ^= a;
+        state[1] = b.wrapping_sub(state[1]);
+        state[2] = c.wrapping_add(state[2]);
+    }
+}
diff --git a/tiger/src/lib.rs b/tiger/src/lib.rs
index 9996735a2..8ef93ca50 100644
--- a/tiger/src/lib.rs
+++ b/tiger/src/lib.rs
@@ -1,1264 +1,211 @@
-//! The [Tiger][1] hash function, implemented through [Digest][2]
+//! An implementation of the [Tiger][1] cryptographic hash algorithms.
 //!
-//! [1]: http://www.cs.technion.ac.il/~biham/Reports/Tiger/tiger/tiger.html
-//! [2]: https://crates.io/crates/digest
+//! Tiger2 is a variant of the original Tiger with a small padding tweak.
+//!
+//! # Usage
+//!
+//! ```rust
+//! use hex_literal::hex;
+//! use tiger::{Tiger, Digest};
+//!
+//! // create a Tiger object
+//! let mut hasher = Tiger::new();
+//!
+//! // process input message
+//! hasher.update(b"hello world");
+//!
+//! // acquire hash digest in the form of GenericArray,
+//! // which in this case is equivalent to [u8; 24]
+//! let result = hasher.finalize();
+//! assert_eq!(result[..], hex!("4c8fbddae0b6f25832af45e7c62811bb64ec3e43691e9cc3"));
+//! ```
+//!
+//! Also see [RustCrypto/hashes][2] readme.
+//!
+//! [1]: https://en.wikipedia.org/wiki/Tiger_(hash_function)
+//! [2]: https://github.com/RustCrypto/hashes
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/tiger/0.2.0"
 )]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-pub use digest;
-
-use block_buffer::{
-    generic_array::typenum::consts::U64 as BB_U64, generic_array::GenericArray as BB_GenericArray,
-    BlockBuffer,
+pub use digest::{self, Digest};
+
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U24, U64},
+    HashMarker, Output,
 };
-use byteorder::{ByteOrder, LE};
-use digest::generic_array::typenum::{U24, U64};
-use digest::generic_array::GenericArray;
 
-type BlockBuffer512 = BlockBuffer<BB_U64>;
+mod compress;
+mod tables;
+use compress::compress;
 
-const PADDING_BLOCK: [u8; 64] = [
-    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-];
-
-const H0: [u64; 3] = [
+type State = [u64; 3];
+const S0: State = [
     0x0123_4567_89AB_CDEF,
     0xFEDC_BA98_7654_3210,
     0xF096_A5B4_C3B2_E187,
 ];
 
-/// The Tiger hasher
+/// Core Tiger hasher state.
 #[derive(Clone)]
-pub struct Tiger {
-    length_bytes: u64,
-    buffer: BlockBuffer512,
-    state: [u64; 3],
+pub struct TigerCore {
+    block_len: u64,
+    state: State,
 }
 
-impl Default for Tiger {
-    fn default() -> Self {
-        Tiger {
-            length_bytes: 0,
-            buffer: Default::default(),
-            state: H0,
-        }
-    }
+impl HashMarker for TigerCore {}
+
+impl BlockSizeUser for TigerCore {
+    type BlockSize = U64;
+}
+
+impl BufferKindUser for TigerCore {
+    type BufferKind = Eager;
 }
 
-impl Tiger {
+impl OutputSizeUser for TigerCore {
+    type OutputSize = U24;
+}
+
+impl UpdateCore for TigerCore {
     #[inline]
-    fn fill_up_to(from: usize, to: usize, modulo: usize) -> usize {
-        let _from = from + 1;
-        let _to = to + modulo;
-        1 + ((_to - _from) % modulo)
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u64;
+        compress(&mut self.state, blocks);
     }
+}
 
+impl FixedOutputCore for TigerCore {
     #[inline]
-    fn finalize(&mut self) -> [u64; 3] {
-        {
-            let self_state = &mut self.state;
-            let pad_size = Self::fill_up_to(self.buffer.position(), 64 - 8, 64);
-            self.buffer.input_block(&PADDING_BLOCK[..pad_size], |d| {
-                Self::compress(self_state, d)
-            });
-
-            let mut length: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
-            LE::write_u64(&mut length, (self.length_bytes << 3) as u64);
-            self.buffer
-                .input_block(&length, |d| Self::compress(self_state, d));
-
-            assert_eq!(self.buffer.position(), 0);
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64 as u64;
+        let pos = buffer.get_pos() as u64;
+        let bit_len = 8 * (pos + bs * self.block_len);
+
+        buffer.digest_pad(1, &bit_len.to_le_bytes(), |b| {
+            compress(&mut self.state, from_ref(b))
+        });
+        for (chunk, v) in out.chunks_exact_mut(8).zip(self.state.iter()) {
+            chunk.copy_from_slice(&v.to_le_bytes());
         }
-        self.state
     }
+}
 
-    fn consume(&mut self, input: &[u8]) {
-        // Unlike Sha1 and Sha2, the length value in Tiger is defined as
-        // the length of the message mod 2^64 - ie: integer overflow is OK.
-        self.length_bytes += input.len() as u64;
-        let self_state = &mut self.state;
-        self.buffer
-            .input_block(input, |d| Self::compress(self_state, d));
+impl Default for TigerCore {
+    fn default() -> Self {
+        Self {
+            block_len: 0,
+            state: S0,
+        }
     }
+}
 
-    #[inline]
-    fn compress(state: &mut [u64; 3], input: &BB_GenericArray<u8, BB_U64>) {
-        let mut inputs: [u64; 8] = Default::default();
-        LE::read_u64_into(input, &mut inputs);
-        let mut a = state[0];
-        let mut b = state[1];
-        let mut c = state[2];
-
-        Self::pass(&mut a, &mut b, &mut c, &inputs, 5);
-        Self::key_schedule(&mut inputs);
-        Self::pass(&mut c, &mut a, &mut b, &inputs, 7);
-        Self::key_schedule(&mut inputs);
-        Self::pass(&mut b, &mut c, &mut a, &inputs, 9);
-
-        state[0] ^= a;
-        state[1] = b.wrapping_sub(state[1]);
-        state[2] = c.wrapping_add(state[2]);
+impl Reset for TigerCore {
+    fn reset(&mut self) {
+        *self = Default::default();
     }
+}
 
-    #[inline]
-    fn pass(a: &mut u64, b: &mut u64, c: &mut u64, x: &[u64; 8], mul: u8) {
-        Self::round(a, b, c, &x[0], mul);
-        Self::round(b, c, a, &x[1], mul);
-        Self::round(c, a, b, &x[2], mul);
-        Self::round(a, b, c, &x[3], mul);
-        Self::round(b, c, a, &x[4], mul);
-        Self::round(c, a, b, &x[5], mul);
-        Self::round(a, b, c, &x[6], mul);
-        Self::round(b, c, a, &x[7], mul);
+impl AlgorithmName for TigerCore {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Tiger")
     }
+}
 
-    #[inline]
-    #[allow(clippy::trivially_copy_pass_by_ref)]
-    fn round(a: &mut u64, b: &mut u64, c: &mut u64, x: &u64, mul: u8) {
-        *c ^= *x;
-        let mut c_: [u8; 8] = Default::default();
-        LE::write_u64(&mut c_, *c);
-        *a = a.wrapping_sub(
-            T1[c_[0] as usize] ^ T2[c_[2] as usize] ^ T3[c_[4] as usize] ^ T4[c_[6] as usize],
-        );
-        *b = b.wrapping_add(
-            T4[c_[1] as usize] ^ T3[c_[3] as usize] ^ T2[c_[5] as usize] ^ T1[c_[7] as usize],
-        );
-        *b = b.wrapping_mul(u64::from(mul));
+impl fmt::Debug for TigerCore {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("TigerCore { ... }")
     }
+}
 
-    #[inline]
-    fn key_schedule(x: &mut [u64; 8]) {
-        x[0] = x[0].wrapping_sub(x[7] ^ 0xA5A5_A5A5_A5A5_A5A5);
-        x[1] ^= x[0];
-        x[2] = x[2].wrapping_add(x[1]);
-        x[3] = x[3].wrapping_sub(x[2] ^ ((!x[1]) << 19));
-        x[4] ^= x[3];
-        x[5] = x[5].wrapping_add(x[4]);
-        x[6] = x[6].wrapping_sub(x[5] ^ ((!x[4]) >> 23));
-        x[7] ^= x[6];
-        x[0] = x[0].wrapping_add(x[7]);
-        x[1] = x[1].wrapping_sub(x[0] ^ ((!x[7]) << 19));
-        x[2] ^= x[1];
-        x[3] = x[3].wrapping_add(x[2]);
-        x[4] = x[4].wrapping_sub(x[3] ^ ((!x[2]) >> 23));
-        x[5] ^= x[4];
-        x[6] = x[6].wrapping_add(x[5]);
-        x[7] = x[7].wrapping_sub(x[6] ^ 0x0123_4567_89AB_CDEF);
-    }
+/// Core Tiger2 hasher state.
+#[derive(Clone)]
+pub struct Tiger2Core {
+    block_len: u64,
+    state: State,
 }
 
-impl digest::BlockInput for Tiger {
+impl HashMarker for Tiger2Core {}
+
+impl BlockSizeUser for Tiger2Core {
     type BlockSize = U64;
 }
 
-impl digest::Reset for Tiger {
-    fn reset(&mut self) {
-        self.buffer.reset();
-        self.length_bytes = 0;
-        self.state = H0;
-    }
+impl BufferKindUser for Tiger2Core {
+    type BufferKind = Eager;
 }
 
-impl digest::Update for Tiger {
-    #[inline]
+impl OutputSizeUser for Tiger2Core {
+    type OutputSize = U24;
+}
 
-    fn update(&mut self, data: impl AsRef<[u8]>) {
-        self.consume(data.as_ref());
+impl UpdateCore for Tiger2Core {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        self.block_len += blocks.len() as u64;
+        compress(&mut self.state, blocks);
     }
 }
 
-impl digest::FixedOutputDirty for Tiger {
-    type OutputSize = U24;
-
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, Self::OutputSize>) {
-        LE::write_u64_into(&self.finalize(), &mut out.as_mut_slice());
+impl FixedOutputCore for Tiger2Core {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let bs = Self::BlockSize::U64 as u64;
+        let pos = buffer.get_pos() as u64;
+        let bit_len = 8 * (pos + bs * self.block_len);
+
+        buffer.len64_padding_le(bit_len, |b| compress(&mut self.state, from_ref(b)));
+        for (chunk, v) in out.chunks_exact_mut(8).zip(self.state.iter()) {
+            chunk.copy_from_slice(&v.to_le_bytes());
+        }
     }
 }
 
-// Conditionally compile the module `test` only when the test-suite is run.
-#[cfg(test)]
-mod test {
-    use super::Tiger;
-    use digest::Digest;
-    use hex_literal::hex;
+impl Default for Tiger2Core {
+    fn default() -> Self {
+        Self {
+            block_len: 0,
+            state: [
+                0x0123_4567_89AB_CDEF,
+                0xFEDC_BA98_7654_3210,
+                0xF096_A5B4_C3B2_E187,
+            ],
+        }
+    }
+}
 
-    #[test]
-    fn test_vectors() {
-        let examples = [
-            ("", hex!("3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3")),
-            ("abc", hex!("2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93")),
-            ("Tiger", hex!("dd00230799f5009fec6debc838bb6a27df2b9d6f110c7937")),
-            (
-                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-",
-                hex!("f71c8583902afb879edfe610f82c0d4786a3a534504486b5"),
-            ),
-            (
-                "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz+0123456789",
-                hex!("48ceeb6308b87d46e95d656112cdf18d97915f9765658957"),
-            ),
-            (
-                "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham",
-                hex!("8a866829040a410c729ad23f5ada711603b3cdd357e4c15e"),
-            ),
-            (
-                "Tiger - A Fast New Hash Function, by Ross Anderson and Eli Biham, proceedings of Fast Software Encryption 3, Cambridge.",
-                hex!("ce55a6afd591f5ebac547ff84f89227f9331dab0b611c889"),
-            ),
-            (
-                "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-                hex!("0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e"),
-            ),
-            (
-                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
-                hex!("08c8dcb467e4c7f910966c399fdc9b164be15b792d432224"),
-            ),
-        ];
+impl Reset for Tiger2Core {
+    #[inline]
+    fn reset(&mut self) {
+        *self = Default::default();
+    }
+}
 
-        for &(input, answer) in examples.iter() {
-            let result = Tiger::digest(input.as_bytes());
+impl AlgorithmName for Tiger2Core {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Tiger2")
+    }
+}
 
-            assert_eq!(*result, answer);
-        }
+impl fmt::Debug for Tiger2Core {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Tiger2Core { ... }")
     }
 }
 
-static T1: [u64; 256] = [
-    0x02AA_B17C_F7E9_0C5E, /*    0 */
-    0xAC42_4B03_E243_A8EC, /*    1 */
-    0x72CD_5BE3_0DD5_FCD3, /*    2 */
-    0x6D01_9B93_F6F9_7F3A, /*    3 */
-    0xCD99_78FF_D21F_9193, /*    4 */
-    0x7573_A1C9_7080_29E2, /*    5 */
-    0xB164_326B_922A_83C3, /*    6 */
-    0x4688_3EEE_0491_5870, /*    7 */
-    0xEAAC_E305_7103_ECE6, /*    8 */
-    0xC541_69B8_08A3_535C, /*    9 */
-    0x4CE7_5491_8DDE_C47C, /*   10 */
-    0x0AA2_F4DF_DC0D_F40C, /*   11 */
-    0x10B7_6F18_A74D_BEFA, /*   12 */
-    0xC6CC_B623_5AD1_AB6A, /*   13 */
-    0x1372_6121_572F_E2FF, /*   14 */
-    0x1A48_8C6F_199D_921E, /*   15 */
-    0x4BC9_F9F4_DA00_07CA, /*   16 */
-    0x26F5_E6F6_E852_41C7, /*   17 */
-    0x8590_79DB_EA59_47B6, /*   18 */
-    0x4F18_85C5_C99E_8C92, /*   19 */
-    0xD78E_761E_A96F_864B, /*   20 */
-    0x8E36_428C_52B5_C17D, /*   21 */
-    0x69CF_6827_3730_63C1, /*   22 */
-    0xB607_C93D_9BB4_C56E, /*   23 */
-    0x7D82_0E76_0E76_B5EA, /*   24 */
-    0x645C_9CC6_F07F_DC42, /*   25 */
-    0xBF38_A078_2433_42E0, /*   26 */
-    0x5F6B_343C_9D2E_7D04, /*   27 */
-    0xF2C2_8AEB_600B_0EC6, /*   28 */
-    0x6C0E_D85F_7254_BCAC, /*   29 */
-    0x7159_2281_A4DB_4FE5, /*   30 */
-    0x1967_FA69_CE0F_ED9F, /*   31 */
-    0xFD52_93F8_B965_45DB, /*   32 */
-    0xC879_E9D7_F2A7_600B, /*   33 */
-    0x8602_4892_0193_194E, /*   34 */
-    0xA4F9_533B_2D9C_C0B3, /*   35 */
-    0x9053_836C_1595_7613, /*   36 */
-    0xDB6D_CF8A_FC35_7BF1, /*   37 */
-    0x18BE_EA7A_7A37_0F57, /*   38 */
-    0x0371_17CA_50B9_9066, /*   39 */
-    0x6AB3_0A97_7442_4A35, /*   40 */
-    0xF4E9_2F02_E325_249B, /*   41 */
-    0x7739_DB07_061C_CAE1, /*   42 */
-    0xD8F3_B49C_ECA4_2A05, /*   43 */
-    0xBD56_BE3F_5138_2F73, /*   44 */
-    0x45FA_ED58_43B0_BB28, /*   45 */
-    0x1C81_3D5C_11BF_1F83, /*   46 */
-    0x8AF0_E4B6_D75F_A169, /*   47 */
-    0x33EE_18A4_87AD_9999, /*   48 */
-    0x3C26_E8EA_B1C9_4410, /*   49 */
-    0xB510_102B_C0A8_22F9, /*   50 */
-    0x141E_EF31_0CE6_123B, /*   51 */
-    0xFC65_B900_59DD_B154, /*   52 */
-    0xE015_8640_C5E0_E607, /*   53 */
-    0x884E_0798_26C3_A3CF, /*   54 */
-    0x930D_0D95_23C5_35FD, /*   55 */
-    0x3563_8D75_4E9A_2B00, /*   56 */
-    0x4085_FCCF_4046_9DD5, /*   57 */
-    0xC4B1_7AD2_8BE2_3A4C, /*   58 */
-    0xCAB2_F0FC_6A3E_6A2E, /*   59 */
-    0x2860_971A_6B94_3FCD, /*   60 */
-    0x3DDE_6EE2_12E3_0446, /*   61 */
-    0x6222_F32A_E017_65AE, /*   62 */
-    0x5D55_0BB5_4783_08FE, /*   63 */
-    0xA9EF_A98D_A0ED_A22A, /*   64 */
-    0xC351_A716_86C4_0DA7, /*   65 */
-    0x1105_586D_9C86_7C84, /*   66 */
-    0xDCFF_EE85_FDA2_2853, /*   67 */
-    0xCCFB_D026_2C5E_EF76, /*   68 */
-    0xBAF2_94CB_8990_D201, /*   69 */
-    0xE694_64F5_2AFA_D975, /*   70 */
-    0x94B0_13AF_DF13_3E14, /*   71 */
-    0x06A7_D1A3_2823_C958, /*   72 */
-    0x6F95_FE51_30F6_1119, /*   73 */
-    0xD92A_B34E_462C_06C0, /*   74 */
-    0xED7B_DE33_887C_71D2, /*   75 */
-    0x7974_6D6E_6518_393E, /*   76 */
-    0x5BA4_1938_5D71_3329, /*   77 */
-    0x7C1B_A6B9_48A9_7564, /*   78 */
-    0x3198_7C19_7BFD_AC67, /*   79 */
-    0xDE6C_23C4_4B05_3D02, /*   80 */
-    0x581C_49FE_D002_D64D, /*   81 */
-    0xDD47_4D63_3826_1571, /*   82 */
-    0xAA45_46C3_E473_D062, /*   83 */
-    0x928F_CE34_9455_F860, /*   84 */
-    0x4816_1BBA_CAAB_94D9, /*   85 */
-    0x6391_2430_770E_6F68, /*   86 */
-    0x6EC8_A5E6_02C6_641C, /*   87 */
-    0x8728_2515_337D_DD2B, /*   88 */
-    0x2CDA_6B42_034B_701B, /*   89 */
-    0xB03D_37C1_81CB_096D, /*   90 */
-    0xE108_4382_66C7_1C6F, /*   91 */
-    0x2B31_80C7_EB51_B255, /*   92 */
-    0xDF92_B82F_96C0_8BBC, /*   93 */
-    0x5C68_C8C0_A632_F3BA, /*   94 */
-    0x5504_CC86_1C3D_0556, /*   95 */
-    0xABBF_A4E5_5FB2_6B8F, /*   96 */
-    0x4184_8B0A_B3BA_CEB4, /*   97 */
-    0xB334_A273_AA44_5D32, /*   98 */
-    0xBCA6_96F0_A85A_D881, /*   99 */
-    0x24F6_EC65_B528_D56C, /*  100 */
-    0x0CE1_512E_90F4_524A, /*  101 */
-    0x4E9D_D79D_5506_D35A, /*  102 */
-    0x2589_05FA_C6CE_9779, /*  103 */
-    0x2019_295B_3E10_9B33, /*  104 */
-    0xF8A9_478B_73A0_54CC, /*  105 */
-    0x2924_F2F9_3441_7EB0, /*  106 */
-    0x3993_357D_536D_1BC4, /*  107 */
-    0x38A8_1AC2_1DB6_FF8B, /*  108 */
-    0x47C4_FBF1_7D60_16BF, /*  109 */
-    0x1E0F_AADD_7667_E3F5, /*  110 */
-    0x7ABC_FF62_938B_EB96, /*  111 */
-    0xA78D_AD94_8FC1_79C9, /*  112 */
-    0x8F1F_98B7_2911_E50D, /*  113 */
-    0x61E4_8EAE_2712_1A91, /*  114 */
-    0x4D62_F7AD_3185_9808, /*  115 */
-    0xECEB_A345_EF5C_EAEB, /*  116 */
-    0xF5CE_B25E_BC96_84CE, /*  117 */
-    0xF633_E20C_B7F7_6221, /*  118 */
-    0xA32C_DF06_AB82_93E4, /*  119 */
-    0x985A_202C_A5EE_2CA4, /*  120 */
-    0xCF0B_8447_CC8A_8FB1, /*  121 */
-    0x9F76_5244_9798_59A3, /*  122 */
-    0xA8D5_16B1_A124_0017, /*  123 */
-    0x0BD7_BA3E_BB5D_C726, /*  124 */
-    0xE54B_CA55_B86A_DB39, /*  125 */
-    0x1D7A_3AFD_6C47_8063, /*  126 */
-    0x519E_C608_E766_9EDD, /*  127 */
-    0x0E57_15A2_D149_AA23, /*  128 */
-    0x177D_4571_848F_F194, /*  129 */
-    0xEEB5_5F32_4101_4C22, /*  130 */
-    0x0F5E_5CA1_3A6E_2EC2, /*  131 */
-    0x8029_927B_75F5_C361, /*  132 */
-    0xAD13_9FAB_C3D6_E436, /*  133 */
-    0x0D5D_F1A9_4CCF_402F, /*  134 */
-    0x3E8B_D948_BEA5_DFC8, /*  135 */
-    0xA5A0_D357_BD3F_F77E, /*  136 */
-    0xA2D1_2E25_1F74_F645, /*  137 */
-    0x66FD_9E52_5E81_A082, /*  138 */
-    0x2E0C_90CE_7F68_7A49, /*  139 */
-    0xC2E8_BCBE_BA97_3BC5, /*  140 */
-    0x0000_01BC_E509_745F, /*  141 */
-    0x4237_77BB_E6DA_B3D6, /*  142 */
-    0xD166_1C7E_AEF0_6EB5, /*  143 */
-    0xA178_1F35_4DAA_CFD8, /*  144 */
-    0x2D11_284A_2B16_AFFC, /*  145 */
-    0xF1FC_4F67_FA89_1D1F, /*  146 */
-    0x73EC_C25D_CB92_0ADA, /*  147 */
-    0xAE61_0C22_C2A1_2651, /*  148 */
-    0x96E0_A810_D356_B78A, /*  149 */
-    0x5A9A_381F_2FE7_870F, /*  150 */
-    0xD5AD_62ED_E94E_5530, /*  151 */
-    0xD225_E5E8_368D_1427, /*  152 */
-    0x6597_7B70_C7AF_4631, /*  153 */
-    0x99F8_89B2_DE39_D74F, /*  154 */
-    0x233F_30BF_54E1_D143, /*  155 */
-    0x9A96_75D3_D9A6_3C97, /*  156 */
-    0x5470_554F_F334_F9A8, /*  157 */
-    0x166A_CB74_4A4F_5688, /*  158 */
-    0x70C7_4CAA_B2E4_AEAD, /*  159 */
-    0xF0D0_9164_6F29_4D12, /*  160 */
-    0x57B8_2A89_6840_31D1, /*  161 */
-    0xEFD9_5A5A_61BE_0B6B, /*  162 */
-    0x2FBD_12E9_69F2_F29A, /*  163 */
-    0x9BD3_7013_FEFF_9FE8, /*  164 */
-    0x3F9B_0404_D608_5A06, /*  165 */
-    0x4940_C1F3_166C_FE15, /*  166 */
-    0x0954_2C4D_CDF3_DEFB, /*  167 */
-    0xB4C5_2183_85CD_5CE3, /*  168 */
-    0xC935_B7DC_4462_A641, /*  169 */
-    0x3417_F8A6_8ED3_B63F, /*  170 */
-    0xB809_5929_5B21_5B40, /*  171 */
-    0xF99C_DAEF_3B8C_8572, /*  172 */
-    0x018C_0614_F8FC_B95D, /*  173 */
-    0x1B14_ACCD_1A3A_CDF3, /*  174 */
-    0x84D4_71F2_00BB_732D, /*  175 */
-    0xC1A3_110E_95E8_DA16, /*  176 */
-    0x430A_7220_BF1A_82B8, /*  177 */
-    0xB77E_090D_39DF_210E, /*  178 */
-    0x5EF4_BD9F_3CD0_5E9D, /*  179 */
-    0x9D4F_F6DA_7E57_A444, /*  180 */
-    0xDA1D_60E1_83D4_A5F8, /*  181 */
-    0xB287_C384_1799_8E47, /*  182 */
-    0xFE3E_DC12_1BB3_1886, /*  183 */
-    0xC7FE_3CCC_980C_CBEF, /*  184 */
-    0xE46F_B590_189B_FD03, /*  185 */
-    0x3732_FD46_9A4C_57DC, /*  186 */
-    0x7EF7_00A0_7CF1_AD65, /*  187 */
-    0x59C6_4468_A31D_8859, /*  188 */
-    0x762F_B0B4_D45B_61F6, /*  189 */
-    0x155B_AED0_9904_7718, /*  190 */
-    0x6875_5E4C_3D50_BAA6, /*  191 */
-    0xE921_4E7F_22D8_B4DF, /*  192 */
-    0x2ADD_BF53_2EAC_95F4, /*  193 */
-    0x32AE_3909_B4BD_0109, /*  194 */
-    0x834D_F537_B08E_3450, /*  195 */
-    0xFA20_9DA8_4220_728D, /*  196 */
-    0x9E69_1D9B_9EFE_23F7, /*  197 */
-    0x0446_D288_C4AE_8D7F, /*  198 */
-    0x7B4C_C524_E169_785B, /*  199 */
-    0x21D8_7F01_35CA_1385, /*  200 */
-    0xCEBB_400F_137B_8AA5, /*  201 */
-    0x272E_2B66_5807_96BE, /*  202 */
-    0x3612_2641_25C2_B0DE, /*  203 */
-    0x0577_02BD_AD1E_FBB2, /*  204 */
-    0xD4BA_BB8E_ACF8_4BE9, /*  205 */
-    0x9158_3139_641B_C67B, /*  206 */
-    0x8BDC_2DE0_8036_E024, /*  207 */
-    0x603C_8156_F49F_68ED, /*  208 */
-    0xF7D2_36F7_DBEF_5111, /*  209 */
-    0x9727_C459_8AD2_1E80, /*  210 */
-    0xA08A_0896_670A_5FD7, /*  211 */
-    0xCB4A_8F43_09EB_A9CB, /*  212 */
-    0x81AF_564B_0F70_36A1, /*  213 */
-    0xC0B9_9AA7_7819_9ABD, /*  214 */
-    0x959F_1EC8_3FC8_E952, /*  215 */
-    0x8C50_5077_794A_81B9, /*  216 */
-    0x3ACA_AF8F_0563_38F0, /*  217 */
-    0x07B4_3F50_627A_6778, /*  218 */
-    0x4A44_AB49_F5EC_CC77, /*  219 */
-    0x3BC3_D6E4_B679_EE98, /*  220 */
-    0x9CC0_D4D1_CF14_108C, /*  221 */
-    0x4406_C00B_206B_C8A0, /*  222 */
-    0x82A1_8854_C8D7_2D89, /*  223 */
-    0x67E3_66B3_5C3C_432C, /*  224 */
-    0xB923_DD61_102B_37F2, /*  225 */
-    0x56AB_2779_D884_271D, /*  226 */
-    0xBE83_E1B0_FF15_25AF, /*  227 */
-    0xFB7C_65D4_217E_49A9, /*  228 */
-    0x6BDB_E0E7_6D48_E7D4, /*  229 */
-    0x08DF_8287_45D9_179E, /*  230 */
-    0x22EA_6A9A_DD53_BD34, /*  231 */
-    0xE36E_141C_5622_200A, /*  232 */
-    0x7F80_5D1B_8CB7_50EE, /*  233 */
-    0xAFE5_C7A5_9F58_E837, /*  234 */
-    0xE27F_996A_4FB1_C23C, /*  235 */
-    0xD386_7DFB_0775_F0D0, /*  236 */
-    0xD0E6_73DE_6E88_891A, /*  237 */
-    0x123A_EB9E_AFB8_6C25, /*  238 */
-    0x30F1_D5D5_C145_B895, /*  239 */
-    0xBB43_4A2D_EE72_69E7, /*  240 */
-    0x78CB_67EC_F931_FA38, /*  241 */
-    0xF33B_0372_323B_BF9C, /*  242 */
-    0x52D6_6336_FB27_9C74, /*  243 */
-    0x505F_33AC_0AFB_4EAA, /*  244 */
-    0xE8A5_CD99_A2CC_E187, /*  245 */
-    0x5349_7480_1E2D_30BB, /*  246 */
-    0x8D2D_5711_D587_6D90, /*  247 */
-    0x1F1A_4128_91BC_038E, /*  248 */
-    0xD6E2_E71D_82E5_6648, /*  249 */
-    0x7403_6C3A_4977_32B7, /*  250 */
-    0x89B6_7ED9_6361_F5AB, /*  251 */
-    0xFFED_95D8_F1EA_02A2, /*  252 */
-    0xE72B_3BD6_1464_D43D, /*  253 */
-    0xA630_0F17_0BDC_4820, /*  254 */
-    0xEBC1_8760_ED78_A77A, /*  255 */
-];
-static T2: [u64; 256] = [
-    0xE6A6_BE5A_05A1_2138, /*  256 */
-    0xB5A1_22A5_B4F8_7C98, /*  257 */
-    0x563C_6089_140B_6990, /*  258 */
-    0x4C46_CB2E_391F_5DD5, /*  259 */
-    0xD932_ADDB_C9B7_9434, /*  260 */
-    0x08EA_70E4_2015_AFF5, /*  261 */
-    0xD765_A667_3E47_8CF1, /*  262 */
-    0xC4FB_757E_AB27_8D99, /*  263 */
-    0xDF11_C686_2D6E_0692, /*  264 */
-    0xDDEB_84F1_0D7F_3B16, /*  265 */
-    0x6F2E_F604_A665_EA04, /*  266 */
-    0x4A8E_0F0F_F0E0_DFB3, /*  267 */
-    0xA5ED_EEF8_3DBC_BA51, /*  268 */
-    0xFC4F_0A2A_0EA4_371E, /*  269 */
-    0xE83E_1DA8_5CB3_8429, /*  270 */
-    0xDC8F_F882_BA1B_1CE2, /*  271 */
-    0xCD45_505E_8353_E80D, /*  272 */
-    0x18D1_9A00_D4DB_0717, /*  273 */
-    0x34A0_CFED_A5F3_8101, /*  274 */
-    0x0BE7_7E51_8887_CAF2, /*  275 */
-    0x1E34_1438_B3C4_5136, /*  276 */
-    0xE057_97F4_9089_CCF9, /*  277 */
-    0xFFD2_3F9D_F259_1D14, /*  278 */
-    0x543D_DA22_8595_C5CD, /*  279 */
-    0x661F_81FD_9905_2A33, /*  280 */
-    0x8736_E641_DB0F_7B76, /*  281 */
-    0x1522_7725_418E_5307, /*  282 */
-    0xE25F_7F46_162E_B2FA, /*  283 */
-    0x48A8_B212_6C13_D9FE, /*  284 */
-    0xAFDC_5417_92E7_6EEA, /*  285 */
-    0x03D9_12BF_C6D1_898F, /*  286 */
-    0x31B1_AAFA_1B83_F51B, /*  287 */
-    0xF1AC_2796_E42A_B7D9, /*  288 */
-    0x40A3_A7D7_FCD2_EBAC, /*  289 */
-    0x1056_136D_0AFB_BCC5, /*  290 */
-    0x7889_E1DD_9A6D_0C85, /*  291 */
-    0xD335_2578_2A79_74AA, /*  292 */
-    0xA7E2_5D09_078A_C09B, /*  293 */
-    0xBD41_38B3_EAC6_EDD0, /*  294 */
-    0x920A_BFBE_71EB_9E70, /*  295 */
-    0xA2A5_D0F5_4FC2_625C, /*  296 */
-    0xC054_E36B_0B12_90A3, /*  297 */
-    0xF6DD_59FF_62FE_932B, /*  298 */
-    0x3537_3545_11A8_AC7D, /*  299 */
-    0xCA84_5E91_72FA_DCD4, /*  300 */
-    0x84F8_2B60_329D_20DC, /*  301 */
-    0x79C6_2CE1_CD67_2F18, /*  302 */
-    0x8B09_A2AD_D124_642C, /*  303 */
-    0xD0C1_E96A_19D9_E726, /*  304 */
-    0x5A78_6A9B_4BA9_500C, /*  305 */
-    0x0E02_0336_634C_43F3, /*  306 */
-    0xC17B_474A_EB66_D822, /*  307 */
-    0x6A73_1AE3_EC9B_AAC2, /*  308 */
-    0x8226_667A_E084_0258, /*  309 */
-    0x67D4_5676_91CA_ECA5, /*  310 */
-    0x1D94_155C_4875_ADB5, /*  311 */
-    0x6D00_FD98_5B81_3FDF, /*  312 */
-    0x5128_6EFC_B774_CD06, /*  313 */
-    0x5E88_3447_1FA7_44AF, /*  314 */
-    0xF72C_A0AE_E761_AE2E, /*  315 */
-    0xBE40_E4CD_AEE8_E09A, /*  316 */
-    0xE997_0BBB_5118_F665, /*  317 */
-    0x726E_4BEB_33DF_1964, /*  318 */
-    0x703B_0007_2919_9762, /*  319 */
-    0x4631_D816_F5EF_30A7, /*  320 */
-    0xB880_B5B5_1504_A6BE, /*  321 */
-    0x6417_93C3_7ED8_4B6C, /*  322 */
-    0x7B21_ED77_F6E9_7D96, /*  323 */
-    0x7763_0631_2EF9_6B73, /*  324 */
-    0xAE52_8948_E86F_F3F4, /*  325 */
-    0x53DB_D7F2_86A3_F8F8, /*  326 */
-    0x16CA_DCE7_4CFC_1063, /*  327 */
-    0x005C_19BD_FA52_C6DD, /*  328 */
-    0x6886_8F5D_64D4_6AD3, /*  329 */
-    0x3A9D_512C_CF1E_186A, /*  330 */
-    0x367E_62C2_3856_60AE, /*  331 */
-    0xE359_E7EA_77DC_B1D7, /*  332 */
-    0x526C_0773_749A_BE6E, /*  333 */
-    0x735A_E5F9_D09F_734B, /*  334 */
-    0x493F_C7CC_8A55_8BA8, /*  335 */
-    0xB0B9_C153_3041_AB45, /*  336 */
-    0x3219_58BA_470A_59BD, /*  337 */
-    0x852D_B00B_5F46_C393, /*  338 */
-    0x9120_9B2B_D336_B0E5, /*  339 */
-    0x6E60_4F7D_659E_F19F, /*  340 */
-    0xB99A_8AE2_782C_CB24, /*  341 */
-    0xCCF5_2AB6_C814_C4C7, /*  342 */
-    0x4727_D9AF_BE11_727B, /*  343 */
-    0x7E95_0D0C_0121_B34D, /*  344 */
-    0x756F_4356_70AD_471F, /*  345 */
-    0xF5AD_D442_615A_6849, /*  346 */
-    0x4E87_E099_80B9_957A, /*  347 */
-    0x2ACF_A1DF_50AE_E355, /*  348 */
-    0xD898_263A_FD2F_D556, /*  349 */
-    0xC8F4_924D_D80C_8FD6, /*  350 */
-    0xCF99_CA3D_754A_173A, /*  351 */
-    0xFE47_7BAC_AF91_BF3C, /*  352 */
-    0xED53_71F6_D690_C12D, /*  353 */
-    0x831A_5C28_5E68_7094, /*  354 */
-    0xC5D3_C90A_3708_A0A4, /*  355 */
-    0x0F7F_9037_17D0_6580, /*  356 */
-    0x19F9_BB13_B8FD_F27F, /*  357 */
-    0xB1BD_6F1B_4D50_2843, /*  358 */
-    0x1C76_1BA3_8FFF_4012, /*  359 */
-    0x0D15_30C4_E2E2_1F3B, /*  360 */
-    0x8943_CE69_A737_2C8A, /*  361 */
-    0xE518_4E11_FEB5_CE66, /*  362 */
-    0x618B_DB80_BD73_6621, /*  363 */
-    0x7D29_BAD6_8B57_4D0B, /*  364 */
-    0x81BB_613E_25E6_FE5B, /*  365 */
-    0x071C_9C10_BC07_913F, /*  366 */
-    0xC7BE_EB79_09AC_2D97, /*  367 */
-    0xC3E5_8D35_3BC5_D757, /*  368 */
-    0xEB01_7892_F38F_61E8, /*  369 */
-    0xD4EF_FB9C_9B1C_C21A, /*  370 */
-    0x9972_7D26_F494_F7AB, /*  371 */
-    0xA3E0_63A2_956B_3E03, /*  372 */
-    0x9D4A_8B9A_4AA0_9C30, /*  373 */
-    0x3F6A_B7D5_0009_0FB4, /*  374 */
-    0x9CC0_F2A0_5726_8AC0, /*  375 */
-    0x3DEE_9D2D_EDBF_42D1, /*  376 */
-    0x330F_49C8_7960_A972, /*  377 */
-    0xC6B2_7202_8742_1B41, /*  378 */
-    0x0AC5_9EC0_7C00_369C, /*  379 */
-    0xEF4E_AC49_CB35_3425, /*  380 */
-    0xF450_244E_EF01_29D8, /*  381 */
-    0x8ACC_46E5_CAF4_DEB6, /*  382 */
-    0x2FFE_AB63_9892_63F7, /*  383 */
-    0x8F7C_B9FE_5D7A_4578, /*  384 */
-    0x5BD8_F764_4E63_4635, /*  385 */
-    0x427A_7315_BF2D_C900, /*  386 */
-    0x17D0_C4AA_2125_261C, /*  387 */
-    0x3992_486C_9351_8E50, /*  388 */
-    0xB4CB_FEE0_A2D7_D4C3, /*  389 */
-    0x7C75_D620_2C5D_DD8D, /*  390 */
-    0xDBC2_95D8_E35B_6C61, /*  391 */
-    0x60B3_69D3_0203_2B19, /*  392 */
-    0xCE42_685F_DCE4_4132, /*  393 */
-    0x06F3_DDB9_DDF6_5610, /*  394 */
-    0x8EA4_D21D_B5E1_48F0, /*  395 */
-    0x20B0_FCE6_2FCD_496F, /*  396 */
-    0x2C1B_9123_58B0_EE31, /*  397 */
-    0xB283_17B8_18F5_A308, /*  398 */
-    0xA89C_1E18_9CA6_D2CF, /*  399 */
-    0x0C6B_1857_6AAA_DBC8, /*  400 */
-    0xB65D_EAA9_1299_FAE3, /*  401 */
-    0xFB2B_794B_7F10_27E7, /*  402 */
-    0x04E4_317F_443B_5BEB, /*  403 */
-    0x4B85_2D32_5939_D0A6, /*  404 */
-    0xD5AE_6BEE_FB20_7FFC, /*  405 */
-    0x3096_82B2_81C7_D374, /*  406 */
-    0xBAE3_09A1_94C3_B475, /*  407 */
-    0x8CC3_F97B_13B4_9F05, /*  408 */
-    0x98A9_422F_F829_3967, /*  409 */
-    0x244B_16B0_1076_FF7C, /*  410 */
-    0xF8BF_571C_663D_67EE, /*  411 */
-    0x1F0D_6758_EEE3_0DA1, /*  412 */
-    0xC9B6_11D9_7ADE_B9B7, /*  413 */
-    0xB7AF_D588_7B6C_57A2, /*  414 */
-    0x6290_AE84_6B98_4FE1, /*  415 */
-    0x94DF_4CDE_ACC1_A5FD, /*  416 */
-    0x058A_5BD1_C548_3AFF, /*  417 */
-    0x6316_6CC1_42BA_3C37, /*  418 */
-    0x8DB8_526E_B2F7_6F40, /*  419 */
-    0xE108_8003_6F0D_6D4E, /*  420 */
-    0x9E05_23C9_971D_311D, /*  421 */
-    0x45EC_2824_CC7C_D691, /*  422 */
-    0x575B_8359_E623_82C9, /*  423 */
-    0xFA9E_400D_C488_9995, /*  424 */
-    0xD182_3ECB_4572_1568, /*  425 */
-    0xDAFD_983B_8206_082F, /*  426 */
-    0xAA7D_2908_2386_A8CB, /*  427 */
-    0x269F_CD44_03B8_7588, /*  428 */
-    0x1B91_F5F7_28BD_D1E0, /*  429 */
-    0xE466_9F39_0402_01F6, /*  430 */
-    0x7A1D_7C21_8CF0_4ADE, /*  431 */
-    0x6562_3C29_D79C_E5CE, /*  432 */
-    0x2368_4490_96C0_0BB1, /*  433 */
-    0xAB9B_F187_9DA5_03BA, /*  434 */
-    0xBC23_ECB1_A458_058E, /*  435 */
-    0x9A58_DF01_BB40_1ECC, /*  436 */
-    0xA070_E868_A85F_143D, /*  437 */
-    0x4FF1_8830_7DF2_239E, /*  438 */
-    0x14D5_65B4_1A64_1183, /*  439 */
-    0xEE13_3374_5270_1602, /*  440 */
-    0x950E_3DCF_3F28_5E09, /*  441 */
-    0x5993_0254_B9C8_0953, /*  442 */
-    0x3BF2_9940_8930_DA6D, /*  443 */
-    0xA955_943F_5369_1387, /*  444 */
-    0xA15E_DECA_A9CB_8784, /*  445 */
-    0x2914_2127_352B_E9A0, /*  446 */
-    0x76F0_371F_FF4E_7AFB, /*  447 */
-    0x0239_F450_274F_2228, /*  448 */
-    0xBB07_3AF0_1D5E_868B, /*  449 */
-    0xBFC8_0571_C10E_96C1, /*  450 */
-    0xD267_0885_6822_2E23, /*  451 */
-    0x9671_A3D4_8E80_B5B0, /*  452 */
-    0x55B5_D38A_E193_BB81, /*  453 */
-    0x693A_E2D0_A18B_04B8, /*  454 */
-    0x5C48_B4EC_ADD5_335F, /*  455 */
-    0xFD74_3B19_4916_A1CA, /*  456 */
-    0x2577_0181_34BE_98C4, /*  457 */
-    0xE779_87E8_3C54_A4AD, /*  458 */
-    0x28E1_1014_DA33_E1B9, /*  459 */
-    0x270C_C59E_226A_A213, /*  460 */
-    0x7149_5F75_6D1A_5F60, /*  461 */
-    0x9BE8_53FB_60AF_EF77, /*  462 */
-    0xADC7_86A7_F744_3DBF, /*  463 */
-    0x0904_4561_73B2_9A82, /*  464 */
-    0x58BC_7A66_C232_BD5E, /*  465 */
-    0xF306_558C_673A_C8B2, /*  466 */
-    0x41F6_39C6_B6C9_772A, /*  467 */
-    0x216D_EFE9_9FDA_35DA, /*  468 */
-    0x1164_0CC7_1C7B_E615, /*  469 */
-    0x93C4_3694_565C_5527, /*  470 */
-    0xEA03_8E62_4677_7839, /*  471 */
-    0xF9AB_F3CE_5A3E_2469, /*  472 */
-    0x741E_768D_0FD3_12D2, /*  473 */
-    0x0144_B883_CED6_52C6, /*  474 */
-    0xC20B_5A5B_A33F_8552, /*  475 */
-    0x1AE6_9633_C343_5A9D, /*  476 */
-    0x97A2_8CA4_088C_FDEC, /*  477 */
-    0x8824_A43C_1E96_F420, /*  478 */
-    0x3761_2FA6_6EEE_A746, /*  479 */
-    0x6B4C_B165_F9CF_0E5A, /*  480 */
-    0x43AA_1C06_A0AB_FB4A, /*  481 */
-    0x7F4D_C26F_F162_796B, /*  482 */
-    0x6CBA_CC8E_54ED_9B0F, /*  483 */
-    0xA6B7_FFEF_D2BB_253E, /*  484 */
-    0x2E25_BC95_B0A2_9D4F, /*  485 */
-    0x86D6_A58B_DEF1_388C, /*  486 */
-    0xDED7_4AC5_76B6_F054, /*  487 */
-    0x8030_BDBC_2B45_805D, /*  488 */
-    0x3C81_AF70_E94D_9289, /*  489 */
-    0x3EFF_6DDA_9E31_00DB, /*  490 */
-    0xB38D_C39F_DFCC_8847, /*  491 */
-    0x1238_8552_8D17_B87E, /*  492 */
-    0xF2DA_0ED2_40B1_B642, /*  493 */
-    0x44CE_FADC_D54B_F9A9, /*  494 */
-    0x1312_200E_433C_7EE6, /*  495 */
-    0x9FFC_C84F_3A78_C748, /*  496 */
-    0xF0CD_1F72_2485_76BB, /*  497 */
-    0xEC69_7405_3638_CFE4, /*  498 */
-    0x2BA7_B67C_0CEC_4E4C, /*  499 */
-    0xAC2F_4DF3_E5CE_32ED, /*  500 */
-    0xCB33_D143_26EA_4C11, /*  501 */
-    0xA4E9_044C_C77E_58BC, /*  502 */
-    0x5F51_3293_D934_FCEF, /*  503 */
-    0x5DC9_6455_06E5_5444, /*  504 */
-    0x50DE_418F_317D_E40A, /*  505 */
-    0x388C_B31A_69DD_E259, /*  506 */
-    0x2DB4_A834_5582_0A86, /*  507 */
-    0x9010_A91E_8471_1AE9, /*  508 */
-    0x4DF7_F0B7_B149_8371, /*  509 */
-    0xD62A_2EAB_C097_7179, /*  510 */
-    0x22FA_C097_AA8D_5C0E, /*  511 */
-];
-static T3: [u64; 256] = [
-    0xF49F_CC2F_F1DA_F39B, /*  512 */
-    0x487F_D5C6_6FF2_9281, /*  513 */
-    0xE8A3_0667_FCDC_A83F, /*  514 */
-    0x2C9B_4BE3_D2FC_CE63, /*  515 */
-    0xDA3F_F74B_93FB_BBC2, /*  516 */
-    0x2FA1_65D2_FE70_BA66, /*  517 */
-    0xA103_E279_970E_93D4, /*  518 */
-    0xBECD_EC77_B0E4_5E71, /*  519 */
-    0xCFB4_1E72_3985_E497, /*  520 */
-    0xB70A_AA02_5EF7_5017, /*  521 */
-    0xD423_09F0_3840_B8E0, /*  522 */
-    0x8EFC_1AD0_3589_8579, /*  523 */
-    0x96C6_920B_E2B2_ABC5, /*  524 */
-    0x66AF_4163_375A_9172, /*  525 */
-    0x2174_ABDC_CA71_27FB, /*  526 */
-    0xB33C_CEA6_4A72_FF41, /*  527 */
-    0xF04A_4933_0830_66A5, /*  528 */
-    0x8D97_0ACD_D728_9AF5, /*  529 */
-    0x8F96_E8E0_31C8_C25E, /*  530 */
-    0xF3FE_C022_7687_5D47, /*  531 */
-    0xEC7B_F310_0561_90DD, /*  532 */
-    0xF5AD_B0AE_BB0F_1491, /*  533 */
-    0x9B50_F885_0FD5_8892, /*  534 */
-    0x4975_4883_58B7_4DE8, /*  535 */
-    0xA335_4FF6_9153_1C61, /*  536 */
-    0x0702_BBE4_81D2_C6EE, /*  537 */
-    0x89FB_2405_7DED_ED98, /*  538 */
-    0xAC30_7513_8596_E902, /*  539 */
-    0x1D2D_3580_1727_72ED, /*  540 */
-    0xEB73_8FC2_8E6B_C30D, /*  541 */
-    0x5854_EF8F_6304_4326, /*  542 */
-    0x9E5C_5232_5ADD_3BBE, /*  543 */
-    0x90AA_53CF_325C_4623, /*  544 */
-    0xC1D2_4D51_349D_D067, /*  545 */
-    0x2051_CFEE_A69E_A624, /*  546 */
-    0x1322_0F0A_862E_7E4F, /*  547 */
-    0xCE39_3994_04E0_4864, /*  548 */
-    0xD9C4_2CA4_7086_FCB7, /*  549 */
-    0x685A_D223_8A03_E7CC, /*  550 */
-    0x0664_84B2_AB2F_F1DB, /*  551 */
-    0xFE9D_5D70_EFBF_79EC, /*  552 */
-    0x5B13_B9DD_9C48_1854, /*  553 */
-    0x15F0_D475_ED15_09AD, /*  554 */
-    0x0BEB_CD06_0EC7_9851, /*  555 */
-    0xD58C_6791_183A_B7F8, /*  556 */
-    0xD118_7C50_52F3_EEE4, /*  557 */
-    0xC95D_1192_E54E_82FF, /*  558 */
-    0x86EE_A14C_B9AC_6CA2, /*  559 */
-    0x3485_BEB1_5367_7D5D, /*  560 */
-    0xDD19_1D78_1F8C_492A, /*  561 */
-    0xF608_66BA_A784_EBF9, /*  562 */
-    0x518F_643B_A2D0_8C74, /*  563 */
-    0x8852_E956_E108_7C22, /*  564 */
-    0xA768_CB8D_C410_AE8D, /*  565 */
-    0x3804_7726_BFEC_8E1A, /*  566 */
-    0xA677_38B4_CD3B_45AA, /*  567 */
-    0xAD16_691C_EC0D_DE19, /*  568 */
-    0xC6D4_3193_8046_2E07, /*  569 */
-    0xC5A5_876D_0BA6_1938, /*  570 */
-    0x16B9_FA1F_A58F_D840, /*  571 */
-    0x188A_B117_3CA7_4F18, /*  572 */
-    0xABDA_2F98_C99C_021F, /*  573 */
-    0x3E05_80AB_134A_E816, /*  574 */
-    0x5F3B_05B7_7364_5ABB, /*  575 */
-    0x2501_A2BE_5575_F2F6, /*  576 */
-    0x1B2F_7400_4E7E_8BA9, /*  577 */
-    0x1CD7_5803_71E8_D953, /*  578 */
-    0x7F6E_D895_6276_4E30, /*  579 */
-    0xB159_26FF_596F_003D, /*  580 */
-    0x9F65_293D_A8C5_D6B9, /*  581 */
-    0x6ECE_F04D_D690_F84C, /*  582 */
-    0x4782_275F_FF33_AF88, /*  583 */
-    0xE414_3308_3F82_0801, /*  584 */
-    0xFD0D_FE40_9A1A_F9B5, /*  585 */
-    0x4325_A334_2CDB_396B, /*  586 */
-    0x8AE7_7E62_B301_B252, /*  587 */
-    0xC36F_9E9F_6655_615A, /*  588 */
-    0x8545_5A2D_92D3_2C09, /*  589 */
-    0xF2C7_DEA9_4947_7485, /*  590 */
-    0x63CF_B4C1_33A3_9EBA, /*  591 */
-    0x83B0_40CC_6EBC_5462, /*  592 */
-    0x3B94_54C8_FDB3_26B0, /*  593 */
-    0x56F5_6A9E_87FF_D78C, /*  594 */
-    0x2DC2_940D_99F4_2BC6, /*  595 */
-    0x98F7_DF09_6B09_6E2D, /*  596 */
-    0x19A6_E01E_3AD8_52BF, /*  597 */
-    0x42A9_9CCB_DBD4_B40B, /*  598 */
-    0xA599_98AF_45E9_C559, /*  599 */
-    0x3662_95E8_07D9_3186, /*  600 */
-    0x6B48_181B_FAA1_F773, /*  601 */
-    0x1FEC_57E2_157A_0A1D, /*  602 */
-    0x4667_446A_F620_1AD5, /*  603 */
-    0xE615_EBCA_CFB0_F075, /*  604 */
-    0xB8F3_1F4F_6829_0778, /*  605 */
-    0x2271_3ED6_CE22_D11E, /*  606 */
-    0x3057_C1A7_2EC3_C93B, /*  607 */
-    0xCB46_ACC3_7C3F_1F2F, /*  608 */
-    0xDBB8_93FD_02AA_F50E, /*  609 */
-    0x331F_D92E_600B_9FCF, /*  610 */
-    0xA498_F961_48EA_3AD6, /*  611 */
-    0xA8D8_426E_8B6A_83EA, /*  612 */
-    0xA089_B274_B773_5CDC, /*  613 */
-    0x87F6_B373_1E52_4A11, /*  614 */
-    0x1188_08E5_CBC9_6749, /*  615 */
-    0x9906_E4C7_B19B_D394, /*  616 */
-    0xAFED_7F7E_9B24_A20C, /*  617 */
-    0x6509_EADE_EB36_44A7, /*  618 */
-    0x6C1E_F1D3_E8EF_0EDE, /*  619 */
-    0xB9C9_7D43_E979_8FB4, /*  620 */
-    0xA2F2_D784_740C_28A3, /*  621 */
-    0x7B84_9647_6197_566F, /*  622 */
-    0x7A5B_E3E6_B65F_069D, /*  623 */
-    0xF963_30ED_78BE_6F10, /*  624 */
-    0xEEE6_0DE7_7A07_6A15, /*  625 */
-    0x2B4B_EE4A_A08B_9BD0, /*  626 */
-    0x6A56_A63E_C7B8_894E, /*  627 */
-    0x0212_1359_BA34_FEF4, /*  628 */
-    0x4CBF_99F8_2837_03FC, /*  629 */
-    0x3980_7135_0CAF_30C8, /*  630 */
-    0xD0A7_7A89_F017_687A, /*  631 */
-    0xF1C1_A9EB_9E42_3569, /*  632 */
-    0x8C79_7628_2DEE_8199, /*  633 */
-    0x5D17_37A5_DD1F_7ABD, /*  634 */
-    0x4F53_433C_09A9_FA80, /*  635 */
-    0xFA8B_0C53_DF7C_A1D9, /*  636 */
-    0x3FD9_DCBC_886C_CB77, /*  637 */
-    0xC040_917C_A91B_4720, /*  638 */
-    0x7DD0_0142_F9D1_DCDF, /*  639 */
-    0x8476_FC1D_4F38_7B58, /*  640 */
-    0x23F8_E7C5_F331_6503, /*  641 */
-    0x032A_2244_E7E3_7339, /*  642 */
-    0x5C87_A5D7_50F5_A74B, /*  643 */
-    0x082B_4CC4_3698_992E, /*  644 */
-    0xDF91_7BEC_B858_F63C, /*  645 */
-    0x3270_B8FC_5BF8_6DDA, /*  646 */
-    0x10AE_72BB_29B5_DD76, /*  647 */
-    0x576A_C94E_7700_362B, /*  648 */
-    0x1AD1_12DA_C61E_FB8F, /*  649 */
-    0x691B_C30E_C5FA_A427, /*  650 */
-    0xFF24_6311_CC32_7143, /*  651 */
-    0x3142_368E_30E5_3206, /*  652 */
-    0x7138_0E31_E02C_A396, /*  653 */
-    0x958D_5C96_0AAD_76F1, /*  654 */
-    0xF8D6_F430_C16D_A536, /*  655 */
-    0xC8FF_D13F_1BE7_E1D2, /*  656 */
-    0x7578_AE66_004D_DBE1, /*  657 */
-    0x0583_3F01_067B_E646, /*  658 */
-    0xBB34_B5AD_3BFE_586D, /*  659 */
-    0x095F_34C9_A12B_97F0, /*  660 */
-    0x247A_B645_25D6_0CA8, /*  661 */
-    0xDCDB_C6F3_0174_77D1, /*  662 */
-    0x4A2E_14D4_DECA_D24D, /*  663 */
-    0xBDB5_E6D9_BE0A_1EEB, /*  664 */
-    0x2A7E_70F7_7943_01AB, /*  665 */
-    0xDEF4_2D8A_2705_40FD, /*  666 */
-    0x0107_8EC0_A34C_22C1, /*  667 */
-    0xE5DE_511A_F4C1_6387, /*  668 */
-    0x7EBB_3A52_BD9A_330A, /*  669 */
-    0x7769_7857_AA7D_6435, /*  670 */
-    0x004E_8316_03AE_4C32, /*  671 */
-    0xE7A2_1020_AD78_E312, /*  672 */
-    0x9D41_A70C_6AB4_20F2, /*  673 */
-    0x28E0_6C18_EA11_41E6, /*  674 */
-    0xD2B2_8CBD_984F_6B28, /*  675 */
-    0x26B7_5F6C_446E_9D83, /*  676 */
-    0xBA47_568C_4D41_8D7F, /*  677 */
-    0xD80B_ADBF_E618_3D8E, /*  678 */
-    0x0E20_6D7F_5F16_6044, /*  679 */
-    0xE258_A439_11CB_CA3E, /*  680 */
-    0x723A_1746_B21D_C0BC, /*  681 */
-    0xC7CA_A854_F5D7_CDD3, /*  682 */
-    0x7CAC_3288_3D26_1D9C, /*  683 */
-    0x7690_C264_23BA_942C, /*  684 */
-    0x17E5_5524_4780_42B8, /*  685 */
-    0xE0BE_4776_56A2_389F, /*  686 */
-    0x4D28_9B5E_67AB_2DA0, /*  687 */
-    0x4486_2B9C_8FBB_FD31, /*  688 */
-    0xB47C_C804_9D14_1365, /*  689 */
-    0x822C_1B36_2B91_C793, /*  690 */
-    0x4EB1_4655_FB13_DFD8, /*  691 */
-    0x1ECB_BA07_14E2_A97B, /*  692 */
-    0x6143_459D_5CDE_5F14, /*  693 */
-    0x53A8_FBF1_D5F0_AC89, /*  694 */
-    0x97EA_04D8_1C5E_5B00, /*  695 */
-    0x6221_81A8_D4FD_B3F3, /*  696 */
-    0xE9BC_D341_572A_1208, /*  697 */
-    0x1411_2586_43CC_E58A, /*  698 */
-    0x9144_C5FE_A4C6_E0A4, /*  699 */
-    0x0D33_D065_65CF_620F, /*  700 */
-    0x54A4_8D48_9F21_9CA1, /*  701 */
-    0xC43E_5EAC_6D63_C821, /*  702 */
-    0xA972_8B3A_7277_0DAF, /*  703 */
-    0xD793_4E7B_20DF_87EF, /*  704 */
-    0xE355_03B6_1A3E_86E5, /*  705 */
-    0xCAE3_21FB_C819_D504, /*  706 */
-    0x129A_50B3_AC60_BFA6, /*  707 */
-    0xCD5E_68EA_7E9F_B6C3, /*  708 */
-    0xB01C_9019_9483_B1C7, /*  709 */
-    0x3DE9_3CD5_C295_376C, /*  710 */
-    0xAED5_2EDF_2AB9_AD13, /*  711 */
-    0x2E60_F512_C0A0_7884, /*  712 */
-    0xBC3D_86A3_E362_10C9, /*  713 */
-    0x3526_9D9B_1639_51CE, /*  714 */
-    0x0C7D_6E2A_D0CD_B5FA, /*  715 */
-    0x59E8_6297_D87F_5733, /*  716 */
-    0x298E_F221_898D_B0E7, /*  717 */
-    0x5500_0029_D1A5_AA7E, /*  718 */
-    0x8BC0_8AE1_B506_1B45, /*  719 */
-    0xC2C3_1C2B_6C92_703A, /*  720 */
-    0x94CC_596B_AF25_EF42, /*  721 */
-    0x0A1D_73DB_2254_0456, /*  722 */
-    0x04B6_A0F9_D9C4_179A, /*  723 */
-    0xEFFD_AFA2_AE3D_3C60, /*  724 */
-    0xF7C8_075B_B494_96C4, /*  725 */
-    0x9CC5_C714_1D1C_D4E3, /*  726 */
-    0x78BD_1638_218E_5534, /*  727 */
-    0xB2F1_1568_F850_246A, /*  728 */
-    0xEDFA_BCFA_9502_BC29, /*  729 */
-    0x796C_E5F2_DA23_051B, /*  730 */
-    0xAAE1_28B0_DC93_537C, /*  731 */
-    0x3A49_3DA0_EE4B_29AE, /*  732 */
-    0xB5DF_6B2C_4168_95D7, /*  733 */
-    0xFCAB_BD25_122D_7F37, /*  734 */
-    0x7081_0B58_105D_C4B1, /*  735 */
-    0xE10F_DD37_F788_2A90, /*  736 */
-    0x524D_CAB5_518A_3F5C, /*  737 */
-    0x3C9E_8587_8451_255B, /*  738 */
-    0x4029_8281_19BD_34E2, /*  739 */
-    0x74A0_5B6F_5D3C_ECCB, /*  740 */
-    0xB610_0215_42E1_3ECA, /*  741 */
-    0x0FF9_79D1_2F59_E2AC, /*  742 */
-    0x6037_DA27_E4F9_CC50, /*  743 */
-    0x5E92_975A_0DF1_847D, /*  744 */
-    0xD66D_E190_D3E6_23FE, /*  745 */
-    0x5032_D6B8_7B56_8048, /*  746 */
-    0x9A36_B7CE_8235_216E, /*  747 */
-    0x8027_2A7A_24F6_4B4A, /*  748 */
-    0x93EF_ED8B_8C69_16F7, /*  749 */
-    0x37DD_BFF4_4CCE_1555, /*  750 */
-    0x4B95_DB5D_4B99_BD25, /*  751 */
-    0x92D3_FDA1_6981_2FC0, /*  752 */
-    0xFB1A_4A9A_9066_0BB6, /*  753 */
-    0x730C_1969_46A4_B9B2, /*  754 */
-    0x81E2_89AA_7F49_DA68, /*  755 */
-    0x6466_9A0F_83B1_A05F, /*  756 */
-    0x27B3_FF7D_9644_F48B, /*  757 */
-    0xCC6B_615C_8DB6_75B3, /*  758 */
-    0x674F_20B9_BCEB_BE95, /*  759 */
-    0x6F31_2382_7565_5982, /*  760 */
-    0x5AE4_8871_3E45_CF05, /*  761 */
-    0xBF61_9F99_54C2_1157, /*  762 */
-    0xEABA_C460_40A8_EAE9, /*  763 */
-    0x454C_6FE9_F2C0_C1CD, /*  764 */
-    0x419C_F649_6412_691C, /*  765 */
-    0xD3DC_3BEF_265B_0F70, /*  766 */
-    0x6D0E_60F5_C357_8A9E, /*  767 */
-];
-static T4: [u64; 256] = [
-    0x5B0E_6085_2632_3C55, /*  768 */
-    0x1A46_C1A9_FA1B_59F5, /*  769 */
-    0xA9E2_45A1_7C4C_8FFA, /*  770 */
-    0x65CA_5159_DB29_55D7, /*  771 */
-    0x05DB_0A76_CE35_AFC2, /*  772 */
-    0x81EA_C77E_A911_3D45, /*  773 */
-    0x528E_F88A_B6AC_0A0D, /*  774 */
-    0xA09E_A253_597B_E3FF, /*  775 */
-    0x430D_DFB3_AC48_CD56, /*  776 */
-    0xC4B3_A67A_F45C_E46F, /*  777 */
-    0x4ECE_CFD8_FBE2_D05E, /*  778 */
-    0x3EF5_6F10_B399_35F0, /*  779 */
-    0x0B22_D682_9CD6_19C6, /*  780 */
-    0x17FD_460A_74DF_2069, /*  781 */
-    0x6CF8_CC8E_8510_ED40, /*  782 */
-    0xD6C8_24BF_3A6E_CAA7, /*  783 */
-    0x6124_3D58_1A81_7049, /*  784 */
-    0x048B_ACB6_BBC1_63A2, /*  785 */
-    0xD9A3_8AC2_7D44_CC32, /*  786 */
-    0x7FDD_FF5B_AAF4_10AB, /*  787 */
-    0xAD6D_495A_A804_824B, /*  788 */
-    0xE1A6_A74F_2D8C_9F94, /*  789 */
-    0xD4F7_8512_35DE_E8E3, /*  790 */
-    0xFD4B_7F88_6540_D893, /*  791 */
-    0x247C_2004_2AA4_BFDA, /*  792 */
-    0x096E_A1C5_17D1_327C, /*  793 */
-    0xD569_66B4_361A_6685, /*  794 */
-    0x277D_A5C3_1221_057D, /*  795 */
-    0x94D5_9893_A43A_CFF7, /*  796 */
-    0x64F0_C51C_CDC0_2281, /*  797 */
-    0x3D33_BCC4_FF61_89DB, /*  798 */
-    0xE005_CB18_4CE6_6AF1, /*  799 */
-    0xFF5C_CD1D_1DB9_9BEA, /*  800 */
-    0xB0B8_54A7_FE42_980F, /*  801 */
-    0x7BD4_6A6A_718D_4B9F, /*  802 */
-    0xD10F_A8CC_22A5_FD8C, /*  803 */
-    0xD314_8495_2BE4_BD31, /*  804 */
-    0xC7FA_975F_CB24_3847, /*  805 */
-    0x4886_ED1E_5846_C407, /*  806 */
-    0x28CD_DB79_1EB7_0B04, /*  807 */
-    0xC2B0_0BE2_F573_417F, /*  808 */
-    0x5C95_9045_2180_F877, /*  809 */
-    0x7A6B_DDFF_F370_EB00, /*  810 */
-    0xCE50_9E38_D6D9_D6A4, /*  811 */
-    0xEBEB_0F00_647F_A702, /*  812 */
-    0x1DCC_06CF_7660_6F06, /*  813 */
-    0xE4D9_F28B_A286_FF0A, /*  814 */
-    0xD85A_305D_C918_C262, /*  815 */
-    0x475B_1D87_3222_5F54, /*  816 */
-    0x2D4F_B516_68CC_B5FE, /*  817 */
-    0xA679_B9D9_D72B_BA20, /*  818 */
-    0x5384_1C0D_912D_43A5, /*  819 */
-    0x3B7E_AA48_BF12_A4E8, /*  820 */
-    0x781E_0E47_F22F_1DDF, /*  821 */
-    0xEFF2_0CE6_0AB5_0973, /*  822 */
-    0x20D2_61D1_9DFF_B742, /*  823 */
-    0x16A1_2B03_062A_2E39, /*  824 */
-    0x1960_EB22_3965_0495, /*  825 */
-    0x251C_16FE_D50E_B8B8, /*  826 */
-    0x9AC0_C330_F826_016E, /*  827 */
-    0xED15_2665_953E_7671, /*  828 */
-    0x02D6_3194_A636_9570, /*  829 */
-    0x5074_F083_94B1_C987, /*  830 */
-    0x70BA_598C_90B2_5CE1, /*  831 */
-    0x794A_1581_0B97_42F6, /*  832 */
-    0x0D59_25E9_FCAF_8C6C, /*  833 */
-    0x3067_716C_D868_744E, /*  834 */
-    0x910A_B077_E8D7_731B, /*  835 */
-    0x6A61_BBDB_5AC4_2F61, /*  836 */
-    0x9351_3EFB_F085_1567, /*  837 */
-    0xF494_724B_9E83_E9D5, /*  838 */
-    0xE887_E198_5C09_648D, /*  839 */
-    0x34B1_D3C6_7537_0CFD, /*  840 */
-    0xDC35_E433_BC0D_255D, /*  841 */
-    0xD0AA_B842_3413_1BE0, /*  842 */
-    0x0804_2A50_B48B_7EAF, /*  843 */
-    0x9997_C4EE_44A3_AB35, /*  844 */
-    0x829A_7B49_2017_99D0, /*  845 */
-    0x263B_8307_B7C5_4441, /*  846 */
-    0x752F_95F4_FD6A_6CA6, /*  847 */
-    0x9272_1740_2C08_C6E5, /*  848 */
-    0x2A8A_B754_A795_D9EE, /*  849 */
-    0xA442_F755_2F72_943D, /*  850 */
-    0x2C31_334E_1978_1208, /*  851 */
-    0x4FA9_8D7C_EAEE_6291, /*  852 */
-    0x55C3_862F_665D_B309, /*  853 */
-    0xBD06_1017_5D53_B1F3, /*  854 */
-    0x46FE_6CB8_4041_3F27, /*  855 */
-    0x3FE0_3792_DF0C_FA59, /*  856 */
-    0xCFE7_0037_2EB8_5E8F, /*  857 */
-    0xA7BE_29E7_ADBC_E118, /*  858 */
-    0xE544_EE5C_DE84_31DD, /*  859 */
-    0x8A78_1B1B_41F1_873E, /*  860 */
-    0xA5C9_4C78_A0D2_F0E7, /*  861 */
-    0x3941_2E28_77B6_0728, /*  862 */
-    0xA126_5EF3_AFC9_A62C, /*  863 */
-    0xBCC2_770C_6A25_06C5, /*  864 */
-    0x3AB6_6DD5_DCE1_CE12, /*  865 */
-    0xE654_99D0_4A67_5B37, /*  866 */
-    0x7D8F_5234_81BF_D216, /*  867 */
-    0x0F6F_64FC_EC15_F389, /*  868 */
-    0x74EF_BE61_8B5B_13C8, /*  869 */
-    0xACDC_82B7_1427_3E1D, /*  870 */
-    0xDD40_BFE0_0319_9D17, /*  871 */
-    0x37E9_9257_E7E0_61F8, /*  872 */
-    0xFA52_6269_0477_5AAA, /*  873 */
-    0x8BBB_F63A_463D_56F9, /*  874 */
-    0xF001_3F15_43A2_6E64, /*  875 */
-    0xA830_7E9F_879E_C898, /*  876 */
-    0xCC4C_27A4_1501_77CC, /*  877 */
-    0x1B43_2F2C_CA1D_3348, /*  878 */
-    0xDE1D_1F8F_9F6F_A013, /*  879 */
-    0x6066_02A0_47A7_DDD6, /*  880 */
-    0xD237_AB64_CC1C_B2C7, /*  881 */
-    0x9B93_8E72_25FC_D1D3, /*  882 */
-    0xEC4E_0370_8E0F_F476, /*  883 */
-    0xFEB2_FBDA_3D03_C12D, /*  884 */
-    0xAE0B_CED2_EE43_889A, /*  885 */
-    0x22CB_8923_EBFB_4F43, /*  886 */
-    0x6936_0D01_3CF7_396D, /*  887 */
-    0x855E_3602_D2D4_E022, /*  888 */
-    0x0738_05BA_D01F_784C, /*  889 */
-    0x33E1_7A13_3852_F546, /*  890 */
-    0xDF48_7405_8AC7_B638, /*  891 */
-    0xBA92_B29C_678A_A14A, /*  892 */
-    0x0CE8_9FC7_6CFA_ADCD, /*  893 */
-    0x5F9D_4E09_0833_9E34, /*  894 */
-    0xF1AF_E929_1F59_23B9, /*  895 */
-    0x6E34_80F6_0F4A_265F, /*  896 */
-    0xEEBF_3A2A_B29B_841C, /*  897 */
-    0xE219_38A8_8F91_B4AD, /*  898 */
-    0x57DF_EFF8_45C6_D3C3, /*  899 */
-    0x2F00_6B0B_F62C_AAF2, /*  900 */
-    0x62F4_79EF_6F75_EE78, /*  901 */
-    0x11A5_5AD4_1C89_16A9, /*  902 */
-    0xF229_D290_84FE_D453, /*  903 */
-    0x42F1_C27B_16B0_00E6, /*  904 */
-    0x2B1F_7674_9823_C074, /*  905 */
-    0x4B76_ECA3_C274_5360, /*  906 */
-    0x8C98_F463_B916_91BD, /*  907 */
-    0x14BC_C93C_F1AD_E66A, /*  908 */
-    0x8885_213E_6D45_8397, /*  909 */
-    0x8E17_7DF0_274D_4711, /*  910 */
-    0xB49B_73B5_503F_2951, /*  911 */
-    0x1016_8168_C3F9_6B6B, /*  912 */
-    0x0E3D_963B_63CA_B0AE, /*  913 */
-    0x8DFC_4B56_55A1_DB14, /*  914 */
-    0xF789_F135_6E14_DE5C, /*  915 */
-    0x683E_68AF_4E51_DAC1, /*  916 */
-    0xC9A8_4F9D_8D4B_0FD9, /*  917 */
-    0x3691_E03F_52A0_F9D1, /*  918 */
-    0x5ED8_6E46_E187_8E80, /*  919 */
-    0x3C71_1A0E_99D0_7150, /*  920 */
-    0x5A08_65B2_0C4E_9310, /*  921 */
-    0x56FB_FC1F_E4F0_682E, /*  922 */
-    0xEA8D_5DE3_105E_DF9B, /*  923 */
-    0x71AB_FDB1_2379_187A, /*  924 */
-    0x2EB9_9DE1_BEE7_7B9C, /*  925 */
-    0x21EC_C0EA_33CF_4523, /*  926 */
-    0x59A4_D752_1805_C7A1, /*  927 */
-    0x3896_F5EB_56AE_7C72, /*  928 */
-    0xAA63_8F3D_B18F_75DC, /*  929 */
-    0x9F39_358D_ABE9_808E, /*  930 */
-    0xB7DE_FA91_C00B_72AC, /*  931 */
-    0x6B55_41FD_6249_2D92, /*  932 */
-    0x6DC6_DEE8_F92E_4D5B, /*  933 */
-    0x353F_57AB_C4BE_EA7E, /*  934 */
-    0x7357_69D6_DA56_90CE, /*  935 */
-    0x0A23_4AA6_4239_1484, /*  936 */
-    0xF6F9_5080_28F8_0D9D, /*  937 */
-    0xB8E3_19A2_7AB3_F215, /*  938 */
-    0x31AD_9C11_5134_1A4D, /*  939 */
-    0x773C_22A5_7BEF_5805, /*  940 */
-    0x45C7_561A_0796_8633, /*  941 */
-    0xF913_DA9E_249D_BE36, /*  942 */
-    0xDA65_2D9B_78A6_4C68, /*  943 */
-    0x4C27_A97F_3BC3_34EF, /*  944 */
-    0x7662_1220_E66B_17F4, /*  945 */
-    0x9677_4389_9ACD_7D0B, /*  946 */
-    0xF3EE_5BCA_E0ED_6782, /*  947 */
-    0x409F_7536_00C8_79FC, /*  948 */
-    0x06D0_9A39_B592_6DB6, /*  949 */
-    0x6F83_AEB0_317A_C588, /*  950 */
-    0x01E6_CA4A_8638_1F21, /*  951 */
-    0x66FF_3462_D19F_3025, /*  952 */
-    0x7220_7C24_DDFD_3BFB, /*  953 */
-    0x4AF6_B6D3_E2EC_E2EB, /*  954 */
-    0x9C99_4DBE_C7EA_08DE, /*  955 */
-    0x49AC_E597_B09A_8BC4, /*  956 */
-    0xB38C_4766_CF07_97BA, /*  957 */
-    0x131B_9373_C57C_2A75, /*  958 */
-    0xB182_2CCE_6193_1E58, /*  959 */
-    0x9D75_55B9_09BA_1C0C, /*  960 */
-    0x127F_AFDD_937D_11D2, /*  961 */
-    0x29DA_3BAD_C66D_92E4, /*  962 */
-    0xA2C1_D571_54C2_ECBC, /*  963 */
-    0x58C5_134D_82F6_FE24, /*  964 */
-    0x1C3A_E351_5B62_274F, /*  965 */
-    0xE907_C82E_01CB_8126, /*  966 */
-    0xF8ED_0919_13E3_7FCB, /*  967 */
-    0x3249_D8F9_C800_46C9, /*  968 */
-    0x80CF_9BED_E388_FB63, /*  969 */
-    0x1881_539A_116C_F19E, /*  970 */
-    0x5103_F3F7_6BD5_2457, /*  971 */
-    0x15B7_E6F5_AE47_F7A8, /*  972 */
-    0xDBD7_C6DE_D47E_9CCF, /*  973 */
-    0x44E5_5C41_0228_BB1A, /*  974 */
-    0xB647_D425_5EDB_4E99, /*  975 */
-    0x5D11_882B_B8AA_FC30, /*  976 */
-    0xF509_8BBB_29D3_212A, /*  977 */
-    0x8FB5_EA14_E902_96B3, /*  978 */
-    0x677B_9421_57DD_025A, /*  979 */
-    0xFB58_E7C0_A390_ACB5, /*  980 */
-    0x89D3_674C_83BD_4A01, /*  981 */
-    0x9E2D_A4DF_4BF3_B93B, /*  982 */
-    0xFCC4_1E32_8CAB_4829, /*  983 */
-    0x03F3_8C96_BA58_2C52, /*  984 */
-    0xCAD1_BDBD_7FD8_5DB2, /*  985 */
-    0xBBB4_42C1_6082_AE83, /*  986 */
-    0xB95F_E86B_A5DA_9AB0, /*  987 */
-    0xB22E_0467_3771_A93F, /*  988 */
-    0x8453_58C9_4931_52D8, /*  989 */
-    0xBE2A_4886_97B4_541E, /*  990 */
-    0x95A2_DC2D_D38E_6966, /*  991 */
-    0xC02C_11AC_923C_852B, /*  992 */
-    0x2388_B199_0DF2_A87B, /*  993 */
-    0x7C80_08FA_1B4F_37BE, /*  994 */
-    0x1F70_D0C8_4D54_E503, /*  995 */
-    0x5490_ADEC_7ECE_57D4, /*  996 */
-    0x002B_3C27_D906_3A3A, /*  997 */
-    0x7EAE_A384_8030_A2BF, /*  998 */
-    0xC602_326D_ED20_03C0, /*  999 */
-    0x83A7_287D_69A9_4086, /* 1000 */
-    0xC57A_5FCB_30F5_7A8A, /* 1001 */
-    0xB568_44E4_79EB_E779, /* 1002 */
-    0xA373_B40F_05DC_BCE9, /* 1003 */
-    0xD71A_786E_8857_0EE2, /* 1004 */
-    0x879C_BACD_BDE8_F6A0, /* 1005 */
-    0x976A_D1BC_C164_A32F, /* 1006 */
-    0xAB21_E25E_9666_D78B, /* 1007 */
-    0x9010_63AA_E5E5_C33C, /* 1008 */
-    0x9818_B344_4869_8D90, /* 1009 */
-    0xE364_87AE_3E1E_8ABB, /* 1010 */
-    0xAFBD_F931_893B_DCB4, /* 1011 */
-    0x6345_A0DC_5FBB_D519, /* 1012 */
-    0x8628_FE26_9B94_65CA, /* 1013 */
-    0x1E5D_0160_3F9C_51EC, /* 1014 */
-    0x4DE4_4006_A150_49B7, /* 1015 */
-    0xBF6C_70E5_F776_CBB1, /* 1016 */
-    0x4112_18F2_EF55_2BED, /* 1017 */
-    0xCB0C_0708_705A_36A3, /* 1018 */
-    0xE74D_1475_4F98_6044, /* 1019 */
-    0xCD56_D943_0EA8_280E, /* 1020 */
-    0xC125_91D7_535F_5065, /* 1021 */
-    0xC832_23F1_720A_EF96, /* 1022 */
-    0xC3A0_396F_7363_A51F, /* 1023 */
-];
+/// Tiger hasher state.
+pub type Tiger = CoreWrapper<TigerCore>;
+/// Tiger2 hasher state.
+pub type Tiger2 = CoreWrapper<Tiger2Core>;
diff --git a/tiger/src/tables.rs b/tiger/src/tables.rs
new file mode 100644
index 000000000..dc6e2c21e
--- /dev/null
+++ b/tiger/src/tables.rs
@@ -0,0 +1,1035 @@
+pub(crate) static T1: [u64; 256] = [
+    0x02AA_B17C_F7E9_0C5E,
+    0xAC42_4B03_E243_A8EC,
+    0x72CD_5BE3_0DD5_FCD3,
+    0x6D01_9B93_F6F9_7F3A,
+    0xCD99_78FF_D21F_9193,
+    0x7573_A1C9_7080_29E2,
+    0xB164_326B_922A_83C3,
+    0x4688_3EEE_0491_5870,
+    0xEAAC_E305_7103_ECE6,
+    0xC541_69B8_08A3_535C,
+    0x4CE7_5491_8DDE_C47C,
+    0x0AA2_F4DF_DC0D_F40C,
+    0x10B7_6F18_A74D_BEFA,
+    0xC6CC_B623_5AD1_AB6A,
+    0x1372_6121_572F_E2FF,
+    0x1A48_8C6F_199D_921E,
+    0x4BC9_F9F4_DA00_07CA,
+    0x26F5_E6F6_E852_41C7,
+    0x8590_79DB_EA59_47B6,
+    0x4F18_85C5_C99E_8C92,
+    0xD78E_761E_A96F_864B,
+    0x8E36_428C_52B5_C17D,
+    0x69CF_6827_3730_63C1,
+    0xB607_C93D_9BB4_C56E,
+    0x7D82_0E76_0E76_B5EA,
+    0x645C_9CC6_F07F_DC42,
+    0xBF38_A078_2433_42E0,
+    0x5F6B_343C_9D2E_7D04,
+    0xF2C2_8AEB_600B_0EC6,
+    0x6C0E_D85F_7254_BCAC,
+    0x7159_2281_A4DB_4FE5,
+    0x1967_FA69_CE0F_ED9F,
+    0xFD52_93F8_B965_45DB,
+    0xC879_E9D7_F2A7_600B,
+    0x8602_4892_0193_194E,
+    0xA4F9_533B_2D9C_C0B3,
+    0x9053_836C_1595_7613,
+    0xDB6D_CF8A_FC35_7BF1,
+    0x18BE_EA7A_7A37_0F57,
+    0x0371_17CA_50B9_9066,
+    0x6AB3_0A97_7442_4A35,
+    0xF4E9_2F02_E325_249B,
+    0x7739_DB07_061C_CAE1,
+    0xD8F3_B49C_ECA4_2A05,
+    0xBD56_BE3F_5138_2F73,
+    0x45FA_ED58_43B0_BB28,
+    0x1C81_3D5C_11BF_1F83,
+    0x8AF0_E4B6_D75F_A169,
+    0x33EE_18A4_87AD_9999,
+    0x3C26_E8EA_B1C9_4410,
+    0xB510_102B_C0A8_22F9,
+    0x141E_EF31_0CE6_123B,
+    0xFC65_B900_59DD_B154,
+    0xE015_8640_C5E0_E607,
+    0x884E_0798_26C3_A3CF,
+    0x930D_0D95_23C5_35FD,
+    0x3563_8D75_4E9A_2B00,
+    0x4085_FCCF_4046_9DD5,
+    0xC4B1_7AD2_8BE2_3A4C,
+    0xCAB2_F0FC_6A3E_6A2E,
+    0x2860_971A_6B94_3FCD,
+    0x3DDE_6EE2_12E3_0446,
+    0x6222_F32A_E017_65AE,
+    0x5D55_0BB5_4783_08FE,
+    0xA9EF_A98D_A0ED_A22A,
+    0xC351_A716_86C4_0DA7,
+    0x1105_586D_9C86_7C84,
+    0xDCFF_EE85_FDA2_2853,
+    0xCCFB_D026_2C5E_EF76,
+    0xBAF2_94CB_8990_D201,
+    0xE694_64F5_2AFA_D975,
+    0x94B0_13AF_DF13_3E14,
+    0x06A7_D1A3_2823_C958,
+    0x6F95_FE51_30F6_1119,
+    0xD92A_B34E_462C_06C0,
+    0xED7B_DE33_887C_71D2,
+    0x7974_6D6E_6518_393E,
+    0x5BA4_1938_5D71_3329,
+    0x7C1B_A6B9_48A9_7564,
+    0x3198_7C19_7BFD_AC67,
+    0xDE6C_23C4_4B05_3D02,
+    0x581C_49FE_D002_D64D,
+    0xDD47_4D63_3826_1571,
+    0xAA45_46C3_E473_D062,
+    0x928F_CE34_9455_F860,
+    0x4816_1BBA_CAAB_94D9,
+    0x6391_2430_770E_6F68,
+    0x6EC8_A5E6_02C6_641C,
+    0x8728_2515_337D_DD2B,
+    0x2CDA_6B42_034B_701B,
+    0xB03D_37C1_81CB_096D,
+    0xE108_4382_66C7_1C6F,
+    0x2B31_80C7_EB51_B255,
+    0xDF92_B82F_96C0_8BBC,
+    0x5C68_C8C0_A632_F3BA,
+    0x5504_CC86_1C3D_0556,
+    0xABBF_A4E5_5FB2_6B8F,
+    0x4184_8B0A_B3BA_CEB4,
+    0xB334_A273_AA44_5D32,
+    0xBCA6_96F0_A85A_D881,
+    0x24F6_EC65_B528_D56C,
+    0x0CE1_512E_90F4_524A,
+    0x4E9D_D79D_5506_D35A,
+    0x2589_05FA_C6CE_9779,
+    0x2019_295B_3E10_9B33,
+    0xF8A9_478B_73A0_54CC,
+    0x2924_F2F9_3441_7EB0,
+    0x3993_357D_536D_1BC4,
+    0x38A8_1AC2_1DB6_FF8B,
+    0x47C4_FBF1_7D60_16BF,
+    0x1E0F_AADD_7667_E3F5,
+    0x7ABC_FF62_938B_EB96,
+    0xA78D_AD94_8FC1_79C9,
+    0x8F1F_98B7_2911_E50D,
+    0x61E4_8EAE_2712_1A91,
+    0x4D62_F7AD_3185_9808,
+    0xECEB_A345_EF5C_EAEB,
+    0xF5CE_B25E_BC96_84CE,
+    0xF633_E20C_B7F7_6221,
+    0xA32C_DF06_AB82_93E4,
+    0x985A_202C_A5EE_2CA4,
+    0xCF0B_8447_CC8A_8FB1,
+    0x9F76_5244_9798_59A3,
+    0xA8D5_16B1_A124_0017,
+    0x0BD7_BA3E_BB5D_C726,
+    0xE54B_CA55_B86A_DB39,
+    0x1D7A_3AFD_6C47_8063,
+    0x519E_C608_E766_9EDD,
+    0x0E57_15A2_D149_AA23,
+    0x177D_4571_848F_F194,
+    0xEEB5_5F32_4101_4C22,
+    0x0F5E_5CA1_3A6E_2EC2,
+    0x8029_927B_75F5_C361,
+    0xAD13_9FAB_C3D6_E436,
+    0x0D5D_F1A9_4CCF_402F,
+    0x3E8B_D948_BEA5_DFC8,
+    0xA5A0_D357_BD3F_F77E,
+    0xA2D1_2E25_1F74_F645,
+    0x66FD_9E52_5E81_A082,
+    0x2E0C_90CE_7F68_7A49,
+    0xC2E8_BCBE_BA97_3BC5,
+    0x0000_01BC_E509_745F,
+    0x4237_77BB_E6DA_B3D6,
+    0xD166_1C7E_AEF0_6EB5,
+    0xA178_1F35_4DAA_CFD8,
+    0x2D11_284A_2B16_AFFC,
+    0xF1FC_4F67_FA89_1D1F,
+    0x73EC_C25D_CB92_0ADA,
+    0xAE61_0C22_C2A1_2651,
+    0x96E0_A810_D356_B78A,
+    0x5A9A_381F_2FE7_870F,
+    0xD5AD_62ED_E94E_5530,
+    0xD225_E5E8_368D_1427,
+    0x6597_7B70_C7AF_4631,
+    0x99F8_89B2_DE39_D74F,
+    0x233F_30BF_54E1_D143,
+    0x9A96_75D3_D9A6_3C97,
+    0x5470_554F_F334_F9A8,
+    0x166A_CB74_4A4F_5688,
+    0x70C7_4CAA_B2E4_AEAD,
+    0xF0D0_9164_6F29_4D12,
+    0x57B8_2A89_6840_31D1,
+    0xEFD9_5A5A_61BE_0B6B,
+    0x2FBD_12E9_69F2_F29A,
+    0x9BD3_7013_FEFF_9FE8,
+    0x3F9B_0404_D608_5A06,
+    0x4940_C1F3_166C_FE15,
+    0x0954_2C4D_CDF3_DEFB,
+    0xB4C5_2183_85CD_5CE3,
+    0xC935_B7DC_4462_A641,
+    0x3417_F8A6_8ED3_B63F,
+    0xB809_5929_5B21_5B40,
+    0xF99C_DAEF_3B8C_8572,
+    0x018C_0614_F8FC_B95D,
+    0x1B14_ACCD_1A3A_CDF3,
+    0x84D4_71F2_00BB_732D,
+    0xC1A3_110E_95E8_DA16,
+    0x430A_7220_BF1A_82B8,
+    0xB77E_090D_39DF_210E,
+    0x5EF4_BD9F_3CD0_5E9D,
+    0x9D4F_F6DA_7E57_A444,
+    0xDA1D_60E1_83D4_A5F8,
+    0xB287_C384_1799_8E47,
+    0xFE3E_DC12_1BB3_1886,
+    0xC7FE_3CCC_980C_CBEF,
+    0xE46F_B590_189B_FD03,
+    0x3732_FD46_9A4C_57DC,
+    0x7EF7_00A0_7CF1_AD65,
+    0x59C6_4468_A31D_8859,
+    0x762F_B0B4_D45B_61F6,
+    0x155B_AED0_9904_7718,
+    0x6875_5E4C_3D50_BAA6,
+    0xE921_4E7F_22D8_B4DF,
+    0x2ADD_BF53_2EAC_95F4,
+    0x32AE_3909_B4BD_0109,
+    0x834D_F537_B08E_3450,
+    0xFA20_9DA8_4220_728D,
+    0x9E69_1D9B_9EFE_23F7,
+    0x0446_D288_C4AE_8D7F,
+    0x7B4C_C524_E169_785B,
+    0x21D8_7F01_35CA_1385,
+    0xCEBB_400F_137B_8AA5,
+    0x272E_2B66_5807_96BE,
+    0x3612_2641_25C2_B0DE,
+    0x0577_02BD_AD1E_FBB2,
+    0xD4BA_BB8E_ACF8_4BE9,
+    0x9158_3139_641B_C67B,
+    0x8BDC_2DE0_8036_E024,
+    0x603C_8156_F49F_68ED,
+    0xF7D2_36F7_DBEF_5111,
+    0x9727_C459_8AD2_1E80,
+    0xA08A_0896_670A_5FD7,
+    0xCB4A_8F43_09EB_A9CB,
+    0x81AF_564B_0F70_36A1,
+    0xC0B9_9AA7_7819_9ABD,
+    0x959F_1EC8_3FC8_E952,
+    0x8C50_5077_794A_81B9,
+    0x3ACA_AF8F_0563_38F0,
+    0x07B4_3F50_627A_6778,
+    0x4A44_AB49_F5EC_CC77,
+    0x3BC3_D6E4_B679_EE98,
+    0x9CC0_D4D1_CF14_108C,
+    0x4406_C00B_206B_C8A0,
+    0x82A1_8854_C8D7_2D89,
+    0x67E3_66B3_5C3C_432C,
+    0xB923_DD61_102B_37F2,
+    0x56AB_2779_D884_271D,
+    0xBE83_E1B0_FF15_25AF,
+    0xFB7C_65D4_217E_49A9,
+    0x6BDB_E0E7_6D48_E7D4,
+    0x08DF_8287_45D9_179E,
+    0x22EA_6A9A_DD53_BD34,
+    0xE36E_141C_5622_200A,
+    0x7F80_5D1B_8CB7_50EE,
+    0xAFE5_C7A5_9F58_E837,
+    0xE27F_996A_4FB1_C23C,
+    0xD386_7DFB_0775_F0D0,
+    0xD0E6_73DE_6E88_891A,
+    0x123A_EB9E_AFB8_6C25,
+    0x30F1_D5D5_C145_B895,
+    0xBB43_4A2D_EE72_69E7,
+    0x78CB_67EC_F931_FA38,
+    0xF33B_0372_323B_BF9C,
+    0x52D6_6336_FB27_9C74,
+    0x505F_33AC_0AFB_4EAA,
+    0xE8A5_CD99_A2CC_E187,
+    0x5349_7480_1E2D_30BB,
+    0x8D2D_5711_D587_6D90,
+    0x1F1A_4128_91BC_038E,
+    0xD6E2_E71D_82E5_6648,
+    0x7403_6C3A_4977_32B7,
+    0x89B6_7ED9_6361_F5AB,
+    0xFFED_95D8_F1EA_02A2,
+    0xE72B_3BD6_1464_D43D,
+    0xA630_0F17_0BDC_4820,
+    0xEBC1_8760_ED78_A77A,
+];
+
+pub(crate) static T2: [u64; 256] = [
+    0xE6A6_BE5A_05A1_2138,
+    0xB5A1_22A5_B4F8_7C98,
+    0x563C_6089_140B_6990,
+    0x4C46_CB2E_391F_5DD5,
+    0xD932_ADDB_C9B7_9434,
+    0x08EA_70E4_2015_AFF5,
+    0xD765_A667_3E47_8CF1,
+    0xC4FB_757E_AB27_8D99,
+    0xDF11_C686_2D6E_0692,
+    0xDDEB_84F1_0D7F_3B16,
+    0x6F2E_F604_A665_EA04,
+    0x4A8E_0F0F_F0E0_DFB3,
+    0xA5ED_EEF8_3DBC_BA51,
+    0xFC4F_0A2A_0EA4_371E,
+    0xE83E_1DA8_5CB3_8429,
+    0xDC8F_F882_BA1B_1CE2,
+    0xCD45_505E_8353_E80D,
+    0x18D1_9A00_D4DB_0717,
+    0x34A0_CFED_A5F3_8101,
+    0x0BE7_7E51_8887_CAF2,
+    0x1E34_1438_B3C4_5136,
+    0xE057_97F4_9089_CCF9,
+    0xFFD2_3F9D_F259_1D14,
+    0x543D_DA22_8595_C5CD,
+    0x661F_81FD_9905_2A33,
+    0x8736_E641_DB0F_7B76,
+    0x1522_7725_418E_5307,
+    0xE25F_7F46_162E_B2FA,
+    0x48A8_B212_6C13_D9FE,
+    0xAFDC_5417_92E7_6EEA,
+    0x03D9_12BF_C6D1_898F,
+    0x31B1_AAFA_1B83_F51B,
+    0xF1AC_2796_E42A_B7D9,
+    0x40A3_A7D7_FCD2_EBAC,
+    0x1056_136D_0AFB_BCC5,
+    0x7889_E1DD_9A6D_0C85,
+    0xD335_2578_2A79_74AA,
+    0xA7E2_5D09_078A_C09B,
+    0xBD41_38B3_EAC6_EDD0,
+    0x920A_BFBE_71EB_9E70,
+    0xA2A5_D0F5_4FC2_625C,
+    0xC054_E36B_0B12_90A3,
+    0xF6DD_59FF_62FE_932B,
+    0x3537_3545_11A8_AC7D,
+    0xCA84_5E91_72FA_DCD4,
+    0x84F8_2B60_329D_20DC,
+    0x79C6_2CE1_CD67_2F18,
+    0x8B09_A2AD_D124_642C,
+    0xD0C1_E96A_19D9_E726,
+    0x5A78_6A9B_4BA9_500C,
+    0x0E02_0336_634C_43F3,
+    0xC17B_474A_EB66_D822,
+    0x6A73_1AE3_EC9B_AAC2,
+    0x8226_667A_E084_0258,
+    0x67D4_5676_91CA_ECA5,
+    0x1D94_155C_4875_ADB5,
+    0x6D00_FD98_5B81_3FDF,
+    0x5128_6EFC_B774_CD06,
+    0x5E88_3447_1FA7_44AF,
+    0xF72C_A0AE_E761_AE2E,
+    0xBE40_E4CD_AEE8_E09A,
+    0xE997_0BBB_5118_F665,
+    0x726E_4BEB_33DF_1964,
+    0x703B_0007_2919_9762,
+    0x4631_D816_F5EF_30A7,
+    0xB880_B5B5_1504_A6BE,
+    0x6417_93C3_7ED8_4B6C,
+    0x7B21_ED77_F6E9_7D96,
+    0x7763_0631_2EF9_6B73,
+    0xAE52_8948_E86F_F3F4,
+    0x53DB_D7F2_86A3_F8F8,
+    0x16CA_DCE7_4CFC_1063,
+    0x005C_19BD_FA52_C6DD,
+    0x6886_8F5D_64D4_6AD3,
+    0x3A9D_512C_CF1E_186A,
+    0x367E_62C2_3856_60AE,
+    0xE359_E7EA_77DC_B1D7,
+    0x526C_0773_749A_BE6E,
+    0x735A_E5F9_D09F_734B,
+    0x493F_C7CC_8A55_8BA8,
+    0xB0B9_C153_3041_AB45,
+    0x3219_58BA_470A_59BD,
+    0x852D_B00B_5F46_C393,
+    0x9120_9B2B_D336_B0E5,
+    0x6E60_4F7D_659E_F19F,
+    0xB99A_8AE2_782C_CB24,
+    0xCCF5_2AB6_C814_C4C7,
+    0x4727_D9AF_BE11_727B,
+    0x7E95_0D0C_0121_B34D,
+    0x756F_4356_70AD_471F,
+    0xF5AD_D442_615A_6849,
+    0x4E87_E099_80B9_957A,
+    0x2ACF_A1DF_50AE_E355,
+    0xD898_263A_FD2F_D556,
+    0xC8F4_924D_D80C_8FD6,
+    0xCF99_CA3D_754A_173A,
+    0xFE47_7BAC_AF91_BF3C,
+    0xED53_71F6_D690_C12D,
+    0x831A_5C28_5E68_7094,
+    0xC5D3_C90A_3708_A0A4,
+    0x0F7F_9037_17D0_6580,
+    0x19F9_BB13_B8FD_F27F,
+    0xB1BD_6F1B_4D50_2843,
+    0x1C76_1BA3_8FFF_4012,
+    0x0D15_30C4_E2E2_1F3B,
+    0x8943_CE69_A737_2C8A,
+    0xE518_4E11_FEB5_CE66,
+    0x618B_DB80_BD73_6621,
+    0x7D29_BAD6_8B57_4D0B,
+    0x81BB_613E_25E6_FE5B,
+    0x071C_9C10_BC07_913F,
+    0xC7BE_EB79_09AC_2D97,
+    0xC3E5_8D35_3BC5_D757,
+    0xEB01_7892_F38F_61E8,
+    0xD4EF_FB9C_9B1C_C21A,
+    0x9972_7D26_F494_F7AB,
+    0xA3E0_63A2_956B_3E03,
+    0x9D4A_8B9A_4AA0_9C30,
+    0x3F6A_B7D5_0009_0FB4,
+    0x9CC0_F2A0_5726_8AC0,
+    0x3DEE_9D2D_EDBF_42D1,
+    0x330F_49C8_7960_A972,
+    0xC6B2_7202_8742_1B41,
+    0x0AC5_9EC0_7C00_369C,
+    0xEF4E_AC49_CB35_3425,
+    0xF450_244E_EF01_29D8,
+    0x8ACC_46E5_CAF4_DEB6,
+    0x2FFE_AB63_9892_63F7,
+    0x8F7C_B9FE_5D7A_4578,
+    0x5BD8_F764_4E63_4635,
+    0x427A_7315_BF2D_C900,
+    0x17D0_C4AA_2125_261C,
+    0x3992_486C_9351_8E50,
+    0xB4CB_FEE0_A2D7_D4C3,
+    0x7C75_D620_2C5D_DD8D,
+    0xDBC2_95D8_E35B_6C61,
+    0x60B3_69D3_0203_2B19,
+    0xCE42_685F_DCE4_4132,
+    0x06F3_DDB9_DDF6_5610,
+    0x8EA4_D21D_B5E1_48F0,
+    0x20B0_FCE6_2FCD_496F,
+    0x2C1B_9123_58B0_EE31,
+    0xB283_17B8_18F5_A308,
+    0xA89C_1E18_9CA6_D2CF,
+    0x0C6B_1857_6AAA_DBC8,
+    0xB65D_EAA9_1299_FAE3,
+    0xFB2B_794B_7F10_27E7,
+    0x04E4_317F_443B_5BEB,
+    0x4B85_2D32_5939_D0A6,
+    0xD5AE_6BEE_FB20_7FFC,
+    0x3096_82B2_81C7_D374,
+    0xBAE3_09A1_94C3_B475,
+    0x8CC3_F97B_13B4_9F05,
+    0x98A9_422F_F829_3967,
+    0x244B_16B0_1076_FF7C,
+    0xF8BF_571C_663D_67EE,
+    0x1F0D_6758_EEE3_0DA1,
+    0xC9B6_11D9_7ADE_B9B7,
+    0xB7AF_D588_7B6C_57A2,
+    0x6290_AE84_6B98_4FE1,
+    0x94DF_4CDE_ACC1_A5FD,
+    0x058A_5BD1_C548_3AFF,
+    0x6316_6CC1_42BA_3C37,
+    0x8DB8_526E_B2F7_6F40,
+    0xE108_8003_6F0D_6D4E,
+    0x9E05_23C9_971D_311D,
+    0x45EC_2824_CC7C_D691,
+    0x575B_8359_E623_82C9,
+    0xFA9E_400D_C488_9995,
+    0xD182_3ECB_4572_1568,
+    0xDAFD_983B_8206_082F,
+    0xAA7D_2908_2386_A8CB,
+    0x269F_CD44_03B8_7588,
+    0x1B91_F5F7_28BD_D1E0,
+    0xE466_9F39_0402_01F6,
+    0x7A1D_7C21_8CF0_4ADE,
+    0x6562_3C29_D79C_E5CE,
+    0x2368_4490_96C0_0BB1,
+    0xAB9B_F187_9DA5_03BA,
+    0xBC23_ECB1_A458_058E,
+    0x9A58_DF01_BB40_1ECC,
+    0xA070_E868_A85F_143D,
+    0x4FF1_8830_7DF2_239E,
+    0x14D5_65B4_1A64_1183,
+    0xEE13_3374_5270_1602,
+    0x950E_3DCF_3F28_5E09,
+    0x5993_0254_B9C8_0953,
+    0x3BF2_9940_8930_DA6D,
+    0xA955_943F_5369_1387,
+    0xA15E_DECA_A9CB_8784,
+    0x2914_2127_352B_E9A0,
+    0x76F0_371F_FF4E_7AFB,
+    0x0239_F450_274F_2228,
+    0xBB07_3AF0_1D5E_868B,
+    0xBFC8_0571_C10E_96C1,
+    0xD267_0885_6822_2E23,
+    0x9671_A3D4_8E80_B5B0,
+    0x55B5_D38A_E193_BB81,
+    0x693A_E2D0_A18B_04B8,
+    0x5C48_B4EC_ADD5_335F,
+    0xFD74_3B19_4916_A1CA,
+    0x2577_0181_34BE_98C4,
+    0xE779_87E8_3C54_A4AD,
+    0x28E1_1014_DA33_E1B9,
+    0x270C_C59E_226A_A213,
+    0x7149_5F75_6D1A_5F60,
+    0x9BE8_53FB_60AF_EF77,
+    0xADC7_86A7_F744_3DBF,
+    0x0904_4561_73B2_9A82,
+    0x58BC_7A66_C232_BD5E,
+    0xF306_558C_673A_C8B2,
+    0x41F6_39C6_B6C9_772A,
+    0x216D_EFE9_9FDA_35DA,
+    0x1164_0CC7_1C7B_E615,
+    0x93C4_3694_565C_5527,
+    0xEA03_8E62_4677_7839,
+    0xF9AB_F3CE_5A3E_2469,
+    0x741E_768D_0FD3_12D2,
+    0x0144_B883_CED6_52C6,
+    0xC20B_5A5B_A33F_8552,
+    0x1AE6_9633_C343_5A9D,
+    0x97A2_8CA4_088C_FDEC,
+    0x8824_A43C_1E96_F420,
+    0x3761_2FA6_6EEE_A746,
+    0x6B4C_B165_F9CF_0E5A,
+    0x43AA_1C06_A0AB_FB4A,
+    0x7F4D_C26F_F162_796B,
+    0x6CBA_CC8E_54ED_9B0F,
+    0xA6B7_FFEF_D2BB_253E,
+    0x2E25_BC95_B0A2_9D4F,
+    0x86D6_A58B_DEF1_388C,
+    0xDED7_4AC5_76B6_F054,
+    0x8030_BDBC_2B45_805D,
+    0x3C81_AF70_E94D_9289,
+    0x3EFF_6DDA_9E31_00DB,
+    0xB38D_C39F_DFCC_8847,
+    0x1238_8552_8D17_B87E,
+    0xF2DA_0ED2_40B1_B642,
+    0x44CE_FADC_D54B_F9A9,
+    0x1312_200E_433C_7EE6,
+    0x9FFC_C84F_3A78_C748,
+    0xF0CD_1F72_2485_76BB,
+    0xEC69_7405_3638_CFE4,
+    0x2BA7_B67C_0CEC_4E4C,
+    0xAC2F_4DF3_E5CE_32ED,
+    0xCB33_D143_26EA_4C11,
+    0xA4E9_044C_C77E_58BC,
+    0x5F51_3293_D934_FCEF,
+    0x5DC9_6455_06E5_5444,
+    0x50DE_418F_317D_E40A,
+    0x388C_B31A_69DD_E259,
+    0x2DB4_A834_5582_0A86,
+    0x9010_A91E_8471_1AE9,
+    0x4DF7_F0B7_B149_8371,
+    0xD62A_2EAB_C097_7179,
+    0x22FA_C097_AA8D_5C0E,
+];
+
+pub(crate) static T3: [u64; 256] = [
+    0xF49F_CC2F_F1DA_F39B,
+    0x487F_D5C6_6FF2_9281,
+    0xE8A3_0667_FCDC_A83F,
+    0x2C9B_4BE3_D2FC_CE63,
+    0xDA3F_F74B_93FB_BBC2,
+    0x2FA1_65D2_FE70_BA66,
+    0xA103_E279_970E_93D4,
+    0xBECD_EC77_B0E4_5E71,
+    0xCFB4_1E72_3985_E497,
+    0xB70A_AA02_5EF7_5017,
+    0xD423_09F0_3840_B8E0,
+    0x8EFC_1AD0_3589_8579,
+    0x96C6_920B_E2B2_ABC5,
+    0x66AF_4163_375A_9172,
+    0x2174_ABDC_CA71_27FB,
+    0xB33C_CEA6_4A72_FF41,
+    0xF04A_4933_0830_66A5,
+    0x8D97_0ACD_D728_9AF5,
+    0x8F96_E8E0_31C8_C25E,
+    0xF3FE_C022_7687_5D47,
+    0xEC7B_F310_0561_90DD,
+    0xF5AD_B0AE_BB0F_1491,
+    0x9B50_F885_0FD5_8892,
+    0x4975_4883_58B7_4DE8,
+    0xA335_4FF6_9153_1C61,
+    0x0702_BBE4_81D2_C6EE,
+    0x89FB_2405_7DED_ED98,
+    0xAC30_7513_8596_E902,
+    0x1D2D_3580_1727_72ED,
+    0xEB73_8FC2_8E6B_C30D,
+    0x5854_EF8F_6304_4326,
+    0x9E5C_5232_5ADD_3BBE,
+    0x90AA_53CF_325C_4623,
+    0xC1D2_4D51_349D_D067,
+    0x2051_CFEE_A69E_A624,
+    0x1322_0F0A_862E_7E4F,
+    0xCE39_3994_04E0_4864,
+    0xD9C4_2CA4_7086_FCB7,
+    0x685A_D223_8A03_E7CC,
+    0x0664_84B2_AB2F_F1DB,
+    0xFE9D_5D70_EFBF_79EC,
+    0x5B13_B9DD_9C48_1854,
+    0x15F0_D475_ED15_09AD,
+    0x0BEB_CD06_0EC7_9851,
+    0xD58C_6791_183A_B7F8,
+    0xD118_7C50_52F3_EEE4,
+    0xC95D_1192_E54E_82FF,
+    0x86EE_A14C_B9AC_6CA2,
+    0x3485_BEB1_5367_7D5D,
+    0xDD19_1D78_1F8C_492A,
+    0xF608_66BA_A784_EBF9,
+    0x518F_643B_A2D0_8C74,
+    0x8852_E956_E108_7C22,
+    0xA768_CB8D_C410_AE8D,
+    0x3804_7726_BFEC_8E1A,
+    0xA677_38B4_CD3B_45AA,
+    0xAD16_691C_EC0D_DE19,
+    0xC6D4_3193_8046_2E07,
+    0xC5A5_876D_0BA6_1938,
+    0x16B9_FA1F_A58F_D840,
+    0x188A_B117_3CA7_4F18,
+    0xABDA_2F98_C99C_021F,
+    0x3E05_80AB_134A_E816,
+    0x5F3B_05B7_7364_5ABB,
+    0x2501_A2BE_5575_F2F6,
+    0x1B2F_7400_4E7E_8BA9,
+    0x1CD7_5803_71E8_D953,
+    0x7F6E_D895_6276_4E30,
+    0xB159_26FF_596F_003D,
+    0x9F65_293D_A8C5_D6B9,
+    0x6ECE_F04D_D690_F84C,
+    0x4782_275F_FF33_AF88,
+    0xE414_3308_3F82_0801,
+    0xFD0D_FE40_9A1A_F9B5,
+    0x4325_A334_2CDB_396B,
+    0x8AE7_7E62_B301_B252,
+    0xC36F_9E9F_6655_615A,
+    0x8545_5A2D_92D3_2C09,
+    0xF2C7_DEA9_4947_7485,
+    0x63CF_B4C1_33A3_9EBA,
+    0x83B0_40CC_6EBC_5462,
+    0x3B94_54C8_FDB3_26B0,
+    0x56F5_6A9E_87FF_D78C,
+    0x2DC2_940D_99F4_2BC6,
+    0x98F7_DF09_6B09_6E2D,
+    0x19A6_E01E_3AD8_52BF,
+    0x42A9_9CCB_DBD4_B40B,
+    0xA599_98AF_45E9_C559,
+    0x3662_95E8_07D9_3186,
+    0x6B48_181B_FAA1_F773,
+    0x1FEC_57E2_157A_0A1D,
+    0x4667_446A_F620_1AD5,
+    0xE615_EBCA_CFB0_F075,
+    0xB8F3_1F4F_6829_0778,
+    0x2271_3ED6_CE22_D11E,
+    0x3057_C1A7_2EC3_C93B,
+    0xCB46_ACC3_7C3F_1F2F,
+    0xDBB8_93FD_02AA_F50E,
+    0x331F_D92E_600B_9FCF,
+    0xA498_F961_48EA_3AD6,
+    0xA8D8_426E_8B6A_83EA,
+    0xA089_B274_B773_5CDC,
+    0x87F6_B373_1E52_4A11,
+    0x1188_08E5_CBC9_6749,
+    0x9906_E4C7_B19B_D394,
+    0xAFED_7F7E_9B24_A20C,
+    0x6509_EADE_EB36_44A7,
+    0x6C1E_F1D3_E8EF_0EDE,
+    0xB9C9_7D43_E979_8FB4,
+    0xA2F2_D784_740C_28A3,
+    0x7B84_9647_6197_566F,
+    0x7A5B_E3E6_B65F_069D,
+    0xF963_30ED_78BE_6F10,
+    0xEEE6_0DE7_7A07_6A15,
+    0x2B4B_EE4A_A08B_9BD0,
+    0x6A56_A63E_C7B8_894E,
+    0x0212_1359_BA34_FEF4,
+    0x4CBF_99F8_2837_03FC,
+    0x3980_7135_0CAF_30C8,
+    0xD0A7_7A89_F017_687A,
+    0xF1C1_A9EB_9E42_3569,
+    0x8C79_7628_2DEE_8199,
+    0x5D17_37A5_DD1F_7ABD,
+    0x4F53_433C_09A9_FA80,
+    0xFA8B_0C53_DF7C_A1D9,
+    0x3FD9_DCBC_886C_CB77,
+    0xC040_917C_A91B_4720,
+    0x7DD0_0142_F9D1_DCDF,
+    0x8476_FC1D_4F38_7B58,
+    0x23F8_E7C5_F331_6503,
+    0x032A_2244_E7E3_7339,
+    0x5C87_A5D7_50F5_A74B,
+    0x082B_4CC4_3698_992E,
+    0xDF91_7BEC_B858_F63C,
+    0x3270_B8FC_5BF8_6DDA,
+    0x10AE_72BB_29B5_DD76,
+    0x576A_C94E_7700_362B,
+    0x1AD1_12DA_C61E_FB8F,
+    0x691B_C30E_C5FA_A427,
+    0xFF24_6311_CC32_7143,
+    0x3142_368E_30E5_3206,
+    0x7138_0E31_E02C_A396,
+    0x958D_5C96_0AAD_76F1,
+    0xF8D6_F430_C16D_A536,
+    0xC8FF_D13F_1BE7_E1D2,
+    0x7578_AE66_004D_DBE1,
+    0x0583_3F01_067B_E646,
+    0xBB34_B5AD_3BFE_586D,
+    0x095F_34C9_A12B_97F0,
+    0x247A_B645_25D6_0CA8,
+    0xDCDB_C6F3_0174_77D1,
+    0x4A2E_14D4_DECA_D24D,
+    0xBDB5_E6D9_BE0A_1EEB,
+    0x2A7E_70F7_7943_01AB,
+    0xDEF4_2D8A_2705_40FD,
+    0x0107_8EC0_A34C_22C1,
+    0xE5DE_511A_F4C1_6387,
+    0x7EBB_3A52_BD9A_330A,
+    0x7769_7857_AA7D_6435,
+    0x004E_8316_03AE_4C32,
+    0xE7A2_1020_AD78_E312,
+    0x9D41_A70C_6AB4_20F2,
+    0x28E0_6C18_EA11_41E6,
+    0xD2B2_8CBD_984F_6B28,
+    0x26B7_5F6C_446E_9D83,
+    0xBA47_568C_4D41_8D7F,
+    0xD80B_ADBF_E618_3D8E,
+    0x0E20_6D7F_5F16_6044,
+    0xE258_A439_11CB_CA3E,
+    0x723A_1746_B21D_C0BC,
+    0xC7CA_A854_F5D7_CDD3,
+    0x7CAC_3288_3D26_1D9C,
+    0x7690_C264_23BA_942C,
+    0x17E5_5524_4780_42B8,
+    0xE0BE_4776_56A2_389F,
+    0x4D28_9B5E_67AB_2DA0,
+    0x4486_2B9C_8FBB_FD31,
+    0xB47C_C804_9D14_1365,
+    0x822C_1B36_2B91_C793,
+    0x4EB1_4655_FB13_DFD8,
+    0x1ECB_BA07_14E2_A97B,
+    0x6143_459D_5CDE_5F14,
+    0x53A8_FBF1_D5F0_AC89,
+    0x97EA_04D8_1C5E_5B00,
+    0x6221_81A8_D4FD_B3F3,
+    0xE9BC_D341_572A_1208,
+    0x1411_2586_43CC_E58A,
+    0x9144_C5FE_A4C6_E0A4,
+    0x0D33_D065_65CF_620F,
+    0x54A4_8D48_9F21_9CA1,
+    0xC43E_5EAC_6D63_C821,
+    0xA972_8B3A_7277_0DAF,
+    0xD793_4E7B_20DF_87EF,
+    0xE355_03B6_1A3E_86E5,
+    0xCAE3_21FB_C819_D504,
+    0x129A_50B3_AC60_BFA6,
+    0xCD5E_68EA_7E9F_B6C3,
+    0xB01C_9019_9483_B1C7,
+    0x3DE9_3CD5_C295_376C,
+    0xAED5_2EDF_2AB9_AD13,
+    0x2E60_F512_C0A0_7884,
+    0xBC3D_86A3_E362_10C9,
+    0x3526_9D9B_1639_51CE,
+    0x0C7D_6E2A_D0CD_B5FA,
+    0x59E8_6297_D87F_5733,
+    0x298E_F221_898D_B0E7,
+    0x5500_0029_D1A5_AA7E,
+    0x8BC0_8AE1_B506_1B45,
+    0xC2C3_1C2B_6C92_703A,
+    0x94CC_596B_AF25_EF42,
+    0x0A1D_73DB_2254_0456,
+    0x04B6_A0F9_D9C4_179A,
+    0xEFFD_AFA2_AE3D_3C60,
+    0xF7C8_075B_B494_96C4,
+    0x9CC5_C714_1D1C_D4E3,
+    0x78BD_1638_218E_5534,
+    0xB2F1_1568_F850_246A,
+    0xEDFA_BCFA_9502_BC29,
+    0x796C_E5F2_DA23_051B,
+    0xAAE1_28B0_DC93_537C,
+    0x3A49_3DA0_EE4B_29AE,
+    0xB5DF_6B2C_4168_95D7,
+    0xFCAB_BD25_122D_7F37,
+    0x7081_0B58_105D_C4B1,
+    0xE10F_DD37_F788_2A90,
+    0x524D_CAB5_518A_3F5C,
+    0x3C9E_8587_8451_255B,
+    0x4029_8281_19BD_34E2,
+    0x74A0_5B6F_5D3C_ECCB,
+    0xB610_0215_42E1_3ECA,
+    0x0FF9_79D1_2F59_E2AC,
+    0x6037_DA27_E4F9_CC50,
+    0x5E92_975A_0DF1_847D,
+    0xD66D_E190_D3E6_23FE,
+    0x5032_D6B8_7B56_8048,
+    0x9A36_B7CE_8235_216E,
+    0x8027_2A7A_24F6_4B4A,
+    0x93EF_ED8B_8C69_16F7,
+    0x37DD_BFF4_4CCE_1555,
+    0x4B95_DB5D_4B99_BD25,
+    0x92D3_FDA1_6981_2FC0,
+    0xFB1A_4A9A_9066_0BB6,
+    0x730C_1969_46A4_B9B2,
+    0x81E2_89AA_7F49_DA68,
+    0x6466_9A0F_83B1_A05F,
+    0x27B3_FF7D_9644_F48B,
+    0xCC6B_615C_8DB6_75B3,
+    0x674F_20B9_BCEB_BE95,
+    0x6F31_2382_7565_5982,
+    0x5AE4_8871_3E45_CF05,
+    0xBF61_9F99_54C2_1157,
+    0xEABA_C460_40A8_EAE9,
+    0x454C_6FE9_F2C0_C1CD,
+    0x419C_F649_6412_691C,
+    0xD3DC_3BEF_265B_0F70,
+    0x6D0E_60F5_C357_8A9E,
+];
+
+pub(crate) static T4: [u64; 256] = [
+    0x5B0E_6085_2632_3C55,
+    0x1A46_C1A9_FA1B_59F5,
+    0xA9E2_45A1_7C4C_8FFA,
+    0x65CA_5159_DB29_55D7,
+    0x05DB_0A76_CE35_AFC2,
+    0x81EA_C77E_A911_3D45,
+    0x528E_F88A_B6AC_0A0D,
+    0xA09E_A253_597B_E3FF,
+    0x430D_DFB3_AC48_CD56,
+    0xC4B3_A67A_F45C_E46F,
+    0x4ECE_CFD8_FBE2_D05E,
+    0x3EF5_6F10_B399_35F0,
+    0x0B22_D682_9CD6_19C6,
+    0x17FD_460A_74DF_2069,
+    0x6CF8_CC8E_8510_ED40,
+    0xD6C8_24BF_3A6E_CAA7,
+    0x6124_3D58_1A81_7049,
+    0x048B_ACB6_BBC1_63A2,
+    0xD9A3_8AC2_7D44_CC32,
+    0x7FDD_FF5B_AAF4_10AB,
+    0xAD6D_495A_A804_824B,
+    0xE1A6_A74F_2D8C_9F94,
+    0xD4F7_8512_35DE_E8E3,
+    0xFD4B_7F88_6540_D893,
+    0x247C_2004_2AA4_BFDA,
+    0x096E_A1C5_17D1_327C,
+    0xD569_66B4_361A_6685,
+    0x277D_A5C3_1221_057D,
+    0x94D5_9893_A43A_CFF7,
+    0x64F0_C51C_CDC0_2281,
+    0x3D33_BCC4_FF61_89DB,
+    0xE005_CB18_4CE6_6AF1,
+    0xFF5C_CD1D_1DB9_9BEA,
+    0xB0B8_54A7_FE42_980F,
+    0x7BD4_6A6A_718D_4B9F,
+    0xD10F_A8CC_22A5_FD8C,
+    0xD314_8495_2BE4_BD31,
+    0xC7FA_975F_CB24_3847,
+    0x4886_ED1E_5846_C407,
+    0x28CD_DB79_1EB7_0B04,
+    0xC2B0_0BE2_F573_417F,
+    0x5C95_9045_2180_F877,
+    0x7A6B_DDFF_F370_EB00,
+    0xCE50_9E38_D6D9_D6A4,
+    0xEBEB_0F00_647F_A702,
+    0x1DCC_06CF_7660_6F06,
+    0xE4D9_F28B_A286_FF0A,
+    0xD85A_305D_C918_C262,
+    0x475B_1D87_3222_5F54,
+    0x2D4F_B516_68CC_B5FE,
+    0xA679_B9D9_D72B_BA20,
+    0x5384_1C0D_912D_43A5,
+    0x3B7E_AA48_BF12_A4E8,
+    0x781E_0E47_F22F_1DDF,
+    0xEFF2_0CE6_0AB5_0973,
+    0x20D2_61D1_9DFF_B742,
+    0x16A1_2B03_062A_2E39,
+    0x1960_EB22_3965_0495,
+    0x251C_16FE_D50E_B8B8,
+    0x9AC0_C330_F826_016E,
+    0xED15_2665_953E_7671,
+    0x02D6_3194_A636_9570,
+    0x5074_F083_94B1_C987,
+    0x70BA_598C_90B2_5CE1,
+    0x794A_1581_0B97_42F6,
+    0x0D59_25E9_FCAF_8C6C,
+    0x3067_716C_D868_744E,
+    0x910A_B077_E8D7_731B,
+    0x6A61_BBDB_5AC4_2F61,
+    0x9351_3EFB_F085_1567,
+    0xF494_724B_9E83_E9D5,
+    0xE887_E198_5C09_648D,
+    0x34B1_D3C6_7537_0CFD,
+    0xDC35_E433_BC0D_255D,
+    0xD0AA_B842_3413_1BE0,
+    0x0804_2A50_B48B_7EAF,
+    0x9997_C4EE_44A3_AB35,
+    0x829A_7B49_2017_99D0,
+    0x263B_8307_B7C5_4441,
+    0x752F_95F4_FD6A_6CA6,
+    0x9272_1740_2C08_C6E5,
+    0x2A8A_B754_A795_D9EE,
+    0xA442_F755_2F72_943D,
+    0x2C31_334E_1978_1208,
+    0x4FA9_8D7C_EAEE_6291,
+    0x55C3_862F_665D_B309,
+    0xBD06_1017_5D53_B1F3,
+    0x46FE_6CB8_4041_3F27,
+    0x3FE0_3792_DF0C_FA59,
+    0xCFE7_0037_2EB8_5E8F,
+    0xA7BE_29E7_ADBC_E118,
+    0xE544_EE5C_DE84_31DD,
+    0x8A78_1B1B_41F1_873E,
+    0xA5C9_4C78_A0D2_F0E7,
+    0x3941_2E28_77B6_0728,
+    0xA126_5EF3_AFC9_A62C,
+    0xBCC2_770C_6A25_06C5,
+    0x3AB6_6DD5_DCE1_CE12,
+    0xE654_99D0_4A67_5B37,
+    0x7D8F_5234_81BF_D216,
+    0x0F6F_64FC_EC15_F389,
+    0x74EF_BE61_8B5B_13C8,
+    0xACDC_82B7_1427_3E1D,
+    0xDD40_BFE0_0319_9D17,
+    0x37E9_9257_E7E0_61F8,
+    0xFA52_6269_0477_5AAA,
+    0x8BBB_F63A_463D_56F9,
+    0xF001_3F15_43A2_6E64,
+    0xA830_7E9F_879E_C898,
+    0xCC4C_27A4_1501_77CC,
+    0x1B43_2F2C_CA1D_3348,
+    0xDE1D_1F8F_9F6F_A013,
+    0x6066_02A0_47A7_DDD6,
+    0xD237_AB64_CC1C_B2C7,
+    0x9B93_8E72_25FC_D1D3,
+    0xEC4E_0370_8E0F_F476,
+    0xFEB2_FBDA_3D03_C12D,
+    0xAE0B_CED2_EE43_889A,
+    0x22CB_8923_EBFB_4F43,
+    0x6936_0D01_3CF7_396D,
+    0x855E_3602_D2D4_E022,
+    0x0738_05BA_D01F_784C,
+    0x33E1_7A13_3852_F546,
+    0xDF48_7405_8AC7_B638,
+    0xBA92_B29C_678A_A14A,
+    0x0CE8_9FC7_6CFA_ADCD,
+    0x5F9D_4E09_0833_9E34,
+    0xF1AF_E929_1F59_23B9,
+    0x6E34_80F6_0F4A_265F,
+    0xEEBF_3A2A_B29B_841C,
+    0xE219_38A8_8F91_B4AD,
+    0x57DF_EFF8_45C6_D3C3,
+    0x2F00_6B0B_F62C_AAF2,
+    0x62F4_79EF_6F75_EE78,
+    0x11A5_5AD4_1C89_16A9,
+    0xF229_D290_84FE_D453,
+    0x42F1_C27B_16B0_00E6,
+    0x2B1F_7674_9823_C074,
+    0x4B76_ECA3_C274_5360,
+    0x8C98_F463_B916_91BD,
+    0x14BC_C93C_F1AD_E66A,
+    0x8885_213E_6D45_8397,
+    0x8E17_7DF0_274D_4711,
+    0xB49B_73B5_503F_2951,
+    0x1016_8168_C3F9_6B6B,
+    0x0E3D_963B_63CA_B0AE,
+    0x8DFC_4B56_55A1_DB14,
+    0xF789_F135_6E14_DE5C,
+    0x683E_68AF_4E51_DAC1,
+    0xC9A8_4F9D_8D4B_0FD9,
+    0x3691_E03F_52A0_F9D1,
+    0x5ED8_6E46_E187_8E80,
+    0x3C71_1A0E_99D0_7150,
+    0x5A08_65B2_0C4E_9310,
+    0x56FB_FC1F_E4F0_682E,
+    0xEA8D_5DE3_105E_DF9B,
+    0x71AB_FDB1_2379_187A,
+    0x2EB9_9DE1_BEE7_7B9C,
+    0x21EC_C0EA_33CF_4523,
+    0x59A4_D752_1805_C7A1,
+    0x3896_F5EB_56AE_7C72,
+    0xAA63_8F3D_B18F_75DC,
+    0x9F39_358D_ABE9_808E,
+    0xB7DE_FA91_C00B_72AC,
+    0x6B55_41FD_6249_2D92,
+    0x6DC6_DEE8_F92E_4D5B,
+    0x353F_57AB_C4BE_EA7E,
+    0x7357_69D6_DA56_90CE,
+    0x0A23_4AA6_4239_1484,
+    0xF6F9_5080_28F8_0D9D,
+    0xB8E3_19A2_7AB3_F215,
+    0x31AD_9C11_5134_1A4D,
+    0x773C_22A5_7BEF_5805,
+    0x45C7_561A_0796_8633,
+    0xF913_DA9E_249D_BE36,
+    0xDA65_2D9B_78A6_4C68,
+    0x4C27_A97F_3BC3_34EF,
+    0x7662_1220_E66B_17F4,
+    0x9677_4389_9ACD_7D0B,
+    0xF3EE_5BCA_E0ED_6782,
+    0x409F_7536_00C8_79FC,
+    0x06D0_9A39_B592_6DB6,
+    0x6F83_AEB0_317A_C588,
+    0x01E6_CA4A_8638_1F21,
+    0x66FF_3462_D19F_3025,
+    0x7220_7C24_DDFD_3BFB,
+    0x4AF6_B6D3_E2EC_E2EB,
+    0x9C99_4DBE_C7EA_08DE,
+    0x49AC_E597_B09A_8BC4,
+    0xB38C_4766_CF07_97BA,
+    0x131B_9373_C57C_2A75,
+    0xB182_2CCE_6193_1E58,
+    0x9D75_55B9_09BA_1C0C,
+    0x127F_AFDD_937D_11D2,
+    0x29DA_3BAD_C66D_92E4,
+    0xA2C1_D571_54C2_ECBC,
+    0x58C5_134D_82F6_FE24,
+    0x1C3A_E351_5B62_274F,
+    0xE907_C82E_01CB_8126,
+    0xF8ED_0919_13E3_7FCB,
+    0x3249_D8F9_C800_46C9,
+    0x80CF_9BED_E388_FB63,
+    0x1881_539A_116C_F19E,
+    0x5103_F3F7_6BD5_2457,
+    0x15B7_E6F5_AE47_F7A8,
+    0xDBD7_C6DE_D47E_9CCF,
+    0x44E5_5C41_0228_BB1A,
+    0xB647_D425_5EDB_4E99,
+    0x5D11_882B_B8AA_FC30,
+    0xF509_8BBB_29D3_212A,
+    0x8FB5_EA14_E902_96B3,
+    0x677B_9421_57DD_025A,
+    0xFB58_E7C0_A390_ACB5,
+    0x89D3_674C_83BD_4A01,
+    0x9E2D_A4DF_4BF3_B93B,
+    0xFCC4_1E32_8CAB_4829,
+    0x03F3_8C96_BA58_2C52,
+    0xCAD1_BDBD_7FD8_5DB2,
+    0xBBB4_42C1_6082_AE83,
+    0xB95F_E86B_A5DA_9AB0,
+    0xB22E_0467_3771_A93F,
+    0x8453_58C9_4931_52D8,
+    0xBE2A_4886_97B4_541E,
+    0x95A2_DC2D_D38E_6966,
+    0xC02C_11AC_923C_852B,
+    0x2388_B199_0DF2_A87B,
+    0x7C80_08FA_1B4F_37BE,
+    0x1F70_D0C8_4D54_E503,
+    0x5490_ADEC_7ECE_57D4,
+    0x002B_3C27_D906_3A3A,
+    0x7EAE_A384_8030_A2BF,
+    0xC602_326D_ED20_03C0,
+    0x83A7_287D_69A9_4086,
+    0xC57A_5FCB_30F5_7A8A,
+    0xB568_44E4_79EB_E779,
+    0xA373_B40F_05DC_BCE9,
+    0xD71A_786E_8857_0EE2,
+    0x879C_BACD_BDE8_F6A0,
+    0x976A_D1BC_C164_A32F,
+    0xAB21_E25E_9666_D78B,
+    0x9010_63AA_E5E5_C33C,
+    0x9818_B344_4869_8D90,
+    0xE364_87AE_3E1E_8ABB,
+    0xAFBD_F931_893B_DCB4,
+    0x6345_A0DC_5FBB_D519,
+    0x8628_FE26_9B94_65CA,
+    0x1E5D_0160_3F9C_51EC,
+    0x4DE4_4006_A150_49B7,
+    0xBF6C_70E5_F776_CBB1,
+    0x4112_18F2_EF55_2BED,
+    0xCB0C_0708_705A_36A3,
+    0xE74D_1475_4F98_6044,
+    0xCD56_D943_0EA8_280E,
+    0xC125_91D7_535F_5065,
+    0xC832_23F1_720A_EF96,
+    0xC3A0_396F_7363_A51F,
+];
diff --git a/whirlpool/CHANGELOG.md b/whirlpool/CHANGELOG.md
index 2d3c97dfd..ce759f132 100644
--- a/whirlpool/CHANGELOG.md
+++ b/whirlpool/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.10.0 (2021-12-07)
+### Changed
+- Update to `digest` v0.10 ([#217])
+
+[#217]: https://github.com/RustCrypto/hashes/pull/217
+
 ## 0.9.0 (2020-06-12)
 ### Changed
 - Bump `opaque-debug` to v0.3.0 ([#168])
diff --git a/whirlpool/Cargo.toml b/whirlpool/Cargo.toml
index 395ed8a3c..777785c4a 100644
--- a/whirlpool/Cargo.toml
+++ b/whirlpool/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "whirlpool"
-version = "0.9.0"
+version = "0.10.0" # Also update html_root_url in lib.rs when bumping this
 description = "Whirlpool hash function"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
@@ -12,13 +12,11 @@ keywords = ["crypto", "whirlpool", "hash", "digest"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
-digest = "0.9"
-block-buffer = { version = "0.9", features = ["block-padding"] }
-opaque-debug = "0.3"
-whirlpool-asm = { version = "0.5", optional = true }
+digest = "0.10"
+whirlpool-asm = { version = "0.6", optional = true}
 
 [dev-dependencies]
-digest = { version = "0.9", features = ["dev"] }
+digest = { version = "0.10", features = ["dev"] }
 hex-literal = "0.2"
 
 [features]
diff --git a/whirlpool/benches/lib.rs b/whirlpool/benches/lib.rs
deleted file mode 100644
index 561d5f457..000000000
--- a/whirlpool/benches/lib.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![no_std]
-#![feature(test)]
-
-digest::bench!(whirlpool::Whirlpool);
diff --git a/whirlpool/benches/mod.rs b/whirlpool/benches/mod.rs
new file mode 100644
index 000000000..8fd3cc01c
--- /dev/null
+++ b/whirlpool/benches/mod.rs
@@ -0,0 +1,14 @@
+#![feature(test)]
+extern crate test;
+
+use digest::bench_update;
+use test::Bencher;
+use whirlpool::Whirlpool;
+
+bench_update!(
+    Whirlpool::default();
+    whirlpool_10 10;
+    whirlpool_100 100;
+    whirlpool_1000 1000;
+    whirlpool_10000 10000;
+);
diff --git a/whirlpool/examples/whirlpool_sum.rs b/whirlpool/examples/whirlpool_sum.rs
deleted file mode 100644
index fe2416b8f..000000000
--- a/whirlpool/examples/whirlpool_sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-use whirlpool::{Digest, Whirlpool};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh: D = Default::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Whirlpool, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Whirlpool, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/whirlpool/src/compress.rs b/whirlpool/src/compress.rs
new file mode 100644
index 000000000..4f1664e30
--- /dev/null
+++ b/whirlpool/src/compress.rs
@@ -0,0 +1,60 @@
+use crate::BLOCK_SIZE;
+use core::convert::TryInto;
+
+#[path = "consts.rs"]
+mod consts;
+use consts::*;
+
+fn compress_block(state: &mut [u64; 8], b: &[u8; BLOCK_SIZE]) {
+    let mut k = [0u64; 8];
+    let mut block = [0u64; 8];
+    let mut s = [0u64; 8];
+    let mut l = [0u64; 8];
+
+    for (o, chunk) in block.iter_mut().zip(b.chunks_exact(8)) {
+        *o = u64::from_le_bytes(chunk.try_into().unwrap());
+    }
+    k.copy_from_slice(state);
+
+    for i in 0..8 {
+        s[i] = block[i] ^ k[i];
+    }
+
+    #[allow(clippy::needless_range_loop)]
+    for r in 0..R {
+        for i in 0..8 {
+            l[i] = C0[(k[(i) % 8] & 0xff) as usize]
+                ^ C1[((k[(7 + i) % 8] >> 8) & 0xff) as usize]
+                ^ C2[((k[(6 + i) % 8] >> 16) & 0xff) as usize]
+                ^ C3[((k[(5 + i) % 8] >> 24) & 0xff) as usize]
+                ^ C4[((k[(4 + i) % 8] >> 32) & 0xff) as usize]
+                ^ C5[((k[(3 + i) % 8] >> 40) & 0xff) as usize]
+                ^ C6[((k[(2 + i) % 8] >> 48) & 0xff) as usize]
+                ^ C7[((k[(1 + i) % 8] >> 56) & 0xff) as usize]
+                ^ if i == 0 { RC[r] } else { 0 };
+        }
+        k = l;
+        for i in 0..8 {
+            l[i] = C0[(s[(i) % 8] & 0xff) as usize]
+                ^ C1[((s[(7 + i) % 8] >> 8) & 0xff) as usize]
+                ^ C2[((s[(6 + i) % 8] >> 16) & 0xff) as usize]
+                ^ C3[((s[(5 + i) % 8] >> 24) & 0xff) as usize]
+                ^ C4[((s[(4 + i) % 8] >> 32) & 0xff) as usize]
+                ^ C5[((s[(3 + i) % 8] >> 40) & 0xff) as usize]
+                ^ C6[((s[(2 + i) % 8] >> 48) & 0xff) as usize]
+                ^ C7[((s[(1 + i) % 8] >> 56) & 0xff) as usize]
+                ^ k[i];
+        }
+        s = l;
+    }
+
+    for i in 0..8 {
+        state[i] ^= s[i] ^ block[i];
+    }
+}
+
+pub(crate) fn compress(state: &mut [u64; 8], blocks: &[[u8; BLOCK_SIZE]]) {
+    for block in blocks {
+        compress_block(state, block);
+    }
+}
diff --git a/whirlpool/src/consts.rs b/whirlpool/src/consts.rs
index aad56c63e..756437d21 100644
--- a/whirlpool/src/consts.rs
+++ b/whirlpool/src/consts.rs
@@ -1,2089 +1,2088 @@
 #![allow(clippy::unreadable_literal)]
 
-pub static R: usize = 10;
+pub const R: usize = 10;
 
-pub static RC: [u64; 11 /* R + 1 */] = [
-    0x0000000000000000,
-    0x1823c6e887b8014f,
-    0x36a6d2f5796f9152,
-    0x60bc9b8ea30c7b35,
-    0x1de0d7c22e4bfe57,
-    0x157737e59ff04ada,
-    0x58c9290ab1a06b85,
-    0xbd5d10f4cb3e0567,
-    0xe427418ba77d95d8,
-    0xfbee7c66dd17479e,
-    0xca2dbf07ad5a8333,
+pub const RC: [u64; R] = [
+    0x4f01b887e8c62318,
+    0x52916f79f5d2a636,
+    0x357b0ca38e9bbc60,
+    0x57fe4b2ec2d7e01d,
+    0xda4af09fe5377715,
+    0x856ba0b10a29c958,
+    0x67053ecbf4105dbd,
+    0xd8957da78b4127e4,
+    0x9e4717dd667ceefb,
+    0x33835aad07bf2dca,
 ];
 
-pub static C0: [u64; 256] = [
-    0x18186018c07830d8,
-    0x23238c2305af4626,
-    0xc6c63fc67ef991b8,
-    0xe8e887e8136fcdfb,
-    0x878726874ca113cb,
-    0xb8b8dab8a9626d11,
-    0x0101040108050209,
-    0x4f4f214f426e9e0d,
-    0x3636d836adee6c9b,
-    0xa6a6a2a6590451ff,
-    0xd2d26fd2debdb90c,
-    0xf5f5f3f5fb06f70e,
-    0x7979f979ef80f296,
-    0x6f6fa16f5fcede30,
-    0x91917e91fcef3f6d,
-    0x52525552aa07a4f8,
-    0x60609d6027fdc047,
-    0xbcbccabc89766535,
-    0x9b9b569baccd2b37,
-    0x8e8e028e048c018a,
-    0xa3a3b6a371155bd2,
-    0x0c0c300c603c186c,
-    0x7b7bf17bff8af684,
-    0x3535d435b5e16a80,
-    0x1d1d741de8693af5,
-    0xe0e0a7e05347ddb3,
-    0xd7d77bd7f6acb321,
-    0xc2c22fc25eed999c,
-    0x2e2eb82e6d965c43,
-    0x4b4b314b627a9629,
-    0xfefedffea321e15d,
-    0x575741578216aed5,
-    0x15155415a8412abd,
-    0x7777c1779fb6eee8,
-    0x3737dc37a5eb6e92,
-    0xe5e5b3e57b56d79e,
-    0x9f9f469f8cd92313,
-    0xf0f0e7f0d317fd23,
-    0x4a4a354a6a7f9420,
-    0xdada4fda9e95a944,
-    0x58587d58fa25b0a2,
-    0xc9c903c906ca8fcf,
-    0x2929a429558d527c,
-    0x0a0a280a5022145a,
-    0xb1b1feb1e14f7f50,
-    0xa0a0baa0691a5dc9,
-    0x6b6bb16b7fdad614,
-    0x85852e855cab17d9,
-    0xbdbdcebd8173673c,
-    0x5d5d695dd234ba8f,
-    0x1010401080502090,
-    0xf4f4f7f4f303f507,
-    0xcbcb0bcb16c08bdd,
-    0x3e3ef83eedc67cd3,
-    0x0505140528110a2d,
-    0x676781671fe6ce78,
-    0xe4e4b7e47353d597,
-    0x27279c2725bb4e02,
-    0x4141194132588273,
-    0x8b8b168b2c9d0ba7,
-    0xa7a7a6a7510153f6,
-    0x7d7de97dcf94fab2,
-    0x95956e95dcfb3749,
-    0xd8d847d88e9fad56,
-    0xfbfbcbfb8b30eb70,
-    0xeeee9fee2371c1cd,
-    0x7c7ced7cc791f8bb,
-    0x6666856617e3cc71,
-    0xdddd53dda68ea77b,
-    0x17175c17b84b2eaf,
-    0x4747014702468e45,
-    0x9e9e429e84dc211a,
-    0xcaca0fca1ec589d4,
-    0x2d2db42d75995a58,
-    0xbfbfc6bf9179632e,
-    0x07071c07381b0e3f,
-    0xadad8ead012347ac,
-    0x5a5a755aea2fb4b0,
-    0x838336836cb51bef,
-    0x3333cc3385ff66b6,
-    0x636391633ff2c65c,
-    0x02020802100a0412,
-    0xaaaa92aa39384993,
-    0x7171d971afa8e2de,
-    0xc8c807c80ecf8dc6,
-    0x19196419c87d32d1,
-    0x494939497270923b,
-    0xd9d943d9869aaf5f,
-    0xf2f2eff2c31df931,
-    0xe3e3abe34b48dba8,
-    0x5b5b715be22ab6b9,
-    0x88881a8834920dbc,
-    0x9a9a529aa4c8293e,
-    0x262698262dbe4c0b,
-    0x3232c8328dfa64bf,
-    0xb0b0fab0e94a7d59,
-    0xe9e983e91b6acff2,
-    0x0f0f3c0f78331e77,
-    0xd5d573d5e6a6b733,
-    0x80803a8074ba1df4,
-    0xbebec2be997c6127,
-    0xcdcd13cd26de87eb,
-    0x3434d034bde46889,
-    0x48483d487a759032,
-    0xffffdbffab24e354,
-    0x7a7af57af78ff48d,
-    0x90907a90f4ea3d64,
-    0x5f5f615fc23ebe9d,
-    0x202080201da0403d,
-    0x6868bd6867d5d00f,
-    0x1a1a681ad07234ca,
-    0xaeae82ae192c41b7,
-    0xb4b4eab4c95e757d,
-    0x54544d549a19a8ce,
-    0x93937693ece53b7f,
-    0x222288220daa442f,
-    0x64648d6407e9c863,
-    0xf1f1e3f1db12ff2a,
-    0x7373d173bfa2e6cc,
-    0x12124812905a2482,
-    0x40401d403a5d807a,
-    0x0808200840281048,
-    0xc3c32bc356e89b95,
-    0xecec97ec337bc5df,
-    0xdbdb4bdb9690ab4d,
-    0xa1a1bea1611f5fc0,
-    0x8d8d0e8d1c830791,
-    0x3d3df43df5c97ac8,
-    0x97976697ccf1335b,
+pub const C0: [u64; 256] = [
+    0xd83078c018601818,
+    0x2646af05238c2323,
+    0xb891f97ec63fc6c6,
+    0xfbcd6f13e887e8e8,
+    0xcb13a14c87268787,
+    0x116d62a9b8dab8b8,
+    0x0902050801040101,
+    0x0d9e6e424f214f4f,
+    0x9b6ceead36d83636,
+    0xff510459a6a2a6a6,
+    0x0cb9bdded26fd2d2,
+    0x0ef706fbf5f3f5f5,
+    0x96f280ef79f97979,
+    0x30dece5f6fa16f6f,
+    0x6d3feffc917e9191,
+    0xf8a407aa52555252,
+    0x47c0fd27609d6060,
+    0x35657689bccabcbc,
+    0x372bcdac9b569b9b,
+    0x8a018c048e028e8e,
+    0xd25b1571a3b6a3a3,
+    0x6c183c600c300c0c,
+    0x84f68aff7bf17b7b,
+    0x806ae1b535d43535,
+    0xf53a69e81d741d1d,
+    0xb3dd4753e0a7e0e0,
+    0x21b3acf6d77bd7d7,
+    0x9c99ed5ec22fc2c2,
+    0x435c966d2eb82e2e,
+    0x29967a624b314b4b,
+    0x5de121a3fedffefe,
+    0xd5ae168257415757,
+    0xbd2a41a815541515,
+    0xe8eeb69f77c17777,
+    0x926eeba537dc3737,
+    0x9ed7567be5b3e5e5,
+    0x1323d98c9f469f9f,
+    0x23fd17d3f0e7f0f0,
+    0x20947f6a4a354a4a,
+    0x44a9959eda4fdada,
+    0xa2b025fa587d5858,
+    0xcf8fca06c903c9c9,
+    0x7c528d5529a42929,
+    0x5a1422500a280a0a,
+    0x507f4fe1b1feb1b1,
+    0xc95d1a69a0baa0a0,
+    0x14d6da7f6bb16b6b,
+    0xd917ab5c852e8585,
+    0x3c677381bdcebdbd,
+    0x8fba34d25d695d5d,
+    0x9020508010401010,
+    0x07f503f3f4f7f4f4,
+    0xdd8bc016cb0bcbcb,
+    0xd37cc6ed3ef83e3e,
+    0x2d0a112805140505,
+    0x78cee61f67816767,
+    0x97d55373e4b7e4e4,
+    0x024ebb25279c2727,
+    0x7382583241194141,
+    0xa70b9d2c8b168b8b,
+    0xf6530151a7a6a7a7,
+    0xb2fa94cf7de97d7d,
+    0x4937fbdc956e9595,
+    0x56ad9f8ed847d8d8,
+    0x70eb308bfbcbfbfb,
+    0xcdc17123ee9feeee,
+    0xbbf891c77ced7c7c,
+    0x71cce31766856666,
+    0x7ba78ea6dd53dddd,
+    0xaf2e4bb8175c1717,
+    0x458e460247014747,
+    0x1a21dc849e429e9e,
+    0xd489c51eca0fcaca,
+    0x585a99752db42d2d,
+    0x2e637991bfc6bfbf,
+    0x3f0e1b38071c0707,
+    0xac472301ad8eadad,
+    0xb0b42fea5a755a5a,
+    0xef1bb56c83368383,
+    0xb666ff8533cc3333,
+    0x5cc6f23f63916363,
+    0x12040a1002080202,
+    0x93493839aa92aaaa,
+    0xdee2a8af71d97171,
+    0xc68dcf0ec807c8c8,
+    0xd1327dc819641919,
+    0x3b92707249394949,
+    0x5faf9a86d943d9d9,
+    0x31f91dc3f2eff2f2,
+    0xa8db484be3abe3e3,
+    0xb9b62ae25b715b5b,
+    0xbc0d9234881a8888,
+    0x3e29c8a49a529a9a,
+    0x0b4cbe2d26982626,
+    0xbf64fa8d32c83232,
+    0x597d4ae9b0fab0b0,
+    0xf2cf6a1be983e9e9,
+    0x771e33780f3c0f0f,
+    0x33b7a6e6d573d5d5,
+    0xf41dba74803a8080,
+    0x27617c99bec2bebe,
+    0xeb87de26cd13cdcd,
+    0x8968e4bd34d03434,
+    0x3290757a483d4848,
+    0x54e324abffdbffff,
+    0x8df48ff77af57a7a,
+    0x643deaf4907a9090,
+    0x9dbe3ec25f615f5f,
+    0x3d40a01d20802020,
+    0x0fd0d56768bd6868,
+    0xca3472d01a681a1a,
+    0xb7412c19ae82aeae,
+    0x7d755ec9b4eab4b4,
+    0xcea8199a544d5454,
+    0x7f3be5ec93769393,
+    0x2f44aa0d22882222,
+    0x63c8e907648d6464,
+    0x2aff12dbf1e3f1f1,
+    0xcce6a2bf73d17373,
+    0x82245a9012481212,
+    0x7a805d3a401d4040,
+    0x4810284008200808,
+    0x959be856c32bc3c3,
+    0xdfc57b33ec97ecec,
+    0x4dab9096db4bdbdb,
+    0xc05f1f61a1bea1a1,
+    0x9107831c8d0e8d8d,
+    0xc87ac9f53df43d3d,
+    0x5b33f1cc97669797,
     0x0000000000000000,
-    0xcfcf1bcf36d483f9,
-    0x2b2bac2b4587566e,
-    0x7676c57697b3ece1,
-    0x8282328264b019e6,
-    0xd6d67fd6fea9b128,
-    0x1b1b6c1bd87736c3,
-    0xb5b5eeb5c15b7774,
-    0xafaf86af112943be,
-    0x6a6ab56a77dfd41d,
-    0x50505d50ba0da0ea,
-    0x45450945124c8a57,
-    0xf3f3ebf3cb18fb38,
-    0x3030c0309df060ad,
-    0xefef9bef2b74c3c4,
-    0x3f3ffc3fe5c37eda,
-    0x55554955921caac7,
-    0xa2a2b2a2791059db,
-    0xeaea8fea0365c9e9,
-    0x656589650fecca6a,
-    0xbabad2bab9686903,
-    0x2f2fbc2f65935e4a,
-    0xc0c027c04ee79d8e,
-    0xdede5fdebe81a160,
-    0x1c1c701ce06c38fc,
-    0xfdfdd3fdbb2ee746,
-    0x4d4d294d52649a1f,
-    0x92927292e4e03976,
-    0x7575c9758fbceafa,
-    0x06061806301e0c36,
-    0x8a8a128a249809ae,
-    0xb2b2f2b2f940794b,
-    0xe6e6bfe66359d185,
-    0x0e0e380e70361c7e,
-    0x1f1f7c1ff8633ee7,
-    0x6262956237f7c455,
-    0xd4d477d4eea3b53a,
-    0xa8a89aa829324d81,
-    0x96966296c4f43152,
-    0xf9f9c3f99b3aef62,
-    0xc5c533c566f697a3,
-    0x2525942535b14a10,
-    0x59597959f220b2ab,
-    0x84842a8454ae15d0,
-    0x7272d572b7a7e4c5,
-    0x3939e439d5dd72ec,
-    0x4c4c2d4c5a619816,
-    0x5e5e655eca3bbc94,
-    0x7878fd78e785f09f,
-    0x3838e038ddd870e5,
-    0x8c8c0a8c14860598,
-    0xd1d163d1c6b2bf17,
-    0xa5a5aea5410b57e4,
-    0xe2e2afe2434dd9a1,
-    0x616199612ff8c24e,
-    0xb3b3f6b3f1457b42,
-    0x2121842115a54234,
-    0x9c9c4a9c94d62508,
-    0x1e1e781ef0663cee,
-    0x4343114322528661,
-    0xc7c73bc776fc93b1,
-    0xfcfcd7fcb32be54f,
-    0x0404100420140824,
-    0x51515951b208a2e3,
-    0x99995e99bcc72f25,
-    0x6d6da96d4fc4da22,
-    0x0d0d340d68391a65,
-    0xfafacffa8335e979,
-    0xdfdf5bdfb684a369,
-    0x7e7ee57ed79bfca9,
-    0x242490243db44819,
-    0x3b3bec3bc5d776fe,
-    0xabab96ab313d4b9a,
-    0xcece1fce3ed181f0,
-    0x1111441188552299,
-    0x8f8f068f0c890383,
-    0x4e4e254e4a6b9c04,
-    0xb7b7e6b7d1517366,
-    0xebeb8beb0b60cbe0,
-    0x3c3cf03cfdcc78c1,
-    0x81813e817cbf1ffd,
-    0x94946a94d4fe3540,
-    0xf7f7fbf7eb0cf31c,
-    0xb9b9deb9a1676f18,
-    0x13134c13985f268b,
-    0x2c2cb02c7d9c5851,
-    0xd3d36bd3d6b8bb05,
-    0xe7e7bbe76b5cd38c,
-    0x6e6ea56e57cbdc39,
-    0xc4c437c46ef395aa,
-    0x03030c03180f061b,
-    0x565645568a13acdc,
-    0x44440d441a49885e,
-    0x7f7fe17fdf9efea0,
-    0xa9a99ea921374f88,
-    0x2a2aa82a4d825467,
-    0xbbbbd6bbb16d6b0a,
-    0xc1c123c146e29f87,
-    0x53535153a202a6f1,
-    0xdcdc57dcae8ba572,
-    0x0b0b2c0b58271653,
-    0x9d9d4e9d9cd32701,
-    0x6c6cad6c47c1d82b,
-    0x3131c43195f562a4,
-    0x7474cd7487b9e8f3,
-    0xf6f6fff6e309f115,
-    0x464605460a438c4c,
-    0xacac8aac092645a5,
-    0x89891e893c970fb5,
-    0x14145014a04428b4,
-    0xe1e1a3e15b42dfba,
-    0x16165816b04e2ca6,
-    0x3a3ae83acdd274f7,
-    0x6969b9696fd0d206,
-    0x09092409482d1241,
-    0x7070dd70a7ade0d7,
-    0xb6b6e2b6d954716f,
-    0xd0d067d0ceb7bd1e,
-    0xeded93ed3b7ec7d6,
-    0xcccc17cc2edb85e2,
-    0x424215422a578468,
-    0x98985a98b4c22d2c,
-    0xa4a4aaa4490e55ed,
-    0x2828a0285d885075,
-    0x5c5c6d5cda31b886,
-    0xf8f8c7f8933fed6b,
-    0x8686228644a411c2,
+    0xf983d436cf1bcfcf,
+    0x6e5687452bac2b2b,
+    0xe1ecb39776c57676,
+    0xe619b06482328282,
+    0x28b1a9fed67fd6d6,
+    0xc33677d81b6c1b1b,
+    0x74775bc1b5eeb5b5,
+    0xbe432911af86afaf,
+    0x1dd4df776ab56a6a,
+    0xeaa00dba505d5050,
+    0x578a4c1245094545,
+    0x38fb18cbf3ebf3f3,
+    0xad60f09d30c03030,
+    0xc4c3742bef9befef,
+    0xda7ec3e53ffc3f3f,
+    0xc7aa1c9255495555,
+    0xdb591079a2b2a2a2,
+    0xe9c96503ea8feaea,
+    0x6acaec0f65896565,
+    0x036968b9bad2baba,
+    0x4a5e93652fbc2f2f,
+    0x8e9de74ec027c0c0,
+    0x60a181bede5fdede,
+    0xfc386ce01c701c1c,
+    0x46e72ebbfdd3fdfd,
+    0x1f9a64524d294d4d,
+    0x7639e0e492729292,
+    0xfaeabc8f75c97575,
+    0x360c1e3006180606,
+    0xae0998248a128a8a,
+    0x4b7940f9b2f2b2b2,
+    0x85d15963e6bfe6e6,
+    0x7e1c36700e380e0e,
+    0xe73e63f81f7c1f1f,
+    0x55c4f73762956262,
+    0x3ab5a3eed477d4d4,
+    0x814d3229a89aa8a8,
+    0x5231f4c496629696,
+    0x62ef3a9bf9c3f9f9,
+    0xa397f666c533c5c5,
+    0x104ab13525942525,
+    0xabb220f259795959,
+    0xd015ae54842a8484,
+    0xc5e4a7b772d57272,
+    0xec72ddd539e43939,
+    0x1698615a4c2d4c4c,
+    0x94bc3bca5e655e5e,
+    0x9ff085e778fd7878,
+    0xe570d8dd38e03838,
+    0x980586148c0a8c8c,
+    0x17bfb2c6d163d1d1,
+    0xe4570b41a5aea5a5,
+    0xa1d94d43e2afe2e2,
+    0x4ec2f82f61996161,
+    0x427b45f1b3f6b3b3,
+    0x3442a51521842121,
+    0x0825d6949c4a9c9c,
+    0xee3c66f01e781e1e,
+    0x6186522243114343,
+    0xb193fc76c73bc7c7,
+    0x4fe52bb3fcd7fcfc,
+    0x2408142004100404,
+    0xe3a208b251595151,
+    0x252fc7bc995e9999,
+    0x22dac44f6da96d6d,
+    0x651a39680d340d0d,
+    0x79e93583facffafa,
+    0x69a384b6df5bdfdf,
+    0xa9fc9bd77ee57e7e,
+    0x1948b43d24902424,
+    0xfe76d7c53bec3b3b,
+    0x9a4b3d31ab96abab,
+    0xf081d13ece1fcece,
+    0x9922558811441111,
+    0x8303890c8f068f8f,
+    0x049c6b4a4e254e4e,
+    0x667351d1b7e6b7b7,
+    0xe0cb600beb8bebeb,
+    0xc178ccfd3cf03c3c,
+    0xfd1fbf7c813e8181,
+    0x4035fed4946a9494,
+    0x1cf30cebf7fbf7f7,
+    0x186f67a1b9deb9b9,
+    0x8b265f98134c1313,
+    0x51589c7d2cb02c2c,
+    0x05bbb8d6d36bd3d3,
+    0x8cd35c6be7bbe7e7,
+    0x39dccb576ea56e6e,
+    0xaa95f36ec437c4c4,
+    0x1b060f18030c0303,
+    0xdcac138a56455656,
+    0x5e88491a440d4444,
+    0xa0fe9edf7fe17f7f,
+    0x884f3721a99ea9a9,
+    0x6754824d2aa82a2a,
+    0x0a6b6db1bbd6bbbb,
+    0x879fe246c123c1c1,
+    0xf1a602a253515353,
+    0x72a58baedc57dcdc,
+    0x531627580b2c0b0b,
+    0x0127d39c9d4e9d9d,
+    0x2bd8c1476cad6c6c,
+    0xa462f59531c43131,
+    0xf3e8b98774cd7474,
+    0x15f109e3f6fff6f6,
+    0x4c8c430a46054646,
+    0xa5452609ac8aacac,
+    0xb50f973c891e8989,
+    0xb42844a014501414,
+    0xbadf425be1a3e1e1,
+    0xa62c4eb016581616,
+    0xf774d2cd3ae83a3a,
+    0x06d2d06f69b96969,
+    0x41122d4809240909,
+    0xd7e0ada770dd7070,
+    0x6f7154d9b6e2b6b6,
+    0x1ebdb7ced067d0d0,
+    0xd6c77e3bed93eded,
+    0xe285db2ecc17cccc,
+    0x6884572a42154242,
+    0x2c2dc2b4985a9898,
+    0xed550e49a4aaa4a4,
+    0x7550885d28a02828,
+    0x86b831da5c6d5c5c,
+    0x6bed3f93f8c7f8f8,
+    0xc211a44486228686,
 ];
 
-pub static C1: [u64; 256] = [
-    0xd818186018c07830,
-    0x2623238c2305af46,
-    0xb8c6c63fc67ef991,
-    0xfbe8e887e8136fcd,
-    0xcb878726874ca113,
-    0x11b8b8dab8a9626d,
-    0x0901010401080502,
-    0x0d4f4f214f426e9e,
-    0x9b3636d836adee6c,
-    0xffa6a6a2a6590451,
-    0x0cd2d26fd2debdb9,
-    0x0ef5f5f3f5fb06f7,
-    0x967979f979ef80f2,
-    0x306f6fa16f5fcede,
-    0x6d91917e91fcef3f,
-    0xf852525552aa07a4,
-    0x4760609d6027fdc0,
-    0x35bcbccabc897665,
-    0x379b9b569baccd2b,
-    0x8a8e8e028e048c01,
-    0xd2a3a3b6a371155b,
-    0x6c0c0c300c603c18,
-    0x847b7bf17bff8af6,
-    0x803535d435b5e16a,
-    0xf51d1d741de8693a,
-    0xb3e0e0a7e05347dd,
-    0x21d7d77bd7f6acb3,
-    0x9cc2c22fc25eed99,
-    0x432e2eb82e6d965c,
-    0x294b4b314b627a96,
-    0x5dfefedffea321e1,
-    0xd5575741578216ae,
-    0xbd15155415a8412a,
-    0xe87777c1779fb6ee,
-    0x923737dc37a5eb6e,
-    0x9ee5e5b3e57b56d7,
-    0x139f9f469f8cd923,
-    0x23f0f0e7f0d317fd,
-    0x204a4a354a6a7f94,
-    0x44dada4fda9e95a9,
-    0xa258587d58fa25b0,
-    0xcfc9c903c906ca8f,
-    0x7c2929a429558d52,
-    0x5a0a0a280a502214,
-    0x50b1b1feb1e14f7f,
-    0xc9a0a0baa0691a5d,
-    0x146b6bb16b7fdad6,
-    0xd985852e855cab17,
-    0x3cbdbdcebd817367,
-    0x8f5d5d695dd234ba,
-    0x9010104010805020,
-    0x07f4f4f7f4f303f5,
-    0xddcbcb0bcb16c08b,
-    0xd33e3ef83eedc67c,
-    0x2d0505140528110a,
-    0x78676781671fe6ce,
-    0x97e4e4b7e47353d5,
-    0x0227279c2725bb4e,
-    0x7341411941325882,
-    0xa78b8b168b2c9d0b,
-    0xf6a7a7a6a7510153,
-    0xb27d7de97dcf94fa,
-    0x4995956e95dcfb37,
-    0x56d8d847d88e9fad,
-    0x70fbfbcbfb8b30eb,
-    0xcdeeee9fee2371c1,
-    0xbb7c7ced7cc791f8,
-    0x716666856617e3cc,
-    0x7bdddd53dda68ea7,
-    0xaf17175c17b84b2e,
-    0x454747014702468e,
-    0x1a9e9e429e84dc21,
-    0xd4caca0fca1ec589,
-    0x582d2db42d75995a,
-    0x2ebfbfc6bf917963,
-    0x3f07071c07381b0e,
-    0xacadad8ead012347,
-    0xb05a5a755aea2fb4,
-    0xef838336836cb51b,
-    0xb63333cc3385ff66,
-    0x5c636391633ff2c6,
-    0x1202020802100a04,
-    0x93aaaa92aa393849,
-    0xde7171d971afa8e2,
-    0xc6c8c807c80ecf8d,
-    0xd119196419c87d32,
-    0x3b49493949727092,
-    0x5fd9d943d9869aaf,
-    0x31f2f2eff2c31df9,
-    0xa8e3e3abe34b48db,
-    0xb95b5b715be22ab6,
-    0xbc88881a8834920d,
-    0x3e9a9a529aa4c829,
-    0x0b262698262dbe4c,
-    0xbf3232c8328dfa64,
-    0x59b0b0fab0e94a7d,
-    0xf2e9e983e91b6acf,
-    0x770f0f3c0f78331e,
-    0x33d5d573d5e6a6b7,
-    0xf480803a8074ba1d,
-    0x27bebec2be997c61,
-    0xebcdcd13cd26de87,
-    0x893434d034bde468,
-    0x3248483d487a7590,
-    0x54ffffdbffab24e3,
-    0x8d7a7af57af78ff4,
-    0x6490907a90f4ea3d,
-    0x9d5f5f615fc23ebe,
-    0x3d202080201da040,
-    0x0f6868bd6867d5d0,
-    0xca1a1a681ad07234,
-    0xb7aeae82ae192c41,
-    0x7db4b4eab4c95e75,
-    0xce54544d549a19a8,
-    0x7f93937693ece53b,
-    0x2f222288220daa44,
-    0x6364648d6407e9c8,
-    0x2af1f1e3f1db12ff,
-    0xcc7373d173bfa2e6,
-    0x8212124812905a24,
-    0x7a40401d403a5d80,
-    0x4808082008402810,
-    0x95c3c32bc356e89b,
-    0xdfecec97ec337bc5,
-    0x4ddbdb4bdb9690ab,
-    0xc0a1a1bea1611f5f,
-    0x918d8d0e8d1c8307,
-    0xc83d3df43df5c97a,
-    0x5b97976697ccf133,
+pub const C1: [u64; 256] = [
+    0x3078c018601818d8,
+    0x46af05238c232326,
+    0x91f97ec63fc6c6b8,
+    0xcd6f13e887e8e8fb,
+    0x13a14c87268787cb,
+    0x6d62a9b8dab8b811,
+    0x0205080104010109,
+    0x9e6e424f214f4f0d,
+    0x6ceead36d836369b,
+    0x510459a6a2a6a6ff,
+    0xb9bdded26fd2d20c,
+    0xf706fbf5f3f5f50e,
+    0xf280ef79f9797996,
+    0xdece5f6fa16f6f30,
+    0x3feffc917e91916d,
+    0xa407aa52555252f8,
+    0xc0fd27609d606047,
+    0x657689bccabcbc35,
+    0x2bcdac9b569b9b37,
+    0x018c048e028e8e8a,
+    0x5b1571a3b6a3a3d2,
+    0x183c600c300c0c6c,
+    0xf68aff7bf17b7b84,
+    0x6ae1b535d4353580,
+    0x3a69e81d741d1df5,
+    0xdd4753e0a7e0e0b3,
+    0xb3acf6d77bd7d721,
+    0x99ed5ec22fc2c29c,
+    0x5c966d2eb82e2e43,
+    0x967a624b314b4b29,
+    0xe121a3fedffefe5d,
+    0xae168257415757d5,
+    0x2a41a815541515bd,
+    0xeeb69f77c17777e8,
+    0x6eeba537dc373792,
+    0xd7567be5b3e5e59e,
+    0x23d98c9f469f9f13,
+    0xfd17d3f0e7f0f023,
+    0x947f6a4a354a4a20,
+    0xa9959eda4fdada44,
+    0xb025fa587d5858a2,
+    0x8fca06c903c9c9cf,
+    0x528d5529a429297c,
+    0x1422500a280a0a5a,
+    0x7f4fe1b1feb1b150,
+    0x5d1a69a0baa0a0c9,
+    0xd6da7f6bb16b6b14,
+    0x17ab5c852e8585d9,
+    0x677381bdcebdbd3c,
+    0xba34d25d695d5d8f,
+    0x2050801040101090,
+    0xf503f3f4f7f4f407,
+    0x8bc016cb0bcbcbdd,
+    0x7cc6ed3ef83e3ed3,
+    0x0a1128051405052d,
+    0xcee61f6781676778,
+    0xd55373e4b7e4e497,
+    0x4ebb25279c272702,
+    0x8258324119414173,
+    0x0b9d2c8b168b8ba7,
+    0x530151a7a6a7a7f6,
+    0xfa94cf7de97d7db2,
+    0x37fbdc956e959549,
+    0xad9f8ed847d8d856,
+    0xeb308bfbcbfbfb70,
+    0xc17123ee9feeeecd,
+    0xf891c77ced7c7cbb,
+    0xcce3176685666671,
+    0xa78ea6dd53dddd7b,
+    0x2e4bb8175c1717af,
+    0x8e46024701474745,
+    0x21dc849e429e9e1a,
+    0x89c51eca0fcacad4,
+    0x5a99752db42d2d58,
+    0x637991bfc6bfbf2e,
+    0x0e1b38071c07073f,
+    0x472301ad8eadadac,
+    0xb42fea5a755a5ab0,
+    0x1bb56c83368383ef,
+    0x66ff8533cc3333b6,
+    0xc6f23f639163635c,
+    0x040a100208020212,
+    0x493839aa92aaaa93,
+    0xe2a8af71d97171de,
+    0x8dcf0ec807c8c8c6,
+    0x327dc819641919d1,
+    0x927072493949493b,
+    0xaf9a86d943d9d95f,
+    0xf91dc3f2eff2f231,
+    0xdb484be3abe3e3a8,
+    0xb62ae25b715b5bb9,
+    0x0d9234881a8888bc,
+    0x29c8a49a529a9a3e,
+    0x4cbe2d269826260b,
+    0x64fa8d32c83232bf,
+    0x7d4ae9b0fab0b059,
+    0xcf6a1be983e9e9f2,
+    0x1e33780f3c0f0f77,
+    0xb7a6e6d573d5d533,
+    0x1dba74803a8080f4,
+    0x617c99bec2bebe27,
+    0x87de26cd13cdcdeb,
+    0x68e4bd34d0343489,
+    0x90757a483d484832,
+    0xe324abffdbffff54,
+    0xf48ff77af57a7a8d,
+    0x3deaf4907a909064,
+    0xbe3ec25f615f5f9d,
+    0x40a01d208020203d,
+    0xd0d56768bd68680f,
+    0x3472d01a681a1aca,
+    0x412c19ae82aeaeb7,
+    0x755ec9b4eab4b47d,
+    0xa8199a544d5454ce,
+    0x3be5ec937693937f,
+    0x44aa0d228822222f,
+    0xc8e907648d646463,
+    0xff12dbf1e3f1f12a,
+    0xe6a2bf73d17373cc,
+    0x245a901248121282,
+    0x805d3a401d40407a,
+    0x1028400820080848,
+    0x9be856c32bc3c395,
+    0xc57b33ec97ececdf,
+    0xab9096db4bdbdb4d,
+    0x5f1f61a1bea1a1c0,
+    0x07831c8d0e8d8d91,
+    0x7ac9f53df43d3dc8,
+    0x33f1cc976697975b,
     0x0000000000000000,
-    0xf9cfcf1bcf36d483,
-    0x6e2b2bac2b458756,
-    0xe17676c57697b3ec,
-    0xe68282328264b019,
-    0x28d6d67fd6fea9b1,
-    0xc31b1b6c1bd87736,
-    0x74b5b5eeb5c15b77,
-    0xbeafaf86af112943,
-    0x1d6a6ab56a77dfd4,
-    0xea50505d50ba0da0,
-    0x5745450945124c8a,
-    0x38f3f3ebf3cb18fb,
-    0xad3030c0309df060,
-    0xc4efef9bef2b74c3,
-    0xda3f3ffc3fe5c37e,
-    0xc755554955921caa,
-    0xdba2a2b2a2791059,
-    0xe9eaea8fea0365c9,
-    0x6a656589650fecca,
-    0x03babad2bab96869,
-    0x4a2f2fbc2f65935e,
-    0x8ec0c027c04ee79d,
-    0x60dede5fdebe81a1,
-    0xfc1c1c701ce06c38,
-    0x46fdfdd3fdbb2ee7,
-    0x1f4d4d294d52649a,
-    0x7692927292e4e039,
-    0xfa7575c9758fbcea,
-    0x3606061806301e0c,
-    0xae8a8a128a249809,
-    0x4bb2b2f2b2f94079,
-    0x85e6e6bfe66359d1,
-    0x7e0e0e380e70361c,
-    0xe71f1f7c1ff8633e,
-    0x556262956237f7c4,
-    0x3ad4d477d4eea3b5,
-    0x81a8a89aa829324d,
-    0x5296966296c4f431,
-    0x62f9f9c3f99b3aef,
-    0xa3c5c533c566f697,
-    0x102525942535b14a,
-    0xab59597959f220b2,
-    0xd084842a8454ae15,
-    0xc57272d572b7a7e4,
-    0xec3939e439d5dd72,
-    0x164c4c2d4c5a6198,
-    0x945e5e655eca3bbc,
-    0x9f7878fd78e785f0,
-    0xe53838e038ddd870,
-    0x988c8c0a8c148605,
-    0x17d1d163d1c6b2bf,
-    0xe4a5a5aea5410b57,
-    0xa1e2e2afe2434dd9,
-    0x4e616199612ff8c2,
-    0x42b3b3f6b3f1457b,
-    0x342121842115a542,
-    0x089c9c4a9c94d625,
-    0xee1e1e781ef0663c,
-    0x6143431143225286,
-    0xb1c7c73bc776fc93,
-    0x4ffcfcd7fcb32be5,
-    0x2404041004201408,
-    0xe351515951b208a2,
-    0x2599995e99bcc72f,
-    0x226d6da96d4fc4da,
-    0x650d0d340d68391a,
-    0x79fafacffa8335e9,
-    0x69dfdf5bdfb684a3,
-    0xa97e7ee57ed79bfc,
-    0x19242490243db448,
-    0xfe3b3bec3bc5d776,
-    0x9aabab96ab313d4b,
-    0xf0cece1fce3ed181,
-    0x9911114411885522,
-    0x838f8f068f0c8903,
-    0x044e4e254e4a6b9c,
-    0x66b7b7e6b7d15173,
-    0xe0ebeb8beb0b60cb,
-    0xc13c3cf03cfdcc78,
-    0xfd81813e817cbf1f,
-    0x4094946a94d4fe35,
-    0x1cf7f7fbf7eb0cf3,
-    0x18b9b9deb9a1676f,
-    0x8b13134c13985f26,
-    0x512c2cb02c7d9c58,
-    0x05d3d36bd3d6b8bb,
-    0x8ce7e7bbe76b5cd3,
-    0x396e6ea56e57cbdc,
-    0xaac4c437c46ef395,
-    0x1b03030c03180f06,
-    0xdc565645568a13ac,
-    0x5e44440d441a4988,
-    0xa07f7fe17fdf9efe,
-    0x88a9a99ea921374f,
-    0x672a2aa82a4d8254,
-    0x0abbbbd6bbb16d6b,
-    0x87c1c123c146e29f,
-    0xf153535153a202a6,
-    0x72dcdc57dcae8ba5,
-    0x530b0b2c0b582716,
-    0x019d9d4e9d9cd327,
-    0x2b6c6cad6c47c1d8,
-    0xa43131c43195f562,
-    0xf37474cd7487b9e8,
-    0x15f6f6fff6e309f1,
-    0x4c464605460a438c,
-    0xa5acac8aac092645,
-    0xb589891e893c970f,
-    0xb414145014a04428,
-    0xbae1e1a3e15b42df,
-    0xa616165816b04e2c,
-    0xf73a3ae83acdd274,
-    0x066969b9696fd0d2,
-    0x4109092409482d12,
-    0xd77070dd70a7ade0,
-    0x6fb6b6e2b6d95471,
-    0x1ed0d067d0ceb7bd,
-    0xd6eded93ed3b7ec7,
-    0xe2cccc17cc2edb85,
-    0x68424215422a5784,
-    0x2c98985a98b4c22d,
-    0xeda4a4aaa4490e55,
-    0x752828a0285d8850,
-    0x865c5c6d5cda31b8,
-    0x6bf8f8c7f8933fed,
-    0xc28686228644a411,
+    0x83d436cf1bcfcff9,
+    0x5687452bac2b2b6e,
+    0xecb39776c57676e1,
+    0x19b06482328282e6,
+    0xb1a9fed67fd6d628,
+    0x3677d81b6c1b1bc3,
+    0x775bc1b5eeb5b574,
+    0x432911af86afafbe,
+    0xd4df776ab56a6a1d,
+    0xa00dba505d5050ea,
+    0x8a4c124509454557,
+    0xfb18cbf3ebf3f338,
+    0x60f09d30c03030ad,
+    0xc3742bef9befefc4,
+    0x7ec3e53ffc3f3fda,
+    0xaa1c9255495555c7,
+    0x591079a2b2a2a2db,
+    0xc96503ea8feaeae9,
+    0xcaec0f658965656a,
+    0x6968b9bad2baba03,
+    0x5e93652fbc2f2f4a,
+    0x9de74ec027c0c08e,
+    0xa181bede5fdede60,
+    0x386ce01c701c1cfc,
+    0xe72ebbfdd3fdfd46,
+    0x9a64524d294d4d1f,
+    0x39e0e49272929276,
+    0xeabc8f75c97575fa,
+    0x0c1e300618060636,
+    0x0998248a128a8aae,
+    0x7940f9b2f2b2b24b,
+    0xd15963e6bfe6e685,
+    0x1c36700e380e0e7e,
+    0x3e63f81f7c1f1fe7,
+    0xc4f7376295626255,
+    0xb5a3eed477d4d43a,
+    0x4d3229a89aa8a881,
+    0x31f4c49662969652,
+    0xef3a9bf9c3f9f962,
+    0x97f666c533c5c5a3,
+    0x4ab1352594252510,
+    0xb220f259795959ab,
+    0x15ae54842a8484d0,
+    0xe4a7b772d57272c5,
+    0x72ddd539e43939ec,
+    0x98615a4c2d4c4c16,
+    0xbc3bca5e655e5e94,
+    0xf085e778fd78789f,
+    0x70d8dd38e03838e5,
+    0x0586148c0a8c8c98,
+    0xbfb2c6d163d1d117,
+    0x570b41a5aea5a5e4,
+    0xd94d43e2afe2e2a1,
+    0xc2f82f619961614e,
+    0x7b45f1b3f6b3b342,
+    0x42a5152184212134,
+    0x25d6949c4a9c9c08,
+    0x3c66f01e781e1eee,
+    0x8652224311434361,
+    0x93fc76c73bc7c7b1,
+    0xe52bb3fcd7fcfc4f,
+    0x0814200410040424,
+    0xa208b251595151e3,
+    0x2fc7bc995e999925,
+    0xdac44f6da96d6d22,
+    0x1a39680d340d0d65,
+    0xe93583facffafa79,
+    0xa384b6df5bdfdf69,
+    0xfc9bd77ee57e7ea9,
+    0x48b43d2490242419,
+    0x76d7c53bec3b3bfe,
+    0x4b3d31ab96abab9a,
+    0x81d13ece1fcecef0,
+    0x2255881144111199,
+    0x03890c8f068f8f83,
+    0x9c6b4a4e254e4e04,
+    0x7351d1b7e6b7b766,
+    0xcb600beb8bebebe0,
+    0x78ccfd3cf03c3cc1,
+    0x1fbf7c813e8181fd,
+    0x35fed4946a949440,
+    0xf30cebf7fbf7f71c,
+    0x6f67a1b9deb9b918,
+    0x265f98134c13138b,
+    0x589c7d2cb02c2c51,
+    0xbbb8d6d36bd3d305,
+    0xd35c6be7bbe7e78c,
+    0xdccb576ea56e6e39,
+    0x95f36ec437c4c4aa,
+    0x060f18030c03031b,
+    0xac138a56455656dc,
+    0x88491a440d44445e,
+    0xfe9edf7fe17f7fa0,
+    0x4f3721a99ea9a988,
+    0x54824d2aa82a2a67,
+    0x6b6db1bbd6bbbb0a,
+    0x9fe246c123c1c187,
+    0xa602a253515353f1,
+    0xa58baedc57dcdc72,
+    0x1627580b2c0b0b53,
+    0x27d39c9d4e9d9d01,
+    0xd8c1476cad6c6c2b,
+    0x62f59531c43131a4,
+    0xe8b98774cd7474f3,
+    0xf109e3f6fff6f615,
+    0x8c430a460546464c,
+    0x452609ac8aacaca5,
+    0x0f973c891e8989b5,
+    0x2844a014501414b4,
+    0xdf425be1a3e1e1ba,
+    0x2c4eb016581616a6,
+    0x74d2cd3ae83a3af7,
+    0xd2d06f69b9696906,
+    0x122d480924090941,
+    0xe0ada770dd7070d7,
+    0x7154d9b6e2b6b66f,
+    0xbdb7ced067d0d01e,
+    0xc77e3bed93ededd6,
+    0x85db2ecc17cccce2,
+    0x84572a4215424268,
+    0x2dc2b4985a98982c,
+    0x550e49a4aaa4a4ed,
+    0x50885d28a0282875,
+    0xb831da5c6d5c5c86,
+    0xed3f93f8c7f8f86b,
+    0x11a44486228686c2,
 ];
 
-pub static C2: [u64; 256] = [
-    0x30d818186018c078,
-    0x462623238c2305af,
-    0x91b8c6c63fc67ef9,
-    0xcdfbe8e887e8136f,
-    0x13cb878726874ca1,
-    0x6d11b8b8dab8a962,
-    0x0209010104010805,
-    0x9e0d4f4f214f426e,
-    0x6c9b3636d836adee,
-    0x51ffa6a6a2a65904,
-    0xb90cd2d26fd2debd,
-    0xf70ef5f5f3f5fb06,
-    0xf2967979f979ef80,
-    0xde306f6fa16f5fce,
-    0x3f6d91917e91fcef,
-    0xa4f852525552aa07,
-    0xc04760609d6027fd,
-    0x6535bcbccabc8976,
-    0x2b379b9b569baccd,
-    0x018a8e8e028e048c,
-    0x5bd2a3a3b6a37115,
-    0x186c0c0c300c603c,
-    0xf6847b7bf17bff8a,
-    0x6a803535d435b5e1,
-    0x3af51d1d741de869,
-    0xddb3e0e0a7e05347,
-    0xb321d7d77bd7f6ac,
-    0x999cc2c22fc25eed,
-    0x5c432e2eb82e6d96,
-    0x96294b4b314b627a,
-    0xe15dfefedffea321,
-    0xaed5575741578216,
-    0x2abd15155415a841,
-    0xeee87777c1779fb6,
-    0x6e923737dc37a5eb,
-    0xd79ee5e5b3e57b56,
-    0x23139f9f469f8cd9,
-    0xfd23f0f0e7f0d317,
-    0x94204a4a354a6a7f,
-    0xa944dada4fda9e95,
-    0xb0a258587d58fa25,
-    0x8fcfc9c903c906ca,
-    0x527c2929a429558d,
-    0x145a0a0a280a5022,
-    0x7f50b1b1feb1e14f,
-    0x5dc9a0a0baa0691a,
-    0xd6146b6bb16b7fda,
-    0x17d985852e855cab,
-    0x673cbdbdcebd8173,
-    0xba8f5d5d695dd234,
-    0x2090101040108050,
-    0xf507f4f4f7f4f303,
-    0x8bddcbcb0bcb16c0,
-    0x7cd33e3ef83eedc6,
-    0x0a2d050514052811,
-    0xce78676781671fe6,
-    0xd597e4e4b7e47353,
-    0x4e0227279c2725bb,
-    0x8273414119413258,
-    0x0ba78b8b168b2c9d,
-    0x53f6a7a7a6a75101,
-    0xfab27d7de97dcf94,
-    0x374995956e95dcfb,
-    0xad56d8d847d88e9f,
-    0xeb70fbfbcbfb8b30,
-    0xc1cdeeee9fee2371,
-    0xf8bb7c7ced7cc791,
-    0xcc716666856617e3,
-    0xa77bdddd53dda68e,
-    0x2eaf17175c17b84b,
-    0x8e45474701470246,
-    0x211a9e9e429e84dc,
-    0x89d4caca0fca1ec5,
-    0x5a582d2db42d7599,
-    0x632ebfbfc6bf9179,
-    0x0e3f07071c07381b,
-    0x47acadad8ead0123,
-    0xb4b05a5a755aea2f,
-    0x1bef838336836cb5,
-    0x66b63333cc3385ff,
-    0xc65c636391633ff2,
-    0x041202020802100a,
-    0x4993aaaa92aa3938,
-    0xe2de7171d971afa8,
-    0x8dc6c8c807c80ecf,
-    0x32d119196419c87d,
-    0x923b494939497270,
-    0xaf5fd9d943d9869a,
-    0xf931f2f2eff2c31d,
-    0xdba8e3e3abe34b48,
-    0xb6b95b5b715be22a,
-    0x0dbc88881a883492,
-    0x293e9a9a529aa4c8,
-    0x4c0b262698262dbe,
-    0x64bf3232c8328dfa,
-    0x7d59b0b0fab0e94a,
-    0xcff2e9e983e91b6a,
-    0x1e770f0f3c0f7833,
-    0xb733d5d573d5e6a6,
-    0x1df480803a8074ba,
-    0x6127bebec2be997c,
-    0x87ebcdcd13cd26de,
-    0x68893434d034bde4,
-    0x903248483d487a75,
-    0xe354ffffdbffab24,
-    0xf48d7a7af57af78f,
-    0x3d6490907a90f4ea,
-    0xbe9d5f5f615fc23e,
-    0x403d202080201da0,
-    0xd00f6868bd6867d5,
-    0x34ca1a1a681ad072,
-    0x41b7aeae82ae192c,
-    0x757db4b4eab4c95e,
-    0xa8ce54544d549a19,
-    0x3b7f93937693ece5,
-    0x442f222288220daa,
-    0xc86364648d6407e9,
-    0xff2af1f1e3f1db12,
-    0xe6cc7373d173bfa2,
-    0x248212124812905a,
-    0x807a40401d403a5d,
-    0x1048080820084028,
-    0x9b95c3c32bc356e8,
-    0xc5dfecec97ec337b,
-    0xab4ddbdb4bdb9690,
-    0x5fc0a1a1bea1611f,
-    0x07918d8d0e8d1c83,
-    0x7ac83d3df43df5c9,
-    0x335b97976697ccf1,
+pub const C2: [u64; 256] = [
+    0x78c018601818d830,
+    0xaf05238c23232646,
+    0xf97ec63fc6c6b891,
+    0x6f13e887e8e8fbcd,
+    0xa14c87268787cb13,
+    0x62a9b8dab8b8116d,
+    0x0508010401010902,
+    0x6e424f214f4f0d9e,
+    0xeead36d836369b6c,
+    0x0459a6a2a6a6ff51,
+    0xbdded26fd2d20cb9,
+    0x06fbf5f3f5f50ef7,
+    0x80ef79f9797996f2,
+    0xce5f6fa16f6f30de,
+    0xeffc917e91916d3f,
+    0x07aa52555252f8a4,
+    0xfd27609d606047c0,
+    0x7689bccabcbc3565,
+    0xcdac9b569b9b372b,
+    0x8c048e028e8e8a01,
+    0x1571a3b6a3a3d25b,
+    0x3c600c300c0c6c18,
+    0x8aff7bf17b7b84f6,
+    0xe1b535d43535806a,
+    0x69e81d741d1df53a,
+    0x4753e0a7e0e0b3dd,
+    0xacf6d77bd7d721b3,
+    0xed5ec22fc2c29c99,
+    0x966d2eb82e2e435c,
+    0x7a624b314b4b2996,
+    0x21a3fedffefe5de1,
+    0x168257415757d5ae,
+    0x41a815541515bd2a,
+    0xb69f77c17777e8ee,
+    0xeba537dc3737926e,
+    0x567be5b3e5e59ed7,
+    0xd98c9f469f9f1323,
+    0x17d3f0e7f0f023fd,
+    0x7f6a4a354a4a2094,
+    0x959eda4fdada44a9,
+    0x25fa587d5858a2b0,
+    0xca06c903c9c9cf8f,
+    0x8d5529a429297c52,
+    0x22500a280a0a5a14,
+    0x4fe1b1feb1b1507f,
+    0x1a69a0baa0a0c95d,
+    0xda7f6bb16b6b14d6,
+    0xab5c852e8585d917,
+    0x7381bdcebdbd3c67,
+    0x34d25d695d5d8fba,
+    0x5080104010109020,
+    0x03f3f4f7f4f407f5,
+    0xc016cb0bcbcbdd8b,
+    0xc6ed3ef83e3ed37c,
+    0x1128051405052d0a,
+    0xe61f6781676778ce,
+    0x5373e4b7e4e497d5,
+    0xbb25279c2727024e,
+    0x5832411941417382,
+    0x9d2c8b168b8ba70b,
+    0x0151a7a6a7a7f653,
+    0x94cf7de97d7db2fa,
+    0xfbdc956e95954937,
+    0x9f8ed847d8d856ad,
+    0x308bfbcbfbfb70eb,
+    0x7123ee9feeeecdc1,
+    0x91c77ced7c7cbbf8,
+    0xe3176685666671cc,
+    0x8ea6dd53dddd7ba7,
+    0x4bb8175c1717af2e,
+    0x460247014747458e,
+    0xdc849e429e9e1a21,
+    0xc51eca0fcacad489,
+    0x99752db42d2d585a,
+    0x7991bfc6bfbf2e63,
+    0x1b38071c07073f0e,
+    0x2301ad8eadadac47,
+    0x2fea5a755a5ab0b4,
+    0xb56c83368383ef1b,
+    0xff8533cc3333b666,
+    0xf23f639163635cc6,
+    0x0a10020802021204,
+    0x3839aa92aaaa9349,
+    0xa8af71d97171dee2,
+    0xcf0ec807c8c8c68d,
+    0x7dc819641919d132,
+    0x7072493949493b92,
+    0x9a86d943d9d95faf,
+    0x1dc3f2eff2f231f9,
+    0x484be3abe3e3a8db,
+    0x2ae25b715b5bb9b6,
+    0x9234881a8888bc0d,
+    0xc8a49a529a9a3e29,
+    0xbe2d269826260b4c,
+    0xfa8d32c83232bf64,
+    0x4ae9b0fab0b0597d,
+    0x6a1be983e9e9f2cf,
+    0x33780f3c0f0f771e,
+    0xa6e6d573d5d533b7,
+    0xba74803a8080f41d,
+    0x7c99bec2bebe2761,
+    0xde26cd13cdcdeb87,
+    0xe4bd34d034348968,
+    0x757a483d48483290,
+    0x24abffdbffff54e3,
+    0x8ff77af57a7a8df4,
+    0xeaf4907a9090643d,
+    0x3ec25f615f5f9dbe,
+    0xa01d208020203d40,
+    0xd56768bd68680fd0,
+    0x72d01a681a1aca34,
+    0x2c19ae82aeaeb741,
+    0x5ec9b4eab4b47d75,
+    0x199a544d5454cea8,
+    0xe5ec937693937f3b,
+    0xaa0d228822222f44,
+    0xe907648d646463c8,
+    0x12dbf1e3f1f12aff,
+    0xa2bf73d17373cce6,
+    0x5a90124812128224,
+    0x5d3a401d40407a80,
+    0x2840082008084810,
+    0xe856c32bc3c3959b,
+    0x7b33ec97ececdfc5,
+    0x9096db4bdbdb4dab,
+    0x1f61a1bea1a1c05f,
+    0x831c8d0e8d8d9107,
+    0xc9f53df43d3dc87a,
+    0xf1cc976697975b33,
     0x0000000000000000,
-    0x83f9cfcf1bcf36d4,
-    0x566e2b2bac2b4587,
-    0xece17676c57697b3,
-    0x19e68282328264b0,
-    0xb128d6d67fd6fea9,
-    0x36c31b1b6c1bd877,
-    0x7774b5b5eeb5c15b,
-    0x43beafaf86af1129,
-    0xd41d6a6ab56a77df,
-    0xa0ea50505d50ba0d,
-    0x8a5745450945124c,
-    0xfb38f3f3ebf3cb18,
-    0x60ad3030c0309df0,
-    0xc3c4efef9bef2b74,
-    0x7eda3f3ffc3fe5c3,
-    0xaac755554955921c,
-    0x59dba2a2b2a27910,
-    0xc9e9eaea8fea0365,
-    0xca6a656589650fec,
-    0x6903babad2bab968,
-    0x5e4a2f2fbc2f6593,
-    0x9d8ec0c027c04ee7,
-    0xa160dede5fdebe81,
-    0x38fc1c1c701ce06c,
-    0xe746fdfdd3fdbb2e,
-    0x9a1f4d4d294d5264,
-    0x397692927292e4e0,
-    0xeafa7575c9758fbc,
-    0x0c3606061806301e,
-    0x09ae8a8a128a2498,
-    0x794bb2b2f2b2f940,
-    0xd185e6e6bfe66359,
-    0x1c7e0e0e380e7036,
-    0x3ee71f1f7c1ff863,
-    0xc4556262956237f7,
-    0xb53ad4d477d4eea3,
-    0x4d81a8a89aa82932,
-    0x315296966296c4f4,
-    0xef62f9f9c3f99b3a,
-    0x97a3c5c533c566f6,
-    0x4a102525942535b1,
-    0xb2ab59597959f220,
-    0x15d084842a8454ae,
-    0xe4c57272d572b7a7,
-    0x72ec3939e439d5dd,
-    0x98164c4c2d4c5a61,
-    0xbc945e5e655eca3b,
-    0xf09f7878fd78e785,
-    0x70e53838e038ddd8,
-    0x05988c8c0a8c1486,
-    0xbf17d1d163d1c6b2,
-    0x57e4a5a5aea5410b,
-    0xd9a1e2e2afe2434d,
-    0xc24e616199612ff8,
-    0x7b42b3b3f6b3f145,
-    0x42342121842115a5,
-    0x25089c9c4a9c94d6,
-    0x3cee1e1e781ef066,
-    0x8661434311432252,
-    0x93b1c7c73bc776fc,
-    0xe54ffcfcd7fcb32b,
-    0x0824040410042014,
-    0xa2e351515951b208,
-    0x2f2599995e99bcc7,
-    0xda226d6da96d4fc4,
-    0x1a650d0d340d6839,
-    0xe979fafacffa8335,
-    0xa369dfdf5bdfb684,
-    0xfca97e7ee57ed79b,
-    0x4819242490243db4,
-    0x76fe3b3bec3bc5d7,
-    0x4b9aabab96ab313d,
-    0x81f0cece1fce3ed1,
-    0x2299111144118855,
-    0x03838f8f068f0c89,
-    0x9c044e4e254e4a6b,
-    0x7366b7b7e6b7d151,
-    0xcbe0ebeb8beb0b60,
-    0x78c13c3cf03cfdcc,
-    0x1ffd81813e817cbf,
-    0x354094946a94d4fe,
-    0xf31cf7f7fbf7eb0c,
-    0x6f18b9b9deb9a167,
-    0x268b13134c13985f,
-    0x58512c2cb02c7d9c,
-    0xbb05d3d36bd3d6b8,
-    0xd38ce7e7bbe76b5c,
-    0xdc396e6ea56e57cb,
-    0x95aac4c437c46ef3,
-    0x061b03030c03180f,
-    0xacdc565645568a13,
-    0x885e44440d441a49,
-    0xfea07f7fe17fdf9e,
-    0x4f88a9a99ea92137,
-    0x54672a2aa82a4d82,
-    0x6b0abbbbd6bbb16d,
-    0x9f87c1c123c146e2,
-    0xa6f153535153a202,
-    0xa572dcdc57dcae8b,
-    0x16530b0b2c0b5827,
-    0x27019d9d4e9d9cd3,
-    0xd82b6c6cad6c47c1,
-    0x62a43131c43195f5,
-    0xe8f37474cd7487b9,
-    0xf115f6f6fff6e309,
-    0x8c4c464605460a43,
-    0x45a5acac8aac0926,
-    0x0fb589891e893c97,
-    0x28b414145014a044,
-    0xdfbae1e1a3e15b42,
-    0x2ca616165816b04e,
-    0x74f73a3ae83acdd2,
-    0xd2066969b9696fd0,
-    0x124109092409482d,
-    0xe0d77070dd70a7ad,
-    0x716fb6b6e2b6d954,
-    0xbd1ed0d067d0ceb7,
-    0xc7d6eded93ed3b7e,
-    0x85e2cccc17cc2edb,
-    0x8468424215422a57,
-    0x2d2c98985a98b4c2,
-    0x55eda4a4aaa4490e,
-    0x50752828a0285d88,
-    0xb8865c5c6d5cda31,
-    0xed6bf8f8c7f8933f,
-    0x11c28686228644a4,
+    0xd436cf1bcfcff983,
+    0x87452bac2b2b6e56,
+    0xb39776c57676e1ec,
+    0xb06482328282e619,
+    0xa9fed67fd6d628b1,
+    0x77d81b6c1b1bc336,
+    0x5bc1b5eeb5b57477,
+    0x2911af86afafbe43,
+    0xdf776ab56a6a1dd4,
+    0x0dba505d5050eaa0,
+    0x4c1245094545578a,
+    0x18cbf3ebf3f338fb,
+    0xf09d30c03030ad60,
+    0x742bef9befefc4c3,
+    0xc3e53ffc3f3fda7e,
+    0x1c9255495555c7aa,
+    0x1079a2b2a2a2db59,
+    0x6503ea8feaeae9c9,
+    0xec0f658965656aca,
+    0x68b9bad2baba0369,
+    0x93652fbc2f2f4a5e,
+    0xe74ec027c0c08e9d,
+    0x81bede5fdede60a1,
+    0x6ce01c701c1cfc38,
+    0x2ebbfdd3fdfd46e7,
+    0x64524d294d4d1f9a,
+    0xe0e4927292927639,
+    0xbc8f75c97575faea,
+    0x1e3006180606360c,
+    0x98248a128a8aae09,
+    0x40f9b2f2b2b24b79,
+    0x5963e6bfe6e685d1,
+    0x36700e380e0e7e1c,
+    0x63f81f7c1f1fe73e,
+    0xf7376295626255c4,
+    0xa3eed477d4d43ab5,
+    0x3229a89aa8a8814d,
+    0xf4c4966296965231,
+    0x3a9bf9c3f9f962ef,
+    0xf666c533c5c5a397,
+    0xb13525942525104a,
+    0x20f259795959abb2,
+    0xae54842a8484d015,
+    0xa7b772d57272c5e4,
+    0xddd539e43939ec72,
+    0x615a4c2d4c4c1698,
+    0x3bca5e655e5e94bc,
+    0x85e778fd78789ff0,
+    0xd8dd38e03838e570,
+    0x86148c0a8c8c9805,
+    0xb2c6d163d1d117bf,
+    0x0b41a5aea5a5e457,
+    0x4d43e2afe2e2a1d9,
+    0xf82f619961614ec2,
+    0x45f1b3f6b3b3427b,
+    0xa515218421213442,
+    0xd6949c4a9c9c0825,
+    0x66f01e781e1eee3c,
+    0x5222431143436186,
+    0xfc76c73bc7c7b193,
+    0x2bb3fcd7fcfc4fe5,
+    0x1420041004042408,
+    0x08b251595151e3a2,
+    0xc7bc995e9999252f,
+    0xc44f6da96d6d22da,
+    0x39680d340d0d651a,
+    0x3583facffafa79e9,
+    0x84b6df5bdfdf69a3,
+    0x9bd77ee57e7ea9fc,
+    0xb43d249024241948,
+    0xd7c53bec3b3bfe76,
+    0x3d31ab96abab9a4b,
+    0xd13ece1fcecef081,
+    0x5588114411119922,
+    0x890c8f068f8f8303,
+    0x6b4a4e254e4e049c,
+    0x51d1b7e6b7b76673,
+    0x600beb8bebebe0cb,
+    0xccfd3cf03c3cc178,
+    0xbf7c813e8181fd1f,
+    0xfed4946a94944035,
+    0x0cebf7fbf7f71cf3,
+    0x67a1b9deb9b9186f,
+    0x5f98134c13138b26,
+    0x9c7d2cb02c2c5158,
+    0xb8d6d36bd3d305bb,
+    0x5c6be7bbe7e78cd3,
+    0xcb576ea56e6e39dc,
+    0xf36ec437c4c4aa95,
+    0x0f18030c03031b06,
+    0x138a56455656dcac,
+    0x491a440d44445e88,
+    0x9edf7fe17f7fa0fe,
+    0x3721a99ea9a9884f,
+    0x824d2aa82a2a6754,
+    0x6db1bbd6bbbb0a6b,
+    0xe246c123c1c1879f,
+    0x02a253515353f1a6,
+    0x8baedc57dcdc72a5,
+    0x27580b2c0b0b5316,
+    0xd39c9d4e9d9d0127,
+    0xc1476cad6c6c2bd8,
+    0xf59531c43131a462,
+    0xb98774cd7474f3e8,
+    0x09e3f6fff6f615f1,
+    0x430a460546464c8c,
+    0x2609ac8aacaca545,
+    0x973c891e8989b50f,
+    0x44a014501414b428,
+    0x425be1a3e1e1badf,
+    0x4eb016581616a62c,
+    0xd2cd3ae83a3af774,
+    0xd06f69b9696906d2,
+    0x2d48092409094112,
+    0xada770dd7070d7e0,
+    0x54d9b6e2b6b66f71,
+    0xb7ced067d0d01ebd,
+    0x7e3bed93ededd6c7,
+    0xdb2ecc17cccce285,
+    0x572a421542426884,
+    0xc2b4985a98982c2d,
+    0x0e49a4aaa4a4ed55,
+    0x885d28a028287550,
+    0x31da5c6d5c5c86b8,
+    0x3f93f8c7f8f86bed,
+    0xa44486228686c211,
 ];
 
-pub static C3: [u64; 256] = [
-    0x7830d818186018c0,
-    0xaf462623238c2305,
-    0xf991b8c6c63fc67e,
-    0x6fcdfbe8e887e813,
-    0xa113cb878726874c,
-    0x626d11b8b8dab8a9,
-    0x0502090101040108,
-    0x6e9e0d4f4f214f42,
-    0xee6c9b3636d836ad,
-    0x0451ffa6a6a2a659,
-    0xbdb90cd2d26fd2de,
-    0x06f70ef5f5f3f5fb,
-    0x80f2967979f979ef,
-    0xcede306f6fa16f5f,
-    0xef3f6d91917e91fc,
-    0x07a4f852525552aa,
-    0xfdc04760609d6027,
-    0x766535bcbccabc89,
-    0xcd2b379b9b569bac,
-    0x8c018a8e8e028e04,
-    0x155bd2a3a3b6a371,
-    0x3c186c0c0c300c60,
-    0x8af6847b7bf17bff,
-    0xe16a803535d435b5,
-    0x693af51d1d741de8,
-    0x47ddb3e0e0a7e053,
-    0xacb321d7d77bd7f6,
-    0xed999cc2c22fc25e,
-    0x965c432e2eb82e6d,
-    0x7a96294b4b314b62,
-    0x21e15dfefedffea3,
-    0x16aed55757415782,
-    0x412abd15155415a8,
-    0xb6eee87777c1779f,
-    0xeb6e923737dc37a5,
-    0x56d79ee5e5b3e57b,
-    0xd923139f9f469f8c,
-    0x17fd23f0f0e7f0d3,
-    0x7f94204a4a354a6a,
-    0x95a944dada4fda9e,
-    0x25b0a258587d58fa,
-    0xca8fcfc9c903c906,
-    0x8d527c2929a42955,
-    0x22145a0a0a280a50,
-    0x4f7f50b1b1feb1e1,
-    0x1a5dc9a0a0baa069,
-    0xdad6146b6bb16b7f,
-    0xab17d985852e855c,
-    0x73673cbdbdcebd81,
-    0x34ba8f5d5d695dd2,
-    0x5020901010401080,
-    0x03f507f4f4f7f4f3,
-    0xc08bddcbcb0bcb16,
-    0xc67cd33e3ef83eed,
-    0x110a2d0505140528,
-    0xe6ce78676781671f,
-    0x53d597e4e4b7e473,
-    0xbb4e0227279c2725,
-    0x5882734141194132,
-    0x9d0ba78b8b168b2c,
-    0x0153f6a7a7a6a751,
-    0x94fab27d7de97dcf,
-    0xfb374995956e95dc,
-    0x9fad56d8d847d88e,
-    0x30eb70fbfbcbfb8b,
-    0x71c1cdeeee9fee23,
-    0x91f8bb7c7ced7cc7,
-    0xe3cc716666856617,
-    0x8ea77bdddd53dda6,
-    0x4b2eaf17175c17b8,
-    0x468e454747014702,
-    0xdc211a9e9e429e84,
-    0xc589d4caca0fca1e,
-    0x995a582d2db42d75,
-    0x79632ebfbfc6bf91,
-    0x1b0e3f07071c0738,
-    0x2347acadad8ead01,
-    0x2fb4b05a5a755aea,
-    0xb51bef838336836c,
-    0xff66b63333cc3385,
-    0xf2c65c636391633f,
-    0x0a04120202080210,
-    0x384993aaaa92aa39,
-    0xa8e2de7171d971af,
-    0xcf8dc6c8c807c80e,
-    0x7d32d119196419c8,
-    0x70923b4949394972,
-    0x9aaf5fd9d943d986,
-    0x1df931f2f2eff2c3,
-    0x48dba8e3e3abe34b,
-    0x2ab6b95b5b715be2,
-    0x920dbc88881a8834,
-    0xc8293e9a9a529aa4,
-    0xbe4c0b262698262d,
-    0xfa64bf3232c8328d,
-    0x4a7d59b0b0fab0e9,
-    0x6acff2e9e983e91b,
-    0x331e770f0f3c0f78,
-    0xa6b733d5d573d5e6,
-    0xba1df480803a8074,
-    0x7c6127bebec2be99,
-    0xde87ebcdcd13cd26,
-    0xe468893434d034bd,
-    0x75903248483d487a,
-    0x24e354ffffdbffab,
-    0x8ff48d7a7af57af7,
-    0xea3d6490907a90f4,
-    0x3ebe9d5f5f615fc2,
-    0xa0403d202080201d,
-    0xd5d00f6868bd6867,
-    0x7234ca1a1a681ad0,
-    0x2c41b7aeae82ae19,
-    0x5e757db4b4eab4c9,
-    0x19a8ce54544d549a,
-    0xe53b7f93937693ec,
-    0xaa442f222288220d,
-    0xe9c86364648d6407,
-    0x12ff2af1f1e3f1db,
-    0xa2e6cc7373d173bf,
-    0x5a24821212481290,
-    0x5d807a40401d403a,
-    0x2810480808200840,
-    0xe89b95c3c32bc356,
-    0x7bc5dfecec97ec33,
-    0x90ab4ddbdb4bdb96,
-    0x1f5fc0a1a1bea161,
-    0x8307918d8d0e8d1c,
-    0xc97ac83d3df43df5,
-    0xf1335b97976697cc,
+pub const C3: [u64; 256] = [
+    0xc018601818d83078,
+    0x05238c23232646af,
+    0x7ec63fc6c6b891f9,
+    0x13e887e8e8fbcd6f,
+    0x4c87268787cb13a1,
+    0xa9b8dab8b8116d62,
+    0x0801040101090205,
+    0x424f214f4f0d9e6e,
+    0xad36d836369b6cee,
+    0x59a6a2a6a6ff5104,
+    0xded26fd2d20cb9bd,
+    0xfbf5f3f5f50ef706,
+    0xef79f9797996f280,
+    0x5f6fa16f6f30dece,
+    0xfc917e91916d3fef,
+    0xaa52555252f8a407,
+    0x27609d606047c0fd,
+    0x89bccabcbc356576,
+    0xac9b569b9b372bcd,
+    0x048e028e8e8a018c,
+    0x71a3b6a3a3d25b15,
+    0x600c300c0c6c183c,
+    0xff7bf17b7b84f68a,
+    0xb535d43535806ae1,
+    0xe81d741d1df53a69,
+    0x53e0a7e0e0b3dd47,
+    0xf6d77bd7d721b3ac,
+    0x5ec22fc2c29c99ed,
+    0x6d2eb82e2e435c96,
+    0x624b314b4b29967a,
+    0xa3fedffefe5de121,
+    0x8257415757d5ae16,
+    0xa815541515bd2a41,
+    0x9f77c17777e8eeb6,
+    0xa537dc3737926eeb,
+    0x7be5b3e5e59ed756,
+    0x8c9f469f9f1323d9,
+    0xd3f0e7f0f023fd17,
+    0x6a4a354a4a20947f,
+    0x9eda4fdada44a995,
+    0xfa587d5858a2b025,
+    0x06c903c9c9cf8fca,
+    0x5529a429297c528d,
+    0x500a280a0a5a1422,
+    0xe1b1feb1b1507f4f,
+    0x69a0baa0a0c95d1a,
+    0x7f6bb16b6b14d6da,
+    0x5c852e8585d917ab,
+    0x81bdcebdbd3c6773,
+    0xd25d695d5d8fba34,
+    0x8010401010902050,
+    0xf3f4f7f4f407f503,
+    0x16cb0bcbcbdd8bc0,
+    0xed3ef83e3ed37cc6,
+    0x28051405052d0a11,
+    0x1f6781676778cee6,
+    0x73e4b7e4e497d553,
+    0x25279c2727024ebb,
+    0x3241194141738258,
+    0x2c8b168b8ba70b9d,
+    0x51a7a6a7a7f65301,
+    0xcf7de97d7db2fa94,
+    0xdc956e95954937fb,
+    0x8ed847d8d856ad9f,
+    0x8bfbcbfbfb70eb30,
+    0x23ee9feeeecdc171,
+    0xc77ced7c7cbbf891,
+    0x176685666671cce3,
+    0xa6dd53dddd7ba78e,
+    0xb8175c1717af2e4b,
+    0x0247014747458e46,
+    0x849e429e9e1a21dc,
+    0x1eca0fcacad489c5,
+    0x752db42d2d585a99,
+    0x91bfc6bfbf2e6379,
+    0x38071c07073f0e1b,
+    0x01ad8eadadac4723,
+    0xea5a755a5ab0b42f,
+    0x6c83368383ef1bb5,
+    0x8533cc3333b666ff,
+    0x3f639163635cc6f2,
+    0x100208020212040a,
+    0x39aa92aaaa934938,
+    0xaf71d97171dee2a8,
+    0x0ec807c8c8c68dcf,
+    0xc819641919d1327d,
+    0x72493949493b9270,
+    0x86d943d9d95faf9a,
+    0xc3f2eff2f231f91d,
+    0x4be3abe3e3a8db48,
+    0xe25b715b5bb9b62a,
+    0x34881a8888bc0d92,
+    0xa49a529a9a3e29c8,
+    0x2d269826260b4cbe,
+    0x8d32c83232bf64fa,
+    0xe9b0fab0b0597d4a,
+    0x1be983e9e9f2cf6a,
+    0x780f3c0f0f771e33,
+    0xe6d573d5d533b7a6,
+    0x74803a8080f41dba,
+    0x99bec2bebe27617c,
+    0x26cd13cdcdeb87de,
+    0xbd34d034348968e4,
+    0x7a483d4848329075,
+    0xabffdbffff54e324,
+    0xf77af57a7a8df48f,
+    0xf4907a9090643dea,
+    0xc25f615f5f9dbe3e,
+    0x1d208020203d40a0,
+    0x6768bd68680fd0d5,
+    0xd01a681a1aca3472,
+    0x19ae82aeaeb7412c,
+    0xc9b4eab4b47d755e,
+    0x9a544d5454cea819,
+    0xec937693937f3be5,
+    0x0d228822222f44aa,
+    0x07648d646463c8e9,
+    0xdbf1e3f1f12aff12,
+    0xbf73d17373cce6a2,
+    0x901248121282245a,
+    0x3a401d40407a805d,
+    0x4008200808481028,
+    0x56c32bc3c3959be8,
+    0x33ec97ececdfc57b,
+    0x96db4bdbdb4dab90,
+    0x61a1bea1a1c05f1f,
+    0x1c8d0e8d8d910783,
+    0xf53df43d3dc87ac9,
+    0xcc976697975b33f1,
     0x0000000000000000,
-    0xd483f9cfcf1bcf36,
-    0x87566e2b2bac2b45,
-    0xb3ece17676c57697,
-    0xb019e68282328264,
-    0xa9b128d6d67fd6fe,
-    0x7736c31b1b6c1bd8,
-    0x5b7774b5b5eeb5c1,
-    0x2943beafaf86af11,
-    0xdfd41d6a6ab56a77,
-    0x0da0ea50505d50ba,
-    0x4c8a574545094512,
-    0x18fb38f3f3ebf3cb,
-    0xf060ad3030c0309d,
-    0x74c3c4efef9bef2b,
-    0xc37eda3f3ffc3fe5,
-    0x1caac75555495592,
-    0x1059dba2a2b2a279,
-    0x65c9e9eaea8fea03,
-    0xecca6a656589650f,
-    0x686903babad2bab9,
-    0x935e4a2f2fbc2f65,
-    0xe79d8ec0c027c04e,
-    0x81a160dede5fdebe,
-    0x6c38fc1c1c701ce0,
-    0x2ee746fdfdd3fdbb,
-    0x649a1f4d4d294d52,
-    0xe0397692927292e4,
-    0xbceafa7575c9758f,
-    0x1e0c360606180630,
-    0x9809ae8a8a128a24,
-    0x40794bb2b2f2b2f9,
-    0x59d185e6e6bfe663,
-    0x361c7e0e0e380e70,
-    0x633ee71f1f7c1ff8,
-    0xf7c4556262956237,
-    0xa3b53ad4d477d4ee,
-    0x324d81a8a89aa829,
-    0xf4315296966296c4,
-    0x3aef62f9f9c3f99b,
-    0xf697a3c5c533c566,
-    0xb14a102525942535,
-    0x20b2ab59597959f2,
-    0xae15d084842a8454,
-    0xa7e4c57272d572b7,
-    0xdd72ec3939e439d5,
-    0x6198164c4c2d4c5a,
-    0x3bbc945e5e655eca,
-    0x85f09f7878fd78e7,
-    0xd870e53838e038dd,
-    0x8605988c8c0a8c14,
-    0xb2bf17d1d163d1c6,
-    0x0b57e4a5a5aea541,
-    0x4dd9a1e2e2afe243,
-    0xf8c24e616199612f,
-    0x457b42b3b3f6b3f1,
-    0xa542342121842115,
-    0xd625089c9c4a9c94,
-    0x663cee1e1e781ef0,
-    0x5286614343114322,
-    0xfc93b1c7c73bc776,
-    0x2be54ffcfcd7fcb3,
-    0x1408240404100420,
-    0x08a2e351515951b2,
-    0xc72f2599995e99bc,
-    0xc4da226d6da96d4f,
-    0x391a650d0d340d68,
-    0x35e979fafacffa83,
-    0x84a369dfdf5bdfb6,
-    0x9bfca97e7ee57ed7,
-    0xb44819242490243d,
-    0xd776fe3b3bec3bc5,
-    0x3d4b9aabab96ab31,
-    0xd181f0cece1fce3e,
-    0x5522991111441188,
-    0x8903838f8f068f0c,
-    0x6b9c044e4e254e4a,
-    0x517366b7b7e6b7d1,
-    0x60cbe0ebeb8beb0b,
-    0xcc78c13c3cf03cfd,
-    0xbf1ffd81813e817c,
-    0xfe354094946a94d4,
-    0x0cf31cf7f7fbf7eb,
-    0x676f18b9b9deb9a1,
-    0x5f268b13134c1398,
-    0x9c58512c2cb02c7d,
-    0xb8bb05d3d36bd3d6,
-    0x5cd38ce7e7bbe76b,
-    0xcbdc396e6ea56e57,
-    0xf395aac4c437c46e,
-    0x0f061b03030c0318,
-    0x13acdc565645568a,
-    0x49885e44440d441a,
-    0x9efea07f7fe17fdf,
-    0x374f88a9a99ea921,
-    0x8254672a2aa82a4d,
-    0x6d6b0abbbbd6bbb1,
-    0xe29f87c1c123c146,
-    0x02a6f153535153a2,
-    0x8ba572dcdc57dcae,
-    0x2716530b0b2c0b58,
-    0xd327019d9d4e9d9c,
-    0xc1d82b6c6cad6c47,
-    0xf562a43131c43195,
-    0xb9e8f37474cd7487,
-    0x09f115f6f6fff6e3,
-    0x438c4c464605460a,
-    0x2645a5acac8aac09,
-    0x970fb589891e893c,
-    0x4428b414145014a0,
-    0x42dfbae1e1a3e15b,
-    0x4e2ca616165816b0,
-    0xd274f73a3ae83acd,
-    0xd0d2066969b9696f,
-    0x2d12410909240948,
-    0xade0d77070dd70a7,
-    0x54716fb6b6e2b6d9,
-    0xb7bd1ed0d067d0ce,
-    0x7ec7d6eded93ed3b,
-    0xdb85e2cccc17cc2e,
-    0x578468424215422a,
-    0xc22d2c98985a98b4,
-    0x0e55eda4a4aaa449,
-    0x8850752828a0285d,
-    0x31b8865c5c6d5cda,
-    0x3fed6bf8f8c7f893,
-    0xa411c28686228644,
+    0x36cf1bcfcff983d4,
+    0x452bac2b2b6e5687,
+    0x9776c57676e1ecb3,
+    0x6482328282e619b0,
+    0xfed67fd6d628b1a9,
+    0xd81b6c1b1bc33677,
+    0xc1b5eeb5b574775b,
+    0x11af86afafbe4329,
+    0x776ab56a6a1dd4df,
+    0xba505d5050eaa00d,
+    0x1245094545578a4c,
+    0xcbf3ebf3f338fb18,
+    0x9d30c03030ad60f0,
+    0x2bef9befefc4c374,
+    0xe53ffc3f3fda7ec3,
+    0x9255495555c7aa1c,
+    0x79a2b2a2a2db5910,
+    0x03ea8feaeae9c965,
+    0x0f658965656acaec,
+    0xb9bad2baba036968,
+    0x652fbc2f2f4a5e93,
+    0x4ec027c0c08e9de7,
+    0xbede5fdede60a181,
+    0xe01c701c1cfc386c,
+    0xbbfdd3fdfd46e72e,
+    0x524d294d4d1f9a64,
+    0xe4927292927639e0,
+    0x8f75c97575faeabc,
+    0x3006180606360c1e,
+    0x248a128a8aae0998,
+    0xf9b2f2b2b24b7940,
+    0x63e6bfe6e685d159,
+    0x700e380e0e7e1c36,
+    0xf81f7c1f1fe73e63,
+    0x376295626255c4f7,
+    0xeed477d4d43ab5a3,
+    0x29a89aa8a8814d32,
+    0xc4966296965231f4,
+    0x9bf9c3f9f962ef3a,
+    0x66c533c5c5a397f6,
+    0x3525942525104ab1,
+    0xf259795959abb220,
+    0x54842a8484d015ae,
+    0xb772d57272c5e4a7,
+    0xd539e43939ec72dd,
+    0x5a4c2d4c4c169861,
+    0xca5e655e5e94bc3b,
+    0xe778fd78789ff085,
+    0xdd38e03838e570d8,
+    0x148c0a8c8c980586,
+    0xc6d163d1d117bfb2,
+    0x41a5aea5a5e4570b,
+    0x43e2afe2e2a1d94d,
+    0x2f619961614ec2f8,
+    0xf1b3f6b3b3427b45,
+    0x15218421213442a5,
+    0x949c4a9c9c0825d6,
+    0xf01e781e1eee3c66,
+    0x2243114343618652,
+    0x76c73bc7c7b193fc,
+    0xb3fcd7fcfc4fe52b,
+    0x2004100404240814,
+    0xb251595151e3a208,
+    0xbc995e9999252fc7,
+    0x4f6da96d6d22dac4,
+    0x680d340d0d651a39,
+    0x83facffafa79e935,
+    0xb6df5bdfdf69a384,
+    0xd77ee57e7ea9fc9b,
+    0x3d249024241948b4,
+    0xc53bec3b3bfe76d7,
+    0x31ab96abab9a4b3d,
+    0x3ece1fcecef081d1,
+    0x8811441111992255,
+    0x0c8f068f8f830389,
+    0x4a4e254e4e049c6b,
+    0xd1b7e6b7b7667351,
+    0x0beb8bebebe0cb60,
+    0xfd3cf03c3cc178cc,
+    0x7c813e8181fd1fbf,
+    0xd4946a94944035fe,
+    0xebf7fbf7f71cf30c,
+    0xa1b9deb9b9186f67,
+    0x98134c13138b265f,
+    0x7d2cb02c2c51589c,
+    0xd6d36bd3d305bbb8,
+    0x6be7bbe7e78cd35c,
+    0x576ea56e6e39dccb,
+    0x6ec437c4c4aa95f3,
+    0x18030c03031b060f,
+    0x8a56455656dcac13,
+    0x1a440d44445e8849,
+    0xdf7fe17f7fa0fe9e,
+    0x21a99ea9a9884f37,
+    0x4d2aa82a2a675482,
+    0xb1bbd6bbbb0a6b6d,
+    0x46c123c1c1879fe2,
+    0xa253515353f1a602,
+    0xaedc57dcdc72a58b,
+    0x580b2c0b0b531627,
+    0x9c9d4e9d9d0127d3,
+    0x476cad6c6c2bd8c1,
+    0x9531c43131a462f5,
+    0x8774cd7474f3e8b9,
+    0xe3f6fff6f615f109,
+    0x0a460546464c8c43,
+    0x09ac8aacaca54526,
+    0x3c891e8989b50f97,
+    0xa014501414b42844,
+    0x5be1a3e1e1badf42,
+    0xb016581616a62c4e,
+    0xcd3ae83a3af774d2,
+    0x6f69b9696906d2d0,
+    0x480924090941122d,
+    0xa770dd7070d7e0ad,
+    0xd9b6e2b6b66f7154,
+    0xced067d0d01ebdb7,
+    0x3bed93ededd6c77e,
+    0x2ecc17cccce285db,
+    0x2a42154242688457,
+    0xb4985a98982c2dc2,
+    0x49a4aaa4a4ed550e,
+    0x5d28a02828755088,
+    0xda5c6d5c5c86b831,
+    0x93f8c7f8f86bed3f,
+    0x4486228686c211a4,
 ];
 
-pub static C4: [u64; 256] = [
-    0xc07830d818186018,
-    0x05af462623238c23,
-    0x7ef991b8c6c63fc6,
-    0x136fcdfbe8e887e8,
-    0x4ca113cb87872687,
-    0xa9626d11b8b8dab8,
-    0x0805020901010401,
-    0x426e9e0d4f4f214f,
-    0xadee6c9b3636d836,
-    0x590451ffa6a6a2a6,
-    0xdebdb90cd2d26fd2,
-    0xfb06f70ef5f5f3f5,
-    0xef80f2967979f979,
-    0x5fcede306f6fa16f,
-    0xfcef3f6d91917e91,
-    0xaa07a4f852525552,
-    0x27fdc04760609d60,
-    0x89766535bcbccabc,
-    0xaccd2b379b9b569b,
-    0x048c018a8e8e028e,
-    0x71155bd2a3a3b6a3,
-    0x603c186c0c0c300c,
-    0xff8af6847b7bf17b,
-    0xb5e16a803535d435,
-    0xe8693af51d1d741d,
-    0x5347ddb3e0e0a7e0,
-    0xf6acb321d7d77bd7,
-    0x5eed999cc2c22fc2,
-    0x6d965c432e2eb82e,
-    0x627a96294b4b314b,
-    0xa321e15dfefedffe,
-    0x8216aed557574157,
-    0xa8412abd15155415,
-    0x9fb6eee87777c177,
-    0xa5eb6e923737dc37,
-    0x7b56d79ee5e5b3e5,
-    0x8cd923139f9f469f,
-    0xd317fd23f0f0e7f0,
-    0x6a7f94204a4a354a,
-    0x9e95a944dada4fda,
-    0xfa25b0a258587d58,
-    0x06ca8fcfc9c903c9,
-    0x558d527c2929a429,
-    0x5022145a0a0a280a,
-    0xe14f7f50b1b1feb1,
-    0x691a5dc9a0a0baa0,
-    0x7fdad6146b6bb16b,
-    0x5cab17d985852e85,
-    0x8173673cbdbdcebd,
-    0xd234ba8f5d5d695d,
-    0x8050209010104010,
-    0xf303f507f4f4f7f4,
-    0x16c08bddcbcb0bcb,
-    0xedc67cd33e3ef83e,
-    0x28110a2d05051405,
-    0x1fe6ce7867678167,
-    0x7353d597e4e4b7e4,
-    0x25bb4e0227279c27,
-    0x3258827341411941,
-    0x2c9d0ba78b8b168b,
-    0x510153f6a7a7a6a7,
-    0xcf94fab27d7de97d,
-    0xdcfb374995956e95,
-    0x8e9fad56d8d847d8,
-    0x8b30eb70fbfbcbfb,
-    0x2371c1cdeeee9fee,
-    0xc791f8bb7c7ced7c,
-    0x17e3cc7166668566,
-    0xa68ea77bdddd53dd,
-    0xb84b2eaf17175c17,
-    0x02468e4547470147,
-    0x84dc211a9e9e429e,
-    0x1ec589d4caca0fca,
-    0x75995a582d2db42d,
-    0x9179632ebfbfc6bf,
-    0x381b0e3f07071c07,
-    0x012347acadad8ead,
-    0xea2fb4b05a5a755a,
-    0x6cb51bef83833683,
-    0x85ff66b63333cc33,
-    0x3ff2c65c63639163,
-    0x100a041202020802,
-    0x39384993aaaa92aa,
-    0xafa8e2de7171d971,
-    0x0ecf8dc6c8c807c8,
-    0xc87d32d119196419,
-    0x7270923b49493949,
-    0x869aaf5fd9d943d9,
-    0xc31df931f2f2eff2,
-    0x4b48dba8e3e3abe3,
-    0xe22ab6b95b5b715b,
-    0x34920dbc88881a88,
-    0xa4c8293e9a9a529a,
-    0x2dbe4c0b26269826,
-    0x8dfa64bf3232c832,
-    0xe94a7d59b0b0fab0,
-    0x1b6acff2e9e983e9,
-    0x78331e770f0f3c0f,
-    0xe6a6b733d5d573d5,
-    0x74ba1df480803a80,
-    0x997c6127bebec2be,
-    0x26de87ebcdcd13cd,
-    0xbde468893434d034,
-    0x7a75903248483d48,
-    0xab24e354ffffdbff,
-    0xf78ff48d7a7af57a,
-    0xf4ea3d6490907a90,
-    0xc23ebe9d5f5f615f,
-    0x1da0403d20208020,
-    0x67d5d00f6868bd68,
-    0xd07234ca1a1a681a,
-    0x192c41b7aeae82ae,
-    0xc95e757db4b4eab4,
-    0x9a19a8ce54544d54,
-    0xece53b7f93937693,
-    0x0daa442f22228822,
-    0x07e9c86364648d64,
-    0xdb12ff2af1f1e3f1,
-    0xbfa2e6cc7373d173,
-    0x905a248212124812,
-    0x3a5d807a40401d40,
-    0x4028104808082008,
-    0x56e89b95c3c32bc3,
-    0x337bc5dfecec97ec,
-    0x9690ab4ddbdb4bdb,
-    0x611f5fc0a1a1bea1,
-    0x1c8307918d8d0e8d,
-    0xf5c97ac83d3df43d,
-    0xccf1335b97976697,
+pub const C4: [u64; 256] = [
+    0x18601818d83078c0,
+    0x238c23232646af05,
+    0xc63fc6c6b891f97e,
+    0xe887e8e8fbcd6f13,
+    0x87268787cb13a14c,
+    0xb8dab8b8116d62a9,
+    0x0104010109020508,
+    0x4f214f4f0d9e6e42,
+    0x36d836369b6ceead,
+    0xa6a2a6a6ff510459,
+    0xd26fd2d20cb9bdde,
+    0xf5f3f5f50ef706fb,
+    0x79f9797996f280ef,
+    0x6fa16f6f30dece5f,
+    0x917e91916d3feffc,
+    0x52555252f8a407aa,
+    0x609d606047c0fd27,
+    0xbccabcbc35657689,
+    0x9b569b9b372bcdac,
+    0x8e028e8e8a018c04,
+    0xa3b6a3a3d25b1571,
+    0x0c300c0c6c183c60,
+    0x7bf17b7b84f68aff,
+    0x35d43535806ae1b5,
+    0x1d741d1df53a69e8,
+    0xe0a7e0e0b3dd4753,
+    0xd77bd7d721b3acf6,
+    0xc22fc2c29c99ed5e,
+    0x2eb82e2e435c966d,
+    0x4b314b4b29967a62,
+    0xfedffefe5de121a3,
+    0x57415757d5ae1682,
+    0x15541515bd2a41a8,
+    0x77c17777e8eeb69f,
+    0x37dc3737926eeba5,
+    0xe5b3e5e59ed7567b,
+    0x9f469f9f1323d98c,
+    0xf0e7f0f023fd17d3,
+    0x4a354a4a20947f6a,
+    0xda4fdada44a9959e,
+    0x587d5858a2b025fa,
+    0xc903c9c9cf8fca06,
+    0x29a429297c528d55,
+    0x0a280a0a5a142250,
+    0xb1feb1b1507f4fe1,
+    0xa0baa0a0c95d1a69,
+    0x6bb16b6b14d6da7f,
+    0x852e8585d917ab5c,
+    0xbdcebdbd3c677381,
+    0x5d695d5d8fba34d2,
+    0x1040101090205080,
+    0xf4f7f4f407f503f3,
+    0xcb0bcbcbdd8bc016,
+    0x3ef83e3ed37cc6ed,
+    0x051405052d0a1128,
+    0x6781676778cee61f,
+    0xe4b7e4e497d55373,
+    0x279c2727024ebb25,
+    0x4119414173825832,
+    0x8b168b8ba70b9d2c,
+    0xa7a6a7a7f6530151,
+    0x7de97d7db2fa94cf,
+    0x956e95954937fbdc,
+    0xd847d8d856ad9f8e,
+    0xfbcbfbfb70eb308b,
+    0xee9feeeecdc17123,
+    0x7ced7c7cbbf891c7,
+    0x6685666671cce317,
+    0xdd53dddd7ba78ea6,
+    0x175c1717af2e4bb8,
+    0x47014747458e4602,
+    0x9e429e9e1a21dc84,
+    0xca0fcacad489c51e,
+    0x2db42d2d585a9975,
+    0xbfc6bfbf2e637991,
+    0x071c07073f0e1b38,
+    0xad8eadadac472301,
+    0x5a755a5ab0b42fea,
+    0x83368383ef1bb56c,
+    0x33cc3333b666ff85,
+    0x639163635cc6f23f,
+    0x0208020212040a10,
+    0xaa92aaaa93493839,
+    0x71d97171dee2a8af,
+    0xc807c8c8c68dcf0e,
+    0x19641919d1327dc8,
+    0x493949493b927072,
+    0xd943d9d95faf9a86,
+    0xf2eff2f231f91dc3,
+    0xe3abe3e3a8db484b,
+    0x5b715b5bb9b62ae2,
+    0x881a8888bc0d9234,
+    0x9a529a9a3e29c8a4,
+    0x269826260b4cbe2d,
+    0x32c83232bf64fa8d,
+    0xb0fab0b0597d4ae9,
+    0xe983e9e9f2cf6a1b,
+    0x0f3c0f0f771e3378,
+    0xd573d5d533b7a6e6,
+    0x803a8080f41dba74,
+    0xbec2bebe27617c99,
+    0xcd13cdcdeb87de26,
+    0x34d034348968e4bd,
+    0x483d48483290757a,
+    0xffdbffff54e324ab,
+    0x7af57a7a8df48ff7,
+    0x907a9090643deaf4,
+    0x5f615f5f9dbe3ec2,
+    0x208020203d40a01d,
+    0x68bd68680fd0d567,
+    0x1a681a1aca3472d0,
+    0xae82aeaeb7412c19,
+    0xb4eab4b47d755ec9,
+    0x544d5454cea8199a,
+    0x937693937f3be5ec,
+    0x228822222f44aa0d,
+    0x648d646463c8e907,
+    0xf1e3f1f12aff12db,
+    0x73d17373cce6a2bf,
+    0x1248121282245a90,
+    0x401d40407a805d3a,
+    0x0820080848102840,
+    0xc32bc3c3959be856,
+    0xec97ececdfc57b33,
+    0xdb4bdbdb4dab9096,
+    0xa1bea1a1c05f1f61,
+    0x8d0e8d8d9107831c,
+    0x3df43d3dc87ac9f5,
+    0x976697975b33f1cc,
     0x0000000000000000,
-    0x36d483f9cfcf1bcf,
-    0x4587566e2b2bac2b,
-    0x97b3ece17676c576,
-    0x64b019e682823282,
-    0xfea9b128d6d67fd6,
-    0xd87736c31b1b6c1b,
-    0xc15b7774b5b5eeb5,
-    0x112943beafaf86af,
-    0x77dfd41d6a6ab56a,
-    0xba0da0ea50505d50,
-    0x124c8a5745450945,
-    0xcb18fb38f3f3ebf3,
-    0x9df060ad3030c030,
-    0x2b74c3c4efef9bef,
-    0xe5c37eda3f3ffc3f,
-    0x921caac755554955,
-    0x791059dba2a2b2a2,
-    0x0365c9e9eaea8fea,
-    0x0fecca6a65658965,
-    0xb9686903babad2ba,
-    0x65935e4a2f2fbc2f,
-    0x4ee79d8ec0c027c0,
-    0xbe81a160dede5fde,
-    0xe06c38fc1c1c701c,
-    0xbb2ee746fdfdd3fd,
-    0x52649a1f4d4d294d,
-    0xe4e0397692927292,
-    0x8fbceafa7575c975,
-    0x301e0c3606061806,
-    0x249809ae8a8a128a,
-    0xf940794bb2b2f2b2,
-    0x6359d185e6e6bfe6,
-    0x70361c7e0e0e380e,
-    0xf8633ee71f1f7c1f,
-    0x37f7c45562629562,
-    0xeea3b53ad4d477d4,
-    0x29324d81a8a89aa8,
-    0xc4f4315296966296,
-    0x9b3aef62f9f9c3f9,
-    0x66f697a3c5c533c5,
-    0x35b14a1025259425,
-    0xf220b2ab59597959,
-    0x54ae15d084842a84,
-    0xb7a7e4c57272d572,
-    0xd5dd72ec3939e439,
-    0x5a6198164c4c2d4c,
-    0xca3bbc945e5e655e,
-    0xe785f09f7878fd78,
-    0xddd870e53838e038,
-    0x148605988c8c0a8c,
-    0xc6b2bf17d1d163d1,
-    0x410b57e4a5a5aea5,
-    0x434dd9a1e2e2afe2,
-    0x2ff8c24e61619961,
-    0xf1457b42b3b3f6b3,
-    0x15a5423421218421,
-    0x94d625089c9c4a9c,
-    0xf0663cee1e1e781e,
-    0x2252866143431143,
-    0x76fc93b1c7c73bc7,
-    0xb32be54ffcfcd7fc,
-    0x2014082404041004,
-    0xb208a2e351515951,
-    0xbcc72f2599995e99,
-    0x4fc4da226d6da96d,
-    0x68391a650d0d340d,
-    0x8335e979fafacffa,
-    0xb684a369dfdf5bdf,
-    0xd79bfca97e7ee57e,
-    0x3db4481924249024,
-    0xc5d776fe3b3bec3b,
-    0x313d4b9aabab96ab,
-    0x3ed181f0cece1fce,
-    0x8855229911114411,
-    0x0c8903838f8f068f,
-    0x4a6b9c044e4e254e,
-    0xd1517366b7b7e6b7,
-    0x0b60cbe0ebeb8beb,
-    0xfdcc78c13c3cf03c,
-    0x7cbf1ffd81813e81,
-    0xd4fe354094946a94,
-    0xeb0cf31cf7f7fbf7,
-    0xa1676f18b9b9deb9,
-    0x985f268b13134c13,
-    0x7d9c58512c2cb02c,
-    0xd6b8bb05d3d36bd3,
-    0x6b5cd38ce7e7bbe7,
-    0x57cbdc396e6ea56e,
-    0x6ef395aac4c437c4,
-    0x180f061b03030c03,
-    0x8a13acdc56564556,
-    0x1a49885e44440d44,
-    0xdf9efea07f7fe17f,
-    0x21374f88a9a99ea9,
-    0x4d8254672a2aa82a,
-    0xb16d6b0abbbbd6bb,
-    0x46e29f87c1c123c1,
-    0xa202a6f153535153,
-    0xae8ba572dcdc57dc,
-    0x582716530b0b2c0b,
-    0x9cd327019d9d4e9d,
-    0x47c1d82b6c6cad6c,
-    0x95f562a43131c431,
-    0x87b9e8f37474cd74,
-    0xe309f115f6f6fff6,
-    0x0a438c4c46460546,
-    0x092645a5acac8aac,
-    0x3c970fb589891e89,
-    0xa04428b414145014,
-    0x5b42dfbae1e1a3e1,
-    0xb04e2ca616165816,
-    0xcdd274f73a3ae83a,
-    0x6fd0d2066969b969,
-    0x482d124109092409,
-    0xa7ade0d77070dd70,
-    0xd954716fb6b6e2b6,
-    0xceb7bd1ed0d067d0,
-    0x3b7ec7d6eded93ed,
-    0x2edb85e2cccc17cc,
-    0x2a57846842421542,
-    0xb4c22d2c98985a98,
-    0x490e55eda4a4aaa4,
-    0x5d8850752828a028,
-    0xda31b8865c5c6d5c,
-    0x933fed6bf8f8c7f8,
-    0x44a411c286862286,
+    0xcf1bcfcff983d436,
+    0x2bac2b2b6e568745,
+    0x76c57676e1ecb397,
+    0x82328282e619b064,
+    0xd67fd6d628b1a9fe,
+    0x1b6c1b1bc33677d8,
+    0xb5eeb5b574775bc1,
+    0xaf86afafbe432911,
+    0x6ab56a6a1dd4df77,
+    0x505d5050eaa00dba,
+    0x45094545578a4c12,
+    0xf3ebf3f338fb18cb,
+    0x30c03030ad60f09d,
+    0xef9befefc4c3742b,
+    0x3ffc3f3fda7ec3e5,
+    0x55495555c7aa1c92,
+    0xa2b2a2a2db591079,
+    0xea8feaeae9c96503,
+    0x658965656acaec0f,
+    0xbad2baba036968b9,
+    0x2fbc2f2f4a5e9365,
+    0xc027c0c08e9de74e,
+    0xde5fdede60a181be,
+    0x1c701c1cfc386ce0,
+    0xfdd3fdfd46e72ebb,
+    0x4d294d4d1f9a6452,
+    0x927292927639e0e4,
+    0x75c97575faeabc8f,
+    0x06180606360c1e30,
+    0x8a128a8aae099824,
+    0xb2f2b2b24b7940f9,
+    0xe6bfe6e685d15963,
+    0x0e380e0e7e1c3670,
+    0x1f7c1f1fe73e63f8,
+    0x6295626255c4f737,
+    0xd477d4d43ab5a3ee,
+    0xa89aa8a8814d3229,
+    0x966296965231f4c4,
+    0xf9c3f9f962ef3a9b,
+    0xc533c5c5a397f666,
+    0x25942525104ab135,
+    0x59795959abb220f2,
+    0x842a8484d015ae54,
+    0x72d57272c5e4a7b7,
+    0x39e43939ec72ddd5,
+    0x4c2d4c4c1698615a,
+    0x5e655e5e94bc3bca,
+    0x78fd78789ff085e7,
+    0x38e03838e570d8dd,
+    0x8c0a8c8c98058614,
+    0xd163d1d117bfb2c6,
+    0xa5aea5a5e4570b41,
+    0xe2afe2e2a1d94d43,
+    0x619961614ec2f82f,
+    0xb3f6b3b3427b45f1,
+    0x218421213442a515,
+    0x9c4a9c9c0825d694,
+    0x1e781e1eee3c66f0,
+    0x4311434361865222,
+    0xc73bc7c7b193fc76,
+    0xfcd7fcfc4fe52bb3,
+    0x0410040424081420,
+    0x51595151e3a208b2,
+    0x995e9999252fc7bc,
+    0x6da96d6d22dac44f,
+    0x0d340d0d651a3968,
+    0xfacffafa79e93583,
+    0xdf5bdfdf69a384b6,
+    0x7ee57e7ea9fc9bd7,
+    0x249024241948b43d,
+    0x3bec3b3bfe76d7c5,
+    0xab96abab9a4b3d31,
+    0xce1fcecef081d13e,
+    0x1144111199225588,
+    0x8f068f8f8303890c,
+    0x4e254e4e049c6b4a,
+    0xb7e6b7b7667351d1,
+    0xeb8bebebe0cb600b,
+    0x3cf03c3cc178ccfd,
+    0x813e8181fd1fbf7c,
+    0x946a94944035fed4,
+    0xf7fbf7f71cf30ceb,
+    0xb9deb9b9186f67a1,
+    0x134c13138b265f98,
+    0x2cb02c2c51589c7d,
+    0xd36bd3d305bbb8d6,
+    0xe7bbe7e78cd35c6b,
+    0x6ea56e6e39dccb57,
+    0xc437c4c4aa95f36e,
+    0x030c03031b060f18,
+    0x56455656dcac138a,
+    0x440d44445e88491a,
+    0x7fe17f7fa0fe9edf,
+    0xa99ea9a9884f3721,
+    0x2aa82a2a6754824d,
+    0xbbd6bbbb0a6b6db1,
+    0xc123c1c1879fe246,
+    0x53515353f1a602a2,
+    0xdc57dcdc72a58bae,
+    0x0b2c0b0b53162758,
+    0x9d4e9d9d0127d39c,
+    0x6cad6c6c2bd8c147,
+    0x31c43131a462f595,
+    0x74cd7474f3e8b987,
+    0xf6fff6f615f109e3,
+    0x460546464c8c430a,
+    0xac8aacaca5452609,
+    0x891e8989b50f973c,
+    0x14501414b42844a0,
+    0xe1a3e1e1badf425b,
+    0x16581616a62c4eb0,
+    0x3ae83a3af774d2cd,
+    0x69b9696906d2d06f,
+    0x0924090941122d48,
+    0x70dd7070d7e0ada7,
+    0xb6e2b6b66f7154d9,
+    0xd067d0d01ebdb7ce,
+    0xed93ededd6c77e3b,
+    0xcc17cccce285db2e,
+    0x421542426884572a,
+    0x985a98982c2dc2b4,
+    0xa4aaa4a4ed550e49,
+    0x28a028287550885d,
+    0x5c6d5c5c86b831da,
+    0xf8c7f8f86bed3f93,
+    0x86228686c211a444,
 ];
 
-pub static C5: [u64; 256] = [
-    0x18c07830d8181860,
-    0x2305af462623238c,
-    0xc67ef991b8c6c63f,
-    0xe8136fcdfbe8e887,
-    0x874ca113cb878726,
-    0xb8a9626d11b8b8da,
-    0x0108050209010104,
-    0x4f426e9e0d4f4f21,
-    0x36adee6c9b3636d8,
-    0xa6590451ffa6a6a2,
-    0xd2debdb90cd2d26f,
-    0xf5fb06f70ef5f5f3,
-    0x79ef80f2967979f9,
-    0x6f5fcede306f6fa1,
-    0x91fcef3f6d91917e,
-    0x52aa07a4f8525255,
-    0x6027fdc04760609d,
-    0xbc89766535bcbcca,
-    0x9baccd2b379b9b56,
-    0x8e048c018a8e8e02,
-    0xa371155bd2a3a3b6,
-    0x0c603c186c0c0c30,
-    0x7bff8af6847b7bf1,
-    0x35b5e16a803535d4,
-    0x1de8693af51d1d74,
-    0xe05347ddb3e0e0a7,
-    0xd7f6acb321d7d77b,
-    0xc25eed999cc2c22f,
-    0x2e6d965c432e2eb8,
-    0x4b627a96294b4b31,
-    0xfea321e15dfefedf,
-    0x578216aed5575741,
-    0x15a8412abd151554,
-    0x779fb6eee87777c1,
-    0x37a5eb6e923737dc,
-    0xe57b56d79ee5e5b3,
-    0x9f8cd923139f9f46,
-    0xf0d317fd23f0f0e7,
-    0x4a6a7f94204a4a35,
-    0xda9e95a944dada4f,
-    0x58fa25b0a258587d,
-    0xc906ca8fcfc9c903,
-    0x29558d527c2929a4,
-    0x0a5022145a0a0a28,
-    0xb1e14f7f50b1b1fe,
-    0xa0691a5dc9a0a0ba,
-    0x6b7fdad6146b6bb1,
-    0x855cab17d985852e,
-    0xbd8173673cbdbdce,
-    0x5dd234ba8f5d5d69,
-    0x1080502090101040,
-    0xf4f303f507f4f4f7,
-    0xcb16c08bddcbcb0b,
-    0x3eedc67cd33e3ef8,
-    0x0528110a2d050514,
-    0x671fe6ce78676781,
-    0xe47353d597e4e4b7,
-    0x2725bb4e0227279c,
-    0x4132588273414119,
-    0x8b2c9d0ba78b8b16,
-    0xa7510153f6a7a7a6,
-    0x7dcf94fab27d7de9,
-    0x95dcfb374995956e,
-    0xd88e9fad56d8d847,
-    0xfb8b30eb70fbfbcb,
-    0xee2371c1cdeeee9f,
-    0x7cc791f8bb7c7ced,
-    0x6617e3cc71666685,
-    0xdda68ea77bdddd53,
-    0x17b84b2eaf17175c,
-    0x4702468e45474701,
-    0x9e84dc211a9e9e42,
-    0xca1ec589d4caca0f,
-    0x2d75995a582d2db4,
-    0xbf9179632ebfbfc6,
-    0x07381b0e3f07071c,
-    0xad012347acadad8e,
-    0x5aea2fb4b05a5a75,
-    0x836cb51bef838336,
-    0x3385ff66b63333cc,
-    0x633ff2c65c636391,
-    0x02100a0412020208,
-    0xaa39384993aaaa92,
-    0x71afa8e2de7171d9,
-    0xc80ecf8dc6c8c807,
-    0x19c87d32d1191964,
-    0x497270923b494939,
-    0xd9869aaf5fd9d943,
-    0xf2c31df931f2f2ef,
-    0xe34b48dba8e3e3ab,
-    0x5be22ab6b95b5b71,
-    0x8834920dbc88881a,
-    0x9aa4c8293e9a9a52,
-    0x262dbe4c0b262698,
-    0x328dfa64bf3232c8,
-    0xb0e94a7d59b0b0fa,
-    0xe91b6acff2e9e983,
-    0x0f78331e770f0f3c,
-    0xd5e6a6b733d5d573,
-    0x8074ba1df480803a,
-    0xbe997c6127bebec2,
-    0xcd26de87ebcdcd13,
-    0x34bde468893434d0,
-    0x487a75903248483d,
-    0xffab24e354ffffdb,
-    0x7af78ff48d7a7af5,
-    0x90f4ea3d6490907a,
-    0x5fc23ebe9d5f5f61,
-    0x201da0403d202080,
-    0x6867d5d00f6868bd,
-    0x1ad07234ca1a1a68,
-    0xae192c41b7aeae82,
-    0xb4c95e757db4b4ea,
-    0x549a19a8ce54544d,
-    0x93ece53b7f939376,
-    0x220daa442f222288,
-    0x6407e9c86364648d,
-    0xf1db12ff2af1f1e3,
-    0x73bfa2e6cc7373d1,
-    0x12905a2482121248,
-    0x403a5d807a40401d,
-    0x0840281048080820,
-    0xc356e89b95c3c32b,
-    0xec337bc5dfecec97,
-    0xdb9690ab4ddbdb4b,
-    0xa1611f5fc0a1a1be,
-    0x8d1c8307918d8d0e,
-    0x3df5c97ac83d3df4,
-    0x97ccf1335b979766,
+pub const C5: [u64; 256] = [
+    0x601818d83078c018,
+    0x8c23232646af0523,
+    0x3fc6c6b891f97ec6,
+    0x87e8e8fbcd6f13e8,
+    0x268787cb13a14c87,
+    0xdab8b8116d62a9b8,
+    0x0401010902050801,
+    0x214f4f0d9e6e424f,
+    0xd836369b6ceead36,
+    0xa2a6a6ff510459a6,
+    0x6fd2d20cb9bdded2,
+    0xf3f5f50ef706fbf5,
+    0xf9797996f280ef79,
+    0xa16f6f30dece5f6f,
+    0x7e91916d3feffc91,
+    0x555252f8a407aa52,
+    0x9d606047c0fd2760,
+    0xcabcbc35657689bc,
+    0x569b9b372bcdac9b,
+    0x028e8e8a018c048e,
+    0xb6a3a3d25b1571a3,
+    0x300c0c6c183c600c,
+    0xf17b7b84f68aff7b,
+    0xd43535806ae1b535,
+    0x741d1df53a69e81d,
+    0xa7e0e0b3dd4753e0,
+    0x7bd7d721b3acf6d7,
+    0x2fc2c29c99ed5ec2,
+    0xb82e2e435c966d2e,
+    0x314b4b29967a624b,
+    0xdffefe5de121a3fe,
+    0x415757d5ae168257,
+    0x541515bd2a41a815,
+    0xc17777e8eeb69f77,
+    0xdc3737926eeba537,
+    0xb3e5e59ed7567be5,
+    0x469f9f1323d98c9f,
+    0xe7f0f023fd17d3f0,
+    0x354a4a20947f6a4a,
+    0x4fdada44a9959eda,
+    0x7d5858a2b025fa58,
+    0x03c9c9cf8fca06c9,
+    0xa429297c528d5529,
+    0x280a0a5a1422500a,
+    0xfeb1b1507f4fe1b1,
+    0xbaa0a0c95d1a69a0,
+    0xb16b6b14d6da7f6b,
+    0x2e8585d917ab5c85,
+    0xcebdbd3c677381bd,
+    0x695d5d8fba34d25d,
+    0x4010109020508010,
+    0xf7f4f407f503f3f4,
+    0x0bcbcbdd8bc016cb,
+    0xf83e3ed37cc6ed3e,
+    0x1405052d0a112805,
+    0x81676778cee61f67,
+    0xb7e4e497d55373e4,
+    0x9c2727024ebb2527,
+    0x1941417382583241,
+    0x168b8ba70b9d2c8b,
+    0xa6a7a7f6530151a7,
+    0xe97d7db2fa94cf7d,
+    0x6e95954937fbdc95,
+    0x47d8d856ad9f8ed8,
+    0xcbfbfb70eb308bfb,
+    0x9feeeecdc17123ee,
+    0xed7c7cbbf891c77c,
+    0x85666671cce31766,
+    0x53dddd7ba78ea6dd,
+    0x5c1717af2e4bb817,
+    0x014747458e460247,
+    0x429e9e1a21dc849e,
+    0x0fcacad489c51eca,
+    0xb42d2d585a99752d,
+    0xc6bfbf2e637991bf,
+    0x1c07073f0e1b3807,
+    0x8eadadac472301ad,
+    0x755a5ab0b42fea5a,
+    0x368383ef1bb56c83,
+    0xcc3333b666ff8533,
+    0x9163635cc6f23f63,
+    0x08020212040a1002,
+    0x92aaaa93493839aa,
+    0xd97171dee2a8af71,
+    0x07c8c8c68dcf0ec8,
+    0x641919d1327dc819,
+    0x3949493b92707249,
+    0x43d9d95faf9a86d9,
+    0xeff2f231f91dc3f2,
+    0xabe3e3a8db484be3,
+    0x715b5bb9b62ae25b,
+    0x1a8888bc0d923488,
+    0x529a9a3e29c8a49a,
+    0x9826260b4cbe2d26,
+    0xc83232bf64fa8d32,
+    0xfab0b0597d4ae9b0,
+    0x83e9e9f2cf6a1be9,
+    0x3c0f0f771e33780f,
+    0x73d5d533b7a6e6d5,
+    0x3a8080f41dba7480,
+    0xc2bebe27617c99be,
+    0x13cdcdeb87de26cd,
+    0xd034348968e4bd34,
+    0x3d48483290757a48,
+    0xdbffff54e324abff,
+    0xf57a7a8df48ff77a,
+    0x7a9090643deaf490,
+    0x615f5f9dbe3ec25f,
+    0x8020203d40a01d20,
+    0xbd68680fd0d56768,
+    0x681a1aca3472d01a,
+    0x82aeaeb7412c19ae,
+    0xeab4b47d755ec9b4,
+    0x4d5454cea8199a54,
+    0x7693937f3be5ec93,
+    0x8822222f44aa0d22,
+    0x8d646463c8e90764,
+    0xe3f1f12aff12dbf1,
+    0xd17373cce6a2bf73,
+    0x48121282245a9012,
+    0x1d40407a805d3a40,
+    0x2008084810284008,
+    0x2bc3c3959be856c3,
+    0x97ececdfc57b33ec,
+    0x4bdbdb4dab9096db,
+    0xbea1a1c05f1f61a1,
+    0x0e8d8d9107831c8d,
+    0xf43d3dc87ac9f53d,
+    0x6697975b33f1cc97,
     0x0000000000000000,
-    0xcf36d483f9cfcf1b,
-    0x2b4587566e2b2bac,
-    0x7697b3ece17676c5,
-    0x8264b019e6828232,
-    0xd6fea9b128d6d67f,
-    0x1bd87736c31b1b6c,
-    0xb5c15b7774b5b5ee,
-    0xaf112943beafaf86,
-    0x6a77dfd41d6a6ab5,
-    0x50ba0da0ea50505d,
-    0x45124c8a57454509,
-    0xf3cb18fb38f3f3eb,
-    0x309df060ad3030c0,
-    0xef2b74c3c4efef9b,
-    0x3fe5c37eda3f3ffc,
-    0x55921caac7555549,
-    0xa2791059dba2a2b2,
-    0xea0365c9e9eaea8f,
-    0x650fecca6a656589,
-    0xbab9686903babad2,
-    0x2f65935e4a2f2fbc,
-    0xc04ee79d8ec0c027,
-    0xdebe81a160dede5f,
-    0x1ce06c38fc1c1c70,
-    0xfdbb2ee746fdfdd3,
-    0x4d52649a1f4d4d29,
-    0x92e4e03976929272,
-    0x758fbceafa7575c9,
-    0x06301e0c36060618,
-    0x8a249809ae8a8a12,
-    0xb2f940794bb2b2f2,
-    0xe66359d185e6e6bf,
-    0x0e70361c7e0e0e38,
-    0x1ff8633ee71f1f7c,
-    0x6237f7c455626295,
-    0xd4eea3b53ad4d477,
-    0xa829324d81a8a89a,
-    0x96c4f43152969662,
-    0xf99b3aef62f9f9c3,
-    0xc566f697a3c5c533,
-    0x2535b14a10252594,
-    0x59f220b2ab595979,
-    0x8454ae15d084842a,
-    0x72b7a7e4c57272d5,
-    0x39d5dd72ec3939e4,
-    0x4c5a6198164c4c2d,
-    0x5eca3bbc945e5e65,
-    0x78e785f09f7878fd,
-    0x38ddd870e53838e0,
-    0x8c148605988c8c0a,
-    0xd1c6b2bf17d1d163,
-    0xa5410b57e4a5a5ae,
-    0xe2434dd9a1e2e2af,
-    0x612ff8c24e616199,
-    0xb3f1457b42b3b3f6,
-    0x2115a54234212184,
-    0x9c94d625089c9c4a,
-    0x1ef0663cee1e1e78,
-    0x4322528661434311,
-    0xc776fc93b1c7c73b,
-    0xfcb32be54ffcfcd7,
-    0x0420140824040410,
-    0x51b208a2e3515159,
-    0x99bcc72f2599995e,
-    0x6d4fc4da226d6da9,
-    0x0d68391a650d0d34,
-    0xfa8335e979fafacf,
-    0xdfb684a369dfdf5b,
-    0x7ed79bfca97e7ee5,
-    0x243db44819242490,
-    0x3bc5d776fe3b3bec,
-    0xab313d4b9aabab96,
-    0xce3ed181f0cece1f,
-    0x1188552299111144,
-    0x8f0c8903838f8f06,
-    0x4e4a6b9c044e4e25,
-    0xb7d1517366b7b7e6,
-    0xeb0b60cbe0ebeb8b,
-    0x3cfdcc78c13c3cf0,
-    0x817cbf1ffd81813e,
-    0x94d4fe354094946a,
-    0xf7eb0cf31cf7f7fb,
-    0xb9a1676f18b9b9de,
-    0x13985f268b13134c,
-    0x2c7d9c58512c2cb0,
-    0xd3d6b8bb05d3d36b,
-    0xe76b5cd38ce7e7bb,
-    0x6e57cbdc396e6ea5,
-    0xc46ef395aac4c437,
-    0x03180f061b03030c,
-    0x568a13acdc565645,
-    0x441a49885e44440d,
-    0x7fdf9efea07f7fe1,
-    0xa921374f88a9a99e,
-    0x2a4d8254672a2aa8,
-    0xbbb16d6b0abbbbd6,
-    0xc146e29f87c1c123,
-    0x53a202a6f1535351,
-    0xdcae8ba572dcdc57,
-    0x0b582716530b0b2c,
-    0x9d9cd327019d9d4e,
-    0x6c47c1d82b6c6cad,
-    0x3195f562a43131c4,
-    0x7487b9e8f37474cd,
-    0xf6e309f115f6f6ff,
-    0x460a438c4c464605,
-    0xac092645a5acac8a,
-    0x893c970fb589891e,
-    0x14a04428b4141450,
-    0xe15b42dfbae1e1a3,
-    0x16b04e2ca6161658,
-    0x3acdd274f73a3ae8,
-    0x696fd0d2066969b9,
-    0x09482d1241090924,
-    0x70a7ade0d77070dd,
-    0xb6d954716fb6b6e2,
-    0xd0ceb7bd1ed0d067,
-    0xed3b7ec7d6eded93,
-    0xcc2edb85e2cccc17,
-    0x422a578468424215,
-    0x98b4c22d2c98985a,
-    0xa4490e55eda4a4aa,
-    0x285d8850752828a0,
-    0x5cda31b8865c5c6d,
-    0xf8933fed6bf8f8c7,
-    0x8644a411c2868622,
+    0x1bcfcff983d436cf,
+    0xac2b2b6e5687452b,
+    0xc57676e1ecb39776,
+    0x328282e619b06482,
+    0x7fd6d628b1a9fed6,
+    0x6c1b1bc33677d81b,
+    0xeeb5b574775bc1b5,
+    0x86afafbe432911af,
+    0xb56a6a1dd4df776a,
+    0x5d5050eaa00dba50,
+    0x094545578a4c1245,
+    0xebf3f338fb18cbf3,
+    0xc03030ad60f09d30,
+    0x9befefc4c3742bef,
+    0xfc3f3fda7ec3e53f,
+    0x495555c7aa1c9255,
+    0xb2a2a2db591079a2,
+    0x8feaeae9c96503ea,
+    0x8965656acaec0f65,
+    0xd2baba036968b9ba,
+    0xbc2f2f4a5e93652f,
+    0x27c0c08e9de74ec0,
+    0x5fdede60a181bede,
+    0x701c1cfc386ce01c,
+    0xd3fdfd46e72ebbfd,
+    0x294d4d1f9a64524d,
+    0x7292927639e0e492,
+    0xc97575faeabc8f75,
+    0x180606360c1e3006,
+    0x128a8aae0998248a,
+    0xf2b2b24b7940f9b2,
+    0xbfe6e685d15963e6,
+    0x380e0e7e1c36700e,
+    0x7c1f1fe73e63f81f,
+    0x95626255c4f73762,
+    0x77d4d43ab5a3eed4,
+    0x9aa8a8814d3229a8,
+    0x6296965231f4c496,
+    0xc3f9f962ef3a9bf9,
+    0x33c5c5a397f666c5,
+    0x942525104ab13525,
+    0x795959abb220f259,
+    0x2a8484d015ae5484,
+    0xd57272c5e4a7b772,
+    0xe43939ec72ddd539,
+    0x2d4c4c1698615a4c,
+    0x655e5e94bc3bca5e,
+    0xfd78789ff085e778,
+    0xe03838e570d8dd38,
+    0x0a8c8c980586148c,
+    0x63d1d117bfb2c6d1,
+    0xaea5a5e4570b41a5,
+    0xafe2e2a1d94d43e2,
+    0x9961614ec2f82f61,
+    0xf6b3b3427b45f1b3,
+    0x8421213442a51521,
+    0x4a9c9c0825d6949c,
+    0x781e1eee3c66f01e,
+    0x1143436186522243,
+    0x3bc7c7b193fc76c7,
+    0xd7fcfc4fe52bb3fc,
+    0x1004042408142004,
+    0x595151e3a208b251,
+    0x5e9999252fc7bc99,
+    0xa96d6d22dac44f6d,
+    0x340d0d651a39680d,
+    0xcffafa79e93583fa,
+    0x5bdfdf69a384b6df,
+    0xe57e7ea9fc9bd77e,
+    0x9024241948b43d24,
+    0xec3b3bfe76d7c53b,
+    0x96abab9a4b3d31ab,
+    0x1fcecef081d13ece,
+    0x4411119922558811,
+    0x068f8f8303890c8f,
+    0x254e4e049c6b4a4e,
+    0xe6b7b7667351d1b7,
+    0x8bebebe0cb600beb,
+    0xf03c3cc178ccfd3c,
+    0x3e8181fd1fbf7c81,
+    0x6a94944035fed494,
+    0xfbf7f71cf30cebf7,
+    0xdeb9b9186f67a1b9,
+    0x4c13138b265f9813,
+    0xb02c2c51589c7d2c,
+    0x6bd3d305bbb8d6d3,
+    0xbbe7e78cd35c6be7,
+    0xa56e6e39dccb576e,
+    0x37c4c4aa95f36ec4,
+    0x0c03031b060f1803,
+    0x455656dcac138a56,
+    0x0d44445e88491a44,
+    0xe17f7fa0fe9edf7f,
+    0x9ea9a9884f3721a9,
+    0xa82a2a6754824d2a,
+    0xd6bbbb0a6b6db1bb,
+    0x23c1c1879fe246c1,
+    0x515353f1a602a253,
+    0x57dcdc72a58baedc,
+    0x2c0b0b531627580b,
+    0x4e9d9d0127d39c9d,
+    0xad6c6c2bd8c1476c,
+    0xc43131a462f59531,
+    0xcd7474f3e8b98774,
+    0xfff6f615f109e3f6,
+    0x0546464c8c430a46,
+    0x8aacaca5452609ac,
+    0x1e8989b50f973c89,
+    0x501414b42844a014,
+    0xa3e1e1badf425be1,
+    0x581616a62c4eb016,
+    0xe83a3af774d2cd3a,
+    0xb9696906d2d06f69,
+    0x24090941122d4809,
+    0xdd7070d7e0ada770,
+    0xe2b6b66f7154d9b6,
+    0x67d0d01ebdb7ced0,
+    0x93ededd6c77e3bed,
+    0x17cccce285db2ecc,
+    0x1542426884572a42,
+    0x5a98982c2dc2b498,
+    0xaaa4a4ed550e49a4,
+    0xa028287550885d28,
+    0x6d5c5c86b831da5c,
+    0xc7f8f86bed3f93f8,
+    0x228686c211a44486,
 ];
 
-pub static C6: [u64; 256] = [
-    0x6018c07830d81818,
-    0x8c2305af46262323,
-    0x3fc67ef991b8c6c6,
-    0x87e8136fcdfbe8e8,
-    0x26874ca113cb8787,
-    0xdab8a9626d11b8b8,
-    0x0401080502090101,
-    0x214f426e9e0d4f4f,
-    0xd836adee6c9b3636,
-    0xa2a6590451ffa6a6,
-    0x6fd2debdb90cd2d2,
-    0xf3f5fb06f70ef5f5,
-    0xf979ef80f2967979,
-    0xa16f5fcede306f6f,
-    0x7e91fcef3f6d9191,
-    0x5552aa07a4f85252,
-    0x9d6027fdc0476060,
-    0xcabc89766535bcbc,
-    0x569baccd2b379b9b,
-    0x028e048c018a8e8e,
-    0xb6a371155bd2a3a3,
-    0x300c603c186c0c0c,
-    0xf17bff8af6847b7b,
-    0xd435b5e16a803535,
-    0x741de8693af51d1d,
-    0xa7e05347ddb3e0e0,
-    0x7bd7f6acb321d7d7,
-    0x2fc25eed999cc2c2,
-    0xb82e6d965c432e2e,
-    0x314b627a96294b4b,
-    0xdffea321e15dfefe,
-    0x41578216aed55757,
-    0x5415a8412abd1515,
-    0xc1779fb6eee87777,
-    0xdc37a5eb6e923737,
-    0xb3e57b56d79ee5e5,
-    0x469f8cd923139f9f,
-    0xe7f0d317fd23f0f0,
-    0x354a6a7f94204a4a,
-    0x4fda9e95a944dada,
-    0x7d58fa25b0a25858,
-    0x03c906ca8fcfc9c9,
-    0xa429558d527c2929,
-    0x280a5022145a0a0a,
-    0xfeb1e14f7f50b1b1,
-    0xbaa0691a5dc9a0a0,
-    0xb16b7fdad6146b6b,
-    0x2e855cab17d98585,
-    0xcebd8173673cbdbd,
-    0x695dd234ba8f5d5d,
-    0x4010805020901010,
-    0xf7f4f303f507f4f4,
-    0x0bcb16c08bddcbcb,
-    0xf83eedc67cd33e3e,
-    0x140528110a2d0505,
-    0x81671fe6ce786767,
-    0xb7e47353d597e4e4,
-    0x9c2725bb4e022727,
-    0x1941325882734141,
-    0x168b2c9d0ba78b8b,
-    0xa6a7510153f6a7a7,
-    0xe97dcf94fab27d7d,
-    0x6e95dcfb37499595,
-    0x47d88e9fad56d8d8,
-    0xcbfb8b30eb70fbfb,
-    0x9fee2371c1cdeeee,
-    0xed7cc791f8bb7c7c,
-    0x856617e3cc716666,
-    0x53dda68ea77bdddd,
-    0x5c17b84b2eaf1717,
-    0x014702468e454747,
-    0x429e84dc211a9e9e,
-    0x0fca1ec589d4caca,
-    0xb42d75995a582d2d,
-    0xc6bf9179632ebfbf,
-    0x1c07381b0e3f0707,
-    0x8ead012347acadad,
-    0x755aea2fb4b05a5a,
-    0x36836cb51bef8383,
-    0xcc3385ff66b63333,
-    0x91633ff2c65c6363,
-    0x0802100a04120202,
-    0x92aa39384993aaaa,
-    0xd971afa8e2de7171,
-    0x07c80ecf8dc6c8c8,
-    0x6419c87d32d11919,
-    0x39497270923b4949,
-    0x43d9869aaf5fd9d9,
-    0xeff2c31df931f2f2,
-    0xabe34b48dba8e3e3,
-    0x715be22ab6b95b5b,
-    0x1a8834920dbc8888,
-    0x529aa4c8293e9a9a,
-    0x98262dbe4c0b2626,
-    0xc8328dfa64bf3232,
-    0xfab0e94a7d59b0b0,
-    0x83e91b6acff2e9e9,
-    0x3c0f78331e770f0f,
-    0x73d5e6a6b733d5d5,
-    0x3a8074ba1df48080,
-    0xc2be997c6127bebe,
-    0x13cd26de87ebcdcd,
-    0xd034bde468893434,
-    0x3d487a7590324848,
-    0xdbffab24e354ffff,
-    0xf57af78ff48d7a7a,
-    0x7a90f4ea3d649090,
-    0x615fc23ebe9d5f5f,
-    0x80201da0403d2020,
-    0xbd6867d5d00f6868,
-    0x681ad07234ca1a1a,
-    0x82ae192c41b7aeae,
-    0xeab4c95e757db4b4,
-    0x4d549a19a8ce5454,
-    0x7693ece53b7f9393,
-    0x88220daa442f2222,
-    0x8d6407e9c8636464,
-    0xe3f1db12ff2af1f1,
-    0xd173bfa2e6cc7373,
-    0x4812905a24821212,
-    0x1d403a5d807a4040,
-    0x2008402810480808,
-    0x2bc356e89b95c3c3,
-    0x97ec337bc5dfecec,
-    0x4bdb9690ab4ddbdb,
-    0xbea1611f5fc0a1a1,
-    0x0e8d1c8307918d8d,
-    0xf43df5c97ac83d3d,
-    0x6697ccf1335b9797,
+pub const C6: [u64; 256] = [
+    0x1818d83078c01860,
+    0x23232646af05238c,
+    0xc6c6b891f97ec63f,
+    0xe8e8fbcd6f13e887,
+    0x8787cb13a14c8726,
+    0xb8b8116d62a9b8da,
+    0x0101090205080104,
+    0x4f4f0d9e6e424f21,
+    0x36369b6ceead36d8,
+    0xa6a6ff510459a6a2,
+    0xd2d20cb9bdded26f,
+    0xf5f50ef706fbf5f3,
+    0x797996f280ef79f9,
+    0x6f6f30dece5f6fa1,
+    0x91916d3feffc917e,
+    0x5252f8a407aa5255,
+    0x606047c0fd27609d,
+    0xbcbc35657689bcca,
+    0x9b9b372bcdac9b56,
+    0x8e8e8a018c048e02,
+    0xa3a3d25b1571a3b6,
+    0x0c0c6c183c600c30,
+    0x7b7b84f68aff7bf1,
+    0x3535806ae1b535d4,
+    0x1d1df53a69e81d74,
+    0xe0e0b3dd4753e0a7,
+    0xd7d721b3acf6d77b,
+    0xc2c29c99ed5ec22f,
+    0x2e2e435c966d2eb8,
+    0x4b4b29967a624b31,
+    0xfefe5de121a3fedf,
+    0x5757d5ae16825741,
+    0x1515bd2a41a81554,
+    0x7777e8eeb69f77c1,
+    0x3737926eeba537dc,
+    0xe5e59ed7567be5b3,
+    0x9f9f1323d98c9f46,
+    0xf0f023fd17d3f0e7,
+    0x4a4a20947f6a4a35,
+    0xdada44a9959eda4f,
+    0x5858a2b025fa587d,
+    0xc9c9cf8fca06c903,
+    0x29297c528d5529a4,
+    0x0a0a5a1422500a28,
+    0xb1b1507f4fe1b1fe,
+    0xa0a0c95d1a69a0ba,
+    0x6b6b14d6da7f6bb1,
+    0x8585d917ab5c852e,
+    0xbdbd3c677381bdce,
+    0x5d5d8fba34d25d69,
+    0x1010902050801040,
+    0xf4f407f503f3f4f7,
+    0xcbcbdd8bc016cb0b,
+    0x3e3ed37cc6ed3ef8,
+    0x05052d0a11280514,
+    0x676778cee61f6781,
+    0xe4e497d55373e4b7,
+    0x2727024ebb25279c,
+    0x4141738258324119,
+    0x8b8ba70b9d2c8b16,
+    0xa7a7f6530151a7a6,
+    0x7d7db2fa94cf7de9,
+    0x95954937fbdc956e,
+    0xd8d856ad9f8ed847,
+    0xfbfb70eb308bfbcb,
+    0xeeeecdc17123ee9f,
+    0x7c7cbbf891c77ced,
+    0x666671cce3176685,
+    0xdddd7ba78ea6dd53,
+    0x1717af2e4bb8175c,
+    0x4747458e46024701,
+    0x9e9e1a21dc849e42,
+    0xcacad489c51eca0f,
+    0x2d2d585a99752db4,
+    0xbfbf2e637991bfc6,
+    0x07073f0e1b38071c,
+    0xadadac472301ad8e,
+    0x5a5ab0b42fea5a75,
+    0x8383ef1bb56c8336,
+    0x3333b666ff8533cc,
+    0x63635cc6f23f6391,
+    0x020212040a100208,
+    0xaaaa93493839aa92,
+    0x7171dee2a8af71d9,
+    0xc8c8c68dcf0ec807,
+    0x1919d1327dc81964,
+    0x49493b9270724939,
+    0xd9d95faf9a86d943,
+    0xf2f231f91dc3f2ef,
+    0xe3e3a8db484be3ab,
+    0x5b5bb9b62ae25b71,
+    0x8888bc0d9234881a,
+    0x9a9a3e29c8a49a52,
+    0x26260b4cbe2d2698,
+    0x3232bf64fa8d32c8,
+    0xb0b0597d4ae9b0fa,
+    0xe9e9f2cf6a1be983,
+    0x0f0f771e33780f3c,
+    0xd5d533b7a6e6d573,
+    0x8080f41dba74803a,
+    0xbebe27617c99bec2,
+    0xcdcdeb87de26cd13,
+    0x34348968e4bd34d0,
+    0x48483290757a483d,
+    0xffff54e324abffdb,
+    0x7a7a8df48ff77af5,
+    0x9090643deaf4907a,
+    0x5f5f9dbe3ec25f61,
+    0x20203d40a01d2080,
+    0x68680fd0d56768bd,
+    0x1a1aca3472d01a68,
+    0xaeaeb7412c19ae82,
+    0xb4b47d755ec9b4ea,
+    0x5454cea8199a544d,
+    0x93937f3be5ec9376,
+    0x22222f44aa0d2288,
+    0x646463c8e907648d,
+    0xf1f12aff12dbf1e3,
+    0x7373cce6a2bf73d1,
+    0x121282245a901248,
+    0x40407a805d3a401d,
+    0x0808481028400820,
+    0xc3c3959be856c32b,
+    0xececdfc57b33ec97,
+    0xdbdb4dab9096db4b,
+    0xa1a1c05f1f61a1be,
+    0x8d8d9107831c8d0e,
+    0x3d3dc87ac9f53df4,
+    0x97975b33f1cc9766,
     0x0000000000000000,
-    0x1bcf36d483f9cfcf,
-    0xac2b4587566e2b2b,
-    0xc57697b3ece17676,
-    0x328264b019e68282,
-    0x7fd6fea9b128d6d6,
-    0x6c1bd87736c31b1b,
-    0xeeb5c15b7774b5b5,
-    0x86af112943beafaf,
-    0xb56a77dfd41d6a6a,
-    0x5d50ba0da0ea5050,
-    0x0945124c8a574545,
-    0xebf3cb18fb38f3f3,
-    0xc0309df060ad3030,
-    0x9bef2b74c3c4efef,
-    0xfc3fe5c37eda3f3f,
-    0x4955921caac75555,
-    0xb2a2791059dba2a2,
-    0x8fea0365c9e9eaea,
-    0x89650fecca6a6565,
-    0xd2bab9686903baba,
-    0xbc2f65935e4a2f2f,
-    0x27c04ee79d8ec0c0,
-    0x5fdebe81a160dede,
-    0x701ce06c38fc1c1c,
-    0xd3fdbb2ee746fdfd,
-    0x294d52649a1f4d4d,
-    0x7292e4e039769292,
-    0xc9758fbceafa7575,
-    0x1806301e0c360606,
-    0x128a249809ae8a8a,
-    0xf2b2f940794bb2b2,
-    0xbfe66359d185e6e6,
-    0x380e70361c7e0e0e,
-    0x7c1ff8633ee71f1f,
-    0x956237f7c4556262,
-    0x77d4eea3b53ad4d4,
-    0x9aa829324d81a8a8,
-    0x6296c4f431529696,
-    0xc3f99b3aef62f9f9,
-    0x33c566f697a3c5c5,
-    0x942535b14a102525,
-    0x7959f220b2ab5959,
-    0x2a8454ae15d08484,
-    0xd572b7a7e4c57272,
-    0xe439d5dd72ec3939,
-    0x2d4c5a6198164c4c,
-    0x655eca3bbc945e5e,
-    0xfd78e785f09f7878,
-    0xe038ddd870e53838,
-    0x0a8c148605988c8c,
-    0x63d1c6b2bf17d1d1,
-    0xaea5410b57e4a5a5,
-    0xafe2434dd9a1e2e2,
-    0x99612ff8c24e6161,
-    0xf6b3f1457b42b3b3,
-    0x842115a542342121,
-    0x4a9c94d625089c9c,
-    0x781ef0663cee1e1e,
-    0x1143225286614343,
-    0x3bc776fc93b1c7c7,
-    0xd7fcb32be54ffcfc,
-    0x1004201408240404,
-    0x5951b208a2e35151,
-    0x5e99bcc72f259999,
-    0xa96d4fc4da226d6d,
-    0x340d68391a650d0d,
-    0xcffa8335e979fafa,
-    0x5bdfb684a369dfdf,
-    0xe57ed79bfca97e7e,
-    0x90243db448192424,
-    0xec3bc5d776fe3b3b,
-    0x96ab313d4b9aabab,
-    0x1fce3ed181f0cece,
-    0x4411885522991111,
-    0x068f0c8903838f8f,
-    0x254e4a6b9c044e4e,
-    0xe6b7d1517366b7b7,
-    0x8beb0b60cbe0ebeb,
-    0xf03cfdcc78c13c3c,
-    0x3e817cbf1ffd8181,
-    0x6a94d4fe35409494,
-    0xfbf7eb0cf31cf7f7,
-    0xdeb9a1676f18b9b9,
-    0x4c13985f268b1313,
-    0xb02c7d9c58512c2c,
-    0x6bd3d6b8bb05d3d3,
-    0xbbe76b5cd38ce7e7,
-    0xa56e57cbdc396e6e,
-    0x37c46ef395aac4c4,
-    0x0c03180f061b0303,
-    0x45568a13acdc5656,
-    0x0d441a49885e4444,
-    0xe17fdf9efea07f7f,
-    0x9ea921374f88a9a9,
-    0xa82a4d8254672a2a,
-    0xd6bbb16d6b0abbbb,
-    0x23c146e29f87c1c1,
-    0x5153a202a6f15353,
-    0x57dcae8ba572dcdc,
-    0x2c0b582716530b0b,
-    0x4e9d9cd327019d9d,
-    0xad6c47c1d82b6c6c,
-    0xc43195f562a43131,
-    0xcd7487b9e8f37474,
-    0xfff6e309f115f6f6,
-    0x05460a438c4c4646,
-    0x8aac092645a5acac,
-    0x1e893c970fb58989,
-    0x5014a04428b41414,
-    0xa3e15b42dfbae1e1,
-    0x5816b04e2ca61616,
-    0xe83acdd274f73a3a,
-    0xb9696fd0d2066969,
-    0x2409482d12410909,
-    0xdd70a7ade0d77070,
-    0xe2b6d954716fb6b6,
-    0x67d0ceb7bd1ed0d0,
-    0x93ed3b7ec7d6eded,
-    0x17cc2edb85e2cccc,
-    0x15422a5784684242,
-    0x5a98b4c22d2c9898,
-    0xaaa4490e55eda4a4,
-    0xa0285d8850752828,
-    0x6d5cda31b8865c5c,
-    0xc7f8933fed6bf8f8,
-    0x228644a411c28686,
+    0xcfcff983d436cf1b,
+    0x2b2b6e5687452bac,
+    0x7676e1ecb39776c5,
+    0x8282e619b0648232,
+    0xd6d628b1a9fed67f,
+    0x1b1bc33677d81b6c,
+    0xb5b574775bc1b5ee,
+    0xafafbe432911af86,
+    0x6a6a1dd4df776ab5,
+    0x5050eaa00dba505d,
+    0x4545578a4c124509,
+    0xf3f338fb18cbf3eb,
+    0x3030ad60f09d30c0,
+    0xefefc4c3742bef9b,
+    0x3f3fda7ec3e53ffc,
+    0x5555c7aa1c925549,
+    0xa2a2db591079a2b2,
+    0xeaeae9c96503ea8f,
+    0x65656acaec0f6589,
+    0xbaba036968b9bad2,
+    0x2f2f4a5e93652fbc,
+    0xc0c08e9de74ec027,
+    0xdede60a181bede5f,
+    0x1c1cfc386ce01c70,
+    0xfdfd46e72ebbfdd3,
+    0x4d4d1f9a64524d29,
+    0x92927639e0e49272,
+    0x7575faeabc8f75c9,
+    0x0606360c1e300618,
+    0x8a8aae0998248a12,
+    0xb2b24b7940f9b2f2,
+    0xe6e685d15963e6bf,
+    0x0e0e7e1c36700e38,
+    0x1f1fe73e63f81f7c,
+    0x626255c4f7376295,
+    0xd4d43ab5a3eed477,
+    0xa8a8814d3229a89a,
+    0x96965231f4c49662,
+    0xf9f962ef3a9bf9c3,
+    0xc5c5a397f666c533,
+    0x2525104ab1352594,
+    0x5959abb220f25979,
+    0x8484d015ae54842a,
+    0x7272c5e4a7b772d5,
+    0x3939ec72ddd539e4,
+    0x4c4c1698615a4c2d,
+    0x5e5e94bc3bca5e65,
+    0x78789ff085e778fd,
+    0x3838e570d8dd38e0,
+    0x8c8c980586148c0a,
+    0xd1d117bfb2c6d163,
+    0xa5a5e4570b41a5ae,
+    0xe2e2a1d94d43e2af,
+    0x61614ec2f82f6199,
+    0xb3b3427b45f1b3f6,
+    0x21213442a5152184,
+    0x9c9c0825d6949c4a,
+    0x1e1eee3c66f01e78,
+    0x4343618652224311,
+    0xc7c7b193fc76c73b,
+    0xfcfc4fe52bb3fcd7,
+    0x0404240814200410,
+    0x5151e3a208b25159,
+    0x9999252fc7bc995e,
+    0x6d6d22dac44f6da9,
+    0x0d0d651a39680d34,
+    0xfafa79e93583facf,
+    0xdfdf69a384b6df5b,
+    0x7e7ea9fc9bd77ee5,
+    0x24241948b43d2490,
+    0x3b3bfe76d7c53bec,
+    0xabab9a4b3d31ab96,
+    0xcecef081d13ece1f,
+    0x1111992255881144,
+    0x8f8f8303890c8f06,
+    0x4e4e049c6b4a4e25,
+    0xb7b7667351d1b7e6,
+    0xebebe0cb600beb8b,
+    0x3c3cc178ccfd3cf0,
+    0x8181fd1fbf7c813e,
+    0x94944035fed4946a,
+    0xf7f71cf30cebf7fb,
+    0xb9b9186f67a1b9de,
+    0x13138b265f98134c,
+    0x2c2c51589c7d2cb0,
+    0xd3d305bbb8d6d36b,
+    0xe7e78cd35c6be7bb,
+    0x6e6e39dccb576ea5,
+    0xc4c4aa95f36ec437,
+    0x03031b060f18030c,
+    0x5656dcac138a5645,
+    0x44445e88491a440d,
+    0x7f7fa0fe9edf7fe1,
+    0xa9a9884f3721a99e,
+    0x2a2a6754824d2aa8,
+    0xbbbb0a6b6db1bbd6,
+    0xc1c1879fe246c123,
+    0x5353f1a602a25351,
+    0xdcdc72a58baedc57,
+    0x0b0b531627580b2c,
+    0x9d9d0127d39c9d4e,
+    0x6c6c2bd8c1476cad,
+    0x3131a462f59531c4,
+    0x7474f3e8b98774cd,
+    0xf6f615f109e3f6ff,
+    0x46464c8c430a4605,
+    0xacaca5452609ac8a,
+    0x8989b50f973c891e,
+    0x1414b42844a01450,
+    0xe1e1badf425be1a3,
+    0x1616a62c4eb01658,
+    0x3a3af774d2cd3ae8,
+    0x696906d2d06f69b9,
+    0x090941122d480924,
+    0x7070d7e0ada770dd,
+    0xb6b66f7154d9b6e2,
+    0xd0d01ebdb7ced067,
+    0xededd6c77e3bed93,
+    0xcccce285db2ecc17,
+    0x42426884572a4215,
+    0x98982c2dc2b4985a,
+    0xa4a4ed550e49a4aa,
+    0x28287550885d28a0,
+    0x5c5c86b831da5c6d,
+    0xf8f86bed3f93f8c7,
+    0x8686c211a4448622,
 ];
 
-pub static C7: [u64; 256] = [
-    0x186018c07830d818,
-    0x238c2305af462623,
-    0xc63fc67ef991b8c6,
-    0xe887e8136fcdfbe8,
-    0x8726874ca113cb87,
-    0xb8dab8a9626d11b8,
-    0x0104010805020901,
-    0x4f214f426e9e0d4f,
-    0x36d836adee6c9b36,
-    0xa6a2a6590451ffa6,
-    0xd26fd2debdb90cd2,
-    0xf5f3f5fb06f70ef5,
-    0x79f979ef80f29679,
-    0x6fa16f5fcede306f,
-    0x917e91fcef3f6d91,
-    0x525552aa07a4f852,
-    0x609d6027fdc04760,
-    0xbccabc89766535bc,
-    0x9b569baccd2b379b,
-    0x8e028e048c018a8e,
-    0xa3b6a371155bd2a3,
-    0x0c300c603c186c0c,
-    0x7bf17bff8af6847b,
-    0x35d435b5e16a8035,
-    0x1d741de8693af51d,
-    0xe0a7e05347ddb3e0,
-    0xd77bd7f6acb321d7,
-    0xc22fc25eed999cc2,
-    0x2eb82e6d965c432e,
-    0x4b314b627a96294b,
-    0xfedffea321e15dfe,
-    0x5741578216aed557,
-    0x155415a8412abd15,
-    0x77c1779fb6eee877,
-    0x37dc37a5eb6e9237,
-    0xe5b3e57b56d79ee5,
-    0x9f469f8cd923139f,
-    0xf0e7f0d317fd23f0,
-    0x4a354a6a7f94204a,
-    0xda4fda9e95a944da,
-    0x587d58fa25b0a258,
-    0xc903c906ca8fcfc9,
-    0x29a429558d527c29,
-    0x0a280a5022145a0a,
-    0xb1feb1e14f7f50b1,
-    0xa0baa0691a5dc9a0,
-    0x6bb16b7fdad6146b,
-    0x852e855cab17d985,
-    0xbdcebd8173673cbd,
-    0x5d695dd234ba8f5d,
-    0x1040108050209010,
-    0xf4f7f4f303f507f4,
-    0xcb0bcb16c08bddcb,
-    0x3ef83eedc67cd33e,
-    0x05140528110a2d05,
-    0x6781671fe6ce7867,
-    0xe4b7e47353d597e4,
-    0x279c2725bb4e0227,
-    0x4119413258827341,
-    0x8b168b2c9d0ba78b,
-    0xa7a6a7510153f6a7,
-    0x7de97dcf94fab27d,
-    0x956e95dcfb374995,
-    0xd847d88e9fad56d8,
-    0xfbcbfb8b30eb70fb,
-    0xee9fee2371c1cdee,
-    0x7ced7cc791f8bb7c,
-    0x66856617e3cc7166,
-    0xdd53dda68ea77bdd,
-    0x175c17b84b2eaf17,
-    0x47014702468e4547,
-    0x9e429e84dc211a9e,
-    0xca0fca1ec589d4ca,
-    0x2db42d75995a582d,
-    0xbfc6bf9179632ebf,
-    0x071c07381b0e3f07,
-    0xad8ead012347acad,
-    0x5a755aea2fb4b05a,
-    0x8336836cb51bef83,
-    0x33cc3385ff66b633,
-    0x6391633ff2c65c63,
-    0x020802100a041202,
-    0xaa92aa39384993aa,
-    0x71d971afa8e2de71,
-    0xc807c80ecf8dc6c8,
-    0x196419c87d32d119,
-    0x4939497270923b49,
-    0xd943d9869aaf5fd9,
-    0xf2eff2c31df931f2,
-    0xe3abe34b48dba8e3,
-    0x5b715be22ab6b95b,
-    0x881a8834920dbc88,
-    0x9a529aa4c8293e9a,
-    0x2698262dbe4c0b26,
-    0x32c8328dfa64bf32,
-    0xb0fab0e94a7d59b0,
-    0xe983e91b6acff2e9,
-    0x0f3c0f78331e770f,
-    0xd573d5e6a6b733d5,
-    0x803a8074ba1df480,
-    0xbec2be997c6127be,
-    0xcd13cd26de87ebcd,
-    0x34d034bde4688934,
-    0x483d487a75903248,
-    0xffdbffab24e354ff,
-    0x7af57af78ff48d7a,
-    0x907a90f4ea3d6490,
-    0x5f615fc23ebe9d5f,
-    0x2080201da0403d20,
-    0x68bd6867d5d00f68,
-    0x1a681ad07234ca1a,
-    0xae82ae192c41b7ae,
-    0xb4eab4c95e757db4,
-    0x544d549a19a8ce54,
-    0x937693ece53b7f93,
-    0x2288220daa442f22,
-    0x648d6407e9c86364,
-    0xf1e3f1db12ff2af1,
-    0x73d173bfa2e6cc73,
-    0x124812905a248212,
-    0x401d403a5d807a40,
-    0x0820084028104808,
-    0xc32bc356e89b95c3,
-    0xec97ec337bc5dfec,
-    0xdb4bdb9690ab4ddb,
-    0xa1bea1611f5fc0a1,
-    0x8d0e8d1c8307918d,
-    0x3df43df5c97ac83d,
-    0x976697ccf1335b97,
+pub const C7: [u64; 256] = [
+    0x18d83078c0186018,
+    0x232646af05238c23,
+    0xc6b891f97ec63fc6,
+    0xe8fbcd6f13e887e8,
+    0x87cb13a14c872687,
+    0xb8116d62a9b8dab8,
+    0x0109020508010401,
+    0x4f0d9e6e424f214f,
+    0x369b6ceead36d836,
+    0xa6ff510459a6a2a6,
+    0xd20cb9bdded26fd2,
+    0xf50ef706fbf5f3f5,
+    0x7996f280ef79f979,
+    0x6f30dece5f6fa16f,
+    0x916d3feffc917e91,
+    0x52f8a407aa525552,
+    0x6047c0fd27609d60,
+    0xbc35657689bccabc,
+    0x9b372bcdac9b569b,
+    0x8e8a018c048e028e,
+    0xa3d25b1571a3b6a3,
+    0x0c6c183c600c300c,
+    0x7b84f68aff7bf17b,
+    0x35806ae1b535d435,
+    0x1df53a69e81d741d,
+    0xe0b3dd4753e0a7e0,
+    0xd721b3acf6d77bd7,
+    0xc29c99ed5ec22fc2,
+    0x2e435c966d2eb82e,
+    0x4b29967a624b314b,
+    0xfe5de121a3fedffe,
+    0x57d5ae1682574157,
+    0x15bd2a41a8155415,
+    0x77e8eeb69f77c177,
+    0x37926eeba537dc37,
+    0xe59ed7567be5b3e5,
+    0x9f1323d98c9f469f,
+    0xf023fd17d3f0e7f0,
+    0x4a20947f6a4a354a,
+    0xda44a9959eda4fda,
+    0x58a2b025fa587d58,
+    0xc9cf8fca06c903c9,
+    0x297c528d5529a429,
+    0x0a5a1422500a280a,
+    0xb1507f4fe1b1feb1,
+    0xa0c95d1a69a0baa0,
+    0x6b14d6da7f6bb16b,
+    0x85d917ab5c852e85,
+    0xbd3c677381bdcebd,
+    0x5d8fba34d25d695d,
+    0x1090205080104010,
+    0xf407f503f3f4f7f4,
+    0xcbdd8bc016cb0bcb,
+    0x3ed37cc6ed3ef83e,
+    0x052d0a1128051405,
+    0x6778cee61f678167,
+    0xe497d55373e4b7e4,
+    0x27024ebb25279c27,
+    0x4173825832411941,
+    0x8ba70b9d2c8b168b,
+    0xa7f6530151a7a6a7,
+    0x7db2fa94cf7de97d,
+    0x954937fbdc956e95,
+    0xd856ad9f8ed847d8,
+    0xfb70eb308bfbcbfb,
+    0xeecdc17123ee9fee,
+    0x7cbbf891c77ced7c,
+    0x6671cce317668566,
+    0xdd7ba78ea6dd53dd,
+    0x17af2e4bb8175c17,
+    0x47458e4602470147,
+    0x9e1a21dc849e429e,
+    0xcad489c51eca0fca,
+    0x2d585a99752db42d,
+    0xbf2e637991bfc6bf,
+    0x073f0e1b38071c07,
+    0xadac472301ad8ead,
+    0x5ab0b42fea5a755a,
+    0x83ef1bb56c833683,
+    0x33b666ff8533cc33,
+    0x635cc6f23f639163,
+    0x0212040a10020802,
+    0xaa93493839aa92aa,
+    0x71dee2a8af71d971,
+    0xc8c68dcf0ec807c8,
+    0x19d1327dc8196419,
+    0x493b927072493949,
+    0xd95faf9a86d943d9,
+    0xf231f91dc3f2eff2,
+    0xe3a8db484be3abe3,
+    0x5bb9b62ae25b715b,
+    0x88bc0d9234881a88,
+    0x9a3e29c8a49a529a,
+    0x260b4cbe2d269826,
+    0x32bf64fa8d32c832,
+    0xb0597d4ae9b0fab0,
+    0xe9f2cf6a1be983e9,
+    0x0f771e33780f3c0f,
+    0xd533b7a6e6d573d5,
+    0x80f41dba74803a80,
+    0xbe27617c99bec2be,
+    0xcdeb87de26cd13cd,
+    0x348968e4bd34d034,
+    0x483290757a483d48,
+    0xff54e324abffdbff,
+    0x7a8df48ff77af57a,
+    0x90643deaf4907a90,
+    0x5f9dbe3ec25f615f,
+    0x203d40a01d208020,
+    0x680fd0d56768bd68,
+    0x1aca3472d01a681a,
+    0xaeb7412c19ae82ae,
+    0xb47d755ec9b4eab4,
+    0x54cea8199a544d54,
+    0x937f3be5ec937693,
+    0x222f44aa0d228822,
+    0x6463c8e907648d64,
+    0xf12aff12dbf1e3f1,
+    0x73cce6a2bf73d173,
+    0x1282245a90124812,
+    0x407a805d3a401d40,
+    0x0848102840082008,
+    0xc3959be856c32bc3,
+    0xecdfc57b33ec97ec,
+    0xdb4dab9096db4bdb,
+    0xa1c05f1f61a1bea1,
+    0x8d9107831c8d0e8d,
+    0x3dc87ac9f53df43d,
+    0x975b33f1cc976697,
     0x0000000000000000,
-    0xcf1bcf36d483f9cf,
-    0x2bac2b4587566e2b,
-    0x76c57697b3ece176,
-    0x82328264b019e682,
-    0xd67fd6fea9b128d6,
-    0x1b6c1bd87736c31b,
-    0xb5eeb5c15b7774b5,
-    0xaf86af112943beaf,
-    0x6ab56a77dfd41d6a,
-    0x505d50ba0da0ea50,
-    0x450945124c8a5745,
-    0xf3ebf3cb18fb38f3,
-    0x30c0309df060ad30,
-    0xef9bef2b74c3c4ef,
-    0x3ffc3fe5c37eda3f,
-    0x554955921caac755,
-    0xa2b2a2791059dba2,
-    0xea8fea0365c9e9ea,
-    0x6589650fecca6a65,
-    0xbad2bab9686903ba,
-    0x2fbc2f65935e4a2f,
-    0xc027c04ee79d8ec0,
-    0xde5fdebe81a160de,
-    0x1c701ce06c38fc1c,
-    0xfdd3fdbb2ee746fd,
-    0x4d294d52649a1f4d,
-    0x927292e4e0397692,
-    0x75c9758fbceafa75,
-    0x061806301e0c3606,
-    0x8a128a249809ae8a,
-    0xb2f2b2f940794bb2,
-    0xe6bfe66359d185e6,
-    0x0e380e70361c7e0e,
-    0x1f7c1ff8633ee71f,
-    0x62956237f7c45562,
-    0xd477d4eea3b53ad4,
-    0xa89aa829324d81a8,
-    0x966296c4f4315296,
-    0xf9c3f99b3aef62f9,
-    0xc533c566f697a3c5,
-    0x25942535b14a1025,
-    0x597959f220b2ab59,
-    0x842a8454ae15d084,
-    0x72d572b7a7e4c572,
-    0x39e439d5dd72ec39,
-    0x4c2d4c5a6198164c,
-    0x5e655eca3bbc945e,
-    0x78fd78e785f09f78,
-    0x38e038ddd870e538,
-    0x8c0a8c148605988c,
-    0xd163d1c6b2bf17d1,
-    0xa5aea5410b57e4a5,
-    0xe2afe2434dd9a1e2,
-    0x6199612ff8c24e61,
-    0xb3f6b3f1457b42b3,
-    0x21842115a5423421,
-    0x9c4a9c94d625089c,
-    0x1e781ef0663cee1e,
-    0x4311432252866143,
-    0xc73bc776fc93b1c7,
-    0xfcd7fcb32be54ffc,
-    0x0410042014082404,
-    0x515951b208a2e351,
-    0x995e99bcc72f2599,
-    0x6da96d4fc4da226d,
-    0x0d340d68391a650d,
-    0xfacffa8335e979fa,
-    0xdf5bdfb684a369df,
-    0x7ee57ed79bfca97e,
-    0x2490243db4481924,
-    0x3bec3bc5d776fe3b,
-    0xab96ab313d4b9aab,
-    0xce1fce3ed181f0ce,
-    0x1144118855229911,
-    0x8f068f0c8903838f,
-    0x4e254e4a6b9c044e,
-    0xb7e6b7d1517366b7,
-    0xeb8beb0b60cbe0eb,
-    0x3cf03cfdcc78c13c,
-    0x813e817cbf1ffd81,
-    0x946a94d4fe354094,
-    0xf7fbf7eb0cf31cf7,
-    0xb9deb9a1676f18b9,
-    0x134c13985f268b13,
-    0x2cb02c7d9c58512c,
-    0xd36bd3d6b8bb05d3,
-    0xe7bbe76b5cd38ce7,
-    0x6ea56e57cbdc396e,
-    0xc437c46ef395aac4,
-    0x030c03180f061b03,
-    0x5645568a13acdc56,
-    0x440d441a49885e44,
-    0x7fe17fdf9efea07f,
-    0xa99ea921374f88a9,
-    0x2aa82a4d8254672a,
-    0xbbd6bbb16d6b0abb,
-    0xc123c146e29f87c1,
-    0x535153a202a6f153,
-    0xdc57dcae8ba572dc,
-    0x0b2c0b582716530b,
-    0x9d4e9d9cd327019d,
-    0x6cad6c47c1d82b6c,
-    0x31c43195f562a431,
-    0x74cd7487b9e8f374,
-    0xf6fff6e309f115f6,
-    0x4605460a438c4c46,
-    0xac8aac092645a5ac,
-    0x891e893c970fb589,
-    0x145014a04428b414,
-    0xe1a3e15b42dfbae1,
-    0x165816b04e2ca616,
-    0x3ae83acdd274f73a,
-    0x69b9696fd0d20669,
-    0x092409482d124109,
-    0x70dd70a7ade0d770,
-    0xb6e2b6d954716fb6,
-    0xd067d0ceb7bd1ed0,
-    0xed93ed3b7ec7d6ed,
-    0xcc17cc2edb85e2cc,
-    0x4215422a57846842,
-    0x985a98b4c22d2c98,
-    0xa4aaa4490e55eda4,
-    0x28a0285d88507528,
-    0x5c6d5cda31b8865c,
-    0xf8c7f8933fed6bf8,
-    0x86228644a411c286,
+    0xcff983d436cf1bcf,
+    0x2b6e5687452bac2b,
+    0x76e1ecb39776c576,
+    0x82e619b064823282,
+    0xd628b1a9fed67fd6,
+    0x1bc33677d81b6c1b,
+    0xb574775bc1b5eeb5,
+    0xafbe432911af86af,
+    0x6a1dd4df776ab56a,
+    0x50eaa00dba505d50,
+    0x45578a4c12450945,
+    0xf338fb18cbf3ebf3,
+    0x30ad60f09d30c030,
+    0xefc4c3742bef9bef,
+    0x3fda7ec3e53ffc3f,
+    0x55c7aa1c92554955,
+    0xa2db591079a2b2a2,
+    0xeae9c96503ea8fea,
+    0x656acaec0f658965,
+    0xba036968b9bad2ba,
+    0x2f4a5e93652fbc2f,
+    0xc08e9de74ec027c0,
+    0xde60a181bede5fde,
+    0x1cfc386ce01c701c,
+    0xfd46e72ebbfdd3fd,
+    0x4d1f9a64524d294d,
+    0x927639e0e4927292,
+    0x75faeabc8f75c975,
+    0x06360c1e30061806,
+    0x8aae0998248a128a,
+    0xb24b7940f9b2f2b2,
+    0xe685d15963e6bfe6,
+    0x0e7e1c36700e380e,
+    0x1fe73e63f81f7c1f,
+    0x6255c4f737629562,
+    0xd43ab5a3eed477d4,
+    0xa8814d3229a89aa8,
+    0x965231f4c4966296,
+    0xf962ef3a9bf9c3f9,
+    0xc5a397f666c533c5,
+    0x25104ab135259425,
+    0x59abb220f2597959,
+    0x84d015ae54842a84,
+    0x72c5e4a7b772d572,
+    0x39ec72ddd539e439,
+    0x4c1698615a4c2d4c,
+    0x5e94bc3bca5e655e,
+    0x789ff085e778fd78,
+    0x38e570d8dd38e038,
+    0x8c980586148c0a8c,
+    0xd117bfb2c6d163d1,
+    0xa5e4570b41a5aea5,
+    0xe2a1d94d43e2afe2,
+    0x614ec2f82f619961,
+    0xb3427b45f1b3f6b3,
+    0x213442a515218421,
+    0x9c0825d6949c4a9c,
+    0x1eee3c66f01e781e,
+    0x4361865222431143,
+    0xc7b193fc76c73bc7,
+    0xfc4fe52bb3fcd7fc,
+    0x0424081420041004,
+    0x51e3a208b2515951,
+    0x99252fc7bc995e99,
+    0x6d22dac44f6da96d,
+    0x0d651a39680d340d,
+    0xfa79e93583facffa,
+    0xdf69a384b6df5bdf,
+    0x7ea9fc9bd77ee57e,
+    0x241948b43d249024,
+    0x3bfe76d7c53bec3b,
+    0xab9a4b3d31ab96ab,
+    0xcef081d13ece1fce,
+    0x1199225588114411,
+    0x8f8303890c8f068f,
+    0x4e049c6b4a4e254e,
+    0xb7667351d1b7e6b7,
+    0xebe0cb600beb8beb,
+    0x3cc178ccfd3cf03c,
+    0x81fd1fbf7c813e81,
+    0x944035fed4946a94,
+    0xf71cf30cebf7fbf7,
+    0xb9186f67a1b9deb9,
+    0x138b265f98134c13,
+    0x2c51589c7d2cb02c,
+    0xd305bbb8d6d36bd3,
+    0xe78cd35c6be7bbe7,
+    0x6e39dccb576ea56e,
+    0xc4aa95f36ec437c4,
+    0x031b060f18030c03,
+    0x56dcac138a564556,
+    0x445e88491a440d44,
+    0x7fa0fe9edf7fe17f,
+    0xa9884f3721a99ea9,
+    0x2a6754824d2aa82a,
+    0xbb0a6b6db1bbd6bb,
+    0xc1879fe246c123c1,
+    0x53f1a602a2535153,
+    0xdc72a58baedc57dc,
+    0x0b531627580b2c0b,
+    0x9d0127d39c9d4e9d,
+    0x6c2bd8c1476cad6c,
+    0x31a462f59531c431,
+    0x74f3e8b98774cd74,
+    0xf615f109e3f6fff6,
+    0x464c8c430a460546,
+    0xaca5452609ac8aac,
+    0x89b50f973c891e89,
+    0x14b42844a0145014,
+    0xe1badf425be1a3e1,
+    0x16a62c4eb0165816,
+    0x3af774d2cd3ae83a,
+    0x6906d2d06f69b969,
+    0x0941122d48092409,
+    0x70d7e0ada770dd70,
+    0xb66f7154d9b6e2b6,
+    0xd01ebdb7ced067d0,
+    0xedd6c77e3bed93ed,
+    0xcce285db2ecc17cc,
+    0x426884572a421542,
+    0x982c2dc2b4985a98,
+    0xa4ed550e49a4aaa4,
+    0x287550885d28a028,
+    0x5c86b831da5c6d5c,
+    0xf86bed3f93f8c7f8,
+    0x86c211a444862286,
 ];
diff --git a/whirlpool/src/lib.rs b/whirlpool/src/lib.rs
index e2e313f66..54197e8f2 100644
--- a/whirlpool/src/lib.rs
+++ b/whirlpool/src/lib.rs
@@ -37,163 +37,140 @@
 
 #![no_std]
 #![doc(
-    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
-    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
+    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
+    html_root_url = "https://docs.rs/whirlpool/0.10.0"
 )]
-#![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
-#[cfg(feature = "std")]
-extern crate std;
-
-#[cfg(feature = "asm")]
-use whirlpool_asm as utils;
+pub use digest::{self, Digest};
 
 #[cfg(not(feature = "asm"))]
-mod utils;
-
-#[cfg(not(feature = "asm"))]
-mod consts;
-
-pub use digest::Digest;
+mod compress;
 
-use crate::utils::compress;
+#[cfg(feature = "asm")]
+use whirlpool_asm as compress;
+
+use compress::compress;
+
+use core::{fmt, slice::from_ref};
+use digest::{
+    block_buffer::Eager,
+    core_api::{
+        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
+        OutputSizeUser, Reset, UpdateCore,
+    },
+    generic_array::typenum::{Unsigned, U64},
+    HashMarker, Output,
+};
+
+/// Core Whirlpool hasher state.
+#[derive(Clone)]
+pub struct WhirlpoolCore {
+    bit_len: [u64; 4],
+    state: [u64; 8],
+}
 
-use block_buffer::{block_padding::Iso7816, BlockBuffer};
-use digest::{consts::U64, generic_array::GenericArray};
-use digest::{BlockInput, FixedOutputDirty, Reset, Update};
+impl HashMarker for WhirlpoolCore {}
 
-type BlockSize = U64;
+impl BlockSizeUser for WhirlpoolCore {
+    type BlockSize = U64;
+}
 
-/// Structure representing the state of a Whirlpool computation
-#[derive(Clone)]
-pub struct Whirlpool {
-    bit_length: [u8; 32],
-    buffer: BlockBuffer<U64>,
-    #[cfg(not(feature = "asm"))]
-    hash: [u64; 8],
-    #[cfg(feature = "asm")]
-    hash: [u8; 64],
+impl BufferKindUser for WhirlpoolCore {
+    type BufferKind = Eager;
 }
 
-impl Default for Whirlpool {
-    fn default() -> Self {
-        Self {
-            bit_length: [0u8; 32],
-            buffer: BlockBuffer::default(),
-            #[cfg(not(feature = "asm"))]
-            hash: [0u64; 8],
-            #[cfg(feature = "asm")]
-            hash: [0u8; 64],
-        }
-    }
+impl OutputSizeUser for WhirlpoolCore {
+    type OutputSize = U64;
 }
 
-fn convert(block: &GenericArray<u8, U64>) -> &[u8; 64] {
-    #[allow(unsafe_code)]
-    unsafe {
-        &*(block.as_ptr() as *const [u8; 64])
+impl UpdateCore for WhirlpoolCore {
+    #[inline]
+    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
+        let block_bits = 8 * BLOCK_SIZE as u64;
+        self.update_len(block_bits * (blocks.len() as u64));
+        compress(&mut self.state, convert(blocks));
     }
 }
 
-impl Whirlpool {
-    #![cfg_attr(
-        feature = "cargo-clippy",
-        allow(clippy::identity_op, clippy::double_parens)
-    )]
-    fn update_len(&mut self, len: u64) {
-        let len_bits = [
-            (len >> (56 + 5)) as u8,
-            ((len >> (48 + 5)) & 0xff) as u8,
-            ((len >> (40 + 5)) & 0xff) as u8,
-            ((len >> (32 + 5)) & 0xff) as u8,
-            ((len >> (24 + 5)) & 0xff) as u8,
-            ((len >> (16 + 5)) & 0xff) as u8,
-            ((len >> (8 + 5)) & 0xff) as u8,
-            ((len >> (0 + 5)) & 0xff) as u8,
-            ((len << 3) & 0xff) as u8,
-        ];
-
-        let mut carry = false;
-        for i in 0..32 {
-            let mut x = u16::from(self.bit_length[self.bit_length.len() - i - 1]);
-
-            if i < len_bits.len() {
-                x += u16::from(len_bits[len_bits.len() - i - 1]);
-            } else if !carry {
-                break;
-            }
-
-            if carry {
-                x += 1;
-            }
-
-            carry = x > 0xff;
-            let pos = self.bit_length.len() - i - 1;
-            self.bit_length[pos] = (x & 0xff) as u8;
-        }
-    }
+impl FixedOutputCore for WhirlpoolCore {
+    #[inline]
+    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
+        let pos = buffer.get_pos();
+        self.update_len(8 * pos as u64);
 
-    fn finalize_inner(&mut self) {
-        // padding
-        let hash = &mut self.hash;
-        let pos = self.buffer.position();
-        let buf = self
-            .buffer
-            .pad_with::<Iso7816>()
-            .expect("we never use input_lazy");
-
-        if pos + 1 > self.bit_length.len() {
-            compress(hash, convert(buf));
-            buf[..=pos].iter_mut().for_each(|b| *b = 0);
+        let mut buf = [0u8; 4 * 8];
+        for (chunk, v) in buf.chunks_exact_mut(8).zip(self.bit_len.iter()) {
+            chunk.copy_from_slice(&v.to_be_bytes());
         }
 
-        buf[32..].copy_from_slice(&self.bit_length);
-        compress(hash, convert(buf));
+        let mut state = self.state;
+        buffer.digest_pad(0x80, &buf, |block| {
+            compress(&mut state, convert(from_ref(block)))
+        });
+
+        for (chunk, v) in out.chunks_exact_mut(8).zip(state.iter()) {
+            chunk.copy_from_slice(&v.to_le_bytes());
+        }
     }
 }
 
-impl BlockInput for Whirlpool {
-    type BlockSize = BlockSize;
+impl WhirlpoolCore {
+    fn update_len(&mut self, len: u64) {
+        let mut carry = 0;
+        adc(&mut self.bit_len[3], len, &mut carry);
+        adc(&mut self.bit_len[2], 0, &mut carry);
+        adc(&mut self.bit_len[1], 0, &mut carry);
+        adc(&mut self.bit_len[0], 0, &mut carry);
+    }
 }
 
-impl Update for Whirlpool {
-    fn update(&mut self, input: impl AsRef<[u8]>) {
-        let input = input.as_ref();
-        self.update_len(input.len() as u64);
-        let hash = &mut self.hash;
-        self.buffer
-            .input_block(input, |b| compress(hash, convert(b)));
+impl Default for WhirlpoolCore {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            bit_len: Default::default(),
+            state: [0u64; 8],
+        }
     }
 }
 
-impl FixedOutputDirty for Whirlpool {
-    type OutputSize = U64;
-
-    #[cfg(not(feature = "asm"))]
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, U64>) {
-        self.finalize_inner();
-        for (chunk, v) in out.chunks_exact_mut(8).zip(self.hash.iter()) {
-            chunk.copy_from_slice(&v.to_be_bytes());
-        }
+impl Reset for WhirlpoolCore {
+    #[inline]
+    fn reset(&mut self) {
+        *self = Default::default();
     }
+}
 
-    #[cfg(feature = "asm")]
-    fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, U64>) {
-        self.finalize_inner();
-        out.copy_from_slice(&self.hash)
+impl AlgorithmName for WhirlpoolCore {
+    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("Whirlpool")
     }
 }
 
-impl Reset for Whirlpool {
-    fn reset(&mut self) {
-        self.bit_length = [0u8; 32];
-        self.buffer.reset();
-        for v in self.hash.iter_mut() {
-            *v = 0;
-        }
+impl fmt::Debug for WhirlpoolCore {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("WhirlpoolCore { ... }")
     }
 }
 
-opaque_debug::implement!(Whirlpool);
-digest::impl_write!(Whirlpool);
+/// Whirlpool hasher state.
+pub type Whirlpool = CoreWrapper<WhirlpoolCore>;
+
+#[inline(always)]
+fn adc(a: &mut u64, b: u64, carry: &mut u64) {
+    let ret = (*a as u128) + (b as u128) + (*carry as u128);
+    *a = ret as u64;
+    *carry = (ret >> 64) as u64;
+}
+
+const BLOCK_SIZE: usize = <WhirlpoolCore as BlockSizeUser>::BlockSize::USIZE;
+
+#[inline(always)]
+fn convert(blocks: &[Block<WhirlpoolCore>]) -> &[[u8; BLOCK_SIZE]] {
+    // SAFETY: GenericArray<u8, U64> and [u8; 64] have
+    // exactly the same memory layout
+    let p = blocks.as_ptr() as *const [u8; BLOCK_SIZE];
+    unsafe { core::slice::from_raw_parts(p, blocks.len()) }
+}
diff --git a/whirlpool/src/utils.rs b/whirlpool/src/utils.rs
deleted file mode 100644
index 53312ce7f..000000000
--- a/whirlpool/src/utils.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use crate::consts::*;
-use core::convert::TryInto;
-
-pub fn compress(hash: &mut [u64; 8], buffer: &[u8; 64]) {
-    let mut k = [0u64; 8];
-    let mut block = [0u64; 8];
-    let mut state = [0u64; 8];
-    let mut l = [0u64; 8];
-
-    for (o, chunk) in block.iter_mut().zip(buffer.chunks_exact(8)) {
-        *o = u64::from_be_bytes(chunk.try_into().unwrap());
-    }
-    k.copy_from_slice(hash);
-
-    for i in 0..8 {
-        state[i] = block[i] ^ k[i];
-    }
-
-    #[allow(clippy::needless_range_loop)]
-    for r in 1..=R {
-        for i in 0..8 {
-            l[i] = C0[(k[(i) % 8] >> 56) as usize]
-                ^ C1[((k[(7 + i) % 8] >> 48) & 0xff) as usize]
-                ^ C2[((k[(6 + i) % 8] >> 40) & 0xff) as usize]
-                ^ C3[((k[(5 + i) % 8] >> 32) & 0xff) as usize]
-                ^ C4[((k[(4 + i) % 8] >> 24) & 0xff) as usize]
-                ^ C5[((k[(3 + i) % 8] >> 16) & 0xff) as usize]
-                ^ C6[((k[(2 + i) % 8] >> 8) & 0xff) as usize]
-                ^ C7[((k[(1 + i) % 8]) & 0xff) as usize]
-                ^ if i == 0 { RC[r] } else { 0 };
-        }
-        k = l;
-        for i in 0..8 {
-            l[i] = C0[(state[(i) % 8] >> 56) as usize]
-                ^ C1[((state[(7 + i) % 8] >> 48) & 0xff) as usize]
-                ^ C2[((state[(6 + i) % 8] >> 40) & 0xff) as usize]
-                ^ C3[((state[(5 + i) % 8] >> 32) & 0xff) as usize]
-                ^ C4[((state[(4 + i) % 8] >> 24) & 0xff) as usize]
-                ^ C5[((state[(3 + i) % 8] >> 16) & 0xff) as usize]
-                ^ C6[((state[(2 + i) % 8] >> 8) & 0xff) as usize]
-                ^ C7[((state[(1 + i) % 8]) & 0xff) as usize]
-                ^ k[i];
-        }
-        state = l;
-    }
-
-    for i in 0..8 {
-        hash[i] ^= state[i] ^ block[i];
-    }
-}


Test patch:
diff --git a/blake2/tests/data/blake2b/fixed.blb b/blake2/tests/data/blake2b/fixed.blb
index 39d419209..c999b74c6 100644
Binary files a/blake2/tests/data/blake2b/fixed.blb and b/blake2/tests/data/blake2b/fixed.blb differ
diff --git a/blake2/tests/data/blake2b/mac.blb b/blake2/tests/data/blake2b/mac.blb
index 46522340d..eaa6c0395 100644
Binary files a/blake2/tests/data/blake2b/mac.blb and b/blake2/tests/data/blake2b/mac.blb differ
diff --git a/blake2/tests/data/blake2b/variable.blb b/blake2/tests/data/blake2b/variable.blb
index 5d150c024..24ade82aa 100644
Binary files a/blake2/tests/data/blake2b/variable.blb and b/blake2/tests/data/blake2b/variable.blb differ
diff --git a/blake2/tests/data/blake2s/mac.blb b/blake2/tests/data/blake2s/mac.blb
index 2c5f3c67c..9b485edfc 100644
Binary files a/blake2/tests/data/blake2s/mac.blb and b/blake2/tests/data/blake2s/mac.blb differ
diff --git a/blake2/tests/data/blake2s/variable.blb b/blake2/tests/data/blake2s/variable.blb
index 0412b92de..93d4d42ec 100644
Binary files a/blake2/tests/data/blake2s/variable.blb and b/blake2/tests/data/blake2s/variable.blb differ
diff --git a/blake2/tests/lib.rs b/blake2/tests/lib.rs
deleted file mode 100644
index ba42b9ffd..000000000
--- a/blake2/tests/lib.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, variable_test};
-use digest::new_test;
-
-new_test!(blake2b_fixed, "blake2b/fixed", blake2::Blake2b, digest_test);
-new_test!(
-    blake2b_variable,
-    "blake2b/variable",
-    blake2::VarBlake2b,
-    variable_test
-);
-new_test!(
-    blake2s_variable,
-    "blake2s/variable",
-    blake2::VarBlake2s,
-    variable_test
-);
diff --git a/blake2/tests/mac.rs b/blake2/tests/mac.rs
index 833c563e3..ec35a4cc0 100644
--- a/blake2/tests/mac.rs
+++ b/blake2/tests/mac.rs
@@ -1,6 +1,7 @@
-#![no_std]
+#[cfg(not(feature = "reset"))]
+use digest::new_mac_test as new_test;
+#[cfg(feature = "reset")]
+use digest::new_resettable_mac_test as new_test;
 
-use crypto_mac::new_test;
-
-new_test!(blake2b_mac, "blake2b/mac", blake2::Blake2b);
-new_test!(blake2s_mac, "blake2s/mac", blake2::Blake2s);
+new_test!(blake2b_mac, "blake2b/mac", blake2::Blake2bMac512);
+new_test!(blake2s_mac, "blake2s/mac", blake2::Blake2sMac256);
diff --git a/blake2/tests/mod.rs b/blake2/tests/mod.rs
new file mode 100644
index 000000000..010788007
--- /dev/null
+++ b/blake2/tests/mod.rs
@@ -0,0 +1,19 @@
+#[cfg(feature = "reset")]
+use digest::dev::{fixed_reset_test as fixed_fn, variable_reset_test as varaible_fn};
+#[cfg(not(feature = "reset"))]
+use digest::dev::{fixed_test as fixed_fn, variable_test as varaible_fn};
+use digest::new_test;
+
+new_test!(blake2b_fixed, "blake2b/fixed", blake2::Blake2b512, fixed_fn,);
+new_test!(
+    blake2b_variable,
+    "blake2b/variable",
+    blake2::Blake2bVar,
+    varaible_fn,
+);
+new_test!(
+    blake2s_variable,
+    "blake2s/variable",
+    blake2::Blake2sVar,
+    varaible_fn,
+);
diff --git a/blake2/tests/persona.rs b/blake2/tests/persona.rs
index 4defcc801..49e6574e9 100644
--- a/blake2/tests/persona.rs
+++ b/blake2/tests/persona.rs
@@ -1,23 +1,40 @@
-use blake2::{Blake2b, Blake2s, Digest};
+use blake2::{digest::FixedOutput, Blake2bMac512, Blake2sMac256};
 use hex_literal::hex;
 
 #[test]
+#[rustfmt::skip]
 fn blake2s_persona() {
-    let key_bytes = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
-    let persona = "personal";
-    let persona_bytes = persona.as_bytes();
-    let ctx = Blake2s::with_params(&key_bytes, &[], persona_bytes);
+    let key= hex!("
+        000102030405060708090a0b0c0d0e0f
+        101112131415161718191a1b1c1d1e1f
+    ");
+    let persona = b"personal";
+    let ctx = Blake2sMac256::new_with_salt_and_personal(&key, &[], persona).unwrap();
     assert_eq!(
-        ctx.finalize().as_slice(),
-        &hex!("25a4ee63b594aed3f88a971e1877ef7099534f9097291f88fb86c79b5e70d022")[..]
+        ctx.finalize_fixed()[..],
+        hex!("
+            25a4ee63b594aed3f88a971e1877ef70
+            99534f9097291f88fb86c79b5e70d022
+        ")[..],
     );
 }
 
 #[test]
+#[rustfmt::skip]
 fn blake2b_persona() {
-    let key_bytes = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
-    let persona = "personal";
-    let persona_bytes = persona.as_bytes();
-    let ctx = Blake2b::with_params(&key_bytes, &[], persona_bytes);
-    assert_eq!(ctx.finalize().as_slice(), &hex!("03de3b295dcfc3b25b05abb09bc95fe3e9ff3073638badc68101d1e42019d0771dd07525a3aae8318e92c5e5d967ba92e4810d0021d7bf3b49da0b4b4a8a4e1f")[..]);
+    let key = hex!("
+        000102030405060708090a0b0c0d0e0f
+        101112131415161718191a1b1c1d1e1f
+    ");
+    let persona = b"personal";
+    let ctx = Blake2bMac512::new_with_salt_and_personal(&key, &[], persona).unwrap();
+    assert_eq!(
+        ctx.finalize_fixed()[..],
+        hex!("
+            03de3b295dcfc3b25b05abb09bc95fe3
+            e9ff3073638badc68101d1e42019d077
+            1dd07525a3aae8318e92c5e5d967ba92
+            e4810d0021d7bf3b49da0b4b4a8a4e1f
+        ")[..],
+    );
 }
diff --git a/fsb/tests/data/fsb160.blb b/fsb/tests/data/fsb160.blb
index deecdfec3..7ea2346e7 100644
Binary files a/fsb/tests/data/fsb160.blb and b/fsb/tests/data/fsb160.blb differ
diff --git a/fsb/tests/data/fsb224.blb b/fsb/tests/data/fsb224.blb
index 57ca2febc..9a476c723 100644
Binary files a/fsb/tests/data/fsb224.blb and b/fsb/tests/data/fsb224.blb differ
diff --git a/fsb/tests/data/fsb256.blb b/fsb/tests/data/fsb256.blb
index 0caef2731..6e3e4672a 100644
Binary files a/fsb/tests/data/fsb256.blb and b/fsb/tests/data/fsb256.blb differ
diff --git a/fsb/tests/data/fsb384.blb b/fsb/tests/data/fsb384.blb
index 65cc572e6..60ce430d1 100644
Binary files a/fsb/tests/data/fsb384.blb and b/fsb/tests/data/fsb384.blb differ
diff --git a/fsb/tests/data/fsb512.blb b/fsb/tests/data/fsb512.blb
index fa6c7b3d9..f20de348a 100644
Binary files a/fsb/tests/data/fsb512.blb and b/fsb/tests/data/fsb512.blb differ
diff --git a/fsb/tests/lib.rs b/fsb/tests/lib.rs
deleted file mode 100644
index f44d6c9e3..000000000
--- a/fsb/tests/lib.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use digest::dev::digest_test;
-use digest::new_test;
-
-new_test!(fsb160_main, "fsb160", fsb::Fsb160, digest_test);
-new_test!(fsb224_main, "fsb224", fsb::Fsb224, digest_test);
-new_test!(fsb256_main, "fsb256", fsb::Fsb256, digest_test);
-new_test!(fsb384_main, "fsb384", fsb::Fsb384, digest_test);
-new_test!(fsb512_main, "fsb512", fsb::Fsb512, digest_test);
diff --git a/fsb/tests/mod.rs b/fsb/tests/mod.rs
new file mode 100644
index 000000000..bf9749450
--- /dev/null
+++ b/fsb/tests/mod.rs
@@ -0,0 +1,68 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use fsb::{Digest, Fsb160, Fsb224, Fsb256, Fsb384, Fsb512};
+use hex_literal::hex;
+
+new_test!(fsb160_main, "fsb160", Fsb160, fixed_reset_test);
+new_test!(fsb224_main, "fsb224", Fsb224, fixed_reset_test);
+new_test!(fsb256_main, "fsb256", Fsb256, fixed_reset_test);
+new_test!(fsb384_main, "fsb384", Fsb384, fixed_reset_test);
+new_test!(fsb512_main, "fsb512", Fsb512, fixed_reset_test);
+
+#[test]
+fn fsb160_rand() {
+    let mut h = Fsb160::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("454b28a8d158ad63ff59e3f761919c7581ee78d3")[..]
+    );
+}
+
+#[test]
+fn fsb224_rand() {
+    let mut h = Fsb224::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("80ef345c462dc88261355eaf44ee2bb7277d01db77b46b2828a918b6")[..]
+    );
+}
+
+#[test]
+fn fsb256_rand() {
+    let mut h = Fsb256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("301cbfd7031de3568bf4c4ffa86c2295bde89937acc8ee470446b8c55b88334a")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn fsb384_rand() {
+    let mut h = Fsb384::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            d11c0ea4ef363916ad8c2a4d8b4758bf0c36e4de93f2bbaeba037b0726c83179
+            0ec4e5d9d3e9d66e0810d391a00bf60e
+        ")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn fsb512_rand() {
+    let mut h = Fsb512::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            eb15b6c3626e38141e4f17b3b89d7deed007c4ae727452010601bc4e16deef82
+            f81415566defb1aba3db9b1b14746bd81cf3689a0f79e6d00434ff4ca19b3e66
+        ")[..]
+    );
+}
diff --git a/gost94/examples/gost94_test_sum.rs b/gost94/examples/gost94_test_sum.rs
deleted file mode 100644
index 8f31b1906..000000000
--- a/gost94/examples/gost94_test_sum.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use gost94::{Digest, Gost94Test};
-use std::env;
-use std::fs;
-use std::io::{self, Read};
-
-const BUFFER_SIZE: usize = 1024;
-
-/// Print digest result as hex string and name pair
-fn print_result(sum: &[u8], name: &str) {
-    for byte in sum {
-        print!("{:02x}", byte);
-    }
-    println!("\t{}", name);
-}
-
-/// Compute digest value for given `Reader` and print it
-/// On any error simply return without doing anything
-fn process<D: Digest + Default, R: Read>(reader: &mut R, name: &str) {
-    let mut sh = D::default();
-    let mut buffer = [0u8; BUFFER_SIZE];
-    loop {
-        let n = match reader.read(&mut buffer) {
-            Ok(n) => n,
-            Err(_) => return,
-        };
-        sh.update(&buffer[..n]);
-        if n == 0 || n < BUFFER_SIZE {
-            break;
-        }
-    }
-    print_result(&sh.finalize(), name);
-}
-
-fn main() {
-    let args = env::args();
-    // Process files listed in command line arguments one by one
-    // If no files provided process input from stdin
-    if args.len() > 1 {
-        for path in args.skip(1) {
-            if let Ok(mut file) = fs::File::open(&path) {
-                process::<Gost94Test, _>(&mut file, &path);
-            }
-        }
-    } else {
-        process::<Gost94Test, _>(&mut io::stdin(), "-");
-    }
-}
diff --git a/gost94/src/test_param.rs b/gost94/src/test_param.rs
deleted file mode 100644
index f1b202f6e..000000000
--- a/gost94/src/test_param.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-const S_TEST: SBox = [
-    [4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],
-    [14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],
-    [5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],
-    [7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],
-    [6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],
-    [4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],
-    [13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],
-    [1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12],
-];
-
-gost94_impl!(Gost94Test, S_TEST);
diff --git a/gost94/tests/data/cryptopro.blb b/gost94/tests/data/cryptopro.blb
index 8ff577b11..ace31477a 100644
Binary files a/gost94/tests/data/cryptopro.blb and b/gost94/tests/data/cryptopro.blb differ
diff --git a/gost94/tests/data/cryptopro_one_million_a.bin b/gost94/tests/data/cryptopro_one_million_a.bin
deleted file mode 100644
index e69abf93d..000000000
--- a/gost94/tests/data/cryptopro_one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-��(z�/�x��1.��klNJA����'�UO
\ No newline at end of file
diff --git a/gost94/tests/data/test.blb b/gost94/tests/data/test.blb
index 12fcc2b56..94f08fe9a 100644
Binary files a/gost94/tests/data/test.blb and b/gost94/tests/data/test.blb differ
diff --git a/gost94/tests/data/test_one_million_a.bin b/gost94/tests/data/test_one_million_a.bin
deleted file mode 100644
index 7b1773c6f..000000000
Binary files a/gost94/tests/data/test_one_million_a.bin and /dev/null differ
diff --git a/gost94/tests/lib.rs b/gost94/tests/mod.rs
similarity index 64%
rename from gost94/tests/lib.rs
rename to gost94/tests/mod.rs
index 8ff2c04db..afafa3794 100644
--- a/gost94/tests/lib.rs
+++ b/gost94/tests/mod.rs
@@ -1,34 +1,41 @@
-use digest::dev::{digest_test, one_million_a};
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
 use digest::new_test;
+use gost94::{Digest, Gost94CryptoPro, Gost94Test};
+use hex_literal::hex;
 
-new_test!(gost94_test_main, "test", gost94::Gost94Test, digest_test);
+new_test!(gost94_test_main, "test", Gost94Test, fixed_reset_test);
 new_test!(
     gost94_cryptopro_main,
     "cryptopro",
-    gost94::Gost94CryptoPro,
-    digest_test
+    Gost94CryptoPro,
+    fixed_reset_test
 );
 
 #[test]
-fn gost94_test_1million_a() {
-    let output = include_bytes!("data/test_one_million_a.bin");
-    one_million_a::<gost94::Gost94Test>(output);
+fn gost94_test_rand() {
+    let mut h = Gost94Test::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("fdd1b9f220898c117f82d664716795e12f5e9f458ee8cd71d014329438db5089")[..]
+    );
 }
 
 #[test]
-fn gost94_cryptopro_1million_a() {
-    let output = include_bytes!("data/cryptopro_one_million_a.bin");
-    one_million_a::<gost94::Gost94CryptoPro>(output);
+fn gost94_cryptopro_rand() {
+    let mut h = Gost94CryptoPro::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("1d539ea8a318df8c13d304fcfd9beeec188bb48683d9d7f4c4a3750cff6ef22a")[..]
+    );
 }
 
 /// Test vectors from:
 /// https://github.com/gost-engine/engine/blob/master/test/01-digest.t
 #[test]
 fn gost_engine_tests() {
-    use digest::Digest;
-    use hex_literal::hex;
-
-    let mut h = gost94::Gost94CryptoPro::new();
+    let mut h = Gost94CryptoPro::new();
     for _ in 0..128 {
         h.update(b"12345670");
     }
@@ -70,9 +77,7 @@ fn gost_engine_tests() {
 
 #[test]
 fn arithmetic_overflow_regression() {
-    use digest::Digest;
-
-    let mut h = gost94::Gost94Test::default();
+    let mut h = Gost94Test::default();
     h.update(&include_bytes!("data/arithmetic_overflow.bin")[..]);
     h.finalize().as_slice();
 }
diff --git a/groestl/tests/data/groestl224.blb b/groestl/tests/data/groestl224.blb
index 090b5473e..1705d414c 100644
Binary files a/groestl/tests/data/groestl224.blb and b/groestl/tests/data/groestl224.blb differ
diff --git a/groestl/tests/data/groestl256.blb b/groestl/tests/data/groestl256.blb
index b24aed4db..122de3971 100644
Binary files a/groestl/tests/data/groestl256.blb and b/groestl/tests/data/groestl256.blb differ
diff --git a/groestl/tests/data/groestl384.blb b/groestl/tests/data/groestl384.blb
index 72174f6a8..cf0e6a152 100644
Binary files a/groestl/tests/data/groestl384.blb and b/groestl/tests/data/groestl384.blb differ
diff --git a/groestl/tests/data/groestl512.blb b/groestl/tests/data/groestl512.blb
index 89b904f19..24ce9ee34 100644
Binary files a/groestl/tests/data/groestl512.blb and b/groestl/tests/data/groestl512.blb differ
diff --git a/groestl/tests/lib.rs b/groestl/tests/lib.rs
deleted file mode 100755
index 0a88e5d3d..000000000
--- a/groestl/tests/lib.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-#![no_std]
-
-use digest::{dev::digest_test, new_test};
-
-new_test!(
-    groestl_224_main,
-    "groestl224",
-    groestl::Groestl224,
-    digest_test
-);
-new_test!(
-    groestl_256_main,
-    "groestl256",
-    groestl::Groestl256,
-    digest_test
-);
-new_test!(
-    groestl_384_main,
-    "groestl384",
-    groestl::Groestl384,
-    digest_test
-);
-new_test!(
-    groestl_512_main,
-    "groestl512",
-    groestl::Groestl512,
-    digest_test
-);
diff --git a/groestl/tests/mod.rs b/groestl/tests/mod.rs
new file mode 100755
index 000000000..41a5cf261
--- /dev/null
+++ b/groestl/tests/mod.rs
@@ -0,0 +1,57 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use groestl::{Digest, Groestl224, Groestl256, Groestl384, Groestl512};
+use hex_literal::hex;
+
+new_test!(groestl_224_main, "groestl224", Groestl224, fixed_reset_test);
+new_test!(groestl_256_main, "groestl256", Groestl256, fixed_reset_test);
+new_test!(groestl_384_main, "groestl384", Groestl384, fixed_reset_test);
+new_test!(groestl_512_main, "groestl512", Groestl512, fixed_reset_test);
+
+#[test]
+fn groestl224_rand() {
+    let mut h = Groestl224::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("2000744c2f85a7fb4733e97da8db00069dd6defa9186dac3461dfeb8")[..]
+    );
+}
+
+#[test]
+fn groestl256_rand() {
+    let mut h = Groestl256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("aac71c789678f627a6474605322ae98d1647e47f405d00b1461b90ee5f0cfbc4")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn groestl384_rand() {
+    let mut h = Groestl384::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            dab78eea895a6dde0c53dc02fc79c7986f5d6811618ca6e5922f01e8aca9bfeb
+            20ed5eda4130bf0ab474ac0b6f0290f8
+        ")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn groestl512_rand() {
+    let mut h = Groestl512::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            7e4d8257c217c7ae59331126e0f984f145e9789862de7c099675ac29e46424ef
+            e93543974fa7113190d492f607f629a03db35ec5551abcb2785ae145fd3c543f
+        ")[..],
+    );
+}
diff --git a/k12/tests/lib.rs b/k12/tests/lib.rs
deleted file mode 100644
index a71c0a2cd..000000000
--- a/k12/tests/lib.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use core::iter;
-use k12::{
-    digest::{ExtendableOutput, Update},
-    KangarooTwelve,
-};
-
-fn read_bytes<T: AsRef<[u8]>>(s: T) -> Box<[u8]> {
-    fn b(c: u8) -> u8 {
-        match c {
-            b'0'..=b'9' => c - b'0',
-            b'a'..=b'f' => c - b'a' + 10,
-            b'A'..=b'F' => c - b'A' + 10,
-            _ => unreachable!(),
-        }
-    }
-
-    let s = s.as_ref();
-    let mut i = 0;
-    let mut v = Vec::new();
-
-    while i < s.len() {
-        if s[i] == b' ' || s[i] == b'\n' {
-            i += 1;
-            continue;
-        }
-
-        let n = b(s[i]) * 16 + b(s[i + 1]);
-        v.push(n);
-        i += 2;
-    }
-
-    v.into_boxed_slice()
-}
-
-#[test]
-fn empty() {
-    // Source: reference paper
-    assert_eq!(
-        KangarooTwelve::new().chain(b"").finalize_boxed(32),
-        read_bytes(
-            "1a c2 d4 50 fc 3b 42 05 d1 9d a7 bf ca
-                1b 37 51 3c 08 03 57 7a c7 16 7f 06 fe 2c e1 f0 ef 39 e5"
-        )
-    );
-
-    assert_eq!(
-        KangarooTwelve::new().chain(b"").finalize_boxed(64),
-        read_bytes(
-            "1a c2 d4 50 fc 3b 42 05 d1 9d a7 bf ca
-                1b 37 51 3c 08 03 57 7a c7 16 7f 06 fe 2c e1 f0 ef 39 e5 42 69 c0 56 b8 c8 2e
-                48 27 60 38 b6 d2 92 96 6c c0 7a 3d 46 45 27 2e 31 ff 38 50 81 39 eb 0a 71"
-        )
-    );
-
-    assert_eq!(
-        KangarooTwelve::new().chain(b"").finalize_boxed(10032)[10000..],
-        read_bytes(
-            "e8 dc 56 36 42 f7 22 8c 84
-                68 4c 89 84 05 d3 a8 34 79 91 58 c0 79 b1 28 80 27 7a 1d 28 e2 ff 6d"
-        )[..]
-    );
-}
-
-#[test]
-fn pat_m() {
-    let expected = [
-        "2b da 92 45 0e 8b 14 7f 8a 7c b6 29 e7 84 a0 58 ef ca 7c f7
-                d8 21 8e 02 d3 45 df aa 65 24 4a 1f",
-        "6b f7 5f a2 23 91 98 db 47 72 e3 64 78 f8 e1 9b 0f 37 12 05
-                f6 a9 a9 3a 27 3f 51 df 37 12 28 88",
-        "0c 31 5e bc de db f6 14 26 de 7d cf 8f b7 25 d1 e7 46 75 d7
-                f5 32 7a 50 67 f3 67 b1 08 ec b6 7c",
-        "cb 55 2e 2e c7 7d 99 10 70 1d 57 8b 45 7d df 77 2c 12 e3 22
-                e4 ee 7f e4 17 f9 2c 75 8f 0d 59 d0",
-        "87 01 04 5e 22 20 53 45 ff 4d da 05 55 5c bb 5c 3a f1 a7 71
-                c2 b8 9b ae f3 7d b4 3d 99 98 b9 fe",
-        "84 4d 61 09 33 b1 b9 96 3c bd eb 5a e3 b6 b0 5c c7 cb d6 7c
-                ee df 88 3e b6 78 a0 a8 e0 37 16 82",
-        "3c 39 07 82 a8 a4 e8 9f a6 36 7f 72 fe aa f1 32 55 c8 d9 58
-                78 48 1d 3c d8 ce 85 f5 8e 88 0a f8",
-    ];
-    for i in 0..5
-    /*NOTE: can be up to 7 but is slow*/
-    {
-        let len = 17usize.pow(i);
-        let m: Vec<u8> = (0..len).map(|j| (j % 251) as u8).collect();
-        let result = KangarooTwelve::new().chain(&m).finalize_boxed(32);
-        assert_eq!(result, read_bytes(expected[i as usize]));
-    }
-}
-
-#[test]
-fn pat_c() {
-    let expected = [
-        "fa b6 58 db 63 e9 4a 24 61 88 bf 7a f6 9a 13 30 45 f4 6e e9
-                84 c5 6e 3c 33 28 ca af 1a a1 a5 83",
-        "d8 48 c5 06 8c ed 73 6f 44 62 15 9b 98 67 fd 4c 20 b8 08 ac
-                c3 d5 bc 48 e0 b0 6b a0 a3 76 2e c4",
-        "c3 89 e5 00 9a e5 71 20 85 4c 2e 8c 64 67 0a c0 13 58 cf 4c
-                1b af 89 44 7a 72 42 34 dc 7c ed 74",
-        "75 d2 f8 6a 2e 64 45 66 72 6b 4f bc fc 56 57 b9 db cf 07 0c
-                7b 0d ca 06 45 0a b2 91 d7 44 3b cf",
-    ];
-    for i in 0..4 {
-        let m: Vec<u8> = iter::repeat(0xFF).take(2usize.pow(i) - 1).collect();
-        let len = 41usize.pow(i);
-        let c: Vec<u8> = (0..len).map(|j| (j % 251) as u8).collect();
-        let result = KangarooTwelve::new_with_customization(c)
-            .chain(&m)
-            .finalize_boxed(32);
-        assert_eq!(result, read_bytes(expected[i as usize]));
-    }
-}
diff --git a/k12/tests/mod.rs b/k12/tests/mod.rs
new file mode 100644
index 000000000..bd27c4464
--- /dev/null
+++ b/k12/tests/mod.rs
@@ -0,0 +1,75 @@
+use core::iter;
+use hex_literal::hex;
+use k12::{
+    digest::{ExtendableOutput, Update},
+    KangarooTwelve,
+};
+
+fn digest_and_box(data: &[u8], n: usize) -> Box<[u8]> {
+    let mut h = KangarooTwelve::new();
+    h.update(data);
+    h.finalize_boxed(n)
+}
+
+#[test]
+#[rustfmt::skip]
+fn empty() {
+    // Source: reference paper
+    assert_eq!(
+        digest_and_box(b"", 32)[..],
+        hex!("1ac2d450fc3b4205d19da7bfca1b37513c0803577ac7167f06fe2ce1f0ef39e5")[..]
+    );
+
+    assert_eq!(
+        digest_and_box(b"", 64)[..],
+        hex!("
+            1ac2d450fc3b4205d19da7bfca1b37513c0803577ac7167f06fe2ce1f0ef39e5
+            4269c056b8c82e48276038b6d292966cc07a3d4645272e31ff38508139eb0a71
+        ")[..]
+    );
+
+    assert_eq!(
+        digest_and_box(b"", 10032)[10000..],
+        hex!("e8dc563642f7228c84684c898405d3a834799158c079b12880277a1d28e2ff6d")[..]
+    );
+}
+
+#[test]
+fn pat_m() {
+    let expected = [
+        hex!("2bda92450e8b147f8a7cb629e784a058efca7cf7d8218e02d345dfaa65244a1f"),
+        hex!("6bf75fa2239198db4772e36478f8e19b0f371205f6a9a93a273f51df37122888"),
+        hex!("0c315ebcdedbf61426de7dcf8fb725d1e74675d7f5327a5067f367b108ecb67c"),
+        hex!("cb552e2ec77d9910701d578b457ddf772c12e322e4ee7fe417f92c758f0d59d0"),
+        hex!("8701045e22205345ff4dda05555cbb5c3af1a771c2b89baef37db43d9998b9fe"),
+        hex!("844d610933b1b9963cbdeb5ae3b6b05cc7cbd67ceedf883eb678a0a8e0371682"),
+        hex!("3c390782a8a4e89fa6367f72feaaf13255c8d95878481d3cd8ce85f58e880af8"),
+    ];
+    for i in 0..5
+    /*NOTE: can be up to 7 but is slow*/
+    {
+        let len = 17usize.pow(i);
+        let m: Vec<u8> = (0..len).map(|j| (j % 251) as u8).collect();
+        let result = digest_and_box(&m, 32);
+        assert_eq!(result[..], expected[i as usize][..]);
+    }
+}
+
+#[test]
+fn pat_c() {
+    let expected = [
+        hex!("fab658db63e94a246188bf7af69a133045f46ee984c56e3c3328caaf1aa1a583"),
+        hex!("d848c5068ced736f4462159b9867fd4c20b808acc3d5bc48e0b06ba0a3762ec4"),
+        hex!("c389e5009ae57120854c2e8c64670ac01358cf4c1baf89447a724234dc7ced74"),
+        hex!("75d2f86a2e644566726b4fbcfc5657b9dbcf070c7b0dca06450ab291d7443bcf"),
+    ];
+    for i in 0..4 {
+        let m: Vec<u8> = iter::repeat(0xFF).take(2usize.pow(i) - 1).collect();
+        let len = 41usize.pow(i);
+        let c: Vec<u8> = (0..len).map(|j| (j % 251) as u8).collect();
+        let mut h = KangarooTwelve::new_with_customization(c);
+        h.update(&m);
+        let result = h.finalize_boxed(32);
+        assert_eq!(result[..], expected[i as usize][..]);
+    }
+}
diff --git a/md2/tests/data/md2.blb b/md2/tests/data/md2.blb
index 4b03cb78d..986c38e60 100644
Binary files a/md2/tests/data/md2.blb and b/md2/tests/data/md2.blb differ
diff --git a/md2/tests/data/one_million_a.bin b/md2/tests/data/one_million_a.bin
deleted file mode 100644
index b94d13b1d..000000000
--- a/md2/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,2 +0,0 @@
-�
-	�쯕�	S�.�
\ No newline at end of file
diff --git a/md2/tests/lib.rs b/md2/tests/lib.rs
deleted file mode 100644
index 6e3eaea7b..000000000
--- a/md2/tests/lib.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-
-digest::new_test!(md2_main, "md2", md2::Md2, digest_test);
-
-#[test]
-fn md2_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<md2::Md2>(output);
-}
diff --git a/md2/tests/mod.rs b/md2/tests/mod.rs
new file mode 100644
index 000000000..d43f0f4cb
--- /dev/null
+++ b/md2/tests/mod.rs
@@ -0,0 +1,15 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use hex_literal::hex;
+use md2::{Digest, Md2};
+
+digest::new_test!(md2_main, "md2", Md2, fixed_reset_test);
+
+#[test]
+fn md2_rand() {
+    let mut h = Md2::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("f9638c7be725f4d0b5ac342560af1a5b")[..]
+    );
+}
diff --git a/md4/tests/data/md4.blb b/md4/tests/data/md4.blb
index e590934ec..63ad206a5 100644
Binary files a/md4/tests/data/md4.blb and b/md4/tests/data/md4.blb differ
diff --git a/md4/tests/data/one_million_a.bin b/md4/tests/data/one_million_a.bin
deleted file mode 100644
index 4f5878e89..000000000
--- a/md4/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-�΀�k�^\gE�N쩤
\ No newline at end of file
diff --git a/md4/tests/lib.rs b/md4/tests/lib.rs
deleted file mode 100644
index f0fc41c8c..000000000
--- a/md4/tests/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(md4_main, "md4", md4::Md4, digest_test);
-
-#[test]
-fn md4_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<md4::Md4>(output);
-}
diff --git a/md4/tests/mod.rs b/md4/tests/mod.rs
new file mode 100644
index 000000000..e7aa88db5
--- /dev/null
+++ b/md4/tests/mod.rs
@@ -0,0 +1,15 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use hex_literal::hex;
+use md4::{Digest, Md4};
+
+digest::new_test!(md4_main, "md4", Md4, fixed_reset_test);
+
+#[test]
+fn md4_rand() {
+    let mut h = Md4::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("07345abfb6192d85bf6a211381926120")[..]
+    );
+}
diff --git a/md5/tests/data/md5.blb b/md5/tests/data/md5.blb
index 37f86c4c4..3f6d64383 100644
Binary files a/md5/tests/data/md5.blb and b/md5/tests/data/md5.blb differ
diff --git a/md5/tests/data/one_million_a.bin b/md5/tests/data/one_million_a.bin
deleted file mode 100644
index 89c1bba80..000000000
--- a/md5/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-w֮N|p5�)o!
\ No newline at end of file
diff --git a/md5/tests/lib.rs b/md5/tests/lib.rs
deleted file mode 100644
index 9b512f19e..000000000
--- a/md5/tests/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(md5_main, "md5", md5::Md5, digest_test);
-
-#[test]
-fn md5_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<md5::Md5>(output);
-}
diff --git a/md5/tests/mod.rs b/md5/tests/mod.rs
new file mode 100644
index 000000000..316cb8fbe
--- /dev/null
+++ b/md5/tests/mod.rs
@@ -0,0 +1,15 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use hex_literal::hex;
+use md5::{Digest, Md5};
+
+digest::new_test!(md5_main, "md5", md5::Md5, fixed_reset_test);
+
+#[test]
+fn md5_rand() {
+    let mut h = Md5::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("61aec26f1b909578ef638ae02dac0977")[..]
+    );
+}
diff --git a/ripemd/tests/data/ripemd160.blb b/ripemd/tests/data/ripemd160.blb
new file mode 100644
index 000000000..ee23310ad
Binary files /dev/null and b/ripemd/tests/data/ripemd160.blb differ
diff --git a/ripemd/tests/data/ripemd256.blb b/ripemd/tests/data/ripemd256.blb
new file mode 100644
index 000000000..733bc5772
Binary files /dev/null and b/ripemd/tests/data/ripemd256.blb differ
diff --git a/ripemd/tests/data/ripemd320.blb b/ripemd/tests/data/ripemd320.blb
new file mode 100644
index 000000000..cf006b8b1
Binary files /dev/null and b/ripemd/tests/data/ripemd320.blb differ
diff --git a/ripemd/tests/mod.rs b/ripemd/tests/mod.rs
new file mode 100644
index 000000000..04e5a289a
--- /dev/null
+++ b/ripemd/tests/mod.rs
@@ -0,0 +1,45 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use hex_literal::hex;
+use ripemd::{Digest, Ripemd160, Ripemd256, Ripemd320};
+
+// Test vectors from FIPS 180-1 and from the [RIPEMD webpage][1].
+//
+// [1] https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+new_test!(ripemd160_main, "ripemd160", Ripemd160, fixed_reset_test);
+new_test!(ripemd256_main, "ripemd256", Ripemd256, fixed_reset_test);
+new_test!(ripemd320_main, "ripemd320", Ripemd320, fixed_reset_test);
+
+#[test]
+fn ripemd160_rand() {
+    let mut h = Ripemd160::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("bcd8c672932125776af3c60eeeb58bbaf206f386")[..]
+    );
+}
+
+#[test]
+fn ripemd256_rand() {
+    let mut h = Ripemd256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("6492ffe075896441b737900bdf58fc960e77477e42a2a61bc02c66fd689b69d0")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn ripemd320_rand() {
+    let mut h = Ripemd320::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            3a905312162c5c173639f6cc1cdf51d14e8bda02
+            865767592e26d9343fbec348ce55ce39b4b4b56f
+        ")[..]
+    );
+}
diff --git a/ripemd160/tests/data/one_million_a.bin b/ripemd160/tests/data/one_million_a.bin
deleted file mode 100644
index df1956351..000000000
--- a/ripemd160/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-Rx2C�i{��m7�h��%�(
\ No newline at end of file
diff --git a/ripemd160/tests/data/ripemd160.blb b/ripemd160/tests/data/ripemd160.blb
deleted file mode 100644
index c3ce45f16..000000000
--- a/ripemd160/tests/data/ripemd160.blb
+++ /dev/null
@@ -1,1 +0,0 @@
-blobby1abc����]�z�J��ư��Z�8abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq�S8J����l'����b�++The quick brown fox jumps over the lazy dog7�2���{���Ԗ�q�g��;+The quick brown fox jumps over the lazy cog r�i	3�^���w��J��
\ No newline at end of file
diff --git a/ripemd160/tests/lib.rs b/ripemd160/tests/lib.rs
deleted file mode 100644
index ccb7bce78..000000000
--- a/ripemd160/tests/lib.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! Test messages from FIPS 180-1
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(
-    ripemd160_main,
-    "ripemd160",
-    ripemd160::Ripemd160,
-    digest_test
-);
-
-#[test]
-fn ripemd160_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<ripemd160::Ripemd160>(output);
-}
diff --git a/ripemd256/tests/lib.rs b/ripemd256/tests/lib.rs
deleted file mode 100644
index 099ad9968..000000000
--- a/ripemd256/tests/lib.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use digest::dev::one_million_a;
-use hex_literal::hex;
-use ripemd256::{Digest, Ripemd256};
-
-fn hash_test(msg: &str, expected: [u8; 32]) {
-    let mut hasher = Ripemd256::new();
-    hasher.update(msg.as_bytes());
-    let result = hasher.finalize();
-    assert_eq!(result[..], expected);
-}
-
-#[test]
-fn ripemd256_messages() {
-    hash_test(
-        "",
-        hex!("02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d"),
-    );
-    hash_test(
-        "a",
-        hex!("f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925"),
-    );
-    hash_test(
-        "abc",
-        hex!("afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65"),
-    );
-    hash_test(
-        "message digest",
-        hex!("87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e"),
-    );
-    hash_test(
-        "abcdefghijklmnopqrstuvwxyz",
-        hex!("649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133"),
-    );
-    hash_test(
-        "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-        hex!("3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f"),
-    );
-    hash_test(
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-        hex!("5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8"),
-    );
-    hash_test(
-        "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-        hex!("06fdcc7a409548aaf91368c06a6275b553e3f099bf0ea4edfd6778df89a890dd"),
-    );
-}
-
-#[test]
-fn ripemd256_1million_a() {
-    one_million_a::<ripemd256::Ripemd256>(&hex!(
-        "ac953744e10e31514c150d4d8d7b677342e33399788296e43ae4850ce4f97978"
-    ));
-}
diff --git a/ripemd320/tests/data/one_million_a.bin b/ripemd320/tests/data/one_million_a.bin
deleted file mode 100644
index ebf119470..000000000
--- a/ripemd320/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-��7�7 dk��-�)*�o@�^��	�=���P>+c�E�f
\ No newline at end of file
diff --git a/ripemd320/tests/data/ripemd320.blb b/ripemd320/tests/data/ripemd320.blb
deleted file mode 100644
index 2b5f1d993..000000000
Binary files a/ripemd320/tests/data/ripemd320.blb and /dev/null differ
diff --git a/ripemd320/tests/lib.rs b/ripemd320/tests/lib.rs
deleted file mode 100644
index 727131595..000000000
--- a/ripemd320/tests/lib.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-//! Test messages from FIPS 180-1 and from the RIPEMD-160 webpage[1]
-//! [1] https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(
-    ripemd320_main,
-    "ripemd320",
-    ripemd320::Ripemd320,
-    digest_test
-);
-
-#[test]
-fn ripemd320_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<ripemd320::Ripemd320>(&output[..]);
-}
diff --git a/sha1/tests/data/one_million_a.bin b/sha1/tests/data/one_million_a.bin
deleted file mode 100644
index 0c759d80f..000000000
--- a/sha1/tests/data/one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-4��<��ڤ��+ۭ'1e4o
\ No newline at end of file
diff --git a/sha1/tests/data/sha1.blb b/sha1/tests/data/sha1.blb
index 13ae2df26..e8dc49163 100644
Binary files a/sha1/tests/data/sha1.blb and b/sha1/tests/data/sha1.blb differ
diff --git a/sha1/tests/lib.rs b/sha1/tests/lib.rs
deleted file mode 100644
index c7452c902..000000000
--- a/sha1/tests/lib.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(sha1_main, "sha1", sha1::Sha1, digest_test);
-
-#[test]
-fn sha1_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<sha1::Sha1>(output);
-}
diff --git a/sha1/tests/mod.rs b/sha1/tests/mod.rs
new file mode 100644
index 000000000..04aa69f1c
--- /dev/null
+++ b/sha1/tests/mod.rs
@@ -0,0 +1,15 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use hex_literal::hex;
+use sha1::{Digest, Sha1};
+
+digest::new_test!(sha1_main, "sha1", Sha1, fixed_reset_test);
+
+#[test]
+fn sha1_rand() {
+    let mut h = Sha1::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("7e565a25a8b123e9881addbcedcd927b23377a78")[..]
+    );
+}
diff --git a/sha2/tests/data/sha224.blb b/sha2/tests/data/sha224.blb
index 50759c294..69327048a 100644
Binary files a/sha2/tests/data/sha224.blb and b/sha2/tests/data/sha224.blb differ
diff --git a/sha2/tests/data/sha256.blb b/sha2/tests/data/sha256.blb
index 2096fd4ec..389a12578 100644
Binary files a/sha2/tests/data/sha256.blb and b/sha2/tests/data/sha256.blb differ
diff --git a/sha2/tests/data/sha256_one_million_a.bin b/sha2/tests/data/sha256_one_million_a.bin
deleted file mode 100644
index 59757633d..000000000
--- a/sha2/tests/data/sha256_one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-��n\��������>g�H�� m9��,�
\ No newline at end of file
diff --git a/sha2/tests/data/sha384.blb b/sha2/tests/data/sha384.blb
index 7e9062497..74a151f42 100644
Binary files a/sha2/tests/data/sha384.blb and b/sha2/tests/data/sha384.blb differ
diff --git a/sha2/tests/data/sha512.blb b/sha2/tests/data/sha512.blb
index b073a77cb..bb2ec390c 100644
Binary files a/sha2/tests/data/sha512.blb and b/sha2/tests/data/sha512.blb differ
diff --git a/sha2/tests/data/sha512_224.blb b/sha2/tests/data/sha512_224.blb
index 4d2ef87ff..3c3ab8fac 100644
Binary files a/sha2/tests/data/sha512_224.blb and b/sha2/tests/data/sha512_224.blb differ
diff --git a/sha2/tests/data/sha512_256.blb b/sha2/tests/data/sha512_256.blb
index 5346c9494..acd380aa0 100644
Binary files a/sha2/tests/data/sha512_256.blb and b/sha2/tests/data/sha512_256.blb differ
diff --git a/sha2/tests/data/sha512_one_million_a.bin b/sha2/tests/data/sha512_one_million_a.bin
deleted file mode 100644
index 6edb1d6cf..000000000
Binary files a/sha2/tests/data/sha512_one_million_a.bin and /dev/null differ
diff --git a/sha2/tests/lib.rs b/sha2/tests/lib.rs
deleted file mode 100644
index fe120455b..000000000
--- a/sha2/tests/lib.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(sha224_main, "sha224", sha2::Sha224, digest_test);
-new_test!(sha256_main, "sha256", sha2::Sha256, digest_test);
-new_test!(sha384_main, "sha384", sha2::Sha384, digest_test);
-new_test!(sha512_main, "sha512", sha2::Sha512, digest_test);
-new_test!(
-    sha512_224_main,
-    "sha512_224",
-    sha2::Sha512Trunc224,
-    digest_test
-);
-new_test!(
-    sha512_256_main,
-    "sha512_256",
-    sha2::Sha512Trunc256,
-    digest_test
-);
-
-#[test]
-fn sha256_1million_a() {
-    let output = include_bytes!("data/sha256_one_million_a.bin");
-    one_million_a::<sha2::Sha256>(output);
-}
-
-#[test]
-#[rustfmt::skip]
-fn sha512_avx2_bug() {
-    use sha2::Digest;
-    use hex_literal::hex;
-
-    let mut msg = [0u8; 256];
-    msg[0] = 42;
-    let expected = hex!("
-        2a3e943072f30afa45f2bf57ccd386f29b76dbcdb3a861224ca0b77bc3f55c7a
-        d3880a49c0c9c166eedf7f209c41b380896886155acb8f6c7c07044343a3e692
-    ");
-    let res = sha2::Sha512::digest(&msg);
-    assert_eq!(res[..], expected[..]);
-}
-
-#[test]
-fn sha512_1million_a() {
-    let output = include_bytes!("data/sha512_one_million_a.bin");
-    one_million_a::<sha2::Sha512>(output);
-}
diff --git a/sha2/tests/mod.rs b/sha2/tests/mod.rs
new file mode 100644
index 000000000..91df4683b
--- /dev/null
+++ b/sha2/tests/mod.rs
@@ -0,0 +1,35 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use hex_literal::hex;
+use sha2::{Digest, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256};
+
+new_test!(sha224_main, "sha224", Sha224, fixed_reset_test);
+new_test!(sha256_main, "sha256", Sha256, fixed_reset_test);
+new_test!(sha512_224_main, "sha512_224", Sha512_224, fixed_reset_test);
+new_test!(sha512_256_main, "sha512_256", Sha512_256, fixed_reset_test);
+new_test!(sha384_main, "sha384", Sha384, fixed_reset_test);
+new_test!(sha512_main, "sha512", Sha512, fixed_reset_test);
+
+#[test]
+fn sha256_rand() {
+    let mut h = Sha256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("45f51fead87328fe837a86f4f1ac0eb15116ab1473adc0423ef86c62eb2320c7")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn sha512_rand() {
+    let mut h = Sha512::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            9084d75a7c0721541d737b6171eb465dc9ba08a119a182a8508484aa27a176cd
+            e7c2103b108393eb024493ced4aac56be6f57222cac41b801f11494886264997
+        ")[..]
+    );
+}
diff --git a/sha3/tests/data/keccak_224.blb b/sha3/tests/data/keccak_224.blb
index 97d8c4975..393d0aceb 100644
Binary files a/sha3/tests/data/keccak_224.blb and b/sha3/tests/data/keccak_224.blb differ
diff --git a/sha3/tests/data/keccak_256.blb b/sha3/tests/data/keccak_256.blb
index 6558741ba..d964e2094 100644
Binary files a/sha3/tests/data/keccak_256.blb and b/sha3/tests/data/keccak_256.blb differ
diff --git a/sha3/tests/data/keccak_256_full.blb b/sha3/tests/data/keccak_256_full.blb
index 733224241..71af3f234 100644
Binary files a/sha3/tests/data/keccak_256_full.blb and b/sha3/tests/data/keccak_256_full.blb differ
diff --git a/sha3/tests/data/keccak_384.blb b/sha3/tests/data/keccak_384.blb
index c21ab269b..250942944 100644
Binary files a/sha3/tests/data/keccak_384.blb and b/sha3/tests/data/keccak_384.blb differ
diff --git a/sha3/tests/data/keccak_512.blb b/sha3/tests/data/keccak_512.blb
index 687719d26..a9e47e077 100644
Binary files a/sha3/tests/data/keccak_512.blb and b/sha3/tests/data/keccak_512.blb differ
diff --git a/sha3/tests/data/sha3_224.blb b/sha3/tests/data/sha3_224.blb
index 40d4412ea..510e160c4 100644
Binary files a/sha3/tests/data/sha3_224.blb and b/sha3/tests/data/sha3_224.blb differ
diff --git a/sha3/tests/data/sha3_256.blb b/sha3/tests/data/sha3_256.blb
index 3634b8af8..00fe6fcd9 100644
Binary files a/sha3/tests/data/sha3_256.blb and b/sha3/tests/data/sha3_256.blb differ
diff --git a/sha3/tests/data/sha3_384.blb b/sha3/tests/data/sha3_384.blb
index b5006290d..802759adf 100644
Binary files a/sha3/tests/data/sha3_384.blb and b/sha3/tests/data/sha3_384.blb differ
diff --git a/sha3/tests/data/sha3_512.blb b/sha3/tests/data/sha3_512.blb
index b64785eff..b02800c0f 100644
Binary files a/sha3/tests/data/sha3_512.blb and b/sha3/tests/data/sha3_512.blb differ
diff --git a/sha3/tests/data/shake128.blb b/sha3/tests/data/shake128.blb
index 70177c7db..58fb20814 100644
Binary files a/sha3/tests/data/shake128.blb and b/sha3/tests/data/shake128.blb differ
diff --git a/sha3/tests/data/shake256.blb b/sha3/tests/data/shake256.blb
index e2ee0bb64..c5340e26a 100644
Binary files a/sha3/tests/data/shake256.blb and b/sha3/tests/data/shake256.blb differ
diff --git a/sha3/tests/lib.rs b/sha3/tests/lib.rs
deleted file mode 100644
index 5dc81ed88..000000000
--- a/sha3/tests/lib.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, xof_test};
-use digest::new_test;
-
-new_test!(keccak_224, "keccak_224", sha3::Keccak224, digest_test);
-new_test!(keccak_256, "keccak_256", sha3::Keccak256, digest_test);
-new_test!(keccak_384, "keccak_384", sha3::Keccak384, digest_test);
-new_test!(keccak_512, "keccak_512", sha3::Keccak512, digest_test);
-// tests are from https://github.com/kazcw/yellowsun/blob/test-keccak/src/lib.rs#L171
-new_test!(
-    keccak_256_full,
-    "keccak_256_full",
-    sha3::Keccak256Full,
-    digest_test
-);
-
-new_test!(sha3_224, "sha3_224", sha3::Sha3_224, digest_test);
-new_test!(sha3_256, "sha3_256", sha3::Sha3_256, digest_test);
-new_test!(sha3_384, "sha3_384", sha3::Sha3_384, digest_test);
-new_test!(sha3_512, "sha3_512", sha3::Sha3_512, digest_test);
-
-new_test!(shake128, "shake128", sha3::Shake128, xof_test);
-new_test!(shake256, "shake256", sha3::Shake256, xof_test);
diff --git a/sha3/tests/mod.rs b/sha3/tests/mod.rs
new file mode 100644
index 000000000..120ff41e2
--- /dev/null
+++ b/sha3/tests/mod.rs
@@ -0,0 +1,24 @@
+#![no_std]
+
+use digest::dev::{fixed_reset_test, xof_reset_test};
+use digest::new_test;
+
+new_test!(keccak_224, "keccak_224", sha3::Keccak224, fixed_reset_test);
+new_test!(keccak_256, "keccak_256", sha3::Keccak256, fixed_reset_test);
+new_test!(keccak_384, "keccak_384", sha3::Keccak384, fixed_reset_test);
+new_test!(keccak_512, "keccak_512", sha3::Keccak512, fixed_reset_test);
+// tests are from https://github.com/kazcw/yellowsun/blob/test-keccak/src/lib.rs#L171
+new_test!(
+    keccak_256_full,
+    "keccak_256_full",
+    sha3::Keccak256Full,
+    fixed_reset_test
+);
+
+new_test!(sha3_224, "sha3_224", sha3::Sha3_224, fixed_reset_test);
+new_test!(sha3_256, "sha3_256", sha3::Sha3_256, fixed_reset_test);
+new_test!(sha3_384, "sha3_384", sha3::Sha3_384, fixed_reset_test);
+new_test!(sha3_512, "sha3_512", sha3::Sha3_512, fixed_reset_test);
+
+new_test!(shake128, "shake128", sha3::Shake128, xof_reset_test);
+new_test!(shake256, "shake256", sha3::Shake256, xof_reset_test);
diff --git a/shabal/tests/data/shabal192.blb b/shabal/tests/data/shabal192.blb
index b205df8a2..7eb0a8719 100644
Binary files a/shabal/tests/data/shabal192.blb and b/shabal/tests/data/shabal192.blb differ
diff --git a/shabal/tests/data/shabal192_one_million_a.bin b/shabal/tests/data/shabal192_one_million_a.bin
deleted file mode 100644
index f0f88ef08..000000000
--- a/shabal/tests/data/shabal192_one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-���@I����>F����z�HI5@
\ No newline at end of file
diff --git a/shabal/tests/data/shabal224.blb b/shabal/tests/data/shabal224.blb
index 29de7decf..cdcb37f5f 100644
Binary files a/shabal/tests/data/shabal224.blb and b/shabal/tests/data/shabal224.blb differ
diff --git a/shabal/tests/data/shabal224_one_million_a.bin b/shabal/tests/data/shabal224_one_million_a.bin
deleted file mode 100644
index 536fb4ba4..000000000
--- a/shabal/tests/data/shabal224_one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-4�3�ȅ��\+ͦ��Γ�x�o_��
\ No newline at end of file
diff --git a/shabal/tests/data/shabal256.blb b/shabal/tests/data/shabal256.blb
index e978cb199..643aea8b1 100644
Binary files a/shabal/tests/data/shabal256.blb and b/shabal/tests/data/shabal256.blb differ
diff --git a/shabal/tests/data/shabal256_one_million_a.bin b/shabal/tests/data/shabal256_one_million_a.bin
deleted file mode 100644
index e6c070a1a..000000000
--- a/shabal/tests/data/shabal256_one_million_a.bin
+++ /dev/null
@@ -1,2 +0,0 @@
-
-�!�5D�	���㙘g����H��bx�e�
\ No newline at end of file
diff --git a/shabal/tests/data/shabal384.blb b/shabal/tests/data/shabal384.blb
index 266410a76..3dcbf4062 100644
Binary files a/shabal/tests/data/shabal384.blb and b/shabal/tests/data/shabal384.blb differ
diff --git a/shabal/tests/data/shabal384_one_million_a.bin b/shabal/tests/data/shabal384_one_million_a.bin
deleted file mode 100644
index 5d5f5492d..000000000
--- a/shabal/tests/data/shabal384_one_million_a.bin
+++ /dev/null
@@ -1,1 +0,0 @@
-�xՋ��!����v#T�~ջ�<��i�{��穭xm0��85�{<��p�
\ No newline at end of file
diff --git a/shabal/tests/data/shabal512.blb b/shabal/tests/data/shabal512.blb
index d874809f7..2d57688f9 100644
Binary files a/shabal/tests/data/shabal512.blb and b/shabal/tests/data/shabal512.blb differ
diff --git a/shabal/tests/data/shabal512_one_million_a.bin b/shabal/tests/data/shabal512_one_million_a.bin
deleted file mode 100644
index 1c26f7e06..000000000
Binary files a/shabal/tests/data/shabal512_one_million_a.bin and /dev/null differ
diff --git a/shabal/tests/lib.rs b/shabal/tests/lib.rs
deleted file mode 100644
index bf0891bf5..000000000
--- a/shabal/tests/lib.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(shabal192_main, "shabal192", shabal::Shabal192, digest_test);
-new_test!(shabal224_main, "shabal224", shabal::Shabal224, digest_test);
-new_test!(shabal256_main, "shabal256", shabal::Shabal256, digest_test);
-new_test!(shabal384_main, "shabal384", shabal::Shabal384, digest_test);
-new_test!(shabal512_main, "shabal512", shabal::Shabal512, digest_test);
-
-#[test]
-fn sha192_1million_a() {
-    let output = include_bytes!("data/shabal192_one_million_a.bin");
-    one_million_a::<shabal::Shabal192>(output);
-}
-
-#[test]
-fn sha224_1million_a() {
-    let output = include_bytes!("data/shabal224_one_million_a.bin");
-    one_million_a::<shabal::Shabal224>(output);
-}
-
-#[test]
-fn sha256_1million_a() {
-    let output = include_bytes!("data/shabal256_one_million_a.bin");
-    one_million_a::<shabal::Shabal256>(output);
-}
-
-#[test]
-fn sha384_1million_a() {
-    let output = include_bytes!("data/shabal384_one_million_a.bin");
-    one_million_a::<shabal::Shabal384>(output);
-}
-
-#[test]
-fn sha512_1million_a() {
-    let output = include_bytes!("data/shabal512_one_million_a.bin");
-    one_million_a::<shabal::Shabal512>(output);
-}
diff --git a/shabal/tests/mod.rs b/shabal/tests/mod.rs
new file mode 100644
index 000000000..0e43643d4
--- /dev/null
+++ b/shabal/tests/mod.rs
@@ -0,0 +1,70 @@
+#![no_std]
+
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use hex_literal::hex;
+use shabal::{Digest, Shabal192, Shabal224, Shabal256, Shabal384, Shabal512};
+
+new_test!(shabal192_main, "shabal192", Shabal192, fixed_reset_test);
+new_test!(shabal224_main, "shabal224", Shabal224, fixed_reset_test);
+new_test!(shabal256_main, "shabal256", Shabal256, fixed_reset_test);
+new_test!(shabal384_main, "shabal384", Shabal384, fixed_reset_test);
+new_test!(shabal512_main, "shabal512", Shabal512, fixed_reset_test);
+
+#[test]
+fn shabal192_rand() {
+    let mut h = Shabal192::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("a3e480026be158db97976a895b7a015e9e5205986ebc8a89")[..]
+    );
+}
+
+#[test]
+fn shabal224_rand() {
+    let mut h = Shabal224::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("a09bedfed92fdffc896e6043ec175aa1f07383c65bde990a3661e3d0")[..]
+    );
+}
+
+#[test]
+fn shabal256_rand() {
+    let mut h = Shabal256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("53252a6467450aa1afc1ac25efb493aa65b70e5b2280a4bed7f672c0cfe6f40e")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn shabal384_rand() {
+    let mut h = Shabal384::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            15b8ece81e490848c997dba603523be8842c654262e5adc29138d22a01ff0c9f
+            2b0a0dc9f3e7702ac3598fb1b9ff2db2
+        ")[..]
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn shabal512_rand() {
+    let mut h = Shabal512::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            66f091bc2ba6c571a776441c08ee0711752344ba8b4c88ea17a078baa70d8c0a
+            717b7da24e765867cfcf273a43a58f90e07c0130d1e97adc49f66a0502536e82
+        ")[..]
+    );
+}
diff --git a/sm3/tests/data/sm3.blb b/sm3/tests/data/sm3.blb
index 21d6e4b59..345e7cff2 100644
Binary files a/sm3/tests/data/sm3.blb and b/sm3/tests/data/sm3.blb differ
diff --git a/sm3/tests/data/sm3_one_million_a.bin b/sm3/tests/data/sm3_one_million_a.bin
deleted file mode 100644
index bbc7bd22d..000000000
--- a/sm3/tests/data/sm3_one_million_a.bin
+++ /dev/null
@@ -1,2 +0,0 @@
-Ȫ��)U@)�1�*�
-�����%�z:s+;�
\ No newline at end of file
diff --git a/sm3/tests/lib.rs b/sm3/tests/lib.rs
deleted file mode 100644
index 100ca6754..000000000
--- a/sm3/tests/lib.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(sm3_main, "sm3", sm3::Sm3, digest_test);
-
-#[test]
-fn sm3_1million_a() {
-    let output = include_bytes!("data/sm3_one_million_a.bin");
-    one_million_a::<sm3::Sm3>(output);
-}
-
-/// Test vectors from libgcrypt
-#[test]
-#[rustfmt::skip]
-fn sm3_tests() {
-    use digest::Digest;
-    use hex_literal::hex;
-
-    let hash = &mut sm3::Sm3::new();
-
-    hash.update(b"abc");
-    assert_eq!(hash.finalize_reset().as_slice(),
-        hex!("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"));
-
-    hash.update(b"abcdefghijklmnopqrstuvwxyz");
-    assert_eq!(hash.finalize_reset().as_slice(),
-        hex!("b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595"));
-}
diff --git a/sm3/tests/mod.rs b/sm3/tests/mod.rs
new file mode 100644
index 000000000..fa746c443
--- /dev/null
+++ b/sm3/tests/mod.rs
@@ -0,0 +1,16 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use hex_literal::hex;
+use sm3::{Digest, Sm3};
+
+new_test!(sm3_main, "sm3", Sm3, fixed_reset_test);
+
+#[test]
+fn sm3_rand() {
+    let mut h = Sm3::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("ad154967b08d636a148dd4c688a6df7add1ed1946af18eb358a9b320de2aca86")[..]
+    );
+}
diff --git a/streebog/tests/data/streebog256.blb b/streebog/tests/data/streebog256.blb
index af1df789e..149e1ec17 100644
Binary files a/streebog/tests/data/streebog256.blb and b/streebog/tests/data/streebog256.blb differ
diff --git a/streebog/tests/data/streebog512.blb b/streebog/tests/data/streebog512.blb
index a32fb4bc7..3de2dff70 100644
Binary files a/streebog/tests/data/streebog512.blb and b/streebog/tests/data/streebog512.blb differ
diff --git a/streebog/tests/lib.rs b/streebog/tests/mod.rs
similarity index 71%
rename from streebog/tests/lib.rs
rename to streebog/tests/mod.rs
index 017fb32e5..aeddaefd1 100644
--- a/streebog/tests/lib.rs
+++ b/streebog/tests/mod.rs
@@ -1,20 +1,28 @@
-use digest::dev::digest_test;
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
 use digest::new_test;
-use streebog::{Streebog256, Streebog512};
+use hex_literal::hex;
+use streebog::{Digest, Streebog256, Streebog512};
 
 // Test vectors from:
 // https://github.com/gost-engine/engine/tree/master/etalon
-new_test!(streebog256_main, "streebog256", Streebog256, digest_test);
-new_test!(streebog512_main, "streebog512", Streebog512, digest_test);
+new_test!(
+    streebog256_main,
+    "streebog256",
+    Streebog256,
+    fixed_reset_test,
+);
+new_test!(
+    streebog512_main,
+    "streebog512",
+    Streebog512,
+    fixed_reset_test,
+);
 
 /// Test vectors from:
 /// https://github.com/gost-engine/engine/blob/master/test/01-digest.t
 #[test]
 #[rustfmt::skip]
 fn gost_engine_tests() {
-    use digest::Digest;
-    use hex_literal::hex;
-
     let h256 = &mut streebog::Streebog256::new();
     let h512 = &mut streebog::Streebog512::new();
 
@@ -40,7 +48,7 @@ fn gost_engine_tests() {
     );
 
     for _ in 0..128 {
-        update(h256, h512, b"\x00\x01\x02\x15\x84\x67\x45\x31");
+        update(h256, h512, &hex!("0001021584674531"));
     }
     check(
         h256, h512,
@@ -81,3 +89,27 @@ fn gost_engine_tests() {
         "),
     );
 }
+
+#[test]
+fn streebog256_rand() {
+    let mut h = Streebog256::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("eb5783a2d3f1aa52136701c07c90272a45f017733d898cdfc02302ad2ac8ebed")[..],
+    );
+}
+
+#[test]
+#[rustfmt::skip]
+fn streebog512_rand() {
+    let mut h = Streebog512::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            d78479790925e257b1d65bec84cbe9bbd9bf0abcefb9f99aa065cc533187224f
+            2bead756c96297dcd17728a838e3117a9123559be655175bf4cdac0ee11fba75
+        ")[..],
+    );
+}
diff --git a/tiger/tests/data/tiger.blb b/tiger/tests/data/tiger.blb
new file mode 100644
index 000000000..c936452a2
Binary files /dev/null and b/tiger/tests/data/tiger.blb differ
diff --git a/tiger/tests/data/tiger2.blb b/tiger/tests/data/tiger2.blb
new file mode 100644
index 000000000..cbfb6c4a2
Binary files /dev/null and b/tiger/tests/data/tiger2.blb differ
diff --git a/tiger/tests/mod.rs b/tiger/tests/mod.rs
new file mode 100644
index 000000000..09e78d6c3
--- /dev/null
+++ b/tiger/tests/mod.rs
@@ -0,0 +1,27 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use digest::new_test;
+use hex_literal::hex;
+use tiger::{Digest, Tiger, Tiger2};
+
+new_test!(tiger, "tiger", tiger::Tiger, fixed_reset_test);
+new_test!(tiger2, "tiger2", tiger::Tiger2, fixed_reset_test);
+
+#[test]
+fn tiger_rand() {
+    let mut h = Tiger::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("d12f382ecf3250c14aca7726df15b999dfe99f905cf163d2")[..]
+    );
+}
+
+#[test]
+fn tiger2_rand() {
+    let mut h = Tiger2::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("1bb7a80144c97f831fdefb635477776dd6c164048ce5895d")[..]
+    );
+}
diff --git a/whirlpool/tests/data/one_million_a.bin b/whirlpool/tests/data/one_million_a.bin
deleted file mode 100644
index a3685d43c..000000000
Binary files a/whirlpool/tests/data/one_million_a.bin and /dev/null differ
diff --git a/whirlpool/tests/data/whirlpool.blb b/whirlpool/tests/data/whirlpool.blb
index a82e549b5..4222b2787 100644
Binary files a/whirlpool/tests/data/whirlpool.blb and b/whirlpool/tests/data/whirlpool.blb differ
diff --git a/whirlpool/tests/lib.rs b/whirlpool/tests/lib.rs
deleted file mode 100644
index 286a50e6b..000000000
--- a/whirlpool/tests/lib.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-#![no_std]
-
-use digest::dev::{digest_test, one_million_a};
-use digest::new_test;
-
-new_test!(
-    whirlpool_main,
-    "whirlpool",
-    whirlpool::Whirlpool,
-    digest_test
-);
-
-#[test]
-fn whirlpool_1million_a() {
-    let output = include_bytes!("data/one_million_a.bin");
-    one_million_a::<whirlpool::Whirlpool>(output);
-}
diff --git a/whirlpool/tests/mod.rs b/whirlpool/tests/mod.rs
new file mode 100644
index 000000000..491348ed0
--- /dev/null
+++ b/whirlpool/tests/mod.rs
@@ -0,0 +1,19 @@
+use digest::dev::{feed_rand_16mib, fixed_reset_test};
+use hex_literal::hex;
+use whirlpool::{Digest, Whirlpool};
+
+digest::new_test!(whirlpool_main, "whirlpool", Whirlpool, fixed_reset_test);
+
+#[test]
+#[rustfmt::skip]
+fn whirlpool_rand() {
+    let mut h = Whirlpool::new();
+    feed_rand_16mib(&mut h);
+    assert_eq!(
+        h.finalize()[..],
+        hex!("
+            8db0acd78686f8160203b53bfb0c0c1ee2332b856732a311f7de8e4ea4c100cc
+            dd5267e8b63207e644c96d2ef5cfbb53f2519af1904c48fd2ecf937541998b11
+        ")[..]
+    );
+}