stringzillas-cuda 4.2.0__tar.gz → 4.2.2__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 (49) hide show
  1. {stringzillas_cuda-4.2.0/stringzillas_cuda.egg-info → stringzillas_cuda-4.2.2}/PKG-INFO +176 -8
  2. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/README.md +174 -6
  3. stringzillas_cuda-4.2.2/VERSION +1 -0
  4. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/c/stringzilla.c +14 -8
  5. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/c/stringzillas.cuh +10 -0
  6. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/compare.h +4 -4
  7. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/find.h +4 -4
  8. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/hash.h +385 -722
  9. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/intersect.h +5 -5
  10. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/memory.h +69 -4
  11. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/sort.h +2 -2
  12. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/stringzilla.h +79 -5
  13. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/types.h +1 -1
  14. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/similarities.hpp +21 -15
  15. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/setup.py +7 -2
  16. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2/stringzillas_cuda.egg-info}/PKG-INFO +176 -8
  17. stringzillas_cuda-4.2.2/stringzillas_cuda.egg-info/requires.txt +1 -0
  18. stringzillas_cuda-4.2.0/VERSION +0 -1
  19. stringzillas_cuda-4.2.0/stringzillas_cuda.egg-info/requires.txt +0 -1
  20. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/LICENSE +0 -0
  21. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/MANIFEST.in +0 -0
  22. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/SZ_TARGET.env +0 -0
  23. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/build_backend.py +0 -0
  24. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/c/stringzillas.cpp +0 -0
  25. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/c/stringzillas.cu +0 -0
  26. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/cli/__init__.py +0 -0
  27. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/cli/split.py +0 -0
  28. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/cli/wc.py +0 -0
  29. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/fork_union/include/fork_union.h +0 -0
  30. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/fork_union/include/fork_union.hpp +0 -0
  31. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/module.modulemap +0 -0
  32. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/small_string.h +0 -0
  33. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/stringzilla.hpp +0 -0
  34. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzilla/types.hpp +0 -0
  35. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/fingerprints.cuh +0 -0
  36. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/fingerprints.hpp +0 -0
  37. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/similarities.cuh +0 -0
  38. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/stringzillas.h +0 -0
  39. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/types.cuh +0 -0
  40. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/include/stringzillas/types.hpp +0 -0
  41. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/pyproject.toml +0 -0
  42. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/python/stringzilla.c +0 -0
  43. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/python/stringzillas.c +0 -0
  44. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/scripts/test_stringzilla.py +0 -0
  45. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/scripts/test_stringzillas.py +0 -0
  46. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/setup.cfg +0 -0
  47. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/stringzillas_cuda.egg-info/SOURCES.txt +0 -0
  48. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/stringzillas_cuda.egg-info/dependency_links.txt +0 -0
  49. {stringzillas_cuda-4.2.0 → stringzillas_cuda-4.2.2}/stringzillas_cuda.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stringzillas-cuda
3
- Version: 4.2.0
3
+ Version: 4.2.2
4
4
  Summary: Search, hash, sort, fingerprint, and fuzzy-match strings faster via SWAR, SIMD, and CUDA on Nvidia GPUs
5
5
  Home-page: https://github.com/ashvardanian/StringZilla
6
6
  Author: Ash Vardanian
@@ -30,7 +30,7 @@ Classifier: Topic :: Text Processing :: Indexing
30
30
  Requires-Python: >=3.8
31
31
  Description-Content-Type: text/markdown
32
32
  License-File: LICENSE
33
- Requires-Dist: stringzilla==4.2.0
33
+ Requires-Dist: stringzilla==4.2.2
34
34
  Dynamic: author
35
35
  Dynamic: author-email
36
36
  Dynamic: classifier
@@ -374,6 +374,7 @@ Consider contributing if you need a feature that's not yet implemented.
374
374
  | Character Set Search | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
375
375
  | Sorting & Sequence Operations | 🌳 | ✅ | ✅ | ✅ | ✅ | ⚪ | ⚪ | ⚪ |
376
376
  | Streaming Hashes | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
377
+ | SHA-256 Checksums | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
377
378
  | Small String Class | 🧐 | ✅ | ✅ | ❌ | ⚪ | ❌ | ❌ | ❌ |
378
379
  | Lazy Ranges, Compressed Arrays | 🌳 | ❌ | ✅ | ✅ | ✅ | ❌ | ⚪ | ⚪ |
379
380
  | | | | | | | | | |
@@ -507,6 +508,55 @@ streamed = hasher.digest() # or `hexdigest()` for a string
507
508
  assert one == streamed
508
509
  ```
509
510
 
511
+ ### SHA-256 Checksums
512
+
513
+ SHA-256 cryptographic checksums are also available for single-shot and incremental hashing:
514
+
515
+ ```py
516
+ import stringzilla as sz
517
+
518
+ # One-shot SHA-256
519
+ digest_bytes = sz.sha256(b"Hello, world!")
520
+ assert len(digest_bytes) == 32
521
+
522
+ # Incremental SHA-256
523
+ hasher = sz.Sha256()
524
+ hasher.update(b"Hello, ").update(b"world!")
525
+ digest_bytes = hasher.digest()
526
+ digest_hex = hasher.hexdigest() # 64 character lowercase hex string
527
+
528
+ # HMAC-SHA256 for message authentication
529
+ mac = sz.hmac_sha256(key=b"secret", message=b"Hello, world!")
530
+ ```
531
+
532
+ StringZilla integrates seamlessly with memory-mapped files for efficient large file processing.
533
+ The traditional approach with `hashlib`:
534
+
535
+ ```python
536
+ import hashlib
537
+
538
+ with open("xlsum.csv", "rb") as streamed_file:
539
+ hasher = hashlib.sha256()
540
+ while chunk := streamed_file.read(4096):
541
+ hasher.update(chunk)
542
+ checksum = hasher.hexdigest()
543
+ ```
544
+
545
+ Can be simplified with StringZilla:
546
+
547
+ ```python
548
+ from stringzilla import Sha256, File
549
+
550
+ mapped_file = File("xlsum.csv")
551
+ checksum = Sha256().update(mapped_file).hexdigest()
552
+ ```
553
+
554
+ Both output the same digest: `7278165ce01a4ac1e8806c97f32feae908036ca3d910f5177d2cf375e20aeae1`.
555
+ OpenSSL (powering `hashlib`) has faster Assembly kernels, but StringZilla avoids file I/O overhead with memory mapping and skips Python's abstraction layers:
556
+
557
+ - OpenSSL-backed `hashlib.sha256`: 12.6s
558
+ - StringZilla end-to-end: 4.0s — __3× faster!__
559
+
510
560
  ### Collection-Level Operations
511
561
 
512
562
  Once split into a `Strs` object, you can sort, shuffle, and reorganize the slices with minimal memory footprint.
@@ -800,12 +850,19 @@ sz_u64_t hash = sz_hash(haystack.start, haystack.length, 42); // 42 is the se
800
850
  sz_u64_t checksum = sz_bytesum(haystack.start, haystack.length); // or accumulate byte values
801
851
 
802
852
  // Hash strings incrementally with "init", "update", and "digest":
803
- sz_hash_state_t state;
853
+ sz_hash_state_t state;
804
854
  sz_hash_state_init(&state, 42);
805
855
  sz_hash_state_update(&state, haystack.start, 1); // first char
806
856
  sz_hash_state_update(&state, haystack.start + 1, haystack.length - 1); // rest of the string
807
857
  sz_u64_t streamed_hash = sz_hash_state_digest(&state);
808
858
 
859
+ // SHA-256 cryptographic checksums
860
+ sz_u8_t digest[32];
861
+ sz_sha256_state_t sha_state;
862
+ sz_sha256_state_init(&sha_state);
863
+ sz_sha256_state_update(&sha_state, haystack.start, haystack.length);
864
+ sz_sha256_state_digest(&sha_state, digest);
865
+
809
866
  // Perform collection level operations
810
867
  sz_sequence_t array = {your_handle, your_count, your_get_start, your_get_length};
811
868
  sz_sequence_argsort(&array, &your_config);
@@ -1463,7 +1520,7 @@ __`SZ_DEBUG`__:
1463
1520
  > If you want to enable more aggressive bounds-checking, define `SZ_DEBUG` before including the header.
1464
1521
  > If not explicitly set, it will be inferred from the build type.
1465
1522
 
1466
- __`SZ_USE_WESTMERE`, `SZ_USE_HASWELL`, `SZ_USE_SKYLAKE`, `SZ_USE_ICE`, `SZ_USE_NEON`, `SZ_USE_NEON_AES`, `SZ_USE_SVE`, `SZ_USE_SVE2`, `SZ_USE_SVE2_AES`__:
1523
+ __`SZ_USE_GOLDMONT`, `SZ_USE_WESTMERE`, `SZ_USE_HASWELL`, `SZ_USE_SKYLAKE`, `SZ_USE_ICE`, `SZ_USE_NEON`, `SZ_USE_NEON_AES`, `SZ_USE_NEON_SHA`, `SZ_USE_SVE`, `SZ_USE_SVE2`, `SZ_USE_SVE2_AES`__:
1467
1524
 
1468
1525
  > One can explicitly disable certain families of SIMD instructions for compatibility purposes.
1469
1526
  > Default values are inferred at compile time depending on compiler support (for dynamic dispatch) and the target architecture (for static dispatch).
@@ -1597,6 +1654,27 @@ map.insert("a", 1);
1597
1654
  assert_eq!(map.get("a"), Some(&1));
1598
1655
  ```
1599
1656
 
1657
+ ### SHA-256 Checksums
1658
+
1659
+ SHA-256 cryptographic checksums are available:
1660
+
1661
+ ```rs
1662
+ use stringzilla::sz;
1663
+
1664
+ // One-shot SHA-256
1665
+ let digest = sz::Sha256::hash(b"Hello, world!");
1666
+ assert_eq!(digest.len(), 32);
1667
+
1668
+ // Incremental SHA-256
1669
+ let mut hasher = sz::Sha256::new();
1670
+ hasher.update(b"Hello, ");
1671
+ hasher.update(b"world!");
1672
+ let digest = hasher.digest();
1673
+
1674
+ // HMAC-SHA256 for message authentication
1675
+ let mac = sz::hmac_sha256(b"secret", b"Hello, world!");
1676
+ ```
1677
+
1600
1678
  ### Similarity Scores
1601
1679
 
1602
1680
  StringZilla exposes high-performance, batch-oriented similarity via the `szs` module.
@@ -1775,6 +1853,24 @@ const streamedHash = hasher.digest(); // returns BigInt
1775
1853
  console.assert(hash === streamedHash);
1776
1854
  ```
1777
1855
 
1856
+ ### SHA-256 Checksums
1857
+
1858
+ SHA-256 cryptographic checksums are available:
1859
+
1860
+ ```js
1861
+ import sz from 'stringzilla';
1862
+
1863
+ // One-shot SHA-256
1864
+ const digest = sz.sha256(Buffer.from('Hello, world!')); // returns Buffer (32 bytes)
1865
+
1866
+ // Incremental SHA-256
1867
+ const hasher = new sz.Sha256();
1868
+ hasher.update(Buffer.from('Hello, '));
1869
+ hasher.update(Buffer.from('world!'));
1870
+ const digestBuffer = hasher.digest(); // returns Buffer (32 bytes)
1871
+ const digestHex = hasher.hexdigest(); // returns string (64 hex chars)
1872
+ ```
1873
+
1778
1874
  ## Quick Start: Swift 🍏
1779
1875
 
1780
1876
  StringZilla can be added as a dependency in the Swift Package Manager.
@@ -1808,13 +1904,31 @@ import StringZilla
1808
1904
  let hash = "Hello, world!".hash(seed: 42)
1809
1905
 
1810
1906
  // Incremental hashing for streaming data
1811
- var hasher = SZHasher(seed: 42)
1907
+ var hasher = StringZillaHasher(seed: 42)
1812
1908
  hasher.update("Hello, ")
1813
1909
  hasher.update("world!")
1814
1910
  let streamedHash = hasher.digest()
1815
1911
  assert(hash == streamedHash)
1816
1912
  ```
1817
1913
 
1914
+ ### SHA-256 Checksums
1915
+
1916
+ SHA-256 cryptographic checksums are available:
1917
+
1918
+ ```swift
1919
+ import StringZilla
1920
+
1921
+ // One-shot SHA-256
1922
+ let digest = "Hello, world!".sha256() // returns [UInt8] (32 bytes)
1923
+
1924
+ // Incremental SHA-256
1925
+ var hasher = StringZillaSha256Hasher()
1926
+ hasher.update("Hello, ")
1927
+ hasher.update("world!")
1928
+ let digestBytes = hasher.digest() // [UInt8] (32 bytes)
1929
+ let digestHex = hasher.hexdigest() // String (64 hex chars)
1930
+ ```
1931
+
1818
1932
  ## Quick Start: GoLang 🦫
1819
1933
 
1820
1934
  Add the Go binding as a module dependency:
@@ -1867,16 +1981,64 @@ func main() {
1867
1981
 
1868
1982
  ### Hash
1869
1983
 
1870
- Single-shot and incremental hashing are both supported:
1984
+ Single-shot and incremental hashing are both supported.
1985
+ The `Hasher` type implements Go's standard `hash.Hash64` and `io.Writer` interfaces:
1871
1986
 
1872
1987
  ```go
1988
+ import (
1989
+ "io"
1990
+ sz "github.com/ashvardanian/stringzilla/golang"
1991
+ )
1992
+
1993
+ // One-shot hashing
1873
1994
  one := sz.Hash("Hello, world!", 42)
1874
1995
 
1996
+ // Streaming hasher (implements hash.Hash64 and io.Writer)
1875
1997
  hasher := sz.NewHasher(42)
1876
1998
  hasher.Write([]byte("Hello, "))
1877
1999
  hasher.Write([]byte("world!"))
1878
- streamed := hasher.Digest()
1879
- fmt.Println(one == streamed) // true
2000
+ streamed := hasher.Digest() // or hasher.Sum64()
2001
+ fmt.Println(one == streamed) // true
2002
+
2003
+ // Works with io.Copy and any io.Reader
2004
+ file, _ := os.Open("data.txt")
2005
+ hasher.Reset()
2006
+ io.Copy(hasher, file)
2007
+ fileHash := hasher.Sum64()
2008
+ ```
2009
+
2010
+ ### SHA-256 Checksums
2011
+
2012
+ SHA-256 cryptographic checksums are available.
2013
+ The `Sha256` type implements Go's standard `hash.Hash` and `io.Writer` interfaces:
2014
+
2015
+ ```go
2016
+ import (
2017
+ "io"
2018
+ sz "github.com/ashvardanian/stringzilla/golang"
2019
+ )
2020
+
2021
+ // One-shot SHA-256
2022
+ digest := sz.HashSha256([]byte("Hello, world!"))
2023
+ fmt.Printf("%x\n", digest) // prints 32-byte hash in hex
2024
+
2025
+ // Streaming SHA-256 (implements hash.Hash and io.Writer)
2026
+ hasher := sz.NewSha256()
2027
+ hasher.Write([]byte("Hello, "))
2028
+ hasher.Write([]byte("world!"))
2029
+ digestBytes := hasher.Digest() // [32]byte
2030
+ digestHex := hasher.Hexdigest() // string (64 hex chars)
2031
+
2032
+ // Works with io.Copy and any io.Reader
2033
+ file, _ := os.Open("data.bin")
2034
+ hasher.Reset()
2035
+ io.Copy(hasher, file)
2036
+ fileDigest := hasher.Digest()
2037
+
2038
+ // Standard hash.Hash interface methods
2039
+ sum := hasher.Sum(nil) // []byte with 32 bytes
2040
+ size := hasher.Size() // 32
2041
+ blockSize := hasher.BlockSize() // 64
1880
2042
  ```
1881
2043
 
1882
2044
  ## Algorithms & Design Decisions 📚
@@ -2173,6 +2335,11 @@ Also, unlike some alternatives, with "masked" AVX-512 and "predicated" SVE loads
2173
2335
  > § Reading materials.
2174
2336
  > [Stress-testing hash functions for avalance behaviour, collision bias, and distribution](https://github.com/ashvardanian/HashEvals).
2175
2337
 
2338
+ ### SHA-256 Checksums
2339
+
2340
+ In addition to the fast AES-based hash, StringZilla implements hardware-accelerated SHA-256 cryptographic checksums.
2341
+ The implementation follows the [FIPS 180-4 specification](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) and provides multiple backends.
2342
+
2176
2343
  ### Random Generation
2177
2344
 
2178
2345
  StringZilla implements a fast [Pseudorandom Number Generator][faq-prng] inspired by the "AES-CTR-128" algorithm, reusing the same AES primitives as the hash function.
@@ -2261,6 +2428,7 @@ If you are looking for more reading materials on this topic, consider the follow
2261
2428
  - [The Painful Pitfalls of C++ STL Strings](https://ashvardanian.com/posts/painful-strings/).
2262
2429
  - [Processing Strings 109x Faster than Nvidia on H100](https://ashvardanian.com/posts/stringwars-on-gpus/).
2263
2430
  - [How a String Library Beat OpenCV at Image Processing by 4x](https://ashvardanian.com/posts/image-processing-with-strings/).
2431
+ - [2x Faster Hashes on AWS Graviton: NEON → SVE2](https://ashvardanian.com/posts/aws-graviton-checksums-on-neon-vs-sve/).
2264
2432
 
2265
2433
  ## License 📜
2266
2434
 
@@ -329,6 +329,7 @@ Consider contributing if you need a feature that's not yet implemented.
329
329
  | Character Set Search | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
330
330
  | Sorting & Sequence Operations | 🌳 | ✅ | ✅ | ✅ | ✅ | ⚪ | ⚪ | ⚪ |
331
331
  | Streaming Hashes | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
332
+ | SHA-256 Checksums | 🌳 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
332
333
  | Small String Class | 🧐 | ✅ | ✅ | ❌ | ⚪ | ❌ | ❌ | ❌ |
333
334
  | Lazy Ranges, Compressed Arrays | 🌳 | ❌ | ✅ | ✅ | ✅ | ❌ | ⚪ | ⚪ |
334
335
  | | | | | | | | | |
@@ -462,6 +463,55 @@ streamed = hasher.digest() # or `hexdigest()` for a string
462
463
  assert one == streamed
463
464
  ```
464
465
 
466
+ ### SHA-256 Checksums
467
+
468
+ SHA-256 cryptographic checksums are also available for single-shot and incremental hashing:
469
+
470
+ ```py
471
+ import stringzilla as sz
472
+
473
+ # One-shot SHA-256
474
+ digest_bytes = sz.sha256(b"Hello, world!")
475
+ assert len(digest_bytes) == 32
476
+
477
+ # Incremental SHA-256
478
+ hasher = sz.Sha256()
479
+ hasher.update(b"Hello, ").update(b"world!")
480
+ digest_bytes = hasher.digest()
481
+ digest_hex = hasher.hexdigest() # 64 character lowercase hex string
482
+
483
+ # HMAC-SHA256 for message authentication
484
+ mac = sz.hmac_sha256(key=b"secret", message=b"Hello, world!")
485
+ ```
486
+
487
+ StringZilla integrates seamlessly with memory-mapped files for efficient large file processing.
488
+ The traditional approach with `hashlib`:
489
+
490
+ ```python
491
+ import hashlib
492
+
493
+ with open("xlsum.csv", "rb") as streamed_file:
494
+ hasher = hashlib.sha256()
495
+ while chunk := streamed_file.read(4096):
496
+ hasher.update(chunk)
497
+ checksum = hasher.hexdigest()
498
+ ```
499
+
500
+ Can be simplified with StringZilla:
501
+
502
+ ```python
503
+ from stringzilla import Sha256, File
504
+
505
+ mapped_file = File("xlsum.csv")
506
+ checksum = Sha256().update(mapped_file).hexdigest()
507
+ ```
508
+
509
+ Both output the same digest: `7278165ce01a4ac1e8806c97f32feae908036ca3d910f5177d2cf375e20aeae1`.
510
+ OpenSSL (powering `hashlib`) has faster Assembly kernels, but StringZilla avoids file I/O overhead with memory mapping and skips Python's abstraction layers:
511
+
512
+ - OpenSSL-backed `hashlib.sha256`: 12.6s
513
+ - StringZilla end-to-end: 4.0s — __3× faster!__
514
+
465
515
  ### Collection-Level Operations
466
516
 
467
517
  Once split into a `Strs` object, you can sort, shuffle, and reorganize the slices with minimal memory footprint.
@@ -755,12 +805,19 @@ sz_u64_t hash = sz_hash(haystack.start, haystack.length, 42); // 42 is the se
755
805
  sz_u64_t checksum = sz_bytesum(haystack.start, haystack.length); // or accumulate byte values
756
806
 
757
807
  // Hash strings incrementally with "init", "update", and "digest":
758
- sz_hash_state_t state;
808
+ sz_hash_state_t state;
759
809
  sz_hash_state_init(&state, 42);
760
810
  sz_hash_state_update(&state, haystack.start, 1); // first char
761
811
  sz_hash_state_update(&state, haystack.start + 1, haystack.length - 1); // rest of the string
762
812
  sz_u64_t streamed_hash = sz_hash_state_digest(&state);
763
813
 
814
+ // SHA-256 cryptographic checksums
815
+ sz_u8_t digest[32];
816
+ sz_sha256_state_t sha_state;
817
+ sz_sha256_state_init(&sha_state);
818
+ sz_sha256_state_update(&sha_state, haystack.start, haystack.length);
819
+ sz_sha256_state_digest(&sha_state, digest);
820
+
764
821
  // Perform collection level operations
765
822
  sz_sequence_t array = {your_handle, your_count, your_get_start, your_get_length};
766
823
  sz_sequence_argsort(&array, &your_config);
@@ -1418,7 +1475,7 @@ __`SZ_DEBUG`__:
1418
1475
  > If you want to enable more aggressive bounds-checking, define `SZ_DEBUG` before including the header.
1419
1476
  > If not explicitly set, it will be inferred from the build type.
1420
1477
 
1421
- __`SZ_USE_WESTMERE`, `SZ_USE_HASWELL`, `SZ_USE_SKYLAKE`, `SZ_USE_ICE`, `SZ_USE_NEON`, `SZ_USE_NEON_AES`, `SZ_USE_SVE`, `SZ_USE_SVE2`, `SZ_USE_SVE2_AES`__:
1478
+ __`SZ_USE_GOLDMONT`, `SZ_USE_WESTMERE`, `SZ_USE_HASWELL`, `SZ_USE_SKYLAKE`, `SZ_USE_ICE`, `SZ_USE_NEON`, `SZ_USE_NEON_AES`, `SZ_USE_NEON_SHA`, `SZ_USE_SVE`, `SZ_USE_SVE2`, `SZ_USE_SVE2_AES`__:
1422
1479
 
1423
1480
  > One can explicitly disable certain families of SIMD instructions for compatibility purposes.
1424
1481
  > Default values are inferred at compile time depending on compiler support (for dynamic dispatch) and the target architecture (for static dispatch).
@@ -1552,6 +1609,27 @@ map.insert("a", 1);
1552
1609
  assert_eq!(map.get("a"), Some(&1));
1553
1610
  ```
1554
1611
 
1612
+ ### SHA-256 Checksums
1613
+
1614
+ SHA-256 cryptographic checksums are available:
1615
+
1616
+ ```rs
1617
+ use stringzilla::sz;
1618
+
1619
+ // One-shot SHA-256
1620
+ let digest = sz::Sha256::hash(b"Hello, world!");
1621
+ assert_eq!(digest.len(), 32);
1622
+
1623
+ // Incremental SHA-256
1624
+ let mut hasher = sz::Sha256::new();
1625
+ hasher.update(b"Hello, ");
1626
+ hasher.update(b"world!");
1627
+ let digest = hasher.digest();
1628
+
1629
+ // HMAC-SHA256 for message authentication
1630
+ let mac = sz::hmac_sha256(b"secret", b"Hello, world!");
1631
+ ```
1632
+
1555
1633
  ### Similarity Scores
1556
1634
 
1557
1635
  StringZilla exposes high-performance, batch-oriented similarity via the `szs` module.
@@ -1730,6 +1808,24 @@ const streamedHash = hasher.digest(); // returns BigInt
1730
1808
  console.assert(hash === streamedHash);
1731
1809
  ```
1732
1810
 
1811
+ ### SHA-256 Checksums
1812
+
1813
+ SHA-256 cryptographic checksums are available:
1814
+
1815
+ ```js
1816
+ import sz from 'stringzilla';
1817
+
1818
+ // One-shot SHA-256
1819
+ const digest = sz.sha256(Buffer.from('Hello, world!')); // returns Buffer (32 bytes)
1820
+
1821
+ // Incremental SHA-256
1822
+ const hasher = new sz.Sha256();
1823
+ hasher.update(Buffer.from('Hello, '));
1824
+ hasher.update(Buffer.from('world!'));
1825
+ const digestBuffer = hasher.digest(); // returns Buffer (32 bytes)
1826
+ const digestHex = hasher.hexdigest(); // returns string (64 hex chars)
1827
+ ```
1828
+
1733
1829
  ## Quick Start: Swift 🍏
1734
1830
 
1735
1831
  StringZilla can be added as a dependency in the Swift Package Manager.
@@ -1763,13 +1859,31 @@ import StringZilla
1763
1859
  let hash = "Hello, world!".hash(seed: 42)
1764
1860
 
1765
1861
  // Incremental hashing for streaming data
1766
- var hasher = SZHasher(seed: 42)
1862
+ var hasher = StringZillaHasher(seed: 42)
1767
1863
  hasher.update("Hello, ")
1768
1864
  hasher.update("world!")
1769
1865
  let streamedHash = hasher.digest()
1770
1866
  assert(hash == streamedHash)
1771
1867
  ```
1772
1868
 
1869
+ ### SHA-256 Checksums
1870
+
1871
+ SHA-256 cryptographic checksums are available:
1872
+
1873
+ ```swift
1874
+ import StringZilla
1875
+
1876
+ // One-shot SHA-256
1877
+ let digest = "Hello, world!".sha256() // returns [UInt8] (32 bytes)
1878
+
1879
+ // Incremental SHA-256
1880
+ var hasher = StringZillaSha256Hasher()
1881
+ hasher.update("Hello, ")
1882
+ hasher.update("world!")
1883
+ let digestBytes = hasher.digest() // [UInt8] (32 bytes)
1884
+ let digestHex = hasher.hexdigest() // String (64 hex chars)
1885
+ ```
1886
+
1773
1887
  ## Quick Start: GoLang 🦫
1774
1888
 
1775
1889
  Add the Go binding as a module dependency:
@@ -1822,16 +1936,64 @@ func main() {
1822
1936
 
1823
1937
  ### Hash
1824
1938
 
1825
- Single-shot and incremental hashing are both supported:
1939
+ Single-shot and incremental hashing are both supported.
1940
+ The `Hasher` type implements Go's standard `hash.Hash64` and `io.Writer` interfaces:
1826
1941
 
1827
1942
  ```go
1943
+ import (
1944
+ "io"
1945
+ sz "github.com/ashvardanian/stringzilla/golang"
1946
+ )
1947
+
1948
+ // One-shot hashing
1828
1949
  one := sz.Hash("Hello, world!", 42)
1829
1950
 
1951
+ // Streaming hasher (implements hash.Hash64 and io.Writer)
1830
1952
  hasher := sz.NewHasher(42)
1831
1953
  hasher.Write([]byte("Hello, "))
1832
1954
  hasher.Write([]byte("world!"))
1833
- streamed := hasher.Digest()
1834
- fmt.Println(one == streamed) // true
1955
+ streamed := hasher.Digest() // or hasher.Sum64()
1956
+ fmt.Println(one == streamed) // true
1957
+
1958
+ // Works with io.Copy and any io.Reader
1959
+ file, _ := os.Open("data.txt")
1960
+ hasher.Reset()
1961
+ io.Copy(hasher, file)
1962
+ fileHash := hasher.Sum64()
1963
+ ```
1964
+
1965
+ ### SHA-256 Checksums
1966
+
1967
+ SHA-256 cryptographic checksums are available.
1968
+ The `Sha256` type implements Go's standard `hash.Hash` and `io.Writer` interfaces:
1969
+
1970
+ ```go
1971
+ import (
1972
+ "io"
1973
+ sz "github.com/ashvardanian/stringzilla/golang"
1974
+ )
1975
+
1976
+ // One-shot SHA-256
1977
+ digest := sz.HashSha256([]byte("Hello, world!"))
1978
+ fmt.Printf("%x\n", digest) // prints 32-byte hash in hex
1979
+
1980
+ // Streaming SHA-256 (implements hash.Hash and io.Writer)
1981
+ hasher := sz.NewSha256()
1982
+ hasher.Write([]byte("Hello, "))
1983
+ hasher.Write([]byte("world!"))
1984
+ digestBytes := hasher.Digest() // [32]byte
1985
+ digestHex := hasher.Hexdigest() // string (64 hex chars)
1986
+
1987
+ // Works with io.Copy and any io.Reader
1988
+ file, _ := os.Open("data.bin")
1989
+ hasher.Reset()
1990
+ io.Copy(hasher, file)
1991
+ fileDigest := hasher.Digest()
1992
+
1993
+ // Standard hash.Hash interface methods
1994
+ sum := hasher.Sum(nil) // []byte with 32 bytes
1995
+ size := hasher.Size() // 32
1996
+ blockSize := hasher.BlockSize() // 64
1835
1997
  ```
1836
1998
 
1837
1999
  ## Algorithms & Design Decisions 📚
@@ -2128,6 +2290,11 @@ Also, unlike some alternatives, with "masked" AVX-512 and "predicated" SVE loads
2128
2290
  > § Reading materials.
2129
2291
  > [Stress-testing hash functions for avalance behaviour, collision bias, and distribution](https://github.com/ashvardanian/HashEvals).
2130
2292
 
2293
+ ### SHA-256 Checksums
2294
+
2295
+ In addition to the fast AES-based hash, StringZilla implements hardware-accelerated SHA-256 cryptographic checksums.
2296
+ The implementation follows the [FIPS 180-4 specification](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf) and provides multiple backends.
2297
+
2131
2298
  ### Random Generation
2132
2299
 
2133
2300
  StringZilla implements a fast [Pseudorandom Number Generator][faq-prng] inspired by the "AES-CTR-128" algorithm, reusing the same AES primitives as the hash function.
@@ -2216,6 +2383,7 @@ If you are looking for more reading materials on this topic, consider the follow
2216
2383
  - [The Painful Pitfalls of C++ STL Strings](https://ashvardanian.com/posts/painful-strings/).
2217
2384
  - [Processing Strings 109x Faster than Nvidia on H100](https://ashvardanian.com/posts/stringwars-on-gpus/).
2218
2385
  - [How a String Library Beat OpenCV at Image Processing by 4x](https://ashvardanian.com/posts/image-processing-with-strings/).
2386
+ - [2x Faster Hashes on AWS Graviton: NEON → SVE2](https://ashvardanian.com/posts/aws-graviton-checksums-on-neon-vs-sve/).
2219
2387
 
2220
2388
  ## License 📜
2221
2389
 
@@ -0,0 +1 @@
1
+ 4.2.2
@@ -20,17 +20,10 @@
20
20
  #include <stringzilla/stringzilla.h>
21
21
 
22
22
  #if SZ_AVOID_LIBC
23
- // If we don't have the LibC, the `malloc` definition in `stringzilla.h` will be illformed.
24
23
  #ifdef _MSC_VER
25
24
  typedef sz_size_t size_t; // Reuse the type definition we've inferred from `stringzilla.h`
26
- extern __declspec(dllimport) int rand(void);
27
- extern __declspec(dllimport) void free(void *start);
28
- extern __declspec(dllimport) void *malloc(size_t length);
29
25
  #else
30
26
  typedef __SIZE_TYPE__ size_t; // For GCC/Clang
31
- extern int rand(void);
32
- extern void free(void *start);
33
- extern void *malloc(size_t length);
34
27
  #endif
35
28
  #endif
36
29
 
@@ -128,6 +121,14 @@ static void sz_dispatch_table_update_implementation_(sz_capability_t caps) {
128
121
  }
129
122
  #endif
130
123
 
124
+ #if SZ_USE_GOLDMONT
125
+ if (caps & sz_cap_goldmont_k) {
126
+ impl->sha256_state_init = sz_sha256_state_init_goldmont;
127
+ impl->sha256_state_update = sz_sha256_state_update_goldmont;
128
+ impl->sha256_state_digest = sz_sha256_state_digest_goldmont;
129
+ }
130
+ #endif
131
+
131
132
  #if SZ_USE_HASWELL
132
133
  if (caps & sz_cap_haswell_k) {
133
134
  impl->equal = sz_equal_haswell;
@@ -189,6 +190,10 @@ static void sz_dispatch_table_update_implementation_(sz_capability_t caps) {
189
190
  impl->hash_state_digest = sz_hash_state_digest_ice;
190
191
  impl->fill_random = sz_fill_random_ice;
191
192
 
193
+ impl->sha256_state_init = sz_sha256_state_init_ice;
194
+ impl->sha256_state_update = sz_sha256_state_update_ice;
195
+ impl->sha256_state_digest = sz_sha256_state_digest_ice;
196
+
192
197
  impl->sequence_intersect = sz_sequence_intersect_ice;
193
198
  }
194
199
  #endif
@@ -239,7 +244,8 @@ static void sz_dispatch_table_update_implementation_(sz_capability_t caps) {
239
244
  impl->copy = sz_copy_sve;
240
245
  impl->move = sz_move_sve;
241
246
  impl->fill = sz_fill_sve;
242
-
247
+ impl->lookup = sz_lookup_sve;
248
+
243
249
  impl->find = sz_find_sve;
244
250
  // TODO: impl->rfind = sz_rfind_sve;
245
251
  impl->find_byte = sz_find_byte_sve;
@@ -910,6 +910,7 @@ SZ_DYNAMIC sz_status_t sz_memory_allocator_init_unified(sz_memory_allocator_t *a
910
910
  alloc->handle = nullptr;
911
911
  return propagate_error(sz::status_t::success_k, error_message);
912
912
  #else
913
+ sz_unused_(alloc); // Suppress unused parameter warning when CUDA is not used
913
914
  return propagate_error(sz::status_t::missing_gpu_k, error_message);
914
915
  #endif
915
916
  }
@@ -1073,6 +1074,8 @@ SZ_DYNAMIC sz_status_t szs_levenshtein_distances_init(
1073
1074
  sz_memory_allocator_t const *alloc, sz_capability_t capabilities, //
1074
1075
  szs_levenshtein_distances_t *engine_punned, char const **error_message) {
1075
1076
 
1077
+ sz_unused_(alloc); // Custom allocator not yet implemented, using default
1078
+ sz_unused_(capabilities); // Optional backends may be compiled out
1076
1079
  sz_assert_(engine_punned != nullptr && *engine_punned == nullptr && "Engine must be uninitialized");
1077
1080
 
1078
1081
  // If the gap opening and extension costs are identical we can use less memory
@@ -1253,6 +1256,7 @@ SZ_DYNAMIC sz_status_t szs_levenshtein_distances_utf8_init(
1253
1256
  sz_memory_allocator_t const *alloc, sz_capability_t capabilities, //
1254
1257
  szs_levenshtein_distances_utf8_t *engine_punned, char const **error_message) {
1255
1258
 
1259
+ sz_unused_(alloc); // Custom allocator not yet implemented, using default
1256
1260
  sz_assert_(engine_punned != nullptr && *engine_punned == nullptr && "Engine must be uninitialized");
1257
1261
 
1258
1262
  // If the gap opening and extension costs are identical we can use less memory
@@ -1357,6 +1361,8 @@ SZ_DYNAMIC sz_status_t szs_needleman_wunsch_scores_init( /
1357
1361
  sz_memory_allocator_t const *alloc, sz_capability_t capabilities, //
1358
1362
  szs_needleman_wunsch_scores_t *engine_punned, char const **error_message) {
1359
1363
 
1364
+ sz_unused_(alloc); // Custom allocator not yet implemented, using default
1365
+ sz_unused_(capabilities); // Optional backends may be compiled out
1360
1366
  sz_assert_(engine_punned != nullptr && *engine_punned == nullptr && "Engine must be uninitialized");
1361
1367
 
1362
1368
  // If the gap opening and extension costs are identical we can use less memory
@@ -1511,6 +1517,8 @@ SZ_DYNAMIC sz_status_t szs_smith_waterman_scores_init( /
1511
1517
  sz_memory_allocator_t const *alloc, sz_capability_t capabilities, //
1512
1518
  szs_smith_waterman_scores_t *engine_punned, char const **error_message) {
1513
1519
 
1520
+ sz_unused_(alloc); // Custom allocator not yet implemented, using default
1521
+ sz_unused_(capabilities); // Optional backends may be compiled out
1514
1522
  sz_assert_(engine_punned != nullptr && *engine_punned == nullptr && "Engine must be uninitialized");
1515
1523
 
1516
1524
  // If the gap opening and extension costs are identical we can use less memory
@@ -1666,6 +1674,8 @@ SZ_DYNAMIC sz_status_t szs_fingerprints_init( //
1666
1674
  sz_memory_allocator_t const *alloc, sz_capability_t capabilities, //
1667
1675
  szs_fingerprints_t *engine_punned, char const **error_message) {
1668
1676
 
1677
+ sz_unused_(alloc); // Custom allocator not yet implemented, using default
1678
+ sz_unused_(capabilities); // Optional backends may be compiled out
1669
1679
  sz_assert_(engine_punned != nullptr && *engine_punned == nullptr && "Engine must be uninitialized");
1670
1680
 
1671
1681
  // Use some default window widths if none are provided
@@ -432,10 +432,10 @@ SZ_PUBLIC sz_bool_t sz_equal_skylake(sz_cptr_t a, sz_cptr_t b, sz_size_t length)
432
432
  #pragma region NEON Implementation
433
433
  #if SZ_USE_NEON
434
434
  #if defined(__clang__)
435
- #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function)
435
+ #pragma clang attribute push(__attribute__((target("+simd"))), apply_to = function)
436
436
  #elif defined(__GNUC__)
437
437
  #pragma GCC push_options
438
- #pragma GCC target("arch=armv8.2-a+simd")
438
+ #pragma GCC target("+simd")
439
439
  #endif
440
440
 
441
441
  SZ_PUBLIC sz_ordering_t sz_order_neon(sz_cptr_t a, sz_size_t a_length, sz_cptr_t b, sz_size_t b_length) {
@@ -479,10 +479,10 @@ SZ_PUBLIC sz_bool_t sz_equal_neon(sz_cptr_t a, sz_cptr_t b, sz_size_t length) {
479
479
  #pragma region SVE Implementation
480
480
  #if SZ_USE_SVE
481
481
  #if defined(__clang__)
482
- #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+sve"))), apply_to = function)
482
+ #pragma clang attribute push(__attribute__((target("+sve"))), apply_to = function)
483
483
  #elif defined(__GNUC__)
484
484
  #pragma GCC push_options
485
- #pragma GCC target("arch=armv8.2-a+sve")
485
+ #pragma GCC target("+sve")
486
486
  #endif
487
487
 
488
488
  SZ_PUBLIC sz_bool_t sz_equal_sve(sz_cptr_t a, sz_cptr_t b, sz_size_t length) {