hashcodecs 1.0.0__tar.gz → 1.2.0__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.
Files changed (140) hide show
  1. hashcodecs-1.2.0/BENCHMARK.md +120 -0
  2. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/CHANGELOG.md +31 -1
  3. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/CITATION.cff +2 -2
  4. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/Cargo.lock +1 -1
  5. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/Cargo.toml +1 -1
  6. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/PKG-INFO +36 -30
  7. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/README.md +35 -29
  8. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/benches/xxhash.rs +60 -46
  9. hashcodecs-1.2.0/docs/ARCHITECTURE.md +198 -0
  10. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/api/base64.md +14 -14
  11. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/api/murmur3.md +3 -3
  12. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/api/xxh3.md +2 -2
  13. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python-batch-large.svg +50 -50
  14. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python-batch-reusable.svg +39 -39
  15. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python-batch.svg +112 -112
  16. hashcodecs-1.2.0/docs/benchmarks/base64-python-lenient.svg +127 -0
  17. hashcodecs-1.2.0/docs/benchmarks/base64-python-memoryview.svg +159 -0
  18. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python.svg +49 -49
  19. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/murmur3-python.svg +23 -23
  20. hashcodecs-1.2.0/docs/benchmarks/performance-at-a-glance.svg +37 -0
  21. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/results.csv +229 -149
  22. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/xxh3-python.svg +100 -78
  23. hashcodecs-1.2.0/docs/benchmarks/xxh3-rust-batch-remainders.svg +139 -0
  24. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/xxh3-rust.svg +52 -52
  25. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/performance.md +5 -0
  26. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/__init__.py +2 -1
  27. hashcodecs-1.2.0/hashcodecs/__init__.pyi +76 -0
  28. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/_hashcodecs.pyi +32 -8
  29. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/base64.py +12 -4
  30. hashcodecs-1.2.0/hashcodecs/base64.pyi +52 -0
  31. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/murmur3.py +12 -0
  32. hashcodecs-1.2.0/hashcodecs/murmur3.pyi +16 -0
  33. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/xxhash.py +12 -0
  34. hashcodecs-1.2.0/hashcodecs/xxhash.pyi +16 -0
  35. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hatch_build.py +5 -1
  36. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/pyproject.toml +11 -1
  37. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/backend.rs +19 -5
  38. hashcodecs-1.2.0/src/base64/alphabet.rs +41 -0
  39. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/base64/backend.rs +13 -6
  40. hashcodecs-1.0.0/src/base64/aarch64/decode.rs → hashcodecs-1.2.0/src/base64/decode/aarch64.rs +6 -10
  41. hashcodecs-1.2.0/src/base64/decode/avx2.rs +182 -0
  42. hashcodecs-1.2.0/src/base64/decode/avx512.rs +117 -0
  43. hashcodecs-1.2.0/src/base64/decode/sse41.rs +51 -0
  44. hashcodecs-1.2.0/src/base64/decode/ssse3.rs +152 -0
  45. hashcodecs-1.2.0/src/base64/decode/x86_contracts.rs +109 -0
  46. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/base64/decode.rs +20 -7
  47. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/base64/dispatch.rs +58 -32
  48. hashcodecs-1.0.0/src/base64/aarch64/encode.rs → hashcodecs-1.2.0/src/base64/encode/aarch64.rs +2 -2
  49. hashcodecs-1.0.0/src/base64/x86/encode.rs → hashcodecs-1.2.0/src/base64/encode/avx2.rs +3 -85
  50. hashcodecs-1.2.0/src/base64/encode/avx512.rs +132 -0
  51. {hashcodecs-1.0.0/src/base64/x86 → hashcodecs-1.2.0/src/base64/encode}/cache.rs +1 -1
  52. hashcodecs-1.2.0/src/base64/encode/ssse3.rs +86 -0
  53. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/base64/encode.rs +13 -3
  54. hashcodecs-1.2.0/src/base64/error.rs +30 -0
  55. hashcodecs-1.2.0/src/base64/miri_tests.rs +54 -0
  56. hashcodecs-1.2.0/src/base64/output.rs +17 -0
  57. hashcodecs-1.2.0/src/base64/proofs.rs +57 -0
  58. hashcodecs-1.0.0/src/base64/aarch64/tests.rs → hashcodecs-1.2.0/src/base64/tests/aarch64.rs +19 -8
  59. hashcodecs-1.2.0/src/base64.rs +52 -0
  60. hashcodecs-1.0.0/src/bindings/mod.rs → hashcodecs-1.2.0/src/bindings/arguments.rs +10 -125
  61. hashcodecs-1.2.0/src/bindings/base64/callbacks.rs +755 -0
  62. hashcodecs-1.2.0/src/bindings/base64/decode/plan.rs +176 -0
  63. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/base64/decode.rs +588 -234
  64. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/base64/encode.rs +55 -4
  65. hashcodecs-1.2.0/src/bindings/base64/methods.rs +678 -0
  66. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/base64/mod.rs +50 -19
  67. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/buffer.rs +139 -3
  68. hashcodecs-1.2.0/src/bindings/mod.rs +23 -0
  69. hashcodecs-1.2.0/src/bindings/murmur3/digest.rs +25 -0
  70. hashcodecs-1.0.0/src/bindings/murmur3/mod.rs → hashcodecs-1.2.0/src/bindings/murmur3/incremental.rs +8 -119
  71. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/murmur3/methods.rs +13 -16
  72. hashcodecs-1.2.0/src/bindings/murmur3/mod.rs +7 -0
  73. hashcodecs-1.2.0/src/bindings/murmur3/one_shot.rs +86 -0
  74. hashcodecs-1.2.0/src/bindings/objects.rs +147 -0
  75. hashcodecs-1.2.0/src/bindings/runtime.rs +104 -0
  76. hashcodecs-1.2.0/src/bindings/xxhash/batch.rs +270 -0
  77. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/xxhash/methods.rs +29 -26
  78. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/bindings/xxhash/mod.rs +21 -49
  79. hashcodecs-1.2.0/src/murmur3/incremental.rs +42 -0
  80. hashcodecs-1.2.0/src/murmur3/miri_tests.rs +32 -0
  81. hashcodecs-1.2.0/src/murmur3/primitives.rs +73 -0
  82. hashcodecs-1.2.0/src/murmur3/proofs.rs +58 -0
  83. hashcodecs-1.2.0/src/murmur3/tests.rs +281 -0
  84. hashcodecs-1.2.0/src/murmur3/x64_128.rs +254 -0
  85. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/murmur3/x86.rs +12 -35
  86. hashcodecs-1.2.0/src/murmur3/x86_128.rs +294 -0
  87. hashcodecs-1.2.0/src/murmur3/x86_32.rs +221 -0
  88. hashcodecs-1.2.0/src/murmur3.rs +26 -0
  89. hashcodecs-1.2.0/src/xxhash/batch.rs +191 -0
  90. hashcodecs-1.2.0/src/xxhash/hash.rs +104 -0
  91. {hashcodecs-1.0.0/src/xxhash → hashcodecs-1.2.0/src/xxhash/long}/aarch64.rs +11 -8
  92. hashcodecs-1.2.0/src/xxhash/long/avx2.rs +356 -0
  93. {hashcodecs-1.0.0/src/xxhash/x86 → hashcodecs-1.2.0/src/xxhash/long}/avx512.rs +5 -11
  94. hashcodecs-1.0.0/src/xxhash/x86/sse.rs → hashcodecs-1.2.0/src/xxhash/long/ssse3.rs +9 -6
  95. hashcodecs-1.2.0/src/xxhash/long.rs +203 -0
  96. hashcodecs-1.2.0/src/xxhash/miri_tests.rs +35 -0
  97. hashcodecs-1.2.0/src/xxhash/primitives.rs +84 -0
  98. hashcodecs-1.2.0/src/xxhash/proofs.rs +60 -0
  99. hashcodecs-1.2.0/src/xxhash/short.rs +177 -0
  100. hashcodecs-1.2.0/src/xxhash/tests.rs +235 -0
  101. hashcodecs-1.2.0/src/xxhash.rs +28 -0
  102. hashcodecs-1.2.0/tools/generate_api_metadata.py +235 -0
  103. hashcodecs-1.0.0/BENCHMARK.md +0 -63
  104. hashcodecs-1.0.0/docs/ARCHITECTURE.md +0 -133
  105. hashcodecs-1.0.0/docs/benchmarks/base64-python-memoryview.svg +0 -83
  106. hashcodecs-1.0.0/hashcodecs/base64.pyi +0 -219
  107. hashcodecs-1.0.0/src/base64/aarch64.rs +0 -11
  108. hashcodecs-1.0.0/src/base64/x86/avx512.rs +0 -234
  109. hashcodecs-1.0.0/src/base64/x86/decode.rs +0 -460
  110. hashcodecs-1.0.0/src/base64/x86.rs +0 -32
  111. hashcodecs-1.0.0/src/base64.rs +0 -265
  112. hashcodecs-1.0.0/src/bindings/base64/methods.rs +0 -1405
  113. hashcodecs-1.0.0/src/bindings/xxhash/batch.rs +0 -157
  114. hashcodecs-1.0.0/src/murmur3.rs +0 -1208
  115. hashcodecs-1.0.0/src/xxhash/x86/avx2.rs +0 -261
  116. hashcodecs-1.0.0/src/xxhash/x86.rs +0 -50
  117. hashcodecs-1.0.0/src/xxhash.rs +0 -939
  118. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/.gitignore +0 -0
  119. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/LICENSE +0 -0
  120. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/LICENSE-MIT +0 -0
  121. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/SAFETY.md +0 -0
  122. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/SECURITY.md +0 -0
  123. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/benches/base64.rs +0 -0
  124. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/benches/murmur3.rs +0 -0
  125. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/benches/support/mod.rs +0 -0
  126. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/build.rs +0 -0
  127. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/base64.md +0 -0
  128. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python-mutable.svg +0 -0
  129. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-python-reusable.svg +0 -0
  130. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/base64-rust.svg +0 -0
  131. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/murmur3-python-mutable.svg +0 -0
  132. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/benchmarks/murmur3-rust.svg +0 -0
  133. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/compatibility.md +0 -0
  134. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/index.md +0 -0
  135. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/murmur3.md +0 -0
  136. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/requirements.txt +0 -0
  137. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/docs/xxh3.md +0 -0
  138. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/hashcodecs/py.typed +0 -0
  139. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/lib.rs +0 -0
  140. {hashcodecs-1.0.0 → hashcodecs-1.2.0}/src/murmur3/dispatch.rs +0 -0
@@ -0,0 +1,120 @@
1
+ # Benchmark Details
2
+
3
+ Run the suite on Windows 10 x64 with an Intel Core Ultra 7 265K.
4
+
5
+ Pin one logical CPU. Run each case in one thread. Collect 50 Rust samples and 15 Python samples. Compile the C
6
+ baseline with AVX2, the backend that hashcodecs selects on this host. Higher throughput wins.
7
+
8
+ Build the Python wheel with CPython 3.12 and the full C API. Keep competitor values from the latest comparison run.
9
+ Use `uv run python benchmarks/render_charts.py` to render the charts. Read exact values in
10
+ [docs/benchmarks/results.csv](docs/benchmarks/results.csv).
11
+
12
+ ## Timing Controls
13
+
14
+ Every Python benchmark accepts `--samples` (default: 15) and `--minimum-sample-seconds` (default: 0.2). Their
15
+ sampling time per case is at least their product, plus calibration; use lower values only for exploratory runs. For a quicker full
16
+ hashcodecs-only pass, use `--hashcodecs-only --samples 3 --minimum-sample-seconds 0.05` with each Python benchmark
17
+ script.
18
+
19
+ ## Python Call Costs
20
+
21
+ Run `python benchmarks/python_calls.py` to measure positional calls from 0 through 256 bytes in nanoseconds per
22
+ call. Use `--keywords` for positional and keyword calls at 64 bytes, or `--thresholds` for latency around the
23
+ GIL-detachment cutoffs. The `--thread-scaling` mode measures aggregate throughput with one, two, and four threads;
24
+ it does not pin the process to one logical CPU.
25
+
26
+ ## XXH3
27
+
28
+ For the Rust comparison, link hashcodecs with xxHash 0.8.3 through `xxhash-c-sys`. Build the C baseline with AVX2.
29
+ For Python, run the upstream `xxhash` extension beside hashcodecs. Pass 32 equal-size inputs to each batch case.
30
+ The Rust remainder cases pass two or three equal-size long inputs. Run Python remainder cases with
31
+ `python benchmarks/python_xxhash.py --batch-counts 2 3`.
32
+
33
+ Use the focused one-shot run to cover the AVX2 four-chain boundaries:
34
+
35
+ ```sh
36
+ cargo bench --bench xxhash -- --sample-size 50 "xxh3_(64|128)/(241|512|768|1024|1536|2048|4096)/hashcodecs"
37
+ ```
38
+
39
+ [![Rust XXH3 throughput](docs/benchmarks/xxh3-rust.svg)](docs/benchmarks/xxh3-rust.svg)
40
+
41
+ [![Rust XXH3 batch remainder throughput](docs/benchmarks/xxh3-rust-batch-remainders.svg)](docs/benchmarks/xxh3-rust-batch-remainders.svg)
42
+
43
+ [![Python XXH3 throughput](docs/benchmarks/xxh3-python.svg)](docs/benchmarks/xxh3-python.svg)
44
+
45
+ ## Reusable Python Buffers
46
+
47
+ Pass one reusable `bytearray` to each `*_into` call.
48
+
49
+ [![Reusable Python Base64 buffers](docs/benchmarks/base64-python-reusable.svg)](docs/benchmarks/base64-python-reusable.svg)
50
+
51
+ ## Lenient Python Base64
52
+
53
+ Run `python benchmarks/python_base64.py --lenient`. The MIME cases insert CRLF after each 76-character line. The
54
+ noisy cases insert `!` at the same boundaries. Both cases measure returned bytes and reusable output buffers.
55
+
56
+ [![Lenient Python Base64 throughput](docs/benchmarks/base64-python-lenient.svg)](docs/benchmarks/base64-python-lenient.svg)
57
+
58
+ ## Python Memoryview Inputs
59
+
60
+ Use `--memoryview-input` for full immutable views and `--sliced-memoryview-input` for equal-length views with a
61
+ nonzero starting offset. The latter covers the copy/stabilization path used by slices while keeping the encoded data
62
+ identical.
63
+
64
+ [![Python Base64 memoryview inputs](docs/benchmarks/base64-python-memoryview.svg)](docs/benchmarks/base64-python-memoryview.svg)
65
+
66
+ ## Python Base64 Batches
67
+
68
+ Set the horizontal axis to batch size. Read total input throughput on the vertical axis.
69
+
70
+ [![Python Base64 batch throughput](docs/benchmarks/base64-python-batch.svg)](docs/benchmarks/base64-python-batch.svg)
71
+
72
+ For focused runs, override the item and batch sizes directly. `--decode-only` avoids carrying encode allocator state
73
+ into a decode investigation:
74
+
75
+ ```sh
76
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 512 768 1024 1280 2048 --decode-only
77
+ ```
78
+
79
+ Use a single operation when recording a sampling profile, or compare traced allocations without a sampler:
80
+
81
+ ```sh
82
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 1024 --profile-operation returned
83
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 1024 --allocation-profile
84
+ ```
85
+
86
+ Add `--profile-direction encode` for returned encoding. By default, the profiling loop assigns the next result
87
+ before releasing the previous one. Add `--discard-profile-result` to release each result before the next call. Use
88
+ `b64encode_batch_into` for that workload.
89
+
90
+ ```sh
91
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 1024 --profile-direction encode --profile-operation returned
92
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 1024 --profile-direction encode --profile-operation returned --discard-profile-result
93
+ python benchmarks/python_base64_batch.py --item-sizes 4096 --batch-sizes 1024 --profile-direction encode --allocation-profile
94
+ ```
95
+
96
+ ## Reusable Python Base64 Batch Buffers
97
+
98
+ Pass one reusable `bytearray` to each item in the batch. Use the `*_batch_into` APIs.
99
+
100
+ [![Reusable Python Base64 batch buffers](docs/benchmarks/base64-python-batch-reusable.svg)](docs/benchmarks/base64-python-batch-reusable.svg)
101
+
102
+ ## Large Python Base64 Batches
103
+
104
+ Use 1 MiB for each batch item. Set the horizontal axis to batch size.
105
+
106
+ [![Large Python Base64 batches](docs/benchmarks/base64-python-batch-large.svg)](docs/benchmarks/base64-python-batch-large.svg)
107
+
108
+ ## Mutable Python Inputs
109
+
110
+ ### Base64
111
+
112
+ Pass `bytearray` inputs to the Base64 API.
113
+
114
+ [![Mutable Python Base64 inputs](docs/benchmarks/base64-python-mutable.svg)](docs/benchmarks/base64-python-mutable.svg)
115
+
116
+ ### MurmurHash3
117
+
118
+ Pass `bytearray` inputs to the MurmurHash3 API.
119
+
120
+ [![Mutable Python MurmurHash3 inputs](docs/benchmarks/murmur3-python-mutable.svg)](docs/benchmarks/murmur3-python-mutable.svg)
@@ -4,6 +4,34 @@ This file records notable user-facing changes to `hashcodecs`. Version 1.0.0 sta
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.0] - 2026-08-25
8
+
9
+ ### What's Changed
10
+ * fix: harden free-threaded bindings and API correctness by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/47
11
+ * feat: accelerate Python batch outputs by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/48
12
+ * feat: add native lenient Base64 decoding by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/49
13
+ * update: tune Python detach thresholds by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/50
14
+ * fix: harden Base64 binding edge cases by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/51
15
+ * fix: stabilize Base64 GIL release test by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/52
16
+ * feat: accelerate XXH3 batch remainders by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/53
17
+ * perf: refresh Base64 batch benchmarks by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/54
18
+ * perf: accelerate XXH3 long inputs by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/55
19
+ * perf: profile Base64 batch allocations by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/56
20
+ * refactor: organize Python binding infrastructure by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/57
21
+
22
+
23
+ **Full Changelog**: https://github.com/kozistr/hashcodecs-rs/compare/v1.1.0...v1.2.0
24
+
25
+ ## [1.1.0] - 2026-08-23
26
+
27
+ ### What's Changed
28
+ * chore: use trusted publishing for crates.io by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/44
29
+ * refactor: establish clean architecture boundaries by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/45
30
+ * feat: borrow contiguous Python buffers by @kozistr in https://github.com/kozistr/hashcodecs-rs/pull/46
31
+
32
+
33
+ **Full Changelog**: https://github.com/kozistr/hashcodecs-rs/compare/v1.0.0...v1.1.0
34
+
7
35
  ## [1.0.0] - 2026-08-22
8
36
 
9
37
  ### What's Changed
@@ -89,7 +117,9 @@ This file records notable user-facing changes to `hashcodecs`. Version 1.0.0 sta
89
117
  - Initial Python and Rust APIs for Base64 and MurmurHash3.
90
118
  - Runtime SIMD dispatch and platform-specific CPython wheels.
91
119
 
92
- [Unreleased]: https://github.com/kozistr/hashcodecs-rs/compare/v1.0.0...HEAD
120
+ [Unreleased]: https://github.com/kozistr/hashcodecs-rs/compare/v1.2.0...HEAD
121
+ [1.2.0]: https://github.com/kozistr/hashcodecs-rs/compare/v1.1.0...v1.2.0
122
+ [1.1.0]: https://github.com/kozistr/hashcodecs-rs/compare/v1.0.0...v1.1.0
93
123
  [1.0.0]: https://github.com/kozistr/hashcodecs-rs/compare/v0.6.1...v1.0.0
94
124
  [0.6.1]: https://github.com/kozistr/hashcodecs-rs/compare/v0.6.0...v0.6.1
95
125
  [0.6.0]: https://github.com/kozistr/hashcodecs-rs/compare/v0.5.0...v0.6.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.0.0
9
- date-released: 2026-08-22
8
+ version: 1.2.0
9
+ date-released: 2026-08-25
10
10
  license: "MIT OR Apache-2.0"
11
11
  repository-code: "https://github.com/kozistr/hashcodecs-rs"
12
12
  url: "https://github.com/kozistr/hashcodecs-rs"
@@ -194,7 +194,7 @@ dependencies = [
194
194
 
195
195
  [[package]]
196
196
  name = "hashcodecs"
197
- version = "1.0.0"
197
+ version = "1.2.0"
198
198
  dependencies = [
199
199
  "base64",
200
200
  "base64-turbo",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "hashcodecs"
3
- version = "1.0.0"
3
+ version = "1.2.0"
4
4
  edition = "2024"
5
5
  rust-version = "1.89"
6
6
  description = "SIMD-accelerated Base64 codecs and fast MurmurHash3 and xxHash implementations"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hashcodecs
3
- Version: 1.0.0
3
+ Version: 1.2.0
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
@@ -41,24 +41,26 @@ Description-Content-Type: text/markdown
41
41
  ![Total Downloads](https://img.shields.io/pepy/dt/hashcodecs?style=for-the-badge&label=Total%20Downloads)
42
42
  ![Monthly Downloads](https://img.shields.io/pypi/dm/hashcodecs?style=for-the-badge&label=Monthly%20downloads)
43
43
 
44
+ <p align="center">
45
+ <a href="BENCHMARK.md">
46
+ <img src="docs/benchmarks/performance-at-a-glance.svg" alt="CPython 3.12 URL-safe Base64 encoding and decoding benchmark">
47
+ </a>
48
+ </p>
49
+
44
50
  SIMD-accelerated Base64, MurmurHash3, and XXH3 for Python and Rust.
45
51
 
46
52
  Move byte-heavy work into Rust without changing your Python inputs. `hashcodecs` accepts `bytes`, `bytearray`, and
47
53
  `memoryview`, selects the best available SIMD backend, and exposes batch and reusable-buffer APIs.
48
54
 
49
55
  ## Features
56
+
50
57
  - Base64 encode and decode with standard, URL-safe, padded, unpadded, wrapped, and canonical modes.
51
58
  - MurmurHash3 x86-32, x86-128, and x64-128 with one-shot and incremental APIs.
52
59
  - Bit-for-bit compatible XXH3-64 and XXH3-128 with one-shot and native batch APIs.
53
60
  - Caller-managed `*_into` outputs for allocation-sensitive workloads.
54
61
  - Runtime dispatch across AVX-512, AVX2, SSE4.1, SSSE3, NEON, and scalar implementations where applicable.
55
62
  - Direct CPython buffer handling for `bytes`, `bytearray`, and `memoryview` inputs.
56
- - Install wheels for CPython 3.10 through 3.15 and free-threaded CPython
57
- 3.14t and 3.15t on Linux, macOS, and Windows.
58
-
59
- ## Citation
60
-
61
- If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
63
+ - Install wheels for CPython 3.10 through 3.15 and free-threaded CPython 3.14t and 3.15t on Linux, macOS, and Windows.
62
64
 
63
65
  ## Installation
64
66
 
@@ -66,21 +68,6 @@ If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
66
68
  pip3 install hashcodecs
67
69
  ```
68
70
 
69
- ## Compatibility
70
-
71
- Version 1.x keeps the documented Python API stable under the
72
- [compatibility policy](docs/compatibility.md). Release wheels target CPython 3.10 through 3.15 on manylinux x86-64,
73
- macOS 11+ ARM64, and Windows x86-64. CPython 3.14t and 3.15t receive free-threaded wheels on the same platforms.
74
-
75
- The Rust crate and Python package share one release version. Before 1.0, minor releases may contain breaking Rust API
76
- changes; the documented Python API follows the compatibility policy below.
77
- See [Security Policy](SECURITY.md) for vulnerability reporting.
78
-
79
- ## Performance snapshot
80
-
81
- On the benchmark host, `hashcodecs.xxh3_64` processes a 1 MiB input at 78.83 GiB/s. The operator pinned one logical
82
- CPU and measured hashcodecs alone. At 256 B items in batches of 64, the Base64 batch API reaches 8.56 GiB/s for encode and 7.60 GiB/s for decode. The per-item loop reaches 4.41 and 3.39 GiB/s. Read the [benchmark details](BENCHMARK.md) and [raw results](docs/benchmarks/results.csv).
83
-
84
71
  ## Python
85
72
 
86
73
  The Base64 module follows familiar Python conventions while adding explicit padding, canonical validation, batch,
@@ -163,10 +150,14 @@ assert_eq!(
163
150
 
164
151
  ## Architecture
165
152
 
166
- The Rust core owns algorithm behavior and SIMD dispatch. A thin CPython layer handles argument parsing, buffer
153
+ The Rust core owns algorithm behavior and SIMD dispatch. A substantial CPython layer handles argument parsing, buffer
167
154
  ownership, reusable outputs, and GIL decisions; root-level Python modules provide typed public exports without
168
155
  adding per-call wrappers.
169
156
 
157
+ Each Rust algorithm exposes a small public façade. Base64 groups internals by encode and decode operation and
158
+ places ISA kernels such as `encode/avx2.rs` and `decode/ssse3.rs` under their operation. MurmurHash3 groups code by
159
+ canonical variant. XXH3 uses processing-stage modules, with long-input ISA kernels under `xxhash/long/`.
160
+
170
161
  See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the module layout, dispatch model, algorithm data flows,
171
162
  CPython boundary, and safety invariants.
172
163
 
@@ -222,12 +213,14 @@ cargo bench --bench xxhash
222
213
  uv sync --group benchmark --no-install-project
223
214
  uv run --no-project --with . python benchmarks/python_base64.py
224
215
  uv run --no-project --with . python benchmarks/python_base64_batch.py
216
+ uv run --no-project --with . python benchmarks/python_calls.py
225
217
  uv run --no-project --with . python benchmarks/python_murmur3.py
226
218
  uv run --no-project --with . python benchmarks/python_xxhash.py
227
219
  ```
228
220
 
229
- The Python benchmarks expose focused modes such as `--into`, `--bytearray-input`, `--memoryview-input`,
230
- `--incremental`, `--large`, and `--hashcodecs-only`. Use `--help` on a benchmark script for its supported modes.
221
+ The Python benchmarks expose focused modes such as `--into`, `--lenient`, `--bytearray-input`, `--memoryview-input`,
222
+ `--sliced-memoryview-input`, `--incremental`, `--large`, and `--hashcodecs-only`. All scripts also accept `--samples` and
223
+ `--minimum-sample-seconds`; use `--help` on a benchmark script for its supported modes and defaults.
231
224
 
232
225
  For the same-ISA Windows XXH3 comparison shown above, rebuild the C baseline with:
233
226
 
@@ -237,6 +230,13 @@ cargo clean -p xxhash-c-sys
237
230
  cargo bench --bench xxhash
238
231
  ```
239
232
 
233
+ ## Performance snapshot
234
+
235
+ In the full 2026-08-23 hashcodecs-only run on the benchmark host, `hashcodecs.xxh3_64` processes a 1 MiB input at
236
+ 79.20 GiB/s. With 256 B items in batches of 64, the Base64 batch API reaches 8.80 GiB/s for encode and 7.58 GiB/s for
237
+ decode. The run pins one logical CPU and uses 15 samples with a 0.2-second minimum per sample. Read the
238
+ [benchmark details](BENCHMARK.md) and [raw comparison results](docs/benchmarks/results.csv).
239
+
240
240
  ## Development
241
241
 
242
242
  Build the Python wheel and source distribution:
@@ -259,15 +259,21 @@ uv run --frozen --no-sync pytest tests --cov=hashcodecs --cov-branch --cov-fail-
259
259
  Optimized paths are also checked with differential fuzzing, Kani, strict-provenance Miri, AddressSanitizer, and
260
260
  MemorySanitizer in CI.
261
261
 
262
+ ## Compatibility
263
+
264
+ Version 1.x keeps the documented Python API stable under the
265
+ [compatibility policy](docs/compatibility.md). Release wheels target CPython 3.10 through 3.15 on manylinux x86-64,
266
+ macOS 11+ ARM64, and Windows x86-64. CPython 3.14t and 3.15t receive free-threaded wheels on the same platforms.
267
+
268
+ The Rust crate and Python package share one release version. The documented Python API follows the compatibility
269
+ policy below. See [Security Policy](SECURITY.md) for vulnerability reporting.
270
+
262
271
  ## References
263
272
 
264
273
  The Base64 SIMD implementation follows the approach described in
265
274
  [Faster Base64 Encoding and Decoding using AVX2 Instructions](https://arxiv.org/abs/1704.00605), extended with
266
275
  runtime-selected AVX-512 VBMI and AArch64 NEON backends.
267
276
 
268
- ## License
269
-
270
- Licensed under either of the following, at your option:
277
+ ## Citation
271
278
 
272
- - [Apache License 2.0](LICENSE)
273
- - [MIT License](LICENSE-MIT)
279
+ If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
@@ -10,24 +10,26 @@
10
10
  ![Total Downloads](https://img.shields.io/pepy/dt/hashcodecs?style=for-the-badge&label=Total%20Downloads)
11
11
  ![Monthly Downloads](https://img.shields.io/pypi/dm/hashcodecs?style=for-the-badge&label=Monthly%20downloads)
12
12
 
13
+ <p align="center">
14
+ <a href="BENCHMARK.md">
15
+ <img src="docs/benchmarks/performance-at-a-glance.svg" alt="CPython 3.12 URL-safe Base64 encoding and decoding benchmark">
16
+ </a>
17
+ </p>
18
+
13
19
  SIMD-accelerated Base64, MurmurHash3, and XXH3 for Python and Rust.
14
20
 
15
21
  Move byte-heavy work into Rust without changing your Python inputs. `hashcodecs` accepts `bytes`, `bytearray`, and
16
22
  `memoryview`, selects the best available SIMD backend, and exposes batch and reusable-buffer APIs.
17
23
 
18
24
  ## Features
25
+
19
26
  - Base64 encode and decode with standard, URL-safe, padded, unpadded, wrapped, and canonical modes.
20
27
  - MurmurHash3 x86-32, x86-128, and x64-128 with one-shot and incremental APIs.
21
28
  - Bit-for-bit compatible XXH3-64 and XXH3-128 with one-shot and native batch APIs.
22
29
  - Caller-managed `*_into` outputs for allocation-sensitive workloads.
23
30
  - Runtime dispatch across AVX-512, AVX2, SSE4.1, SSSE3, NEON, and scalar implementations where applicable.
24
31
  - Direct CPython buffer handling for `bytes`, `bytearray`, and `memoryview` inputs.
25
- - Install wheels for CPython 3.10 through 3.15 and free-threaded CPython
26
- 3.14t and 3.15t on Linux, macOS, and Windows.
27
-
28
- ## Citation
29
-
30
- If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
32
+ - Install wheels for CPython 3.10 through 3.15 and free-threaded CPython 3.14t and 3.15t on Linux, macOS, and Windows.
31
33
 
32
34
  ## Installation
33
35
 
@@ -35,21 +37,6 @@ If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
35
37
  pip3 install hashcodecs
36
38
  ```
37
39
 
38
- ## Compatibility
39
-
40
- Version 1.x keeps the documented Python API stable under the
41
- [compatibility policy](docs/compatibility.md). Release wheels target CPython 3.10 through 3.15 on manylinux x86-64,
42
- macOS 11+ ARM64, and Windows x86-64. CPython 3.14t and 3.15t receive free-threaded wheels on the same platforms.
43
-
44
- The Rust crate and Python package share one release version. Before 1.0, minor releases may contain breaking Rust API
45
- changes; the documented Python API follows the compatibility policy below.
46
- See [Security Policy](SECURITY.md) for vulnerability reporting.
47
-
48
- ## Performance snapshot
49
-
50
- On the benchmark host, `hashcodecs.xxh3_64` processes a 1 MiB input at 78.83 GiB/s. The operator pinned one logical
51
- CPU and measured hashcodecs alone. At 256 B items in batches of 64, the Base64 batch API reaches 8.56 GiB/s for encode and 7.60 GiB/s for decode. The per-item loop reaches 4.41 and 3.39 GiB/s. Read the [benchmark details](BENCHMARK.md) and [raw results](docs/benchmarks/results.csv).
52
-
53
40
  ## Python
54
41
 
55
42
  The Base64 module follows familiar Python conventions while adding explicit padding, canonical validation, batch,
@@ -132,10 +119,14 @@ assert_eq!(
132
119
 
133
120
  ## Architecture
134
121
 
135
- The Rust core owns algorithm behavior and SIMD dispatch. A thin CPython layer handles argument parsing, buffer
122
+ The Rust core owns algorithm behavior and SIMD dispatch. A substantial CPython layer handles argument parsing, buffer
136
123
  ownership, reusable outputs, and GIL decisions; root-level Python modules provide typed public exports without
137
124
  adding per-call wrappers.
138
125
 
126
+ Each Rust algorithm exposes a small public façade. Base64 groups internals by encode and decode operation and
127
+ places ISA kernels such as `encode/avx2.rs` and `decode/ssse3.rs` under their operation. MurmurHash3 groups code by
128
+ canonical variant. XXH3 uses processing-stage modules, with long-input ISA kernels under `xxhash/long/`.
129
+
139
130
  See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the module layout, dispatch model, algorithm data flows,
140
131
  CPython boundary, and safety invariants.
141
132
 
@@ -191,12 +182,14 @@ cargo bench --bench xxhash
191
182
  uv sync --group benchmark --no-install-project
192
183
  uv run --no-project --with . python benchmarks/python_base64.py
193
184
  uv run --no-project --with . python benchmarks/python_base64_batch.py
185
+ uv run --no-project --with . python benchmarks/python_calls.py
194
186
  uv run --no-project --with . python benchmarks/python_murmur3.py
195
187
  uv run --no-project --with . python benchmarks/python_xxhash.py
196
188
  ```
197
189
 
198
- The Python benchmarks expose focused modes such as `--into`, `--bytearray-input`, `--memoryview-input`,
199
- `--incremental`, `--large`, and `--hashcodecs-only`. Use `--help` on a benchmark script for its supported modes.
190
+ The Python benchmarks expose focused modes such as `--into`, `--lenient`, `--bytearray-input`, `--memoryview-input`,
191
+ `--sliced-memoryview-input`, `--incremental`, `--large`, and `--hashcodecs-only`. All scripts also accept `--samples` and
192
+ `--minimum-sample-seconds`; use `--help` on a benchmark script for its supported modes and defaults.
200
193
 
201
194
  For the same-ISA Windows XXH3 comparison shown above, rebuild the C baseline with:
202
195
 
@@ -206,6 +199,13 @@ cargo clean -p xxhash-c-sys
206
199
  cargo bench --bench xxhash
207
200
  ```
208
201
 
202
+ ## Performance snapshot
203
+
204
+ In the full 2026-08-23 hashcodecs-only run on the benchmark host, `hashcodecs.xxh3_64` processes a 1 MiB input at
205
+ 79.20 GiB/s. With 256 B items in batches of 64, the Base64 batch API reaches 8.80 GiB/s for encode and 7.58 GiB/s for
206
+ decode. The run pins one logical CPU and uses 15 samples with a 0.2-second minimum per sample. Read the
207
+ [benchmark details](BENCHMARK.md) and [raw comparison results](docs/benchmarks/results.csv).
208
+
209
209
  ## Development
210
210
 
211
211
  Build the Python wheel and source distribution:
@@ -228,15 +228,21 @@ uv run --frozen --no-sync pytest tests --cov=hashcodecs --cov-branch --cov-fail-
228
228
  Optimized paths are also checked with differential fuzzing, Kani, strict-provenance Miri, AddressSanitizer, and
229
229
  MemorySanitizer in CI.
230
230
 
231
+ ## Compatibility
232
+
233
+ Version 1.x keeps the documented Python API stable under the
234
+ [compatibility policy](docs/compatibility.md). Release wheels target CPython 3.10 through 3.15 on manylinux x86-64,
235
+ macOS 11+ ARM64, and Windows x86-64. CPython 3.14t and 3.15t receive free-threaded wheels on the same platforms.
236
+
237
+ The Rust crate and Python package share one release version. The documented Python API follows the compatibility
238
+ policy below. See [Security Policy](SECURITY.md) for vulnerability reporting.
239
+
231
240
  ## References
232
241
 
233
242
  The Base64 SIMD implementation follows the approach described in
234
243
  [Faster Base64 Encoding and Decoding using AVX2 Instructions](https://arxiv.org/abs/1704.00605), extended with
235
244
  runtime-selected AVX-512 VBMI and AArch64 NEON backends.
236
245
 
237
- ## License
238
-
239
- Licensed under either of the following, at your option:
246
+ ## Citation
240
247
 
241
- - [Apache License 2.0](LICENSE)
242
- - [MIT License](LICENSE-MIT)
248
+ If you use `hashcodecs`, cite [CITATION.cff](CITATION.cff).
@@ -6,7 +6,18 @@ use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_m
6
6
 
7
7
  mod support;
8
8
 
9
- const SIZES: [usize; 5] = [64, 1024, 4 * 1024, 1024 * 1024, 8 * 1024 * 1024];
9
+ const SIZES: [usize; 10] = [
10
+ 64,
11
+ 241,
12
+ 512,
13
+ 768,
14
+ 1024,
15
+ 1536,
16
+ 2048,
17
+ 4 * 1024,
18
+ 1024 * 1024,
19
+ 8 * 1024 * 1024,
20
+ ];
10
21
 
11
22
  fn data(size: usize, salt: u8) -> Vec<u8> {
12
23
  (0..size)
@@ -61,52 +72,55 @@ fn one_shot(c: &mut Criterion) {
61
72
  }
62
73
 
63
74
  fn batch(c: &mut Criterion) {
64
- const ITEMS: usize = 32;
65
- for size in [64, 1024, 4 * 1024, 1024 * 1024] {
66
- let owned = (0..ITEMS)
67
- .map(|index| data(size, index as u8))
68
- .collect::<Vec<_>>();
69
- let inputs = owned.iter().map(Vec::as_slice).collect::<Vec<_>>();
70
- let mut group = c.benchmark_group(format!("xxh3_batch/{size}"));
71
- group.throughput(Throughput::Bytes((size * ITEMS) as u64));
75
+ for items in [2, 3, 32] {
76
+ for size in [64, 1024, 4 * 1024, 1024 * 1024] {
77
+ let owned = (0..items)
78
+ .map(|index| data(size, index as u8))
79
+ .collect::<Vec<_>>();
80
+ let inputs = owned.iter().map(Vec::as_slice).collect::<Vec<_>>();
81
+ let mut group = c.benchmark_group(format!("xxh3_batch/{items}_items/{size}"));
82
+ group.throughput(Throughput::Bytes((size * items) as u64));
72
83
 
73
- group.bench_with_input(
74
- BenchmarkId::new("hashcodecs_64", ITEMS),
75
- &inputs,
76
- |bench, inputs| bench.iter(|| hashcodecs::xxhash::xxh3_64_batch(black_box(inputs), 42)),
77
- );
78
- group.bench_with_input(
79
- BenchmarkId::new("upstream_c_64", ITEMS),
80
- &inputs,
81
- |bench, inputs| {
82
- bench.iter(|| {
83
- inputs
84
- .iter()
85
- .map(|input| c_xxh3_64(black_box(input), 42))
86
- .collect::<Vec<_>>()
87
- })
88
- },
89
- );
90
- group.bench_with_input(
91
- BenchmarkId::new("hashcodecs_128", ITEMS),
92
- &inputs,
93
- |bench, inputs| {
94
- bench.iter(|| hashcodecs::xxhash::xxh3_128_batch(black_box(inputs), 42))
95
- },
96
- );
97
- group.bench_with_input(
98
- BenchmarkId::new("upstream_c_128", ITEMS),
99
- &inputs,
100
- |bench, inputs| {
101
- bench.iter(|| {
102
- inputs
103
- .iter()
104
- .map(|input| c_xxh3_128(black_box(input), 42))
105
- .collect::<Vec<_>>()
106
- })
107
- },
108
- );
109
- group.finish();
84
+ group.bench_with_input(
85
+ BenchmarkId::new("hashcodecs_64", items),
86
+ &inputs,
87
+ |bench, inputs| {
88
+ bench.iter(|| hashcodecs::xxhash::xxh3_64_batch(black_box(inputs), 42))
89
+ },
90
+ );
91
+ group.bench_with_input(
92
+ BenchmarkId::new("upstream_c_64", items),
93
+ &inputs,
94
+ |bench, inputs| {
95
+ bench.iter(|| {
96
+ inputs
97
+ .iter()
98
+ .map(|input| c_xxh3_64(black_box(input), 42))
99
+ .collect::<Vec<_>>()
100
+ })
101
+ },
102
+ );
103
+ group.bench_with_input(
104
+ BenchmarkId::new("hashcodecs_128", items),
105
+ &inputs,
106
+ |bench, inputs| {
107
+ bench.iter(|| hashcodecs::xxhash::xxh3_128_batch(black_box(inputs), 42))
108
+ },
109
+ );
110
+ group.bench_with_input(
111
+ BenchmarkId::new("upstream_c_128", items),
112
+ &inputs,
113
+ |bench, inputs| {
114
+ bench.iter(|| {
115
+ inputs
116
+ .iter()
117
+ .map(|input| c_xxh3_128(black_box(input), 42))
118
+ .collect::<Vec<_>>()
119
+ })
120
+ },
121
+ );
122
+ group.finish();
123
+ }
110
124
  }
111
125
  }
112
126