hashcodecs 1.2.0__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/CHANGELOG.md +12 -1
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/CITATION.cff +3 -3
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/Cargo.lock +1 -1
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/Cargo.toml +11 -11
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/PKG-INFO +1 -1
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/base64.md +3 -1
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/_hashcodecs.pyi +22 -17
- hashcodecs-1.2.1/hatch_build.py +86 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/pyproject.toml +4 -6
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/backend.rs +16 -11
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/avx512.rs +2 -2
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode.rs +27 -8
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode/avx512.rs +2 -2
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/tests/aarch64.rs +2 -2
- hashcodecs-1.2.1/src/base64/tests.rs +1110 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64.rs +3 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/decode.rs +318 -26
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/methods.rs +4 -4
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/mod.rs +10 -5
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/buffer.rs +36 -6
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/murmur3/incremental.rs +9 -9
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/xxhash/methods.rs +4 -4
- hashcodecs-1.2.1/src/murmur3/block_buffer.rs +73 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/dispatch.rs +2 -2
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/proofs.rs +4 -3
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/tests.rs +58 -16
- hashcodecs-1.2.1/src/murmur3/x64_128/x86.rs +190 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/x64_128.rs +33 -17
- hashcodecs-1.2.1/src/murmur3/x86_128/x86.rs +206 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/x86_128.rs +29 -11
- hashcodecs-1.2.1/src/murmur3/x86_32/x86.rs +139 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/x86_32.rs +32 -16
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3.rs +1 -3
- hashcodecs-1.2.1/src/xxhash/batch.rs +282 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/hash.rs +3 -36
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/long/aarch64.rs +11 -9
- hashcodecs-1.2.1/src/xxhash/long/scalar.rs +53 -0
- {hashcodecs-1.2.0/src/xxhash/long → hashcodecs-1.2.1/src/xxhash/long/x86}/avx2.rs +28 -98
- hashcodecs-1.2.1/src/xxhash/long/x86/avx2_batch.rs +89 -0
- {hashcodecs-1.2.0/src/xxhash/long → hashcodecs-1.2.1/src/xxhash/long/x86}/avx512.rs +13 -8
- hashcodecs-1.2.1/src/xxhash/long/x86/mod.rs +4 -0
- {hashcodecs-1.2.0/src/xxhash/long → hashcodecs-1.2.1/src/xxhash/long/x86}/ssse3.rs +14 -10
- hashcodecs-1.2.1/src/xxhash/long.rs +485 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/proofs.rs +10 -8
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/tests.rs +34 -12
- hashcodecs-1.2.0/hatch_build.py +0 -60
- hashcodecs-1.2.0/src/murmur3/incremental.rs +0 -42
- hashcodecs-1.2.0/src/murmur3/x86.rs +0 -515
- hashcodecs-1.2.0/src/xxhash/batch.rs +0 -191
- hashcodecs-1.2.0/src/xxhash/long.rs +0 -203
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/.gitignore +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/BENCHMARK.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/LICENSE +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/LICENSE-MIT +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/README.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/SAFETY.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/SECURITY.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/benches/base64.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/benches/murmur3.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/benches/support/mod.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/benches/xxhash.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/build.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/ARCHITECTURE.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/api/base64.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/api/murmur3.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/api/xxh3.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-batch-large.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-batch-reusable.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-batch.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-lenient.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-memoryview.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-mutable.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python-reusable.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-python.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/base64-rust.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/murmur3-python-mutable.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/murmur3-python.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/murmur3-rust.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/performance-at-a-glance.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/results.csv +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/xxh3-python.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/xxh3-rust-batch-remainders.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/benchmarks/xxh3-rust.svg +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/compatibility.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/index.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/murmur3.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/performance.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/requirements.txt +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/docs/xxh3.md +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/__init__.py +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/__init__.pyi +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/base64.py +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/base64.pyi +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/murmur3.py +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/murmur3.pyi +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/py.typed +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/xxhash.py +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/hashcodecs/xxhash.pyi +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/alphabet.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/backend.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/aarch64.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/avx2.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/sse41.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/ssse3.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/decode/x86_contracts.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/dispatch.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode/aarch64.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode/avx2.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode/cache.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode/ssse3.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/encode.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/error.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/miri_tests.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/output.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/base64/proofs.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/arguments.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/callbacks.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/decode/plan.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/base64/encode.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/mod.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/murmur3/digest.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/murmur3/methods.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/murmur3/mod.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/murmur3/one_shot.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/objects.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/runtime.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/xxhash/batch.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/bindings/xxhash/mod.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/lib.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/miri_tests.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/murmur3/primitives.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/miri_tests.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/primitives.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash/short.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/src/xxhash.rs +0 -0
- {hashcodecs-1.2.0 → hashcodecs-1.2.1}/tools/generate_api_metadata.py +0 -0
|
@@ -4,6 +4,16 @@ This file records notable user-facing changes to `hashcodecs`. Version 1.0.0 sta
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.2.1] - 2026-08-26
|
|
8
|
+
|
|
9
|
+
### What's Changed
|
|
10
|
+
* fix: harden buffer and lenient decode paths by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/58
|
|
11
|
+
* Refactor Murmur SIMD and centralize XXH3 long dispatch by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/59
|
|
12
|
+
* Repair sdist verification and roll back unpublished 1.2.1 by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/60
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
**Full Changelog**: https://github.com/kozistr/hashcodecs-rs/compare/v1.2.0...v1.2.1
|
|
16
|
+
|
|
7
17
|
## [1.2.0] - 2026-08-25
|
|
8
18
|
|
|
9
19
|
### What's Changed
|
|
@@ -117,7 +127,8 @@ This file records notable user-facing changes to `hashcodecs`. Version 1.0.0 sta
|
|
|
117
127
|
- Initial Python and Rust APIs for Base64 and MurmurHash3.
|
|
118
128
|
- Runtime SIMD dispatch and platform-specific CPython wheels.
|
|
119
129
|
|
|
120
|
-
[Unreleased]: https://github.com/kozistr/hashcodecs-rs/compare/v1.2.
|
|
130
|
+
[Unreleased]: https://github.com/kozistr/hashcodecs-rs/compare/v1.2.1...HEAD
|
|
131
|
+
[1.2.1]: https://github.com/kozistr/hashcodecs-rs/compare/v1.2.0...v1.2.1
|
|
121
132
|
[1.2.0]: https://github.com/kozistr/hashcodecs-rs/compare/v1.1.0...v1.2.0
|
|
122
133
|
[1.1.0]: https://github.com/kozistr/hashcodecs-rs/compare/v1.0.0...v1.1.0
|
|
123
134
|
[1.0.0]: https://github.com/kozistr/hashcodecs-rs/compare/v0.6.1...v1.0.0
|
|
@@ -5,8 +5,8 @@ authors:
|
|
|
5
5
|
given-names: Hyeongchan
|
|
6
6
|
orcid: https://orcid.org/0000-0002-1729-0580
|
|
7
7
|
title: "hashcodecs: SIMD-accelerated Base64, MurmurHash3, and XXH3 for Python and Rust"
|
|
8
|
-
version: 1.2.
|
|
9
|
-
date-released: 2026-08-
|
|
8
|
+
version: 1.2.1
|
|
9
|
+
date-released: 2026-08-26
|
|
10
10
|
license: "MIT OR Apache-2.0"
|
|
11
11
|
repository-code: "https://github.com/kozistr/hashcodecs-rs"
|
|
12
|
-
url: "https://github.com/kozistr/hashcodecs-rs"
|
|
12
|
+
url: "https://github.com/kozistr/hashcodecs-rs"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "hashcodecs"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.1"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.89"
|
|
6
6
|
description = "SIMD-accelerated Base64 codecs and fast MurmurHash3 and xxHash implementations"
|
|
@@ -36,16 +36,16 @@ pyo3 = { version = "0.29.2", optional = true }
|
|
|
36
36
|
pyo3-build-config = { version = "0.29.2", optional = true }
|
|
37
37
|
|
|
38
38
|
[dev-dependencies]
|
|
39
|
-
base64 = "0.23.1"
|
|
40
|
-
base64-turbo = "0.3.0"
|
|
41
|
-
criterion = { version = "0.8.2", default-features = false, features = ["cargo_bench_support"] }
|
|
42
|
-
fastmurmur3 = "0.2.0"
|
|
43
|
-
mimalloc = "0.1.52"
|
|
44
|
-
mm3h = "0.1.3"
|
|
45
|
-
murmur3 = "0.5"
|
|
46
|
-
murmurs = "1.0.5"
|
|
47
|
-
xxhash-c-sys = "0.8.7"
|
|
48
|
-
xxhash-rust = { version = "0.8.18", features = ["xxh3"] }
|
|
39
|
+
base64 = "=0.23.1"
|
|
40
|
+
base64-turbo = "=0.3.0"
|
|
41
|
+
criterion = { version = "=0.8.2", default-features = false, features = ["cargo_bench_support"] }
|
|
42
|
+
fastmurmur3 = "=0.2.0"
|
|
43
|
+
mimalloc = "=0.1.52"
|
|
44
|
+
mm3h = "=0.1.3"
|
|
45
|
+
murmur3 = "=0.5.2"
|
|
46
|
+
murmurs = "=1.0.5"
|
|
47
|
+
xxhash-c-sys = "=0.8.7"
|
|
48
|
+
xxhash-rust = { version = "=0.8.18", features = ["xxh3"] }
|
|
49
49
|
|
|
50
50
|
[[bench]]
|
|
51
51
|
name = "base64"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hashcodecs
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: SIMD-accelerated Base64, MurmurHash3, and xxHash codecs
|
|
5
5
|
Project-URL: Documentation, https://hashcodecs-rs.readthedocs.io/
|
|
6
6
|
Project-URL: Repository, https://github.com/kozistr/hashcodecs-rs
|
|
@@ -30,10 +30,12 @@ The `standard_b64encode` and `urlsafe_b64encode` helpers select the respective a
|
|
|
30
30
|
|
|
31
31
|
## Decoding and Validation
|
|
32
32
|
|
|
33
|
-
`b64decode(s, altchars=None, validate
|
|
33
|
+
`b64decode(s, altchars=None, validate=..., *, padded=True, ignorechars=..., canonical=False)` decodes an ASCII
|
|
34
34
|
string or bytes-like value.
|
|
35
35
|
|
|
36
36
|
- `validate=True` rejects characters outside the chosen alphabet instead of discarding them.
|
|
37
|
+
- If you supply `ignorechars` without `validate`, hashcodecs uses strict mode. Pass `validate=False` to keep lenient
|
|
38
|
+
mode; hashcodecs then limits discarded bytes to `ignorechars`.
|
|
37
39
|
- `padded=False` accepts an unpadded final Base64 quantum.
|
|
38
40
|
- `ignorechars` specifies which non-alphabet bytes lenient decoding may ignore. Its default preserves the standard
|
|
39
41
|
library's lenient behavior.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import final
|
|
2
|
+
|
|
1
3
|
from _typeshed import ReadableBuffer
|
|
2
4
|
|
|
3
5
|
def b64encode(
|
|
@@ -146,7 +148,7 @@ def b64decode(
|
|
|
146
148
|
bit validation are available for protocols with tighter requirements.
|
|
147
149
|
|
|
148
150
|
Args:
|
|
149
|
-
s: ASCII text or
|
|
151
|
+
s: ASCII text or bytes-like Base64 data.
|
|
150
152
|
altchars: Two characters replacing "+" and "/", or None for the
|
|
151
153
|
standard alphabet.
|
|
152
154
|
validate: Reject non-alphabet bytes when true. The default is lenient
|
|
@@ -256,7 +258,7 @@ def b64decode_into(
|
|
|
256
258
|
destination prefix may already have been modified.
|
|
257
259
|
|
|
258
260
|
Args:
|
|
259
|
-
s: ASCII text or
|
|
261
|
+
s: ASCII text or bytes-like Base64 data.
|
|
260
262
|
output: Destination bytearray with room for the complete result.
|
|
261
263
|
altchars: Two characters replacing "+" and "/", or None for the
|
|
262
264
|
standard alphabet.
|
|
@@ -348,7 +350,7 @@ def standard_b64decode(s: str | ReadableBuffer) -> bytes:
|
|
|
348
350
|
Python's base64.standard_b64decode function.
|
|
349
351
|
|
|
350
352
|
Args:
|
|
351
|
-
s: ASCII text or
|
|
353
|
+
s: ASCII text or bytes-like Base64 data.
|
|
352
354
|
|
|
353
355
|
Returns:
|
|
354
356
|
Newly allocated decoded bytes.
|
|
@@ -368,7 +370,7 @@ def standard_b64decode_into(s: str | ReadableBuffer, output: bytearray) -> int:
|
|
|
368
370
|
"""Decode standard Base64 into a reusable bytearray.
|
|
369
371
|
|
|
370
372
|
Args:
|
|
371
|
-
s: ASCII text or
|
|
373
|
+
s: ASCII text or bytes-like Base64 data.
|
|
372
374
|
output: Destination bytearray with room for the decoded result.
|
|
373
375
|
|
|
374
376
|
Returns:
|
|
@@ -615,7 +617,7 @@ def xxh3_64_batch(items: list[ReadableBuffer], seed: int = 0) -> list[int]:
|
|
|
615
617
|
"""Compute canonical XXH3 64-bit hashes for a list of inputs.
|
|
616
618
|
|
|
617
619
|
Args:
|
|
618
|
-
items: A list of
|
|
620
|
+
items: A list of bytes-like objects to hash.
|
|
619
621
|
seed: Initial unsigned 64-bit seed shared by every item.
|
|
620
622
|
|
|
621
623
|
Returns:
|
|
@@ -638,7 +640,7 @@ def xxh3_64_batch_into(items: list[ReadableBuffer], output: bytearray, seed: int
|
|
|
638
640
|
written prefix remain unchanged.
|
|
639
641
|
|
|
640
642
|
Args:
|
|
641
|
-
items: A list of
|
|
643
|
+
items: A list of bytes-like objects to hash.
|
|
642
644
|
output: Destination with at least 8 * len(items) bytes.
|
|
643
645
|
seed: Initial unsigned 64-bit seed shared by every item.
|
|
644
646
|
|
|
@@ -663,7 +665,7 @@ def xxh3_128_batch(items: list[ReadableBuffer], seed: int = 0) -> list[int]:
|
|
|
663
665
|
"""Compute canonical XXH3 128-bit hashes for a list of inputs.
|
|
664
666
|
|
|
665
667
|
Args:
|
|
666
|
-
items: A list of
|
|
668
|
+
items: A list of bytes-like objects to hash.
|
|
667
669
|
seed: Initial unsigned 64-bit seed shared by every item.
|
|
668
670
|
|
|
669
671
|
Returns:
|
|
@@ -686,7 +688,7 @@ def xxh3_128_batch_into(items: list[ReadableBuffer], output: bytearray, seed: in
|
|
|
686
688
|
written prefix remain unchanged.
|
|
687
689
|
|
|
688
690
|
Args:
|
|
689
|
-
items: A list of
|
|
691
|
+
items: A list of bytes-like objects to hash.
|
|
690
692
|
output: Destination with at least 16 * len(items) bytes.
|
|
691
693
|
seed: Initial unsigned 64-bit seed shared by every item.
|
|
692
694
|
|
|
@@ -707,11 +709,12 @@ def xxh3_128_batch_into(items: list[ReadableBuffer], output: bytearray, seed: in
|
|
|
707
709
|
"""
|
|
708
710
|
...
|
|
709
711
|
|
|
712
|
+
@final
|
|
710
713
|
class murmur3_x86_32:
|
|
711
714
|
"""Incremental MurmurHash3 x86 32-bit hasher.
|
|
712
715
|
|
|
713
716
|
Args:
|
|
714
|
-
data: Optional initial
|
|
717
|
+
data: Optional initial bytes-like data.
|
|
715
718
|
seed: Initial unsigned 32-bit seed.
|
|
716
719
|
|
|
717
720
|
Examples:
|
|
@@ -725,7 +728,7 @@ class murmur3_x86_32:
|
|
|
725
728
|
"""Initialize an incremental x86 32-bit hash state.
|
|
726
729
|
|
|
727
730
|
Args:
|
|
728
|
-
data: Optional initial
|
|
731
|
+
data: Optional initial bytes-like data.
|
|
729
732
|
seed: Initial unsigned 32-bit seed.
|
|
730
733
|
|
|
731
734
|
Raises:
|
|
@@ -738,7 +741,7 @@ class murmur3_x86_32:
|
|
|
738
741
|
"""Append bytes to the hash state.
|
|
739
742
|
|
|
740
743
|
Args:
|
|
741
|
-
data:
|
|
744
|
+
data: Bytes-like data to append.
|
|
742
745
|
|
|
743
746
|
Returns:
|
|
744
747
|
None.
|
|
@@ -804,11 +807,12 @@ class murmur3_x86_32:
|
|
|
804
807
|
"""Return the algorithm name (murmur3_x86_32)."""
|
|
805
808
|
...
|
|
806
809
|
|
|
810
|
+
@final
|
|
807
811
|
class murmur3_x86_128:
|
|
808
812
|
"""Incremental MurmurHash3 x86 128-bit hasher.
|
|
809
813
|
|
|
810
814
|
Args:
|
|
811
|
-
data: Optional initial
|
|
815
|
+
data: Optional initial bytes-like data.
|
|
812
816
|
seed: Initial unsigned 32-bit seed.
|
|
813
817
|
|
|
814
818
|
Examples:
|
|
@@ -822,7 +826,7 @@ class murmur3_x86_128:
|
|
|
822
826
|
"""Initialize an incremental x86 128-bit hash state.
|
|
823
827
|
|
|
824
828
|
Args:
|
|
825
|
-
data: Optional initial
|
|
829
|
+
data: Optional initial bytes-like data.
|
|
826
830
|
seed: Initial unsigned 32-bit seed.
|
|
827
831
|
|
|
828
832
|
Raises:
|
|
@@ -835,7 +839,7 @@ class murmur3_x86_128:
|
|
|
835
839
|
"""Append bytes to the hash state.
|
|
836
840
|
|
|
837
841
|
Args:
|
|
838
|
-
data:
|
|
842
|
+
data: Bytes-like data to append.
|
|
839
843
|
|
|
840
844
|
Returns:
|
|
841
845
|
None.
|
|
@@ -901,11 +905,12 @@ class murmur3_x86_128:
|
|
|
901
905
|
"""Return the algorithm name (murmur3_x86_128)."""
|
|
902
906
|
...
|
|
903
907
|
|
|
908
|
+
@final
|
|
904
909
|
class murmur3_x64_128:
|
|
905
910
|
"""Incremental MurmurHash3 x64 128-bit hasher.
|
|
906
911
|
|
|
907
912
|
Args:
|
|
908
|
-
data: Optional initial
|
|
913
|
+
data: Optional initial bytes-like data.
|
|
909
914
|
seed: Initial unsigned 32-bit seed.
|
|
910
915
|
|
|
911
916
|
Examples:
|
|
@@ -920,7 +925,7 @@ class murmur3_x64_128:
|
|
|
920
925
|
"""Initialize an incremental x64 128-bit hash state.
|
|
921
926
|
|
|
922
927
|
Args:
|
|
923
|
-
data: Optional initial
|
|
928
|
+
data: Optional initial bytes-like data.
|
|
924
929
|
seed: Initial unsigned 32-bit seed.
|
|
925
930
|
|
|
926
931
|
Raises:
|
|
@@ -933,7 +938,7 @@ class murmur3_x64_128:
|
|
|
933
938
|
"""Append bytes to the hash state.
|
|
934
939
|
|
|
935
940
|
Args:
|
|
936
|
-
data:
|
|
941
|
+
data: Bytes-like data to append.
|
|
937
942
|
|
|
938
943
|
Returns:
|
|
939
944
|
None.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Hatchling hook that packages the in-tree PyO3 extension."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
14
|
+
from packaging.tags import sys_tags
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CustomBuildHook(BuildHookInterface[Any]):
|
|
18
|
+
"""Build the CPython extension wheel."""
|
|
19
|
+
|
|
20
|
+
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
|
|
21
|
+
if self.target_name != 'wheel':
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
root = Path(self.root)
|
|
25
|
+
wheel_tag = self._wheel_tag()
|
|
26
|
+
env = os.environ.copy()
|
|
27
|
+
configured_target_dir = env.get('CARGO_TARGET_DIR') or env.get('CARGO_LLVM_COV_TARGET_DIR')
|
|
28
|
+
if configured_target_dir:
|
|
29
|
+
target_dir = Path(configured_target_dir)
|
|
30
|
+
if not target_dir.is_absolute():
|
|
31
|
+
target_dir = root / target_dir
|
|
32
|
+
else:
|
|
33
|
+
target_dir = root / 'target' / 'hatch' / wheel_tag
|
|
34
|
+
env['CARGO_TARGET_DIR'] = str(target_dir)
|
|
35
|
+
env['PYO3_PYTHON'] = sys.executable
|
|
36
|
+
result = subprocess.run(
|
|
37
|
+
[
|
|
38
|
+
'cargo',
|
|
39
|
+
'build',
|
|
40
|
+
'--locked',
|
|
41
|
+
'--manifest-path',
|
|
42
|
+
str(root / 'Cargo.toml'),
|
|
43
|
+
'--release',
|
|
44
|
+
'--features',
|
|
45
|
+
'extension-module',
|
|
46
|
+
'--message-format=json-render-diagnostics',
|
|
47
|
+
],
|
|
48
|
+
check=False,
|
|
49
|
+
cwd=root,
|
|
50
|
+
env=env,
|
|
51
|
+
stdout=subprocess.PIPE,
|
|
52
|
+
text=True,
|
|
53
|
+
)
|
|
54
|
+
result.check_returncode()
|
|
55
|
+
|
|
56
|
+
extension = self._cdylib_artifact(result.stdout)
|
|
57
|
+
build_data['force_include'] = {str(extension): f'hashcodecs/_hashcodecs{self._extension_suffix()}'}
|
|
58
|
+
build_data['pure_python'] = False
|
|
59
|
+
build_data['tag'] = wheel_tag
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _cdylib_artifact(messages: str) -> Path:
|
|
63
|
+
suffixes = {'.dll', '.dylib', '.so'}
|
|
64
|
+
for line in messages.splitlines():
|
|
65
|
+
try:
|
|
66
|
+
message = json.loads(line)
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
continue
|
|
69
|
+
if (
|
|
70
|
+
message.get('reason') == 'compiler-artifact'
|
|
71
|
+
and message.get('target', {}).get('name') == 'hashcodecs'
|
|
72
|
+
and 'cdylib' in message.get('target', {}).get('crate_types', [])
|
|
73
|
+
):
|
|
74
|
+
for filename in message.get('filenames', []):
|
|
75
|
+
artifact = Path(filename)
|
|
76
|
+
if artifact.suffix in suffixes:
|
|
77
|
+
return artifact
|
|
78
|
+
raise RuntimeError('Cargo did not report the hashcodecs cdylib artifact')
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _extension_suffix() -> str:
|
|
82
|
+
return '.pyd' if platform.system() == 'Windows' else '.so'
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _wheel_tag() -> str:
|
|
86
|
+
return str(next(sys_tags()))
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hashcodecs"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
description = "SIMD-accelerated Base64, MurmurHash3, and xxHash codecs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -44,9 +44,9 @@ dev = [
|
|
|
44
44
|
"ruff>=0.16,<0.17",
|
|
45
45
|
]
|
|
46
46
|
benchmark = [
|
|
47
|
-
"mmh3
|
|
48
|
-
"pybase64
|
|
49
|
-
"xxhash
|
|
47
|
+
"mmh3==5.2.1",
|
|
48
|
+
"pybase64==1.4.3",
|
|
49
|
+
"xxhash==3.8.1",
|
|
50
50
|
]
|
|
51
51
|
|
|
52
52
|
[tool.hatch.build.targets.wheel]
|
|
@@ -71,8 +71,6 @@ include = [
|
|
|
71
71
|
"/src",
|
|
72
72
|
"/tools",
|
|
73
73
|
]
|
|
74
|
-
exclude = ["/src/base64/tests.rs"]
|
|
75
|
-
|
|
76
74
|
[tool.hatch.build.hooks.custom]
|
|
77
75
|
path = "hatch_build.py"
|
|
78
76
|
|
|
@@ -101,6 +101,7 @@ fn detect() -> Capabilities {
|
|
|
101
101
|
{
|
|
102
102
|
x86_capabilities(
|
|
103
103
|
std::is_x86_feature_detected!("avx512vbmi"),
|
|
104
|
+
std::is_x86_feature_detected!("avx512bw"),
|
|
104
105
|
std::is_x86_feature_detected!("avx512f"),
|
|
105
106
|
std::is_x86_feature_detected!("avx2"),
|
|
106
107
|
std::is_x86_feature_detected!("sse4.1"),
|
|
@@ -121,6 +122,7 @@ fn detect() -> Capabilities {
|
|
|
121
122
|
#[cfg(any(test, target_arch = "x86", target_arch = "x86_64"))]
|
|
122
123
|
fn x86_capabilities(
|
|
123
124
|
avx512_vbmi: bool,
|
|
125
|
+
avx512_bw: bool,
|
|
124
126
|
avx512: bool,
|
|
125
127
|
avx2: bool,
|
|
126
128
|
sse41: bool,
|
|
@@ -143,7 +145,7 @@ fn x86_capabilities(
|
|
|
143
145
|
if avx512 && avx2 && ssse3 {
|
|
144
146
|
simd |= SimdBackend::Avx512.bit();
|
|
145
147
|
}
|
|
146
|
-
if avx512 && avx512_vbmi && avx2 && ssse3 {
|
|
148
|
+
if avx512 && avx512_bw && avx512_vbmi && avx2 && ssse3 {
|
|
147
149
|
simd |= SimdBackend::Avx512Vbmi.bit();
|
|
148
150
|
}
|
|
149
151
|
Capabilities { simd, bmi2 }
|
|
@@ -163,40 +165,43 @@ mod tests {
|
|
|
163
165
|
|
|
164
166
|
#[test]
|
|
165
167
|
fn x86_detection_tracks_independent_feature_sets() {
|
|
166
|
-
let scalar = x86_capabilities(false, false, false, false, false, false);
|
|
168
|
+
let scalar = x86_capabilities(false, false, false, false, false, false, false);
|
|
167
169
|
assert_eq!(scalar.best(&[SimdBackend::Avx2]), SimdBackend::Scalar);
|
|
168
170
|
|
|
169
|
-
let ssse3 = x86_capabilities(false, false, false, false, true, false);
|
|
171
|
+
let ssse3 = x86_capabilities(false, false, false, false, false, true, false);
|
|
170
172
|
assert!(ssse3.supports(SimdBackend::Ssse3));
|
|
171
173
|
assert!(!ssse3.supports(SimdBackend::Sse41));
|
|
172
174
|
|
|
173
|
-
let incomplete_sse41 = x86_capabilities(false, false, false, true, false, false);
|
|
175
|
+
let incomplete_sse41 = x86_capabilities(false, false, false, false, true, false, false);
|
|
174
176
|
assert!(!incomplete_sse41.supports(SimdBackend::Sse41));
|
|
175
177
|
|
|
176
|
-
let sse41 = x86_capabilities(false, false, false, true, true, false);
|
|
178
|
+
let sse41 = x86_capabilities(false, false, false, false, true, true, false);
|
|
177
179
|
assert!(sse41.supports(SimdBackend::Sse41));
|
|
178
180
|
|
|
179
|
-
let incomplete_avx2 = x86_capabilities(false, false, true, false, false, true);
|
|
181
|
+
let incomplete_avx2 = x86_capabilities(false, false, false, true, false, false, true);
|
|
180
182
|
assert!(!incomplete_avx2.supports(SimdBackend::Avx2));
|
|
181
|
-
let avx2 = x86_capabilities(false, false, true, false, true, true);
|
|
183
|
+
let avx2 = x86_capabilities(false, false, false, true, false, true, true);
|
|
182
184
|
assert!(avx2.supports(SimdBackend::Avx2));
|
|
183
185
|
assert!(!avx2.supports(SimdBackend::Sse41));
|
|
184
186
|
assert!(avx2.has_bmi2());
|
|
185
187
|
|
|
186
|
-
let avx512 = x86_capabilities(false, true, false, false, false, false);
|
|
188
|
+
let avx512 = x86_capabilities(false, false, true, false, false, false, false);
|
|
187
189
|
assert!(!avx512.supports(SimdBackend::Avx512));
|
|
188
190
|
assert!(!avx512.supports(SimdBackend::Avx2));
|
|
189
191
|
assert!(!avx512.supports(SimdBackend::Avx512Vbmi));
|
|
190
192
|
|
|
191
|
-
let avx512 = x86_capabilities(false, true, true, false, true, false);
|
|
193
|
+
let avx512 = x86_capabilities(false, false, true, true, false, true, false);
|
|
192
194
|
assert!(avx512.supports(SimdBackend::Avx512));
|
|
193
195
|
assert!(avx512.supports(SimdBackend::Avx2));
|
|
194
196
|
assert!(!avx512.supports(SimdBackend::Avx512Vbmi));
|
|
195
197
|
|
|
196
|
-
let
|
|
198
|
+
let incomplete_bw = x86_capabilities(true, false, true, true, true, true, true);
|
|
199
|
+
assert!(!incomplete_bw.supports(SimdBackend::Avx512Vbmi));
|
|
200
|
+
|
|
201
|
+
let incomplete_vbmi = x86_capabilities(false, true, true, true, true, true, true);
|
|
197
202
|
assert!(!incomplete_vbmi.supports(SimdBackend::Avx512Vbmi));
|
|
198
203
|
|
|
199
|
-
let vbmi = x86_capabilities(true, true, true, true, true, true);
|
|
204
|
+
let vbmi = x86_capabilities(true, true, true, true, true, true, true);
|
|
200
205
|
assert_eq!(
|
|
201
206
|
vbmi.best(&[
|
|
202
207
|
SimdBackend::Avx512Vbmi,
|
|
@@ -31,7 +31,7 @@ const fn decode_shuffle() -> [u8; 64] {
|
|
|
31
31
|
shuffle
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
#[target_feature(enable = "avx512vbmi")]
|
|
34
|
+
#[target_feature(enable = "avx512vbmi,avx512bw")]
|
|
35
35
|
pub(in crate::base64) unsafe fn decode<A: Decoder, S: Store>(
|
|
36
36
|
input: &[u8],
|
|
37
37
|
output: *mut u8,
|
|
@@ -100,7 +100,7 @@ pub(in crate::base64) unsafe fn decode<A: Decoder, S: Store>(
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
#[target_feature(enable = "avx512vbmi")]
|
|
103
|
+
#[target_feature(enable = "avx512vbmi,avx512bw")]
|
|
104
104
|
#[inline]
|
|
105
105
|
unsafe fn decode_64(
|
|
106
106
|
input: *const u8,
|
|
@@ -24,7 +24,8 @@ use super::{
|
|
|
24
24
|
///
|
|
25
25
|
/// The input must be quartet-aligned, use only the standard alphabet, and have
|
|
26
26
|
/// valid trailing padding. Unlike Python's lenient decoder, this Rust API does
|
|
27
|
-
/// not ignore whitespace or other non-alphabet bytes.
|
|
27
|
+
/// not ignore whitespace or other non-alphabet bytes. Non-zero unused bits in
|
|
28
|
+
/// the final quantum are accepted rather than treated as a canonicality error.
|
|
28
29
|
///
|
|
29
30
|
/// # Arguments
|
|
30
31
|
///
|
|
@@ -54,7 +55,8 @@ pub fn b64decode(input: &[u8]) -> Result<Vec<u8>, Base64Error> {
|
|
|
54
55
|
/// Decodes padded RFC 4648 Base64 with the URL-safe alphabet.
|
|
55
56
|
///
|
|
56
57
|
/// The input must be quartet-aligned, use only the URL-safe alphabet, and have
|
|
57
|
-
/// valid trailing padding.
|
|
58
|
+
/// valid trailing padding. Non-zero unused bits in the final quantum are
|
|
59
|
+
/// accepted rather than treated as a canonicality error.
|
|
58
60
|
///
|
|
59
61
|
/// # Arguments
|
|
60
62
|
///
|
|
@@ -259,6 +261,7 @@ pub(crate) fn decode_unpadded_layout(input: &[u8]) -> Result<DecodeLayout, Base6
|
|
|
259
261
|
.and_then(|length| length.checked_add(tail_len))
|
|
260
262
|
.ok_or(Base64Error::InvalidInput)?;
|
|
261
263
|
Ok(DecodeLayout {
|
|
264
|
+
input_len: input.len(),
|
|
262
265
|
padding: 0,
|
|
263
266
|
output_len,
|
|
264
267
|
})
|
|
@@ -286,7 +289,8 @@ pub(crate) fn decode_to_slice_with_layout_and_alphabet(
|
|
|
286
289
|
layout: DecodeLayout,
|
|
287
290
|
alphabet: DecodeAlphabet,
|
|
288
291
|
) -> Result<(), Base64Error> {
|
|
289
|
-
|
|
292
|
+
assert_eq!(input.len(), layout.input_len);
|
|
293
|
+
assert_eq!(output.len(), layout.output_len);
|
|
290
294
|
// The slice has exactly the required initialized storage, so only bounded
|
|
291
295
|
// stores are permitted.
|
|
292
296
|
unsafe { decode_to_ptr_with_layout(input, output.as_mut_ptr(), layout, alphabet, false) }
|
|
@@ -300,7 +304,8 @@ pub(crate) fn decode_to_slice_with_unpadded_layout_and_alphabet(
|
|
|
300
304
|
layout: DecodeLayout,
|
|
301
305
|
alphabet: DecodeAlphabet,
|
|
302
306
|
) -> Result<(), Base64Error> {
|
|
303
|
-
|
|
307
|
+
assert_eq!(input.len(), layout.input_len);
|
|
308
|
+
assert_eq!(output.len(), layout.output_len);
|
|
304
309
|
unsafe {
|
|
305
310
|
decode_to_ptr_with_unpadded_layout_mode(input, output.as_mut_ptr(), layout, alphabet, false)
|
|
306
311
|
}
|
|
@@ -314,7 +319,8 @@ pub(crate) fn decode_to_slice_with_unpadded_layout_and_alphabet_transactional(
|
|
|
314
319
|
layout: DecodeLayout,
|
|
315
320
|
alphabet: DecodeAlphabet,
|
|
316
321
|
) -> Result<(), Base64Error> {
|
|
317
|
-
|
|
322
|
+
assert_eq!(input.len(), layout.input_len);
|
|
323
|
+
assert_eq!(output.len(), layout.output_len);
|
|
318
324
|
unsafe {
|
|
319
325
|
decode_to_ptr_with_unpadded_layout_mode(input, output.as_mut_ptr(), layout, alphabet, true)
|
|
320
326
|
}
|
|
@@ -350,7 +356,8 @@ pub(crate) fn decode_to_slice_with_layout_and_alphabet_transactional(
|
|
|
350
356
|
layout: DecodeLayout,
|
|
351
357
|
alphabet: DecodeAlphabet,
|
|
352
358
|
) -> Result<(), Base64Error> {
|
|
353
|
-
|
|
359
|
+
assert_eq!(input.len(), layout.input_len);
|
|
360
|
+
assert_eq!(output.len(), layout.output_len);
|
|
354
361
|
// Transactional SIMD error handling writes only complete, validated blocks,
|
|
355
362
|
// so a lenient caller can safely fall back without modifying the suffix.
|
|
356
363
|
unsafe {
|
|
@@ -434,6 +441,7 @@ unsafe fn decode_to_ptr_with_unpadded_layout_mode(
|
|
|
434
441
|
) -> Result<(), Base64Error> {
|
|
435
442
|
let prefix_len = input.len() / 4 * 4;
|
|
436
443
|
let prefix_layout = DecodeLayout {
|
|
444
|
+
input_len: prefix_len,
|
|
437
445
|
padding: 0,
|
|
438
446
|
output_len: prefix_len / 4 * 3,
|
|
439
447
|
};
|
|
@@ -468,6 +476,7 @@ unsafe fn decode_to_ptr_with_unpadded_layout_mode(
|
|
|
468
476
|
pub(crate) fn decode_layout(input: &[u8]) -> Result<DecodeLayout, Base64Error> {
|
|
469
477
|
if input.is_empty() {
|
|
470
478
|
return Ok(DecodeLayout {
|
|
479
|
+
input_len: 0,
|
|
471
480
|
padding: 0,
|
|
472
481
|
output_len: 0,
|
|
473
482
|
});
|
|
@@ -482,6 +491,7 @@ pub(crate) fn decode_layout(input: &[u8]) -> Result<DecodeLayout, Base64Error> {
|
|
|
482
491
|
_ => 0,
|
|
483
492
|
};
|
|
484
493
|
Ok(DecodeLayout {
|
|
494
|
+
input_len: input.len(),
|
|
485
495
|
padding,
|
|
486
496
|
output_len: input.len() / 4 * 3 - padding,
|
|
487
497
|
})
|
|
@@ -489,8 +499,17 @@ pub(crate) fn decode_layout(input: &[u8]) -> Result<DecodeLayout, Base64Error> {
|
|
|
489
499
|
|
|
490
500
|
#[derive(Clone, Copy)]
|
|
491
501
|
pub(crate) struct DecodeLayout {
|
|
492
|
-
|
|
493
|
-
|
|
502
|
+
input_len: usize,
|
|
503
|
+
padding: usize,
|
|
504
|
+
output_len: usize,
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
impl DecodeLayout {
|
|
508
|
+
#[inline(always)]
|
|
509
|
+
#[cfg(any(feature = "python", test))]
|
|
510
|
+
pub(crate) fn output_len(self) -> usize {
|
|
511
|
+
self.output_len
|
|
512
|
+
}
|
|
494
513
|
}
|
|
495
514
|
|
|
496
515
|
pub(crate) unsafe fn decode_quad_ptr(
|
|
@@ -28,7 +28,7 @@ const fn encode_shuffle() -> [u8; 64] {
|
|
|
28
28
|
shuffle
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
#[target_feature(enable = "avx512vbmi")]
|
|
31
|
+
#[target_feature(enable = "avx512vbmi,avx512bw")]
|
|
32
32
|
pub(in crate::base64) unsafe fn encode<const URLSAFE: bool>(
|
|
33
33
|
input: &[u8],
|
|
34
34
|
output: *mut u8,
|
|
@@ -116,7 +116,7 @@ pub(in crate::base64) unsafe fn encode<const URLSAFE: bool>(
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
#[target_feature(enable = "avx512vbmi")]
|
|
119
|
+
#[target_feature(enable = "avx512vbmi,avx512bw")]
|
|
120
120
|
#[inline]
|
|
121
121
|
unsafe fn encode_48(
|
|
122
122
|
input: *const u8,
|
|
@@ -489,10 +489,10 @@ fn invalid_scalar_checkpoint_tail<const URLSAFE: bool, const MIXED: bool>() {
|
|
|
489
489
|
|
|
490
490
|
let output_offset = (input_offset * 7 + invalid_index) & 15;
|
|
491
491
|
let output_start = GUARD + output_offset;
|
|
492
|
-
let mut guarded_output = vec![CANARY; output_start + layout.output_len + GUARD];
|
|
492
|
+
let mut guarded_output = vec![CANARY; output_start + layout.output_len() + GUARD];
|
|
493
493
|
let result = super::super::decode_to_slice_with_layout_and_alphabet(
|
|
494
494
|
input,
|
|
495
|
-
&mut guarded_output[output_start..output_start + layout.output_len],
|
|
495
|
+
&mut guarded_output[output_start..output_start + layout.output_len()],
|
|
496
496
|
layout,
|
|
497
497
|
decode_alphabet::<URLSAFE, MIXED>(),
|
|
498
498
|
);
|