Skip to main content

Search, hash, sort, and process strings faster via SWAR and SIMD

Project description

StringZilla 🦖

StringZilla banner

Strings are the first fundamental data type every programming language implements in software rather than hardware, so dedicated CPU instructions are rare - and the few that exist are hardly ideal. That's why most languages lean on the C standard library (libc) for their string operations, which, despite its name, ships its hottest code in hand-tuned assembly. It does exploit SIMD, but it isn't perfect. 1️⃣ Even on ubiquitous hardware - over a billion 64-bit ARM CPUs - routines such as strstr and memmem top out at roughly one-third of available throughput. 2️⃣ SIMD coverage is uneven: fast forward scans don't guarantee speedy reverse searches, hashing and case-mapping is not even part of the standard. 3️⃣ Many higher-level languages can't rely on libc at all because their strings aren't NUL-terminated - or may even contain embedded zeroes. That's why StringZilla exists: predictable, high performance on every modern platform, OS, and programming language.

StringZilla Python installs StringZilla Rust installs StringZilla code size

StringZilla is the GodZilla of string libraries, using SIMD and SWAR to accelerate binary and UTF-8 string operations on modern CPUs and GPUs. It delivers up to 10x higher CPU throughput in C, C++, Rust, Python, and other languages, and can be 100x faster than existing GPU kernels, covering a broad range of functionality. It accelerates exact and fuzzy string matching, hashing, edit distance computations, sorting, provides allocation-free lazily-evaluated smart-iterators, and even random-string generators.

  • 🐂 C: Upgrade LibC's <string.h> to <stringzilla/stringzilla.h> in C 99
  • 🐉 C++: Upgrade STL's <string> to <stringzilla/stringzilla.hpp> in C++ 11
  • 🧮 CUDA: Process in-bulk with <stringzillas/stringzillas.cuh> in CUDA C++ 17
  • 🐍 Python: Upgrade your str to faster Str
  • 🦀 Rust: Use the StringZilla traits crate
  • 🦫 Go: Use the StringZilla cGo module
  • 🍎 Swift: Use the String+StringZilla extension
  • 🟨 JavaScript: Use the StringZilla library
  • 🐚 Shell: Accelerate common CLI tools with sz- prefix
  • 📚 Researcher? Jump to Algorithms & Design Decisions
  • 💡 Thinking to contribute? Look for "good first issues"
  • 🤝 And check the guide to set up the environment
  • Want more bindings or features? Let me know!

Who is this for?

  • For data-engineers parsing large datasets, like the CommonCrawl, RedPajama, or LAION.
  • For software engineers optimizing strings in their apps and services.
  • For bioinformaticians and search engineers looking for edit-distances for USearch.
  • For DBMS devs, optimizing LIKE, ORDER BY, and GROUP BY operations.
  • For hardware designers, needing a SWAR baseline for string-processing functionality.
  • For students studying SIMD/SWAR applications to non-data-parallel operations.

Performance

C C++ Python StringZilla
Unicode case-folding, expanding characters like ßss
.casefold
x86: 0.4 GB/s
sz.utf8_case_fold
x86: 1.3 GB/s
Unicode case-insensitive substring search
icu.StringSearch
x86: 0.02 GB/s
utf8_case_insensitive_find
x86: 3.0 GB/s
find the first occurrence of a random word from text, ≅ 5 bytes long
strstr 1
x86: 7.4 · arm: 2.0 GB/s
.find
x86: 2.9 · arm: 1.6 GB/s
.find
x86: 1.1 · arm: 0.6 GB/s
sz_find
x86: 10.6 · arm: 7.1 GB/s
find the last occurrence of a random word from text, ≅ 5 bytes long
.rfind
x86: 0.5 · arm: 0.4 GB/s
.rfind
x86: 0.9 · arm: 0.5 GB/s
sz_rfind
x86: 10.8 · arm: 6.7 GB/s
split lines separated by \n or \r 2
strcspn 1
x86: 5.42 · arm: 2.19 GB/s
.find_first_of
x86: 0.59 · arm: 0.46 GB/s
re.finditer
x86: 0.06 · arm: 0.02 GB/s
sz_find_byteset
x86: 4.08 · arm: 3.22 GB/s
find the last occurrence of any of 6 whitespaces 2
.find_last_of
x86: 0.25 · arm: 0.25 GB/s
sz_rfind_byteset
x86: 0.43 · arm: 0.23 GB/s
Random string from a given alphabet, 20 bytes long 3
rand() % n
x86: 18.0 · arm: 9.4 MB/s
uniform_int_distribution
x86: 47.2 · arm: 20.4 MB/s
join(random.choices(x))
x86: 13.3 · arm: 5.9 MB/s
sz_fill_random
x86: 56.2 · arm: 25.8 MB/s
Mapping characters with lookup table transforms
std::transform
x86: 3.81 · arm: 2.65 GB/s
str.translate
x86: 260.0 · arm: 140.0 MB/s
sz_lookup
x86: 21.2 · arm: 8.5 GB/s
Get sorted order, ≅ 8 million English words 4
qsort_r
x86: 3.55 · arm: 5.77 s
std::sort
x86: 2.79 · arm: 4.02 s
numpy.argsort
x86: 7.58 · arm: 13.00 s
sz_sequence_argsort
x86: 1.91 · arm: 2.37 s
Levenshtein edit distance, text lines ≅ 100 bytes long
via NLTK 5 and CuDF
x86: 1,615,306 · arm: 1,349,980 · cuda: 6,532,411,354 CUPS
szs_levenshtein_distances_t
x86: 3,434,427,548 · arm: 1,605,340,403 · cuda: 93,662,026,653 CUPS
Needleman-Wunsch alignment scores, proteins ≅ 1 K amino acids long
via biopython 6
x86: 575,981,513 · arm: 436,350,732 CUPS
szs_needleman_wunsch_scores_t
x86: 452,629,942 · arm: 520,170,239 · cuda: 9,017,327,818 CUPS

Most StringZilla modules ship ready-to-run benchmarks for C, C++, Python, and more. Grab them from ./scripts, and see CONTRIBUTING.md for instructions. On CPUs that permit misaligned loads, even the 64-bit SWAR baseline outruns both libc and the STL. For wider head-to-heads against Rust and Python favorites, browse the StringWars repository. To inspect collision resistance and distribution shapes for our hashers, see HashEvals.

Most benchmarks were conducted on a 1 GB English text corpus, with an average word length of 6 characters. The code was compiled with GCC 12, using glibc v2.35. The benchmarks were performed on Arm-based Graviton3 AWS c7g instances and r7iz Intel Sapphire Rapids. Most modern Arm-based 64-bit CPUs will have similar relative speedups. Variance within x86 CPUs will be larger. For CUDA benchmarks, the Nvidia H100 GPUs were used. 1 Unlike other libraries, LibC requires strings to be NULL-terminated. 2 Six whitespaces in the ASCII set are: \t\n\v\f\r. Python's and other standard libraries have specialized functions for those. 3 All modulo operations were conducted with uint8_t to allow compilers more optimization opportunities. The C++ STL and StringZilla benchmarks used a 64-bit Mersenne Twister as the generator. For C, C++, and StringZilla, an in-place update of the string was used. In Python every string had to be allocated as a new object, which makes it less fair. 4 Contrary to the popular opinion, Python's default sorted function works faster than the C and C++ standard libraries. That holds for large lists or tuples of strings, but fails as soon as you need more complex logic, like sorting dictionaries by a string key, or producing the "sorted order" permutation. The latter is very common in database engines and is most similar to numpy.argsort. The current StringZilla solution can be at least 4x faster without loss of generality. 5 Most Python libraries for strings are also implemented in C. 6 Unlike the rest of BioPython, the alignment score computation is implemented in C.

Functionality

StringZilla is compatible with most modern CPUs, and provides a broad range of functionality. It's split into 2 layers:

  1. StringZilla: single-header C library and C++ wrapper for high-performance string operations.
  2. StringZillas: parallel CPU/GPU backends used for large-batch operations and accelerators.

Having a second C++/CUDA layer greatly simplifies the implementation of similarity scoring and fingerprinting functions, which would otherwise require too much error-prone boilerplate code in pure C. Both layers are designed to be extremely portable:

  • across both little-endian and big-endian architectures.
  • across 32-bit and 64-bit hardware architectures.
  • across operating systems and compilers.
  • across ASCII and UTF-8 encoded inputs.

Not all features are available across all bindings. Consider contributing if you need a feature that's not yet implemented.

Maturity C C++ Python Rust JS Swift Go
Substring Search 🌳
Character Set Search 🌳
Sorting & Sequence Operations 🌳
Lazy Ranges, Compressed Arrays 🌳
One-Shot & Streaming Hashes 🌳
Cryptographic Hashes 🌳
Small String Class 🧐
Random String Generation 🌳
Unicode Case Folding 🧐
Case-Insensitive UTF-8 Search 🚧
TR29 Word Boundary Detection 🚧
Parallel Similarity Scoring 🌳
Parallel Rolling Fingerprints 🌳

🌳 parts are used in production. 🧐 parts are in beta. 🚧 parts are under active development, and are likely to break in subsequent releases. ✅ are implemented. ⚪ are considered. ❌ are not intended.

Quick Start: Python

Python bindings are available on PyPI for Python 3.8+, and can be installed with pip.

pip install stringzilla         # for serial algorithms
pip install stringzillas-cpus   # for parallel multi-CPU backends
pip install stringzillas-cuda   # for parallel Nvidia GPU backend

You can immediately check the installed version and the used hardware capabilities with following commands:

python -c "import stringzilla; print(stringzilla.__version__)"
python -c "import stringzillas; print(stringzillas.__version__)"
python -c "import stringzilla; print(stringzilla.__capabilities__)"     # for serial algorithms
python -c "import stringzillas; print(stringzillas.__capabilities__)"   # for parallel algorithms

Basic Usage

If you've ever used the Python str, bytes, bytearray, or memoryview classes, you'll know what to expect. StringZilla's Str class is a hybrid of the above, providing a str-like interface to byte arrays.

from stringzilla import Str, File

text_from_str = Str('some-string') # no copies, just a view
text_from_bytes = Str(b'some-array') # no copies, just a view
text_from_file = Str(File('some-file.txt')) # memory-mapped file

import numpy as np
alphabet_array = np.arange(ord("a"), ord("z"), dtype=np.uint8)
text_from_array = Str(memoryview(alphabet_array))

The File class memory-maps a file from persistent storage without loading its copy into RAM. The contents of that file would remain immutable, and the mapping can be shared by multiple Python processes simultaneously. A standard dataset pre-processing use case would be to map a sizable textual dataset like Common Crawl into memory, spawn child processes, and split the job between them.

Basic Operations

  • Length: len(text) -> int
  • Indexing: text[42] -> str
  • Slicing: text[42:46] -> Str
  • Substring check: 'substring' in text -> bool
  • Hashing: hash(text) -> int
  • String conversion: str(text) -> str

Advanced Operations

import sys

x: bool = text.contains('substring', start=0, end=sys.maxsize)
x: int = text.find('substring', start=0, end=sys.maxsize)
x: int = text.count('substring', start=0, end=sys.maxsize, allowoverlap=False)
x: str = text.decode(encoding='utf-8', errors='strict')
x: Strs = text.split(separator=' ', maxsplit=sys.maxsize, keepseparator=False)
x: Strs = text.rsplit(separator=' ', maxsplit=sys.maxsize, keepseparator=False)
x: Strs = text.splitlines(keeplinebreaks=False, maxsplit=sys.maxsize)

It's important to note that the last function's behavior is slightly different from Python's str.splitlines. The native version matches \n, \r, \v or \x0b, \f or \x0c, \x1c, \x1d, \x1e, \x85, \r\n, \u2028, \u2029, including 3x two-byte-long runes. The StringZilla version matches only \n, \v, \f, \r, \x1c, \x1d, \x1e, \x85, avoiding two-byte-long runes.

Character Set Operations

Python strings don't natively support character set operations. This forces people to use regular expressions, which are slow and hard to read. To avoid the need for re.finditer, StringZilla provides the following interfaces:

x: int = text.find_first_of('chars', start=0, end=sys.maxsize)
x: int = text.find_last_of('chars', start=0, end=sys.maxsize)
x: int = text.find_first_not_of('chars', start=0, end=sys.maxsize)
x: int = text.find_last_not_of('chars', start=0, end=sys.maxsize)
x: Strs = text.split_byteset(separator='chars', maxsplit=sys.maxsize, keepseparator=False)
x: Strs = text.rsplit_byteset(separator='chars', maxsplit=sys.maxsize, keepseparator=False)

StringZilla also provides string trimming functions and random string generation:

x: str = text.lstrip('chars')  # Strip leading characters
x: str = text.rstrip('chars')  # Strip trailing characters
x: str = text.strip('chars')   # Strip both ends
x: bytes = sz.random(length=100, seed=42, alphabet='ACGT')  # Random string generation
sz.fill_random(buffer, seed=42, alphabet=None)  # Fill mutable buffer with random bytes

You can also transform the string using Look-Up Tables (LUTs), mapping it to a different character set. This would result in a copy - str for str inputs and bytes for other types.

x: str = text.translate('chars', {}, start=0, end=sys.maxsize, inplace=False)
x: bytes = text.translate(b'chars', {}, start=0, end=sys.maxsize, inplace=False)

For efficiency reasons, pass the LUT as a string or bytes object, not as a dictionary. This can be useful in high-throughput applications dealing with binary data, including bioinformatics and image processing. Here is an example:

import stringzilla as sz
look_up_table = bytes(range(256)) # Identity LUT
image = open("/image/path.jpeg", "rb").read()
sz.translate(image, look_up_table, inplace=True)

Hash

Single-shot and incremental hashing are both supported:

import stringzilla as sz

# One-shot - stable 64-bit output across all platforms!
one = sz.hash(b"Hello, world!", seed=42)

# Incremental updates return itself; digest does not consume state
hasher = sz.Hasher(seed=42)
hasher.update(b"Hello, ").update(b"world!")
streamed = hasher.digest() # or `hexdigest()` for a string
assert one == streamed

SHA-256 Checksums

SHA-256 cryptographic checksums are also available for single-shot and incremental hashing:

import stringzilla as sz

# One-shot SHA-256
digest_bytes = sz.sha256(b"Hello, world!")
assert len(digest_bytes) == 32

# Incremental SHA-256
hasher = sz.Sha256()
hasher.update(b"Hello, ").update(b"world!")
digest_bytes = hasher.digest()
digest_hex = hasher.hexdigest()  # 64 character lowercase hex string

# HMAC-SHA256 for message authentication
mac = sz.hmac_sha256(key=b"secret", message=b"Hello, world!")

StringZilla integrates seamlessly with memory-mapped files for efficient large file processing. The traditional approach with hashlib:

import hashlib

with open("xlsum.csv", "rb") as streamed_file:
    hasher = hashlib.sha256()
    while chunk := streamed_file.read(4096):
        hasher.update(chunk)
    checksum = hasher.hexdigest()

Can be simplified with StringZilla:

from stringzilla import Sha256, File

mapped_file = File("xlsum.csv")
checksum = Sha256().update(mapped_file).hexdigest()

Both output the same digest: 7278165ce01a4ac1e8806c97f32feae908036ca3d910f5177d2cf375e20aeae1. OpenSSL (powering hashlib) has faster Assembly kernels, but StringZilla avoids file I/O overhead with memory mapping and skips Python's abstraction layers:

  • OpenSSL-backed hashlib.sha256: 12.6s
  • StringZilla end-to-end: 4.0s — 3× faster!

Unicode Case-Folding and Case-Insensitive Search

StringZilla implements both Unicode Case Folding and Case-Insensitive UTF-8 Search. Unlike most libraries only capable of lower-casing ASCII-represented English alphabet, StringZilla covers over 1M+ codepoints. The case-folding API expects the output buffer to be at least 3× larger than the input, to accommodate for the worst-case character expansions scenarios.

import stringzilla as sz

sz.utf8_case_fold('HELLO')      # b'hello'
sz.utf8_case_fold('Straße')     # b'strasse' — ß (1 char) expands to "ss" (2 chars)
sz.utf8_case_fold('efficient')    # b'efficient' — ffi ligature (1 char) expands to "ffi" (3 chars)

The case-insensitive search returns the byte offset of the match, handling expansions correctly.

import stringzilla as sz

sz.utf8_case_insensitive_find('Der große Hund', 'GROSSE')   # 4 — finds "große" at codepoint 4
sz.utf8_case_insensitive_find('Straße', 'STRASSE')          # 0 — ß matches "SS"
sz.utf8_case_insensitive_find('efficient', 'EFFICIENT')       # 0 — ffi ligature matches "FFI"

# Iterator for finding ALL matches
haystack = 'Straße STRASSE strasse'
for match in sz.utf8_case_insensitive_find_iter(haystack, 'strasse'):
    print(match, match.offset_within(haystack))  # Yields: 'Straße', 'STRASSE', 'strasse'

# With overlapping matches
list(sz.utf8_case_insensitive_find_iter('aaaa', 'aa'))  # ['aa', 'aa'] — 2 non-overlapping
list(sz.utf8_case_insensitive_find_iter('aaaa', 'aa', include_overlapping=True))  # 3 matches

Collection-Level Operations

Once split into a Strs object, you can sort, shuffle, and reorganize the slices with minimal memory footprint. If all the chunks are located in consecutive memory regions, the memory overhead can be as low as 4 bytes per chunk.

lines: Strs = text.split(separator='\n') # 4 bytes per line overhead for under 4 GB of text
batch: Strs = lines.sample(seed=42) # 10x faster than `random.choices`
lines_shuffled: Strs = lines.shuffled(seed=42) # or shuffle all lines and shard with slices
lines_sorted: Strs = lines.sorted() # returns a new Strs in sorted order
order: tuple = lines.argsort() # similar to `numpy.argsort`

Working on RedPajama, addressing 20 billion annotated English documents, one will need only 160 GB of RAM instead of terabytes. Once loaded, the data will be memory-mapped, and can be reused between multiple Python processes without copies. And of course, you can use slices to navigate the dataset and shard it between multiple workers.

lines[::3] # every third line
lines[1::1] # every odd line
lines[:-100:-1] # last 100 lines in reverse order

Iterators and Memory Efficiency

Python's operations like split() and readlines() immediately materialize a list of copied parts. This can be very memory-inefficient for large datasets. StringZilla saves a lot of memory by viewing existing memory regions as substrings, but even more memory can be saved by using lazily evaluated iterators.

x: SplitIterator[Str] = text.split_iter(separator=' ', keepseparator=False)
x: SplitIterator[Str] = text.rsplit_iter(separator=' ', keepseparator=False)
x: SplitIterator[Str] = text.split_byteset_iter(separator='chars', keepseparator=False)
x: SplitIterator[Str] = text.rsplit_byteset_iter(separator='chars', keepseparator=False)

StringZilla can easily be 10x more memory efficient than native Python classes for tokenization. With lazy operations, it practically becomes free.

import stringzilla as sz
%load_ext memory_profiler

text = open("enwik9.txt", "r").read() # 1 GB, mean word length 7.73 bytes
%memit text.split() # increment: 8670.12 MiB (152 ms)
%memit sz.split(text) # increment: 530.75 MiB (25 ms)
%memit sum(1 for _ in sz.split_iter(text)) # increment: 0.00 MiB

Low-Level Python API

Aside from calling the methods on the Str and Strs classes, you can also call the global functions directly on str and bytes instances. Assuming StringZilla CPython bindings are implemented without any intermediate tools like SWIG or PyBind, the call latency should be similar to native classes.

import stringzilla as sz

contains: bool = sz.contains("haystack", "needle", start=0, end=sys.maxsize)
offset: int = sz.find("haystack", "needle", start=0, end=sys.maxsize)
count: int = sz.count("haystack", "needle", start=0, end=sys.maxsize, allowoverlap=False)

Similarity Scores

StringZilla exposes high-performance, batch-oriented similarity via the stringzillas module. Use DeviceScope to pick hardware and optionally limit capabilities per engine.

import stringzilla as sz
import stringzillas as szs

cpu_scope = szs.DeviceScope(cpu_cores=4)    # force CPU-only
gpu_scope = szs.DeviceScope(gpu_device=0)   # pick GPU 0 if available

strings_a = sz.Strs(["kitten", "flaw"])
strings_b = sz.Strs(["sitting", "lawn"])

strings_a = szs.to_device(strings_a) # optional ahead of time transfer
strings_b = szs.to_device(strings_b) # optional ahead of time transfer

engine = szs.LevenshteinDistances(
    match=0, mismatch=2,        # costs don't have to be 1
    open=3, extend=1,           # may be different in Bio
    capabilities=("serial",)    # avoid SIMD 🤭
)
distances = engine(strings_a, strings_b, device=cpu_scope)
assert int(distances[0]) == 3 and int(distances[1]) == 2

Note, that this computes byte-level distances. For UTF-8 codepoints, use a different engine class:

strings_a = sz.Strs(["café", "αβγδ"])
strings_b = sz.Strs(["cafe", "αγδ"])
engine = szs.LevenshteinDistancesUTF8(capabilities=("serial",))
distances = engine(strings_a, strings_b, device=cpu_scope)
assert int(distances[0]) == 1 and int(distances[1]) == 1

For alignment scoring provide a 256×256 substitution matrix using NumPy:

import numpy as np
import stringzilla as sz
import stringzillas as szs

substitution_matrix = np.zeros((256, 256), dtype=np.int8)
substitution_matrix.fill(-1)                # mismatch score
np.fill_diagonal(substitution_matrix, 0)    # match score

engine = szs.NeedlemanWunsch(substitution_matrix=substitution_matrix, open=1, extend=1)
scores = engine(strings_a, strings_b, device=cpu_scope)

Several Python libraries provide edit distance computation. Most are implemented in C but may be slower than StringZilla on large inputs. For proteins ~10k chars, 100 pairs:

Using the same proteins for Needleman-Wunsch alignment scores:

§ Example converting from BioPython to StringZilla.
import numpy as np
from Bio import Align
from Bio.Align import substitution_matrices

aligner = Align.PairwiseAligner()
aligner.substitution_matrix = substitution_matrices.load("BLOSUM62")
aligner.open_gap_score = 1
aligner.extend_gap_score = 1

# Convert the matrix to NumPy
subs_packed = np.array(aligner.substitution_matrix).astype(np.int8)
subs_reconstructed = np.zeros((256, 256), dtype=np.int8)

# Initialize all banned characters to a the largest possible penalty
subs_reconstructed.fill(127)
for packed_row, packed_row_aminoacid in enumerate(aligner.substitution_matrix.alphabet):
    for packed_column, packed_column_aminoacid in enumerate(aligner.substitution_matrix.alphabet):
        reconstructed_row = ord(packed_row_aminoacid)
        reconstructed_column = ord(packed_column_aminoacid)
        subs_reconstructed[reconstructed_row, reconstructed_column] = subs_packed[packed_row, packed_column]

# Let's pick two examples of tripeptides (made of 3 amino acids)
glutathione = "ECG" # Need to rebuild human tissue?
thyrotropin_releasing_hormone = "QHP" # Or to regulate your metabolism?

import stringzillas as szs
engine = szs.NeedlemanWunsch(substitution_matrix=subs_reconstructed, open=1, extend=1)
score = int(engine(sz.Strs([glutathione]), sz.Strs([thyrotropin_releasing_hormone]))[0])
assert score == aligner.score(glutathione, thyrotropin_releasing_hormone) # Equal to 6

Rolling Fingerprints

MinHashing is a common technique for Information Retrieval, producing compact representations of large documents. For $D$ hash-functions and a text of length $L$, in the worst case it involves computing $O(D \cdot L)$ hashes.

import numpy as np
import stringzilla as sz
import stringzillas as szs

texts = sz.Strs([
    "quick brown fox jumps over the lazy dog",
    "quick brown fox jumped over a very lazy dog",
])

cpu = szs.DeviceScope(cpu_cores=4)
ndim = 1024
window_widths = np.array([4, 6, 8, 10], dtype=np.uint64)
engine = szs.Fingerprints(
    ndim=ndim,
    window_widths=window_widths,    # optional
    alphabet_size=256,              # default for byte strings
    capabilities=("serial",),       # defaults to all, can also pass a `DeviceScope`
)

hashes, counts = engine(texts, device=cpu)
assert hashes.shape == (len(texts), ndim)
assert counts.shape == (len(texts), ndim)
assert hashes.dtype == np.uint32 and counts.dtype == np.uint32

Serialization

Filesystem

Similar to how File can be used to read a large file, other interfaces can be used to dump strings to disk faster. The Str class has write_to to write the string to a file, and offset_within to obtain integer offsets of substring view in larger string for navigation.

web_archive = Str("<html>...</html><html>...</html>")
_, end_tag, next_doc = web_archive.partition("</html>") # or use `find`
next_doc_offset = next_doc.offset_within(web_archive)
web_archive.write_to("next_doc.html") # no GIL, no copies, just a view

PyArrow

A Str is easy to cast to PyArrow buffers.

from pyarrow import foreign_buffer
from stringzilla import Strs

strs = Strs(["alpha", "beta", "gamma"])
arrow = foreign_buffer(strs.address, strs.nbytes, strs)

And only slightly harder to convert in reverse direction:

arr = pa.Array.from_buffers(
    pa.large_string() if strs.offsets_are_large else pa.string(),
    len(strs),
    [None,
     pa.foreign_buffer(strs.offsets_address, strs.offsets_nbytes, strs),
     pa.foreign_buffer(strs.tape_address, strs.tape_nbytes, strs)],
)

That means you can convert Str to pyarrow.Buffer and Strs to pyarrow.Array without extra copies. For more details on the tape-like layouts, refer to the StringTape repository.

Quick Start: C/C++

The C library is header-only, so you can just copy the stringzilla.h header into your project. Same applies to C++, where you would copy the stringzilla.hpp header. Alternatively, add it as a submodule, and include it in your build system.

git submodule add https://github.com/ashvardanian/StringZilla.git external/stringzilla
git submodule update --init --recursive

Or using a pure CMake approach:

FetchContent_Declare(
    stringzilla
    GIT_REPOSITORY https://github.com/ashvardanian/StringZilla.git
    GIT_TAG main  # or specify a version tag
)
FetchContent_MakeAvailable(stringzilla)

Last, but not the least, you can also install it as a library, and link against it. This approach is worse for inlining, but brings dynamic runtime dispatch for the most advanced CPU features.

Basic Usage with C 99 and Newer

There is a stable C 99 interface, where all function names are prefixed with sz_. Most interfaces are well documented, and come with self-explanatory names and examples. In some cases, hardware specific overloads are available, like sz_find_skylake or sz_find_neon. Both are companions of the sz_find, first for x86 CPUs with AVX-512 support, and second for Arm NEON-capable CPUs.

#include <stringzilla/stringzilla.h>

// Initialize your haystack and needle
sz_string_view_t haystack = {your_text, your_text_length};
sz_string_view_t needle = {your_subtext, your_subtext_length};

// Perform string-level operations auto-picking the backend or dispatching manually
sz_cptr_t ptr = sz_find(haystack.start, haystack.length, needle.start, needle.length);
sz_size_t substring_position = ptr ? (sz_size_t)(ptr - haystack.start) : SZ_SIZE_MAX; // SZ_SIZE_MAX if not found

// Backend-specific variants return pointers as well
sz_cptr_t ptr = sz_find_skylake(haystack.start, haystack.length, needle.start, needle.length);
sz_cptr_t ptr = sz_find_haswell(haystack.start, haystack.length, needle.start, needle.length);
sz_cptr_t ptr = sz_find_westmere(haystack.start, haystack.length, needle.start, needle.length);
sz_cptr_t ptr = sz_find_neon(haystack.start, haystack.length, needle.start, needle.length);

// Hash strings at once
sz_u64_t hash = sz_hash(haystack.start, haystack.length, 42);    // 42 is the seed
sz_u64_t checksum = sz_bytesum(haystack.start, haystack.length); // or accumulate byte values

// Hash strings incrementally with "init", "update", and "digest":
sz_hash_state_t state;
sz_hash_state_init(&state, 42);
sz_hash_state_update(&state, haystack.start, 1);                        // first char
sz_hash_state_update(&state, haystack.start + 1, haystack.length - 1);  // rest of the string
sz_u64_t streamed_hash = sz_hash_state_digest(&state);

// SHA-256 cryptographic checksums
sz_u8_t digest[32];
sz_sha256_state_t sha_state;
sz_sha256_state_init(&sha_state);
sz_sha256_state_update(&sha_state, haystack.start, haystack.length);
sz_sha256_state_digest(&sha_state, digest);

// Perform collection level operations
sz_sequence_t array = {your_handle, your_count, your_get_start, your_get_length};
sz_sorted_idx_t order[your_count];
sz_sequence_argsort(&array, NULL, order); // NULL allocator uses default
§ Mapping from LibC to StringZilla.

By design, StringZilla has a couple of notable differences from LibC:

  1. all strings are expected to have a length, and are not necessarily null-terminated.
  2. every operations has a reverse order counterpart.

That way sz_find and sz_rfind are similar to strstr and strrstr in LibC. Similarly, sz_find_byte and sz_rfind_byte replace memchr and memrchr. The sz_find_byteset maps to strspn and strcspn, while sz_rfind_byteset has no sibling in LibC.

LibC Functionality StringZilla Equivalents
memchr(haystack, needle, haystack_length), strchr sz_find_byte(haystack, haystack_length, needle)
memrchr(haystack, needle, haystack_length) sz_rfind_byte(haystack, haystack_length, needle)
memcmp, strcmp sz_order, sz_equal
strlen(haystack) sz_find_byte(haystack, haystack_length, needle)
strcspn(haystack, reject) sz_find_byteset(haystack, haystack_length, reject_bitset)
strspn(haystack, accept) sz_find_byte_not_from(haystack, haystack_length, accept, accept_length)
memmem(haystack, haystack_length, needle, needle_length), strstr sz_find(haystack, haystack_length, needle, needle_length)
memcpy(destination, source, destination_length) sz_copy(destination, source, destination_length)
memmove(destination, source, destination_length) sz_move(destination, source, destination_length)
memset(destination, value, destination_length) sz_fill(destination, destination_length, value)

Basic Usage with C++ 11 and Newer

There is a stable C++ 11 interface available in the ashvardanian::stringzilla namespace. It comes with two STL-like classes: string_view and string. The first is a non-owning view of a string, and the second is a mutable string with a Small String Optimization.

#include <stringzilla/stringzilla.hpp>

namespace sz = ashvardanian::stringzilla;

sz::string haystack = "some string";
sz::string_view needle = sz::string_view(haystack).substr(0, 4);

auto substring_position = haystack.find(needle); // Or `rfind`
auto hash = std::hash<sz::string_view>{}(haystack); // Compatible with STL's `std::hash`

haystack.end() - haystack.begin() == haystack.size(); // Or `rbegin`, `rend`
haystack.find_first_of(" \v\t") == 4; // Or `find_last_of`, `find_first_not_of`, `find_last_not_of`
haystack.starts_with(needle) == true; // Or `ends_with`
haystack.remove_prefix(needle.size()); // Why is this operation in-place?!
haystack.contains(needle) == true; // STL has this only from C++ 23 onwards
haystack.compare(needle) == 1; // Or `haystack <=> needle` in C++ 20 and beyond

StringZilla also provides string literals for automatic type resolution, similar to STL:

using sz::literals::operator""_sv;
using std::literals::operator""sv;

auto a = "some string"; // char const *
auto b = "some string"sv; // std::string_view
auto b = "some string"_sv; // sz::string_view

Unicode Case-Folding and Case-Insensitive Search

StringZilla implements both Unicode Case Folding and Case-Insensitive UTF-8 Search. Unlike most libraries only capable of lower-casing ASCII-represented English alphabet, StringZilla covers over 1M+ codepoints. The case-folding API expects the output buffer to be at least 3× larger than the input, to accommodate for the worst-case character expansions scenarios.

char source[] = "Straße";  // German: "Street"
char destination[64];      // Must be at least 3x source length
sz_size_t result_len = sz_utf8_case_fold(source, strlen(source), destination);
// destination now contains "strasse" (7 bytes), result_len = 7

The case-insensitive search API returns a pointer to the start of the first relevant glyph in the haystack, or NULL if not found. It outputs the length of the matched haystack substring in bytes, and accepts a metadata structure to speed up repeated searches for the same needle.

sz_utf8_case_insensitive_needle_metadata_t metadata = {};
sz_size_t match_length;
sz_cptr_t match = sz_utf8_case_insensitive_find(
    haystack, haystack_len,
    needle, needle_len,
    &metadata,      // Reuse for queries with the same needle
    &match_length   // Output: bytes consumed in haystack
);

Same functionality is available in C++:

namespace sz = ashvardanian::stringzilla;

sz::string_view text = "Hello World"; // Single search
auto [offset, length] = text.utf8_case_insensitive_find("HELLO");

sz::utf8_case_insensitive_needle pattern("hello"); // Repeated searches with pre-compiled pattern
for (auto const& haystack : haystacks)
    auto match = haystack.utf8_case_insensitive_find(pattern);

Similarity Scores

StringZilla exposes high-performance, batch-oriented similarity via the stringzillas/stringzillas.h header. Use szs_device_scope_t to pick hardware and optionally limit capabilities per engine.

#include <stringzillas/stringzillas.h>

szs_device_scope_t device = NULL;
szs_device_scope_init_default(&device);

szs_levenshtein_distances_t engine = NULL;
szs_levenshtein_distances_init(0, 1, 1, 1, /*alloc*/ NULL, /*caps*/ sz_cap_serial_k, &engine);

sz_sequence_u32tape_t strings_a {data_a, offsets_a, count}; // or `sz_sequence_u64tape_t` for large inputs
sz_sequence_u32tape_t strings_b {data_b, offsets_b, count}; // or `sz_sequence_t` to pass generic containers

sz_size_t distances[count];
szs_levenshtein_distances_u32tape(engine, device, &strings_a, &strings_b, distances, sizeof(distances[0]));

szs_levenshtein_distances_free(engine);
szs_device_scope_free(device);

To target a different device, use the appropriate szs_device_scope_init_{cpu_cores,gpu_device} function. When dealing with GPU backends, make sure to use the "unified memory" allocators exposed as szs_unified_{alloc,free}. Similar stable C ABIs are exposed for other workloads as well.

  • UTF-8: szs_levenshtein_distances_utf8_{sequence,u32tape,u64tape}
  • Needleman-Wunsch: szs_needleman_wunsch_scores_{sequence,u32tape,u64tape}
  • Smith-Waterman: szs_smith_waterman_scores_{sequence,u32tape,u64tape}

Moreover, in C++ codebases one can tap into the raw templates implementing that functionality, customizing them with custom executors, SIMD plugins, etc. For that include stringzillas/similarities.hpp for C++ and stringzillas/similarities.cuh for CUDA.

#include <stringzillas/similarities.hpp>
#include <stringzilla/types.hpp>       // tape of strings
#include <fork_union.hpp>              // optional thread pool

namespace sz = ashvardanian::stringzilla;
namespace szs = ashvardanian::stringzillas;

// Pack strings into an Arrow-like tape
std::vector<std::string> left = {"kitten", "flaw"};
std::vector<std::string> right = {"sitting", "lawn"};
sz::arrow_strings_tape<char, sz::size_t, std::allocator<char>> tape_a, tape_b;
auto _ = tape_a.try_assign(left.begin(), left.end());
auto _ = tape_b.try_assign(right.begin(), right.end());

// Run on the current thread
using levenshtein_t = szs::levenshtein_distances<char, szs::linear_gap_costs_t, std::allocator<char>, sz_cap_serial_k>;
levenshtein_t engine {szs::uniform_substitution_costs_t{0,1}, szs::linear_gap_costs_t{1}};
std::size_t distances[2];
auto _ = engine(tape_a, tape_b, distances);

// Or run in parallel with a pool
fork_union::basic_pool_t pool;
auto _ = pool.try_spawn(std::thread::hardware_concurrency());
auto _ = engine(tape_a, tape_b, distances, pool);

All of the potentially failing StringZillas' interfaces return error codes, and none raise C++ exceptions. Parallelism is enabled at both collection-level and within individual pairs of large inputs.

Rolling Fingerprints

StringZilla exposes parallel fingerprinting (Min-Hashes or Count-Min-Sketches) via the stringzillas/stringzillas.h header. Use szs_device_scope_t to pick hardware and optionally limit capabilities per engine.

#include <stringzillas/stringzillas.h>

szs_device_scope_t device = NULL;
szs_device_scope_init_default(&device);

szs_fingerprints_t engine = NULL;
sz_size_t const dims = 1024; sz_size_t const window_widths[] = {4, 6, 8, 10};
szs_fingerprints_init(dims, /*alphabet*/ 256, window_widths, 4, /*alloc*/ NULL, /*caps*/ sz_cap_serial_k, &engine);

sz_sequence_u32tape_t texts = {data, offsets, count};
sz_u32_t *min_hashes = (sz_u32_t*)szs_unified_alloc(count * dims * sizeof(*min_hashes));
sz_u32_t *min_counts = (sz_u32_t*)szs_unified_alloc(count * dims * sizeof(*min_counts));
szs_fingerprints_u32tape(engine, device, &texts,
    min_hashes, dims * sizeof(*min_hashes),     // support strided matrices
    min_counts, dims * sizeof(*min_counts));    // for both output arguments

szs_fingerprints_free(engine);
szs_device_scope_free(device);

Moreover, in C++ codebases one can tap into the raw templates implementing that functionality, customizing them with custom executors, SIMD plugins, etc. For that include stringzillas/fingerprints.hpp for C++ and stringzillas/fingerprints.cuh for CUDA.

#include <stringzillas/fingerprints.hpp>
#include <stringzilla/types.hpp>       // tape of strings
#include <fork_union.hpp>              // optional thread pool

namespace sz = ashvardanian::stringzilla;
namespace szs = ashvardanian::stringzillas;

// Pack strings into an Arrow-like tape
std::vector<std::string> docs = {"alpha beta", "alpha betta"};
sz::arrow_strings_tape<char, sz::size_t, std::allocator<char>> tape;
auto _ = tape.try_assign(docs.begin(), docs.end());

// Run on the current thread with a Rabin-Karp family hasher
constexpr std::size_t dimensions_k = 256;
constexpr std::size_t window_width_k = 7;
using row_t = std::array<sz_u32_t, 256>;
using fingerprinter_t = szs::floating_rolling_hashers<sz_cap_serial_k, dimensions_k>;
fingerprinter_t engine;
auto _ = engine.try_extend(window_width_k, dimensions_k);
std::vector<row_t> hashes(docs.size()), counts(docs.size());
auto _ = engine(tape, hashes, counts);

// Or run in parallel with a pool
fork_union::basic_pool_t pool;
auto _ = pool.try_spawn(std::thread::hardware_concurrency());
auto _ = engine(tape, hashes, counts, pool);

CUDA

StringZilla provides CUDA C++ templates for composable string batch-processing operations. Different GPUs have varying warp sizes, shared memory capacities, and register counts, affecting algorithm selection, so it's important to query the gpu_specs_t via gpu_specs_fetch. For memory management, ensure that you use GPU-visible' unified memoryexposed in an STL-compatible manner as aunified_alloctemplate class. For error handling,cuda_status_textends the traditionalstatus_twith GPU-specific information. It's implicitly convertible tostatus_t, so you can use it in places expecting a status_t`.

Most algorithms can load-balance both a large number of small strings and a small number of large strings. Still, with large H100-scale GPUs, it's best to submit thousands of inputs at once.

Memory Ownership and Small String Optimization

Most operations in StringZilla don't assume any memory ownership. But in addition to the read-only search-like operations StringZilla provides a minimalistic C and C++ implementations for a memory owning string "class". Like other efficient string implementations, it uses the Small String Optimization (SSO) to avoid heap allocations for short strings.

typedef union sz_string_t {
    struct internal {
        sz_ptr_t start;
        sz_u8_t length;
        char chars[SZ_STRING_INTERNAL_SPACE]; /// Ends with a null-terminator.
    } internal;

    struct external {
        sz_ptr_t start;
        sz_size_t length;        
        sz_size_t space; /// The length of the heap-allocated buffer.
        sz_size_t padding;
    } external;

} sz_string_t;

As one can see, a short string can be kept on the stack, if it fits within internal.chars array. Before 2015 GCC string implementation was just 8 bytes, and could only fit 7 characters. Different STL implementations today have different thresholds for the Small String Optimization. Similar to GCC, StringZilla is 32 bytes in size, and similar to Clang it can fit 22 characters on stack. Our layout might be preferential, if you want to avoid branches. If you use a different compiler, you may want to check its SSO buffer size with a simple Gist.

libstdc++ in GCC 13 libc++ in Clang 17 StringZilla
String sizeof 32 24 32
Inner Capacity 15 22 22

This design has been since ported to many high-level programming languages. Swift, for example, can store 15 bytes in the String instance itself. StringZilla implements SSO at the C level, providing the sz_string_t union and a simple API for primary operations.

sz_memory_allocator_t allocator;
sz_string_t string;

// Init and make sure we are on stack
sz_string_init(&string);
sz_string_is_on_stack(&string); // == sz_true_k

// Optionally pre-allocate space on the heap for future insertions.
sz_string_grow(&string, 100, &allocator); // == sz_true_k

// Append, erase, insert into the string.
sz_string_expand(&string, 0, "_Hello_", 7, &allocator); // == sz_true_k
sz_string_expand(&string, SZ_SIZE_MAX, "world", 5, &allocator); // == sz_true_k
sz_string_erase(&string, 0, 1);

// Unpacking & introspection.
sz_ptr_t string_start;
sz_size_t string_length;
sz_size_t string_space;
sz_bool_t string_is_external;
sz_string_unpack(string, &string_start, &string_length, &string_space, &string_is_external);
sz_equal(string_start, "Hello_world", 11); // == sz_true_k

// Reclaim some memory.
sz_string_shrink_to_fit(&string, &allocator); // == sz_true_k
sz_string_free(&string, &allocator);

Unlike the conventional C strings, the sz_string_t is allowed to contain null characters. To safely print those, pass the string_length to printf as well.

printf("%.*s\n", (int)string_length, string_start);

What's Wrong with the C Standard Library?

StringZilla is not a drop-in replacement for the C Standard Library. It's designed to be a safer and more modern alternative. Conceptually:

  1. LibC strings are expected to be null-terminated, so to use the efficient LibC implementations on slices of larger strings, you'd have to copy them, which is more expensive than the original string operation.
  2. LibC functionality is asymmetric - you can find the first and the last occurrence of a character within a string, but you can't find the last occurrence of a substring.
  3. LibC function names are typically very short and cryptic.
  4. LibC lacks crucial functionality like hashing and doesn't provide primitives for less critical but relevant operations like fuzzy matching.

Something has to be said about its support for UTF-8. Aside from a single-byte char type, LibC provides wchar_t:

  • The size of wchar_t is not consistent across platforms. On Windows, it's typically 16 bits (suitable for UTF-16), while on Unix-like systems, it's usually 32 bits (suitable for UTF-32). This inconsistency can lead to portability issues when writing cross-platform code.
  • wchar_t is designed to represent wide characters in a fixed-width format (UTF-16 or UTF-32). In contrast, UTF-8 is a variable-length encoding, where each character can take from 1 to 4 bytes. This fundamental difference means that wchar_t and UTF-8 are incompatible.

StringZilla partially addresses those issues.

What's Wrong with the C++ Standard Library?

C++ Code Evaluation Result Invoked Signature
"Loose"s.replace(2, 2, "vath"s, 1) "Loathe" 🤢 (pos1, count1, str2, pos2)
"Loose"s.replace(2, 2, "vath", 1) "Love" 🥰 (pos1, count1, str2, count2)

StringZilla is designed to be a drop-in replacement for the C++ Standard Templates Library. That said, some of the design decisions of STL strings are highly controversial, error-prone, and expensive. Most notably:

  1. Argument order for replace, insert, erase and similar functions is impossible to guess.
  2. Bounds-checking exceptions for substr-like functions are only thrown for one side of the range.
  3. Returning string copies in substr-like functions results in absurd volume of allocations.
  4. Incremental construction via push_back-like functions goes through too many branches.
  5. Inconsistency between string and string_view methods, like the lack of remove_prefix and remove_suffix.

Check the following set of asserts validating the std::string specification. It's not realistic to expect the average developer to remember the 14 overloads of std::string::replace.

using str = std::string;

assert(str("hello world").substr(6) == "world");
assert(str("hello world").substr(6, 100) == "world"); // 106 is beyond the length of the string, but its OK
assert_throws(str("hello world").substr(100), std::out_of_range);   // 100 is beyond the length of the string
assert_throws(str("hello world").substr(20, 5), std::out_of_range); // 20 is beyond the length of the string
assert_throws(str("hello world").substr(-1, 5), std::out_of_range); // -1 casts to unsigned without any warnings...
assert(str("hello world").substr(0, -1) == "hello world");          // -1 casts to unsigned without any warnings...

assert(str("hello").replace(1, 2, "123") == "h123lo");
assert(str("hello").replace(1, 2, str("123"), 1) == "h23lo");
assert(str("hello").replace(1, 2, "123", 1) == "h1lo");
assert(str("hello").replace(1, 2, "123", 1, 1) == "h2lo");
assert(str("hello").replace(1, 2, str("123"), 1, 1) == "h2lo");
assert(str("hello").replace(1, 2, 3, 'a') == "haaalo");
assert(str("hello").replace(1, 2, {'a', 'b'}) == "hablo");

To avoid those issues, StringZilla provides an alternative consistent interface. It supports signed arguments, and doesn't have more than 3 arguments per function or The standard API and our alternative can be conditionally disabled with SZ_SAFETY_OVER_COMPATIBILITY=1. When it's enabled, the subjectively risky overloads from the Standard will be disabled.

using str = sz::string;

str("a:b").front(1) == "a"; // no checks, unlike `substr`
str("a:b").front(2) == "2"; // take first 2 characters
str("a:b").back(-1) == "b"; // accepting negative indices
str("a:b").back(-2) == ":b"; // similar to Python's `"a:b"[-2:]`
str("a:b").sub(1, -1) == ":"; // similar to Python's `"a:b"[1:-1]`
str("a:b").sub(-2, -1) == ":"; // similar to Python's `"a:b"[-2:-1]`
str("a:b").sub(-2, 1) == ""; // similar to Python's `"a:b"[-2:1]`
"a:b"_sv[{-2, -1}] == ":"; // works on views and overloads `operator[]`

Assuming StringZilla is a header-only library you can use the full API in some translation units and gradually transition to safer restricted API in others. Bonus - all the bound checking is branchless, so it has a constant cost and won't hurt your branch predictor.

Beyond the C++ Standard Library - Learning from Python

Python is arguably the most popular programming language for data science. In part, that's due to the simplicity of its standard interfaces. StringZilla brings some of that functionality to C++.

  • Content checks: isalnum, isalpha, isascii, isdigit, islower, isspace, isupper.
  • Trimming character sets: lstrip, rstrip, strip.
  • Trimming string matches: remove_prefix, remove_suffix.
  • Ranges of search results: splitlines, split, rsplit.
  • Number of non-overlapping substring matches: count.
  • Partitioning: partition, rpartition.

For example, when parsing documents, it is often useful to split it into substrings. Most often, after that, you would compute the length of the skipped part, the offset and the length of the remaining part. This results in a lot of pointer arithmetic and is error-prone. StringZilla provides a convenient partition function, which returns a tuple of three string views, making the code cleaner.

auto parts = haystack.partition(':'); // Matching a character
auto [before, match, after] = haystack.partition(':'); // Structure unpacking
auto [before, match, after] = haystack.partition(sz::byteset(":;")); // Character-set argument
auto [before, match, after] = haystack.partition(" : "); // String argument
auto [before, match, after] = haystack.rpartition(sz::whitespaces_set()); // Split around the last whitespace

Combining those with the split function, one can easily parse a CSV file or HTTP headers.

for (auto line : haystack.split("\r\n")) {
    auto [key, _, value] = line.partition(':');
    headers[key.strip()] = value.strip();
}

Some other extensions are not present in the Python standard library either. Let's go through the C++ functionality category by category.

Some of the StringZilla interfaces are not available even Python's native str class. Here is a sneak peek of the most useful ones.

text.hash(); // -> 64 bit unsigned integer 
text.ssize(); // -> 64 bit signed length to avoid `static_cast<std::ssize_t>(text.size())`
text.contains_only(" \w\t"); // == text.find_first_not_of(sz::byteset(" \w\t")) == npos;
text.contains(sz::whitespaces_set()); // == text.find(sz::byteset(sz::whitespaces_set())) != npos;

// Simpler slicing than `substr`
text.front(10); // -> sz::string_view
text.back(10); // -> sz::string_view

// Safe variants, which clamp the range into the string bounds
using sz::string::cap;
text.front(10, cap) == text.front(std::min(10, text.size()));
text.back(10, cap) == text.back(std::min(10, text.size()));

// Character set filtering
text.lstrip(sz::whitespaces_set()).rstrip(sz::newlines_set()); // like Python
text.front(sz::whitespaces_set()); // all leading whitespaces
text.back(sz::digits_set()); // all numerical symbols forming the suffix

// Incremental construction
using sz::string::unchecked;
text.push_back('x'); // no surprises here
text.push_back('x', unchecked); // no bounds checking, Rust style
text.try_push_back('x'); // returns `false` if the string is full and the allocation failed

sz::concatenate(text, "@", domain, ".", tld); // No allocations

Splits and Ranges

One of the most common use cases is to split a string into a collection of substrings. Which would often result in StackOverflow lookups and snippets like the one below.

std::vector<std::string> lines = split(haystack, "\r\n"); // string delimiter
std::vector<std::string> words = split(lines, ' '); // character delimiter

Those allocate memory for each string and the temporary vectors. Each allocation can be orders of magnitude more expensive, than even serial for-loop over characters. To avoid those, StringZilla provides lazily-evaluated ranges, compatible with the Range-v3 library.

for (auto line : haystack.split("\r\n"))
    for (auto word : line.split(sz::byteset(" \w\t.,;:!?")))
        std::cout << word << std::endl;

Each of those is available in reverse order as well. It also allows interleaving matches, if you want both inclusions of xx in xxx. Debugging pointer offsets is not a pleasant exercise, so keep the following functions in mind.

  • haystack.[r]find_all(needle, interleaving)
  • haystack.[r]find_all(sz::byteset(""))
  • haystack.[r]split(needle)
  • haystack.[r]split(sz::byteset(""))

For $N$ matches the split functions will report $N+1$ matches, potentially including empty strings. Ranges have a few convenience methods as well:

range.size(); // -> std::size_t
range.empty(); // -> bool
range.template to<std::set<std::sting>>(); 
range.template to<std::vector<std::sting_view>>(); 

Concatenating Strings without Allocations

Another common string operation is concatenation. The STL provides std::string::operator+ and std::string::append, but those are not very efficient, if multiple invocations are performed.

std::string name, domain, tld;
auto email = name + "@" + domain + "." + tld; // 4 allocations

The efficient approach would be to pre-allocate the memory and copy the strings into it.

std::string email;
email.reserve(name.size() + domain.size() + tld.size() + 2);
email.append(name), email.append("@"), email.append(domain), email.append("."), email.append(tld);

That's mouthful and error-prone. StringZilla provides a more convenient concatenate function, which takes a variadic number of arguments. It also overrides the operator| to concatenate strings lazily, without any allocations.

auto email = sz::concatenate(name, "@", domain, ".", tld);   // 0 allocations
auto email = name | "@" | domain | "." | tld;                // 0 allocations
sz::string email = name | "@" | domain | "." | tld;          // 1 allocations

Random Generation

Software developers often need to generate random strings for testing purposes. The STL provides std::generate and std::random_device, that can be used with StringZilla.

sz::string random_string(std::size_t length, char const *alphabet, std::size_t cardinality) {
    sz::string result(length, '\0');
    static std::random_device seed_source; // Expensive to construct - due to system calls
    static std::mt19937 generator(seed_source()); // Also expensive - due to the state size
    std::uniform_int_distribution<std::size_t> distribution(0, cardinality);
    std::generate(result.begin(), result.end(), [&]() { return alphabet[distribution(generator)]; });
    return result;
}

Mouthful and slow. StringZilla provides a C native method - sz_fill_random and a convenient C++ wrapper - sz::generate. Similar to Python it also defines the commonly used character sets.

auto protein = sz::string::random(300, "ARNDCQEGHILKMFPSTWYV"); // static method
auto dna = sz::basic_string<custom_allocator>::random(3_000_000_000, "ACGT");

dna.fill_random("ACGT"); // `noexcept` pre-allocated version
dna.fill_random(&std::rand, "ACGT"); // pass any generator, like `std::mt19937`

char uuid[36];
sz::fill_random(sz::string_span(uuid, 36), "0123456789abcdef-"); // Overwrite any buffer

Bulk Replacements

In text processing, it's often necessary to replace all occurrences of a specific substring or set of characters within a string. Standard library functions may not offer the most efficient or convenient methods for performing bulk replacements, especially when dealing with large strings or performance-critical applications.

  • haystack.replace_all(needle_string, replacement_string)
  • haystack.replace_all(sz::byteset(""), replacement_string)
  • haystack.try_replace_all(needle_string, replacement_string)
  • haystack.try_replace_all(sz::byteset(""), replacement_string)
  • haystack.lookup(sz::look_up_table::identity())
  • haystack.lookup(sz::look_up_table::identity(), haystack.data())

Sorting in C and C++

LibC provides qsort and STL provides std::sort. Both have their quirks. The LibC standard has no way to pass a context to the comparison function, that's only possible with platform-specific extensions. Those have different arguments order on every OS.

// Linux: https://linux.die.net/man/3/qsort_r
void qsort_r(void *elements, size_t count, size_t element_width, 
    int (*compare)(void const *left, void const *right, void *context),
    void *context);
// macOS and FreeBSD: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/qsort_r.3.html
void qsort_r(void *elements, size_t count, size_t element_width, 
    void *context,
    int (*compare)(void *context, void const *left, void const *right));
// Windows conflicts with ISO `qsort_s`: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/qsort-s?view=msvc-170
void qsort_s(id *elements, size_t count, size_t element_width, 
    int (*compare)(void *context, void const *left, void const *right),
    void *context);

C++ generic algorithm is not perfect either. There is no guarantee in the standard that std::sort won't allocate any memory. If you are running on embedded, in real-time or on 100+ CPU cores per node, you may want to avoid that. StringZilla doesn't solve the general case, but hopes to improve the performance for strings. Use sz_sequence_argsort, or the high-level sz::argsort, which can be used sort any collection of elements convertible to sz::string_view.

std::vector<std::string> data({"c", "b", "a"});
std::vector<std::size_t> order = sz::argsort(data); //< Simple shortcut

// Or, taking care of memory allocation:
sz::argsort(data.begin(), data.end(), order.data(), [](auto const &x) -> sz::string_view { return x; });

Standard C++ Containers with String Keys

The C++ Standard Templates Library provides several associative containers, often used with string keys.

std::map<std::string, int, std::less<std::string>> sorted_words;
std::unordered_map<std::string, int, std::hash<std::string>, std::equal_to<std::string>> words;

The performance of those containers is often limited by the performance of the string keys, especially on reads. StringZilla can be used to accelerate containers with std::string keys, by overriding the default comparator and hash functions.

std::map<std::string, int, sz::less> sorted_words;
std::unordered_map<std::string, int, sz::hash, sz::equal_to> words;

Alternatively, a better approach would be to use the sz::string class as a key. The right hash function and comparator would be automatically selected and the performance gains would be more noticeable if the keys are short.

std::map<sz::string, int> sorted_words;
std::unordered_map<sz::string, int> words;

Compilation Settings and Debugging

SZ_DEBUG:

For maximal performance, the C library does not perform any bounds checking in Release builds. In C++, bounds checking happens only in places where the STL std::string would do it. If you want to enable more aggressive bounds-checking, define SZ_DEBUG before including the header. If not explicitly set, it will be inferred from the build type.

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:

One can explicitly disable certain families of SIMD instructions for compatibility purposes. Default values are inferred at compile time depending on compiler support (for dynamic dispatch) and the target architecture (for static dispatch).

SZ_USE_CUDA, SZ_USE_KEPLER, SZ_USE_HOPPER:

One can explicitly disable certain families of PTX instructions for compatibility purposes. Default values are inferred at compile time depending on compiler support (for dynamic dispatch) and the target architecture (for static dispatch).

SZ_ENFORCE_SVE_OVER_NEON:

SVE and SVE2 are expected to supersede NEON on ARM architectures. Still, oftentimes the equivalent SVE kernels are slower due to equally small register files and higher complexity of the instructions. By default, when both SVE and NEON are available, SVE is used selectively only for the algorithms that benefit from it. If you want to enforce SVE usage everywhere, define this flag.

SZ_DYNAMIC_DISPATCH:

By default, StringZilla is a header-only library. But if you are running on different generations of devices, it makes sense to pre-compile the library for all supported generations at once, and dispatch at runtime. This flag does just that and is used to produce the stringzilla.so shared library, as well as the Python bindings.

SZ_USE_MISALIGNED_LOADS:

Default is platform-dependent: enabled on x86 (where unaligned accesses are fast), disabled on others by default. When enabled, many byte-level operations use word-sized loads, which can significantly accelerate the serial (SWAR) backend. Consider enabling it explicitly if you are targeting platforms that support fast unaligned loads.

SZ_AVOID_LIBC and SZ_OVERRIDE_LIBC:

When using the C header-only library one can disable the use of LibC. This may affect the type resolution system on obscure hardware platforms. Moreover, one may let stringzilla override the common symbols like the memcpy and memset with its own implementations. In that case you can use the LD_PRELOAD trick to prioritize its symbols over the ones from the LibC and accelerate existing string-heavy applications without recompiling them. It also adds a layer of security, as the stringzilla isn't undefined for NULL inputs like memcpy(NULL, NULL, 0).

SZ_AVOID_STL and SZ_SAFETY_OVER_COMPATIBILITY:

When using the C++ interface one can disable implicit conversions from std::string to sz::string and back. If not needed, the <string> and <string_view> headers will be excluded, reducing compilation time. Moreover, if STL compatibility is a low priority, one can make the API safer by disabling the overloads, which are subjectively error prone.

STRINGZILLA_BUILD_SHARED, STRINGZILLA_BUILD_TEST, STRINGZILLA_BUILD_BENCHMARK, STRINGZILLA_TARGET_ARCH for CMake users:

When compiling the tests and benchmarks, you can explicitly set the target hardware architecture. It's synonymous to GCC's -march flag and is used to enable/disable the appropriate instruction sets. You can also disable the shared library build, if you don't need it.

Quick Start: Rust

StringZilla is available as a Rust crate, with documentation available on docs.rs/stringzilla. You can immediately check the installed version and the used hardware capabilities with following commands:

cargo add stringzilla
cargo run --example version

To use the latest crate release in your project, add the following to your Cargo.toml:

[dependencies]
stringzilla = ">=3"                                     # for serial algorithms
stringzilla = { version = ">=3", features = ["cpus"] }  # for parallel multi-CPU backends
stringzilla = { version = ">=3", features = ["cuda"] }  # for parallel Nvidia GPU backend

Or if you want to use the latest pre-release version from the repository:

[dependencies]
stringzilla = { git = "https://github.com/ashvardanian/stringzilla", branch = "main-dev" }

Once installed, all of the functionality is available through the stringzilla namespace. Many interfaces will look familiar to the users of the memchr Rust crate.

use stringzilla::sz;

// Identical to `memchr::memmem::find` and `memchr::memmem::rfind` functions
sz::find("Hello, world!", "world") // 7
sz::rfind("Hello, world!", "world") // 7

// Generalizations of `memchr::memrchr[123]`
sz::find_byte_from("Hello, world!", "world") // 2
sz::rfind_byte_from("Hello, world!", "world") // 11

It also provides no constraints on the size of the character set, while memchr allows only 1, 2, or 3 characters. In addition to global functions, stringzilla provides a StringZilla extension trait:

use stringzilla::StringZilla;

let my_string: String = String::from("Hello, world!");
let my_str = my_string.as_str();
let my_cow_str = Cow::from(&my_string);

// Use the generic function with a String
assert_eq!(my_string.sz_find("world"), Some(7));
assert_eq!(my_string.sz_rfind("world"), Some(7));
assert_eq!(my_string.sz_find_byte_from("world"), Some(2));
assert_eq!(my_string.sz_rfind_byte_from("world"), Some(11));
assert_eq!(my_string.sz_find_byte_not_from("world"), Some(0));
assert_eq!(my_string.sz_rfind_byte_not_from("world"), Some(12));

// Same works for &str and Cow<'_, str>
assert_eq!(my_str.sz_find("world"), Some(7));
assert_eq!(my_cow_str.as_ref().sz_find("world"), Some(7));

Hash

Single-shot and incremental hashing are both supported:

let mut hasher = sz::Hasher::new(42);
hasher.write(b"Hello, ");
hasher.write(b"world!");
let streamed = hasher.finish();

let mut hasher = sz::Hasher::new(42);
hasher.write(b"Hello, world!");
assert_eq!(streamed, hasher.finish());

To use StringZilla with std::collections:

use std::collections::HashMap;
let mut map: HashMap<&str, i32, sz::BuildSzHasher> =
    HashMap::with_hasher(sz::BuildSzHasher::with_seed(42));
map.insert("a", 1);
assert_eq!(map.get("a"), Some(&1));

SHA-256 Checksums

SHA-256 cryptographic checksums are available:

use stringzilla::sz;

// One-shot SHA-256
let digest = sz::Sha256::hash(b"Hello, world!");
assert_eq!(digest.len(), 32);

// Incremental SHA-256
let mut hasher = sz::Sha256::new();
hasher.update(b"Hello, ");
hasher.update(b"world!");
let digest = hasher.digest();

// HMAC-SHA256 for message authentication
let mac = sz::hmac_sha256(b"secret", b"Hello, world!");

Unicode Case-Folding and Case-Insensitive Search

StringZilla implements both Unicode Case Folding and Case-Insensitive UTF-8 Search. Unlike most libraries only capable of lower-casing ASCII-represented English alphabet, StringZilla covers over 1M+ codepoints. The case-folding API expects the output buffer to be at least 3× larger than the input, to accommodate for the worst-case character expansions scenarios.

use stringzilla::stringzilla as sz;

let source = "Straße";           // German: "Street"
let mut dest = [0u8; 64];        // Must be at least 3x source length
let len = sz::utf8_case_fold(source, &mut dest);
assert_eq!(&dest[..len], b"strasse");  // ß (2 bytes) → "ss" (2 bytes)

The case-insensitive search returns Some((offset, matched_length)) or None. The matched_length may differ from needle length due to expansions.

use stringzilla::stringzilla::{utf8_case_insensitive_find, Utf8CaseInsensitiveNeedle};

// Single search — ß (C3 9F) matches "SS"
if let Some((offset, len)) = utf8_case_insensitive_find("Straße", "STRASSE") {
    assert_eq!(offset, 0);
    assert_eq!(len, 7);  // "Straße" is 7 bytes
}

// Repeated searches with pre-compiled needle metadata
let needle = Utf8CaseInsensitiveNeedle::new(b"STRASSE");
for haystack in &["Straße", "STRASSE", "strasse"] {
    if let Some((offset, len)) = utf8_case_insensitive_find(haystack, &needle) {
        println!("Found at byte {} with length {}", offset, len);
    }
}

Similarity Scores

StringZilla exposes high-performance, batch-oriented similarity via the szs module. Use DeviceScope to pick hardware and optionally limit capabilities per engine.

use stringzilla::szs; // re-exported as `szs`

let cpu_scope = szs::DeviceScope::cpu_cores(4).unwrap();    // force CPU-only
let gpu_scope = szs::DeviceScope::gpu_device(0).unwrap();   // pick GPU 0 if available
let strings_a = vec!["kitten", "flaw"];
let strings_b = vec!["sitting", "lawn"];

let engine = szs::LevenshteinDistances::new(
    &cpu_scope,
    0,  // match cost
    2,  // mismatch cost - costs don't have to be 1
    3,  // open cost - may be different in Bio
    1,  // extend cost
).unwrap();
let distances = engine.compute(&cpu_scope, &strings_a, &strings_b).unwrap();
assert_eq!(distances[0], 3);
assert_eq!(distances[1], 2);

Note, that this computes byte-level distances. For UTF-8 codepoints, use a different engine class:

let strings_a = vec!["café", "αβγδ"];
let strings_b = vec!["cafe", "αγδ"];
let engine = szs::LevenshteinDistancesUtf8::new(&cpu_scope, 0, 1, 1, 1).unwrap();
let distances = engine.compute(&cpu_scope, &strings_a, &strings_b).unwrap();
assert_eq!(distances, vec![1, 1]);

Similarly, for variable substitution costs, also pass in a a weights matrix:

let mut substitution_matrix = [-1i8; 256 * 256];
for i in 0..256 { substitution_matrix[i * 256 + i] = 0; }
let engine = szs::NeedlemanWunschScores::new(&cpu_scope, &substitution_matrix, -3, -1).unwrap();
let scores = engine.compute(&cpu_scope, &strings_a, &strings_b).unwrap();

Or for local alignment scores:

let engine = szs::SmithWatermanScores::new(&cpu_scope, &substitution_matrix, -3, -1).unwrap();
let local_scores = engine.compute(&cpu_scope, &strings_a, &strings_b).unwrap();

For high-performance applications, use the StringTape crate to pass strings to compute_into methods without extra memory allocations:

use stringzilla::{szs, StringTape};

// Create StringTape from data (zero-copy compatible format)
let tape_a = StringTape::from_strings(&["kitten", "sitting", "flaw"]);
let tape_b = StringTape::from_strings(&["sitting", "kitten", "lawn"]);

// Use unified memory vector for GPU compatibility, initialized with max values
let mut distances = szs::UnifiedVec::<u32>::from_elem(u32::MAX, tape_a.len());

let engine = szs::LevenshteinDistances::new(&gpu_scope, 0, 1, 1, 1).unwrap();
engine.compute_into(&gpu_scope, &tape_a, &tape_b, &mut distances).unwrap();

// Results computed directly into unified memory, accessible from both CPU/GPU
assert_eq!(distances[0], 3);  // kitten -> sitting
assert_eq!(distances[1], 3);  // sitting -> kitten
assert_eq!(distances[2], 2);  // flaw -> lawn

Rolling Fingerprints

MinHashing is a common technique for Information Retrieval, producing compact representations of large documents. For $D$ hash-functions and a text of length $L$, in the worst case it involves computing $O(D \cdot L)$ hashes.

use stringzilla::szs;

let texts = vec![
    "quick brown fox jumps over the lazy dog",
    "quick brown fox jumped over a very lazy dog",
];
let cpu = szs::DeviceScope::cpu_cores(4).unwrap();
let ndim = 1024;
let window_widths = vec![4u64, 6, 8, 10];

let engine = szs::Fingerprints::new(
    ndim,           // number of hash functions & dimensions
    &window_widths, // optional predefined window widths
    256,            // default alphabet size for byte strings
    &cpu            // device scope
).unwrap();

let (hashes, counts) = engine.compute(&cpu, &texts).unwrap();
assert_eq!(hashes.len(), texts.len() * ndim);
assert_eq!(counts.len(), texts.len() * ndim);

For zero-copy processing with StringTape format and unified memory:

use stringzilla::{szs, StringTape};

let tape = StringTape::from_strings(&[
    "quick brown fox jumps over the lazy dog",
    "quick brown fox jumped over a very lazy dog",
]);

// Pre-allocate unified memory buffers
let mut hashes = szs::UnifiedVec::<u32>::from_elem(u32::MAX, tape.len() * ndim);
let mut counts = szs::UnifiedVec::<u32>::from_elem(u32::MAX, tape.len() * ndim);

let engine = szs::Fingerprints::new(ndim, &window_widths, 256, &cpu).unwrap();
engine.compute_into(&cpu, &tape, &mut hashes, &mut counts).unwrap();

// Results computed directly into unified memory buffers
assert!(hashes.iter().any(|&h| h != u32::MAX));  // Verify computation occurred
assert!(counts.iter().any(|&c| c != u32::MAX));

Quick Start: JavaScript

Install the Node.js package and use zero-copy Buffer APIs.

npm install stringzilla
node -p "require('stringzilla').default.capabilities" # for CommonJS
node -e "import('stringzilla').then(m=>console.log(m.default.capabilities)).catch(console.error)" # for ESM
import sz from 'stringzilla';

const haystack = Buffer.from('Hello, world!');
const needle = Buffer.from('world');

// Substring search (BigInt offsets)
const firstIndex = sz.find(haystack, needle);      // 7n
const lastIndex = sz.findLast(haystack, needle);   // 7n

// Character / charset search
const firstOIndex = sz.findByte(haystack, 'o'.charCodeAt(0));                 // 4n
const firstVowelIndex = sz.findByteFrom(haystack, Buffer.from('aeiou'));      // 1n
const lastVowelIndex = sz.findLastByteFrom(haystack, Buffer.from('aeiou'));   // 8n

// Counting (optionally overlapping)
const lCount = sz.count(haystack, Buffer.from('l'));                // 3n
const llOverlapCount = sz.count(haystack, Buffer.from('ll'), true); // 1n

// Equality/ordering utilities
const isEqual = sz.equal(Buffer.from('a'), Buffer.from('a'));
const order = sz.compare(Buffer.from('a'), Buffer.from('b')); // -1, 0, or 1

// Other helpers
const byteSum = sz.byteSum(haystack); // sum of bytes as BigInt

Unicode Case-Folding and Case-Insensitive Search

StringZilla provides full Unicode case folding (including expansions like ß → ss, ligatures like fi → fi, and special folds like µ → μ, K → k) and a case-insensitive substring search that accounts for those expansions.

import sz from "stringzilla";

// Case folding (returns a UTF-8 Buffer)
console.log(sz.utf8CaseFold(Buffer.from("Straße")).toString("utf8")); // "strasse"
console.log(sz.utf8CaseFold(Buffer.from("office")).toString("utf8"));  // "office" (U+FB01 ligature)

// Case-insensitive substring search (full Unicode case folding)
const text = Buffer.from(
    "Die Temperaturschwankungen im kosmischen Mikrowellenhintergrund sind ein Maß von etwa 20 µK.\n" +
    "Typografisch sieht man auch: ein Maß von etwa 20 μK."
);
const patternBytes = Buffer.from("EIN MASS VON ETWA 20 μK");

const first = sz.utf8CaseInsensitiveFind(text, patternBytes);
console.log(first); // { index: 69n, length: ... } (byte offsets)

// Reuse the same needle efficiently
const pattern = new sz.Utf8CaseInsensitiveNeedle(patternBytes);
const again = pattern.findIn(text);
console.log(again.index === first.index);

Hash

Single-shot and incremental hashing are both supported:

import sz from 'stringzilla';

// One-shot - stable 64-bit output across all platforms!
const hash = sz.hash(Buffer.from('Hello, world!'), 42); // returns BigInt

// Incremental updates - hasher maintains state
const hasher = new sz.Hasher(42); // seed: 42
hasher.update(Buffer.from('Hello, '));
hasher.update(Buffer.from('world!'));
const streamedHash = hasher.digest(); // returns BigInt
console.assert(hash === streamedHash);

SHA-256 Checksums

SHA-256 cryptographic checksums are available:

import sz from 'stringzilla';

// One-shot SHA-256
const digest = sz.sha256(Buffer.from('Hello, world!')); // returns Buffer (32 bytes)

// Incremental SHA-256
const hasher = new sz.Sha256();
hasher.update(Buffer.from('Hello, '));
hasher.update(Buffer.from('world!'));
const digestBuffer = hasher.digest();     // returns Buffer (32 bytes)
const digestHex = hasher.hexdigest();     // returns string (64 hex chars)

Quick Start: Swift

StringZilla can be added as a dependency in the Swift Package Manager. In your Package.swift file, add the following:

dependencies: [
    .package(url: "https://github.com/ashvardanian/stringzilla")
]

The package currently covers only the most basic functionality, but is planned to be extended to cover the full C++ API.

var s = "Hello, world! Welcome to StringZilla. 👋"
s[s.findFirst(substring: "world")!...] // "world! Welcome to StringZilla. 👋"
s[s.findLast(substring: "o")!...] // "o StringZilla. 👋"
s[s.findFirst(characterFrom: "aeiou")!...] // "ello, world! Welcome to StringZilla. 👋"
s[s.findLast(characterFrom: "aeiou")!...] // "a. 👋")
s[s.findFirst(characterNotFrom: "aeiou")!...] // "Hello, world! Welcome to StringZilla. 👋"

Unicode Case-Folding and Case-Insensitive Search

import StringZilla

let folded = "Straße".utf8CaseFoldedBytes()
print(String(decoding: folded, as: UTF8.self)) // "strasse"

let haystack =
    "Die Temperaturschwankungen im kosmischen Mikrowellenhintergrund sind ein Maß von etwa 20 µK.\n"
    + "Typografisch sieht man auch: ein Maß von etwa 20 μK."
let needle = "EIN MASS VON ETWA 20 μK"

if let range = haystack.utf8CaseInsensitiveFind(substring: needle) {
    print(haystack[range]) // "ein Maß von etwa 20 µK"
}

// Reuse the same needle efficiently
let compiledNeedle = Utf8CaseInsensitiveNeedle(needle)
if let range = compiledNeedle.findFirst(in: haystack) {
    print(haystack[range])
}

Hash

StringZilla provides high-performance hashing for Swift strings:

import StringZilla

// One-shot hashing - stable 64-bit output across all platforms!
let hash = "Hello, world!".hash(seed: 42)

// Incremental hashing for streaming data
var hasher = StringZillaHasher(seed: 42)
hasher.update("Hello, ")
hasher.update("world!")
let streamedHash = hasher.digest()
assert(hash == streamedHash)

SHA-256 Checksums

SHA-256 cryptographic checksums are available:

import StringZilla

// One-shot SHA-256
let digest = "Hello, world!".sha256() // returns [UInt8] (32 bytes)

// Incremental SHA-256
var hasher = StringZillaSha256()
hasher.update("Hello, ")
hasher.update("world!")
let digestBytes = hasher.digest()     // [UInt8] (32 bytes)
let digestHex = hasher.hexdigest()    // String (64 hex chars)

Quick Start: GoLang

Add the Go binding as a module dependency:

go get github.com/ashvardanian/stringzilla/golang@latest

Build the shared C library once, then ensure your runtime can locate it (Linux shown):

cmake -B build_shared -D STRINGZILLA_BUILD_SHARED=1 -D CMAKE_BUILD_TYPE=Release
cmake --build build_shared --target stringzilla_shared --config Release
export LD_LIBRARY_PATH="$PWD/build_shared:$LD_LIBRARY_PATH"

Use finders (substring, bytes, and sets):

package main

import (
    "fmt"
    sz "github.com/ashvardanian/stringzilla/golang"
)

func main() {
    s := "the quick brown fox jumps over the lazy dog"

    // Substrings
    fmt.Println(sz.Contains(s, "brown"))        // true
    fmt.Println(sz.Index(s, "the"))             // 0
    fmt.Println(sz.LastIndex(s, "the"))         // 35

    // Single bytes
    fmt.Println(sz.IndexByte(s, 'o'))            // 12
    fmt.Println(sz.LastIndexByte(s, 'o'))        // 41

    // Byte sets
    fmt.Println(sz.IndexAny(s, "aeiou"))        // 2  (first vowel)
    fmt.Println(sz.LastIndexAny(s, "aeiou"))    // 43 (last vowel)

    // Counting with/without overlaps
    fmt.Println(sz.Count("aaaaa", "aa", false)) // 2
    fmt.Println(sz.Count("aaaaa", "aa", true))  // 4
    fmt.Println(sz.Count("abc", "", false))     // 4
    fmt.Println(sz.Bytesum("ABC"), sz.Bytesum("ABCD"))
}

Unicode Case-Folding and Case-Insensitive Search

package main

import (
    "fmt"
    sz "github.com/ashvardanian/stringzilla/golang"
)

func main() {
    folded, _ := sz.Utf8CaseFold("Straße", true)
    fmt.Println(folded) // "strasse"

    haystack := "Die Temperaturschwankungen im kosmischen Mikrowellenhintergrund sind ein Maß von etwa 20 µK.\n" +
        "Typografisch sieht man auch: ein Maß von etwa 20 μK."
    needle := "EIN MASS VON ETWA 20 μK"

    start64, len64, _ := sz.Utf8CaseInsensitiveFind(haystack, needle, true)
    start, end := int(start64), int(start64+len64)
    fmt.Println(haystack[start:end]) // "ein Maß von etwa 20 µK"

    // Reuse the same needle efficiently
    compiled, _ := sz.NewUtf8CaseInsensitiveNeedle(needle, true)
    start64, len64, _ = compiled.FindIn(haystack, true)
    start, end = int(start64), int(start64+len64)
    fmt.Println(haystack[start:end])
}

Hash

Single-shot and incremental hashing are both supported. The Hasher type implements Go's standard hash.Hash64 and io.Writer interfaces:

import (
    "io"
    sz "github.com/ashvardanian/stringzilla/golang"
)

// One-shot hashing
one := sz.Hash("Hello, world!", 42)

// Streaming hasher (implements hash.Hash64 and io.Writer)
hasher := sz.NewHasher(42)
hasher.Write([]byte("Hello, "))
hasher.Write([]byte("world!"))
streamed := hasher.Digest()         // or hasher.Sum64()
fmt.Println(one == streamed)        // true

// Works with io.Copy and any io.Reader
file, _ := os.Open("data.txt")
hasher.Reset()
io.Copy(hasher, file)
fileHash := hasher.Sum64()

SHA-256 Checksums

SHA-256 cryptographic checksums are available. The Sha256 type implements Go's standard hash.Hash and io.Writer interfaces:

import (
    "io"
    sz "github.com/ashvardanian/stringzilla/golang"
)

// One-shot SHA-256
digest := sz.HashSha256([]byte("Hello, world!"))
fmt.Printf("%x\n", digest)          // prints 32-byte hash in hex

// Streaming SHA-256 (implements hash.Hash and io.Writer)
hasher := sz.NewSha256()
hasher.Write([]byte("Hello, "))
hasher.Write([]byte("world!"))
digestBytes := hasher.Digest()      // [32]byte
digestHex := hasher.Hexdigest()     // string (64 hex chars)

// Works with io.Copy and any io.Reader
file, _ := os.Open("data.bin")
hasher.Reset()
io.Copy(hasher, file)
fileDigest := hasher.Digest()

// Standard hash.Hash interface methods
sum := hasher.Sum(nil)              // []byte with 32 bytes
size := hasher.Size()               // 32
blockSize := hasher.BlockSize()     // 64

Algorithms & Design Decisions

StringZilla aims to optimize some of the slowest string operations. Some popular operations, however, like equality comparisons and relative order checking, almost always complete on some of the very first bytes in either string. In such operations vectorization is almost useless, unless huge and very similar strings are considered. StringZilla implements those operations as well, but won't result in substantial speedups. Where vectorization stops being effective, parallelism takes over with the new layered cake architecture:

  • StringZilla C library w/out dependencies
  • StringZillas parallel extensions:
    • Parallel C++ algorithms built with Fork Union
    • Parallel CUDA algorithms for Nvidia GPUs
    • Parallel ROCm algorithms for AMD GPUs 🔜

Exact Substring Search

Substring search algorithms are generally divided into: comparison-based, automaton-based, and bit-parallel. Different families are effective for different alphabet sizes and needle lengths. The more operations are needed per-character - the more effective SIMD would be. The longer the needle - the more effective the skip-tables are. StringZilla uses different exact substring search algorithms for different needle lengths and backends:

  • When no SIMD is available - SWAR (SIMD Within A Register) algorithms are used on 64-bit words.
  • Boyer-Moore-Horspool (BMH) algorithm with Raita heuristic variation for longer needles.
  • SIMD backends compare characters at multiple strategically chosen offsets within the needle to reduce degeneracy.

On very short needles, especially 1-4 characters long, brute force with SIMD is the fastest solution. On mid-length needles, bit-parallel algorithms are effective, as the character masks fit into 32-bit or 64-bit words. Either way, if the needle is under 64-bytes long, on haystack traversal we will still fetch every CPU cache line. So the only way to improve performance is to reduce the number of comparisons.

For 2-byte needles, see sz_find_2byte_serial_ in include/stringzilla/find.h:

https://github.com/ashvardanian/StringZilla/blob/e1966de91600298d3c5cf4fe7be40d434f0f405e/include/stringzilla/find.h#L422-L463

Going beyond that, to long needles, Boyer-Moore (BM) and its variants are often the best choice. It has two tables: the good-suffix shift and the bad-character shift. Common choice is to use the simplified BMH algorithm, which only uses the bad-character shift table, reducing the pre-processing time. We do the same for mid-length needles up to 256 bytes long. That way the stack-allocated shift table remains small.

For mid-length needles (≤256 bytes), see sz_find_horspool_upto_256bytes_serial_ in include/stringzilla/find.h:

https://github.com/ashvardanian/StringZilla/blob/e1966de91600298d3c5cf4fe7be40d434f0f405e/include/stringzilla/find.h#L620-L667

In the C++ Standards Library, the std::string::find function uses the BMH algorithm with Raita's heuristic. Before comparing the entire string, it matches the first, last, and the middle character. Very practical, but can be slow for repetitive characters. Both SWAR and SIMD backends of StringZilla have a cheap pre-processing step, where we locate unique characters. This makes the library a lot more practical when dealing with non-English corpora.

The offset selection heuristic is implemented in sz_locate_needle_anomalies_ in include/stringzilla/find.h:

https://github.com/ashvardanian/StringZilla/blob/e1966de91600298d3c5cf4fe7be40d434f0f405e/include/stringzilla/find.h#L244-L305

All those, still, have $O(hn)$ worst case complexity. To guarantee $O(h)$ worst case time complexity, the Apostolico-Giancarlo (AG) algorithm adds an additional skip-table. Preprocessing phase is $O(n+sigma)$ in time and space. On traversal, performs from $(h/n)$ to $(3h/2)$ comparisons. It however, isn't practical on modern CPUs. A simpler idea, the Galil-rule might be a more relevant optimizations, if many matches must be found.

Other algorithms previously considered and deprecated:

  • Apostolico-Giancarlo algorithm for longer needles. Control-flow is too complex for efficient vectorization.
  • Shift-Or-based Bitap algorithm for short needles. Slower than SWAR.
  • Horspool-style bad-character check in SIMD backends. Effective only for very long needles, and very uneven character distributions between the needle and the haystack. Faster "character-in-set" check needed to generalize.

§ Reading materials. Exact String Matching Algorithms in Java. SIMD-friendly algorithms for substring searching.

Exact Multiple Substring Search

Few algorithms for multiple substring search are known. Most are based on the Aho-Corasick automaton, which is a generalization of the KMP algorithm. The naive implementation, however:

  • Allocates disjoint memory for each Trie node and Automaton state.
  • Requires a lot of pointer chasing, limiting speculative execution.
  • Has a lot of branches and conditional moves, which are hard to predict.
  • Matches text a character at a time, which is slow on modern CPUs.

There are several ways to improve the original algorithm. One is to use sparse DFA representation, which is more cache-friendly, but would require extra processing to navigate state transitions.

Levenshtein Edit Distance

Levenshtein distance is the best known edit-distance for strings, that checks, how many insertions, deletions, and substitutions are needed to transform one string to another. It's extensively used in approximate string-matching, spell-checking, and bioinformatics.

The computational cost of the Levenshtein distance is $O(n * m)$, where $n$ and $m$ are the lengths of the string arguments. To compute that, the naive approach requires $O(n * m)$ space to store the "Levenshtein matrix", the bottom-right corner of which will contain the Levenshtein distance. The algorithm producing the matrix has been simultaneously studied/discovered by the Soviet mathematicians Vladimir Levenshtein in 1965, Taras Vintsyuk in 1968, and American computer scientists - Robert Wagner, David Sankoff, Michael J. Fischer in the following years. Several optimizations are known:

  1. Space Optimization: The matrix can be computed in $O(min(n,m))$ space, by only storing the last two rows of the matrix.
  2. Divide and Conquer: Hirschberg's algorithm can be applied to decompose the computation into subtasks.
  3. Automata: Levenshtein automata can be effective, if one of the strings doesn't change, and is a subject to many comparisons.
  4. Shift-Or: Bit-parallel algorithms transpose the matrix into a bit-matrix, and perform bitwise operations on it.

The last approach is quite powerful and performant, and is used by the great RapidFuzz library. It's less known, than the others, derived from the Baeza-Yates-Gonnet algorithm, extended to bounded edit-distance search by Manber and Wu in 1990s, and further extended by Gene Myers in 1999 and Heikki Hyyro between 2002 and 2004.

StringZilla focuses on a different approach, extensively used in Unum's internal combinatorial optimization libraries. It doesn't change the number of trivial operations, but performs them in a different order, removing the data dependency, that occurs when computing the insertion costs. StringZilla evaluates diagonals instead of rows, exploiting the fact that all cells within a diagonal are independent, and can be computed in parallel. We'll store 3 diagonals instead of the 2 rows, and each consecutive diagonal will be computed from the previous two. Substitution costs will come from the sooner diagonal, while insertion and deletion costs will come from the later diagonal.

Row-by-Row Algorithm
Computing row 4:
    ∅  A  B  C  D  E
 ∅  0  1  2  3  4  5
 P  1  ░  ░  ░  ░  ░
 Q  2  ■  ■  ■  ■  ■
 R  3  ■  ■  □  →  .
 S  4  .  .  .  .  .
 T  5  .  .  .  .  .
Anti-Diagonal Algorithm
Computing diagonal 5:
    ∅  A  B  C  D  E
 ∅  0  1  2  3  4  5
 P  1  ░  ░  ■  ■  □
 Q  2  ░  ■  ■  □  ↘
 R  3  ■  ■  □  ↘  .
 S  4  ■  □  ↘  .  .
 T  5  □  ↘  .  .  .
Legend:
0,1,2,3... = initialization constants    = cells processed and forgotten    = stored cells    = computing in parallel    → ↘ = movement direction    . = cells to compute later

This results in much better vectorization for intra-core parallelism and potentially multi-core evaluation of a single request. Moreover, it's easy to generalize to weighted edit-distances, where the cost of a substitution between two characters may not be the same for all pairs, often used in bioinformatics.

§ Reading materials. Faster Levenshtein Distances with a SIMD-friendly Traversal Order.

Needleman-Wunsch and Smith-Waterman Scores for Bioinformatics

The field of bioinformatics studies various representations of biological structures. The "primary" representations are generally strings over sparse alphabets:

  • DNA sequences, where the alphabet is {A, C, G, T}, ranging from ~100 characters for short reads to 3 billion for the human genome.
  • RNA sequences, where the alphabet is {A, C, G, U}, ranging from ~50 characters for tRNA to thousands for mRNA.
  • Proteins, where the alphabet is made of 22 amino acids, ranging from 2 characters for dipeptide to 35,000 for Titin, the longest protein.

The shorter the representation, the more often researchers may want to use custom substitution matrices. Meaning that the cost of a substitution between two characters may not be the same for all pairs. In the general case the serial algorithm is supposed to work for arbitrary substitution costs for each of 256×256 possible character pairs. That lookup table, however, is too large to fit into CPU registers, so instead, the upcoming design focuses on 32×32 substitution matrices, which fit into 1 KB with single-byte "error costs". That said, most BLOSUM and PAM substitution matrices only contain 4-bit values, so they can be packed even further.

Next design goals:

  • Needleman-Wunsch Automata

Memory Copying, Fills, and Moves

A lot has been written about the time computers spend copying memory and how that operation is implemented in LibC. Interestingly, the operation can still be improved, as most Assembly implementations use outdated instructions. Even performance-oriented STL replacements, like Meta's Folly v2024.09.23 focus on AVX2, and don't take advantage of the new masked instructions in AVX-512 or SVE.

In AVX-512, StringZilla uses non-temporal stores to avoid cache pollution, when dealing with very large strings. Moreover, it handles the unaligned head and the tails of the target buffer separately, ensuring that writes in big copies are always aligned to cache-line boundaries. That's true for both AVX2 and AVX-512 backends.

StringZilla also contains "drafts" of smarter, but less efficient algorithms, that minimize the number of unaligned loads, performing shuffles and permutations. That's a topic for future research, as the performance gains are not yet satisfactory.

§ Reading materials. memset benchmarks by Nadav Rotem. Cache Associativity by Sergey Slotin.

Hashing

StringZilla implements a high-performance 64-bit hash function inspired by the "AquaHash", "aHash", and "GxHash" design and optimized for modern CPU architectures. The algorithm utilizes AES encryption rounds combined with shuffle-and-add operations to achieve exceptional mixing properties while maintaining consistent output across platforms. It passes the rigorous SMHasher test suite, including the --extra flag with no collisions.

The core algorithm operates on a dual-state design:

  • AES State: Initialized with seed XOR-ed against π constants.
  • Sum State: Accumulates shuffled input data with a permutation.

For strings ≤64 bytes, a minimal state processes data in 16-byte blocks. Longer strings employ a 4× wider state (512 bits) that processes 64-byte chunks, maximizing throughput on modern superscalar CPUs. The algorithm can be expressed in pseudocode as:

function sz_hash(text: u8[], length: usize, seed: u64) -> u64:
    # 1024 bits worth of π constants
    pi: u64[16] = [
        0x243F6A8885A308D3, 0x13198A2E03707344, 0xA4093822299F31D0, 0x082EFA98EC4E6C89,
        0x452821E638D01377, 0xBE5466CF34E90C6C, 0xC0AC29B7C97C50DD, 0x3F84D5B5B5470917,
        0x9216D5D98979FB1B, 0xD1310BA698DFB5AC, 0x2FFD72DBD01ADFB7, 0xB8E1AFED6A267E96,
        0xBA7C9045F12C7F99, 0x24A19947B3916CF7, 0x0801F2E2858EFC16, 0x636920D871574E69]

    # Permutation order for the sum state
    shuffle_pattern: u8[16] = [
        0x04, 0x0b, 0x09, 0x06, 0x08, 0x0d, 0x0f, 0x05,
        0x0e, 0x03, 0x01, 0x0c, 0x00, 0x07, 0x0a, 0x02]

    # Initialize key and states
    keys_u64s: u64[2] = [seed, seed]
    aes_u64s: u64[2] = [seed ⊕ pi[0], seed ⊕ pi[1]]
    sum_u64s: u64[2] = [seed ⊕ pi[8], seed ⊕ pi[9]]

    if length ≤ 64:
        # Small input: process 1-4 zero-padded blocks of 16 bytes each
        blocks_u8s: u8[16][] = split_into_blocks(text, length, 16)
        for each block_u8s: u8[16] in blocks_u8s:
            aes_u64s = AESENC(aes_u64s, block_u8s)
            sum_u64s = SHUFFLE(sum_u64s, shuffle_pattern) + block_u8s
    else:
        # Large input: use 4× wider 512-bits states
        aes_u64s: u64[8] = [
            seed ⊕ pi[0], seed ⊕ pi[1], seed ⊕ pi[2], seed ⊕ pi[3],
            seed ⊕ pi[4], seed ⊕ pi[5], seed ⊕ pi[6], seed ⊕ pi[7]]
        sum_u64s: u64[8] = [
            seed ⊕ pi[8], seed ⊕ pi[9], seed ⊕ pi[10], seed ⊕ pi[11],
            seed ⊕ pi[12], seed ⊕ pi[13], seed ⊕ pi[14], seed ⊕ pi[15]]

        # Process 64-byte chunks (4×16-byte blocks)
        for each chunk_u8s: u8[64] in text:
            blocks_u8s: u8[16][4] = split_chunk_into_4_blocks(chunk_u8s)
            for i in 0..3:
                offset: usize = i * 2  # Each lane stores two u64s
                aes_u64s[offset:offset+1] = AESENC(aes_u64s[offset:offset+1], blocks_u8s[i])
                sum_u64s[offset:offset+1] = SHUFFLE(sum_u64s[offset:offset+1], shuffle_pattern) + blocks_u8s[i]

        # Fold 8×u64 state back to 2×u64 for finalization
        aes_u64s: u64[2] = fold_to_2u64(aes_u64s)
        sum_u64s: u64[2] = fold_to_2u64(sum_u64s)

    # Finalization: mix length into key
    key_with_length: u64[2] = [keys_u64s[0] + length, keys_u64s[1]]

    # Multiple AES rounds for SMHasher compliance
    mixed_u64s: u64[2] = AESENC(sum_u64s, aes_u64s)
    result_u64s: u64[2] = AESENC(AESENC(mixed_u64s, key_with_length), mixed_u64s)

    return result_u64s[0]  # Extract low 64 bits

This allows us to balance several design trade-offs. First, it allows us to achieve a high port-level parallelism. Looking at AVX-512 capable CPUs and their ZMM instructions, on each cycle, we'll have at least 2 ports busy when dealing with long strings:

  • VAESENC: 5 cycles on port 0 on Intel Ice Lake, 4 cycles on ports 0/1 on AMD Zen4.
  • VPSHUFB_Z: 3 cycles on port 5 on Intel Ice Lake, 2 cycles on ports 1/2 on AMD Zen4.
  • VPADDQ: 1 cycle on ports 0/5 on Intel Ice Lake, 1 cycle on ports 0/1/2/3 on AMD Zen4.

When dealing with smaller strings, we design our approach to avoid large registers and maintain the CPU at the same energy state, thereby avoiding downclocking and expensive power-state transitions.

Unlike some AES-accelerated alternatives, the length of the input is not mixed into the AES block at the start to allow incremental construction, when the final length is not known in advance. Also, unlike some alternatives, with "masked" AVX-512 and "predicated" SVE loads, we avoid expensive block-shuffling procedures on non-divisible-by-16 lengths.

§ Reading materials. Stress-testing hash functions for avalance behaviour, collision bias, and distribution.

SHA-256 Checksums

In addition to the fast AES-based hash, StringZilla implements hardware-accelerated SHA-256 cryptographic checksums. The implementation follows the FIPS 180-4 specification and provides multiple backends.

Random Generation

StringZilla implements a fast Pseudorandom Number Generator inspired by the "AES-CTR-128" algorithm, reusing the same AES primitives as the hash function. Unlike "NIST SP 800-90A" which uses multiple AES rounds, StringZilla uses only one round of AES mixing for performance while maintaining reproducible output across platforms. The generator operates in counter mode with AESENC(nonce + lane_index, nonce ⊕ pi_constants), rotating through the first 512 bits of π for each 16-byte block. The only state required to reproduce an output is a 64-bit nonce, which is much cheaper than a Mersenne Twister.

Sorting

For lexicographic sorting of string collections, StringZilla exports pointer-sized n‑grams ("pgrams") into a contiguous buffer to improve locality, then recursively QuickSorts those pgrams with a 3‑way partition and dives into equal pgrams to compare deeper characters. Very small inputs fall back to insertion sort.

  • Average time complexity: O(n log n)
  • Worst-case time complexity: quadratic (due to QuickSort), mitigated in practice by 3‑way partitioning and the n‑gram staging

Unicode 17, UTF-8, and Wide Characters

Most StringZilla operations are byte-level, so they work well with ASCII and UTF-8 content out of the box. In some cases, like edit-distance computation, the result of byte-level evaluation and character-level evaluation may differ.

  • szs_levenshtein_distances_utf8("αβγδ", "αγδ") == 1 — one unicode symbol.
  • szs_levenshtein_distances("αβγδ", "αγδ") == 2 — one unicode symbol is two bytes long.

Java, JavaScript, Python 2, C#, and Objective-C, however, use wide characters (wchar) - two byte long codes, instead of the more reasonable fixed-length UTF-32 or variable-length UTF-8. This leads to all kinds of offset-counting issues when facing four-byte long Unicode characters. StringZilla uses proper 32-bit "runes" to represent unpacked Unicode codepoints, ensuring correct results in all operations. Moreover, it implements the Unicode 17.0 standard, being practically the only library besides ICU and PCRE2 to do so, but with order(s) of magnitude better performance.

Case-Folding and Case-Insensitive Search

StringZilla provides Unicode-aware case-insensitive substring search that handles the full complexity of Unicode case folding. This includes multi-character expansions:

Character Codepoint UTF-8 Bytes Case-Folds To Result Bytes
ß U+00DF C3 9F ss 73 73
U+FB03 EF AC 83 ffi 66 66 69
İ U+0130 C4 B0 i + ◌̇ 69 CC 87

The search returns byte offsets and lengths in the original haystack, correctly handling length differences. For example, searching for "STRASSE" (7 bytes) in "Straße" (7 bytes: 53 74 72 61 C3 9F 65) succeeds because both case-fold to "strasse".

Note that Turkish İ and ASCII I are distinct: İstanbul case-folds to i̇stanbul (with combining dot), while ISTANBUL case-folds to istanbul (without). They will not match each other — this is correct Unicode behavior for Turkish locale handling.

For wide-character environments (Java, JavaScript, Python 2, C#), consider transcoding with simdutf.

Dynamic Dispatch

Due to the high-level of fragmentation of SIMD support in different CPUs, StringZilla uses the names of select Intel and ARM CPU generations for its backends. You can query supported backends and use them manually. Use it to guarantee constant performance, or to explore how different algorithms scale on your hardware.

sz_find(text, length, pattern, 3);          // Auto-dispatch
sz_find_westmere(text, length, pattern, 3);  // Intel Westmere+ SSE4.2
sz_find_haswell(text, length, pattern, 3);  // Intel Haswell+ AVX2
sz_find_skylake(text, length, pattern, 3);  // Intel Skylake+ AVX-512
sz_find_neon(text, length, pattern, 3);     // Arm NEON 128-bit
sz_find_sve(text, length, pattern, 3);      // Arm SVE 128/256/512/1024/2048-bit

StringZilla automatically picks the most advanced backend for the given CPU. Similarly, in Python, you can log the auto-detected capabilities:

python -c "import stringzilla; print(stringzilla.__capabilities__)"         # ('serial', 'westmere', 'haswell', 'skylake', 'ice', 'neon', 'sve', 'sve2+aes')
python -c "import stringzilla; print(stringzilla.__capabilities_str__)"     # "haswell, skylake, ice, neon, sve, sve2+aes"

You can also explicitly set the backend to use, or scope the backend to a specific function.

import stringzilla as sz
sz.reset_capabilities(('serial',))          # Force SWAR backend
sz.reset_capabilities(('haswell',))         # Force AVX2 backend
sz.reset_capabilities(('neon',))            # Force NEON backend
sz.reset_capabilities(sz.__capabilities__)  # Reset to auto-dispatch

Contributing 👾

Please check out the contributing guide for more details on how to set up the development environment and contribute to this project. If you like this project, you may also enjoy USearch, UCall, UForm, and SimSIMD. 🤗

If you like strings and value efficiency, you may also enjoy the following projects:

  • simdutf - transcoding UTF-8, UTF-16, and UTF-32 LE and BE.
  • hyperscan - regular expressions with SIMD acceleration.
  • pyahocorasick - Aho-Corasick algorithm in Python.
  • rapidfuzz - fast string matching in C++ and Python.
  • memchr - fast string search in Rust.

If you are looking for more reading materials on this topic, consider the following:

License 📜

Feel free to use the project under Apache 2.0 or the Three-clause BSD license at your preference.

Release history Release notifications | RSS feed

This version

4.6.1

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stringzilla-4.6.1.tar.gz (646.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

stringzilla-4.6.1-cp313-cp313-win_arm64.whl (123.3 kB view details)

Uploaded CPython 3.13Windows ARM64

stringzilla-4.6.1-cp313-cp313-win_amd64.whl (162.6 kB view details)

Uploaded CPython 3.13Windows x86-64

stringzilla-4.6.1-cp313-cp313-win32.whl (114.8 kB view details)

Uploaded CPython 3.13Windows x86

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_s390x.whl (613.5 kB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_ppc64le.whl (619.1 kB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_i686.whl (626.3 kB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_armv7l.whl (587.1 kB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp313-cp313-musllinux_1_2_aarch64.whl (653.9 kB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (640.6 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (657.4 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (689.9 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (644.1 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp313-cp313-macosx_11_0_arm64.whl (199.4 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

stringzilla-4.6.1-cp313-cp313-macosx_10_13_x86_64.whl (212.3 kB view details)

Uploaded CPython 3.13macOS 10.13+ x86-64

stringzilla-4.6.1-cp312-cp312-win_arm64.whl (123.3 kB view details)

Uploaded CPython 3.12Windows ARM64

stringzilla-4.6.1-cp312-cp312-win_amd64.whl (162.6 kB view details)

Uploaded CPython 3.12Windows x86-64

stringzilla-4.6.1-cp312-cp312-win32.whl (114.8 kB view details)

Uploaded CPython 3.12Windows x86

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_s390x.whl (613.4 kB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_ppc64le.whl (619.1 kB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_i686.whl (626.3 kB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_armv7l.whl (587.1 kB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp312-cp312-musllinux_1_2_aarch64.whl (653.9 kB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (640.7 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (657.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (689.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (644.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp312-cp312-macosx_11_0_arm64.whl (199.4 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

stringzilla-4.6.1-cp312-cp312-macosx_10_13_x86_64.whl (212.3 kB view details)

Uploaded CPython 3.12macOS 10.13+ x86-64

stringzilla-4.6.1-cp311-cp311-win_arm64.whl (123.1 kB view details)

Uploaded CPython 3.11Windows ARM64

stringzilla-4.6.1-cp311-cp311-win_amd64.whl (162.3 kB view details)

Uploaded CPython 3.11Windows x86-64

stringzilla-4.6.1-cp311-cp311-win32.whl (114.5 kB view details)

Uploaded CPython 3.11Windows x86

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_s390x.whl (611.0 kB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_ppc64le.whl (618.7 kB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_i686.whl (624.5 kB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_armv7l.whl (583.8 kB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp311-cp311-musllinux_1_2_aarch64.whl (654.1 kB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (640.0 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (656.8 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (690.0 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (640.9 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp311-cp311-macosx_11_0_arm64.whl (199.1 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

stringzilla-4.6.1-cp311-cp311-macosx_10_13_x86_64.whl (211.6 kB view details)

Uploaded CPython 3.11macOS 10.13+ x86-64

stringzilla-4.6.1-cp310-cp310-win_arm64.whl (123.1 kB view details)

Uploaded CPython 3.10Windows ARM64

stringzilla-4.6.1-cp310-cp310-win_amd64.whl (162.4 kB view details)

Uploaded CPython 3.10Windows x86-64

stringzilla-4.6.1-cp310-cp310-win32.whl (114.5 kB view details)

Uploaded CPython 3.10Windows x86

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_s390x.whl (604.6 kB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_ppc64le.whl (612.3 kB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_i686.whl (620.5 kB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_armv7l.whl (578.8 kB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp310-cp310-musllinux_1_2_aarch64.whl (647.5 kB view details)

Uploaded CPython 3.10musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (633.8 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (649.8 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (684.2 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (633.8 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp310-cp310-macosx_11_0_arm64.whl (199.1 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

stringzilla-4.6.1-cp310-cp310-macosx_10_13_x86_64.whl (211.6 kB view details)

Uploaded CPython 3.10macOS 10.13+ x86-64

stringzilla-4.6.1-cp39-cp39-win_arm64.whl (123.1 kB view details)

Uploaded CPython 3.9Windows ARM64

stringzilla-4.6.1-cp39-cp39-win_amd64.whl (162.5 kB view details)

Uploaded CPython 3.9Windows x86-64

stringzilla-4.6.1-cp39-cp39-win32.whl (114.6 kB view details)

Uploaded CPython 3.9Windows x86

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_s390x.whl (604.5 kB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_ppc64le.whl (611.3 kB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_i686.whl (620.8 kB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_armv7l.whl (578.4 kB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp39-cp39-musllinux_1_2_aarch64.whl (647.3 kB view details)

Uploaded CPython 3.9musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (633.4 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (648.6 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (683.6 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (633.4 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp39-cp39-macosx_11_0_arm64.whl (199.0 kB view details)

Uploaded CPython 3.9macOS 11.0+ ARM64

stringzilla-4.6.1-cp39-cp39-macosx_10_13_x86_64.whl (211.5 kB view details)

Uploaded CPython 3.9macOS 10.13+ x86-64

stringzilla-4.6.1-cp38-cp38-win_amd64.whl (162.4 kB view details)

Uploaded CPython 3.8Windows x86-64

stringzilla-4.6.1-cp38-cp38-win32.whl (114.5 kB view details)

Uploaded CPython 3.8Windows x86

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ x86-64

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_s390x.whl (601.6 kB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ s390x

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_ppc64le.whl (608.5 kB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ ppc64le

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_i686.whl (618.6 kB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ i686

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_armv7l.whl (576.1 kB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ ARMv7l

stringzilla-4.6.1-cp38-cp38-musllinux_1_2_aarch64.whl (644.6 kB view details)

Uploaded CPython 3.8musllinux: musl 1.2+ ARM64

stringzilla-4.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

stringzilla-4.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl (630.4 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ s390xmanylinux: glibc 2.28+ s390x

stringzilla-4.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl (645.2 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ppc64lemanylinux: glibc 2.28+ ppc64le

stringzilla-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl (679.9 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64manylinux: glibc 2.28+ ARM64

stringzilla-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (630.5 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ i686manylinux: glibc 2.5+ i686

stringzilla-4.6.1-cp38-cp38-macosx_11_0_arm64.whl (198.8 kB view details)

Uploaded CPython 3.8macOS 11.0+ ARM64

stringzilla-4.6.1-cp38-cp38-macosx_10_13_x86_64.whl (211.2 kB view details)

Uploaded CPython 3.8macOS 10.13+ x86-64

File details

Details for the file stringzilla-4.6.1.tar.gz.

File metadata

  • Download URL: stringzilla-4.6.1.tar.gz
  • Upload date:
  • Size: 646.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1.tar.gz
Algorithm Hash digest
SHA256 47e9b0b95337857146e0dd4309998af7acab6bdca1f85c069adc1c32bbd57cb0
MD5 d049fd65889ada9c4539a7f1fe794066
BLAKE2b-256 4dc008c222e1c89871970121f8ead9764f63df925e32879f68bb479c61f7ad89

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-win_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-win_arm64.whl
Algorithm Hash digest
SHA256 c2cc745ce598af8cdd5067b923ba7631bbe5e77b9d552b7d5a29135566ea3bec
MD5 05b9d311111cb2148eb609bfa41e8f3c
BLAKE2b-256 e97c350482e963262ff6cfb7d094fe5254c6a432c21ce971d83e98a23268a2ed

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 a9da1feb660085a00f8c8e781be85a4984e6a4504f7804af1b13fdf5563a41b7
MD5 427a74c9f7f694c4d63b246c39f57a2d
BLAKE2b-256 97dbc917b8c9428f647bcd04433e66110cbcd63cee1075f735bb3c7ae6b2ba20

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp313-cp313-win32.whl
  • Upload date:
  • Size: 114.8 kB
  • Tags: CPython 3.13, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-win32.whl
Algorithm Hash digest
SHA256 e8b2ab69ee28eb79f54754e9b85cad827580f83aab8d5929ac1eb140fe6e7ade
MD5 9fc07f496d2caca95a4cb4545925efe3
BLAKE2b-256 16834b694084746a7e58604a46dc5a6911341b5ae332360492262a84a265d110

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 0e162700650d25f6e9040fb9d196fec19337c072cad5b1df11fc4f612e7d86c7
MD5 ca2a1e3709c65db96450a5d31c0833fd
BLAKE2b-256 a53bc49a971f9bbec98deb3b388dd51a1ef458472e871223f86608bfdad9cc22

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 19d91a5a4e6a3c7827d89a3b8bfe5e691f90987adaff9418c63bd17380fe1c38
MD5 40d97b14837ffbd3f1d0eadba4aeb6e4
BLAKE2b-256 2ea3ccf774223db71021d0af56d385a17d7f78245d8d34ba5cadc6517fd0e58c

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 f40b8973efa7bc127d07a26c5bbd753197cd9d7b539810896bf8b98a9542f51b
MD5 b438c5257982ffefe0bee3f93ec94239
BLAKE2b-256 3765333f702b366a9595fea03de805379bb672fa3cafe52178697a09f430fc1a

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 1360b78634f495272a85704cacb2197ecce7f00b9b8be1c0d2d6e7c05ffc4efc
MD5 72180915eb085fe6e0393e2fec3b66a4
BLAKE2b-256 dafb33702a7365aba99b0bb7e6866fe35860142a8e9752d6de3de66250bc9f90

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 b23d3172830e5136691a9a13bb2f6bc391fff7d3a3399e02a690bcbfd0f5bbda
MD5 989d0eed2440edb1d235df3f3279848d
BLAKE2b-256 dde8281d32b15adda19346edc0a722e6433e6fc3e9356cfcf9bfef512d9f2a78

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 3c4248555165e1dc5c743932941070c2f523cdaf17b3938ed04dd9a74cc543e8
MD5 5af7eb65e2ce2a9a3a8cf56f2b3eb195
BLAKE2b-256 f0b07e71463c0545f13c9e893391a1c7ba61cd4344240b1c972095bcef06f489

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e288fd253872c6a098d4478b2653a05ec0c6be51b1618a45f25dd78527950a96
MD5 6b6c3c996c36d51a24c04a21a55438fb
BLAKE2b-256 e30486ee8bde71c1898ce2bcf1cbc3f2b80ec749852b9031ffbf3eb7e8802aa1

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 4a924be9e4ca165da0c88017ac33caa888d7b33c2ec9c5cf3cc17e9bf090faff
MD5 a4e0ac6db8bb03da6344f402172d2d5e
BLAKE2b-256 f8d406a86053caa38834ec91c90d1823928a03a9f9080a37dbf087a5cad98c77

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 cf49c4f06e309a2e0b99b01037ad6595a546cdcc32206f15c8b796dc666adb03
MD5 8c89ef45f55507b441ec0559cd324eee
BLAKE2b-256 4c531b017ffc46cde8f64055ac91b18a0b37b708f13e79ba8cd51da337e030f8

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 810d86b24865832ee61641080d59f6de148f95b300dbf5a94cf45145b33c897b
MD5 ab6b5ad0c6038c2ab1f91d547d141fd4
BLAKE2b-256 90d84f1154c70e1da8ff14d10df55e5c0e64602567051e0e9d9f1e0172a158bb

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 58327cc6e53951f2f3df02f697ba2a429ab7a1a123cce58db708398ad24fadd2
MD5 3eea142e422f723d9dbb8c061707405a
BLAKE2b-256 b190cedbc0615d5d8a8b448b954c9d4818768ac5c45e114afee11e5918982a35

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9f5d8f0475d85fa5999b3c474f5c8f7fb1eaea8b38599b7c119cf4fc3ef42757
MD5 abd5b20dd7481226bc52d8a5af21e603
BLAKE2b-256 0f66a641718b57adc22ba9dffa9c3eedb8bc7719726edcb09b385d2d79b24f75

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp313-cp313-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp313-cp313-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 30468b0c5953947bda2bba2234f0c03e724c93c89e28f816ab0f694f63fd15d0
MD5 de9720f8058a1dc50f5ef9f2b5f044c9
BLAKE2b-256 643713e3d79ebe2425769617b95510b5d2a4e1128bc6f2392ceba452b4bf8e45

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-win_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-win_arm64.whl
Algorithm Hash digest
SHA256 f928181e55f49ff14a4c47c651ff853e5dbaf21d1e5ede1f98fe53a94261346f
MD5 99b4f4113d5b698761543df620ef97c0
BLAKE2b-256 af35548fc3cb3131b5c6ebba6ede0c40951f358c6f2df71453a9d9843b358f49

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d9beb6cbd5d2de22816d7e3e5503dea2bb47723c0868c9cd4144b0304112b2d8
MD5 f68c46691ef742241c98c50699f53afd
BLAKE2b-256 cd1e54dd3db9c48bff0de3bd085be57c6efeef0489a8e47e84fdc00f311f4cf5

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp312-cp312-win32.whl
  • Upload date:
  • Size: 114.8 kB
  • Tags: CPython 3.12, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-win32.whl
Algorithm Hash digest
SHA256 4c31cdfcfc4ce304ffb970a353f563d8e668d9819d6c73f0c39c162e7175f83e
MD5 5ffab3a3b31e8820b3fee4e6611ad9cb
BLAKE2b-256 b6458897eff5a8c3e155a2c26639a1c3ba49d5d571b18e5c46abd60642a2ef4f

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ed0ddb8e3ab71812a37f34ee15d45688021ae0e971cd337a55a8ebf04a70b883
MD5 018d337cdc0308613d7b45132959fcef
BLAKE2b-256 b88d057d939d09ef261bdcb5273c00e6a9380d7fe2e6937eef68a2be14aad189

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 e351b69d77d45a96293ee065c62376531d58163dd2b74c8db2f53cafbb970896
MD5 fd3be62e952171f0196971b999aff96d
BLAKE2b-256 2ccbfc4648dcaee966bc7f7ee9b6298fe8c758ac8672f76aa58f145ad153c537

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 a1218590cb4d644549e51c17e020cc5b95bd7129f31025c2dd1e234adb2b5817
MD5 b15d653a9f7ab82447ff9c4acc9d3e1f
BLAKE2b-256 76ea667dc6cc4f77514c6286a8ba302c371ad00fc1d36e7b39f41431f4cb564e

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 fcb8d9e8d5ca26d56c25f04d9d55a864814a8e29ef48987a28e778c5144a3605
MD5 006b45073bc5ea93d2f4f1c1a184e2aa
BLAKE2b-256 7e9d82d3b696268bc07f3f71d2bb152dd60888047f7ea85cc6da92ca5b14d050

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 0a37390c949fbd38a4cfb32c1482faab51322a7af34d2ad9afe71f34f2614c3c
MD5 3302cfbe8e2e6a189c2a60457f8fd2f7
BLAKE2b-256 3afff07d0896cfee42bae82a98068ff34254f354da872db58ec28a78b596e1e8

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 39018a9b33995650fb0e5d144608024629c4e90cc9d47c90eaa4cc8cfdcbdc21
MD5 1145fa19cec25f95c0553cb96559ecae
BLAKE2b-256 0ae4763a653ca1bdef70323c8f064ba09d4437ff52cd85e3ceab128d69b6aeba

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 dd75a62340b09cfedfddf9359bbfc20465c25f24e704331027b994199a34766e
MD5 a19c860f8410dae45564c6ac63f33bb2
BLAKE2b-256 f03c86c930bf4ef7ad4f89ac8b6768bd4afd7010c4d3b45db60a1711fc72e89c

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 4f7d8bdbe20a61acfffbecf3cab9a741c382f8805cb68e513b78234a614548fb
MD5 2cf73dfdb764e3802d1bc427c9c780d8
BLAKE2b-256 8c1a2298271b5b8f35a9fca57841b4f37e60c91e095110dc37691ba32a0d02e0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 393ca09165194b08664ecd615205d26f2d40666ef971e5bdabb205e713ae2269
MD5 41c10050c236694971b714ff1c57484b
BLAKE2b-256 6adbbe3a3d58fc34512de73bc6b84ba0c423e1b5210f07e9c2874ecdd1b5bcaf

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 25454262cd73db2d9718498ea27d61f2cebed89c10c5e8e404049d36502934c5
MD5 655a9a270af0643a25d608b838cf45c5
BLAKE2b-256 87219aa322a1618f2e63e26211f8c96d91b2eb850bd57faef4fff7dbd44e4778

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 1d50158354618ed1b9bf18172be8ee491af30259e0d156394da9dd41d7400264
MD5 31c7c2d5f253be030660c89f56dea96c
BLAKE2b-256 6af941ebe73ab07ab56096c5f76839c98074394f8d1266ad85b850444734cb6d

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 921d5cf2ed53d1e4de89f07b7372053b110be7272c40923f4590b82dd9f74f95
MD5 f30db204a4f244dd763adcd15ee085e5
BLAKE2b-256 6bb6624af99c2c613a22b248eefc79fb272eecd00945f8e172dd520fd55b8d80

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp312-cp312-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp312-cp312-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 6c971a8a86bb4a2adae2a0e9a9afd1bd2dbc56b888d3179de09c43067f232701
MD5 4d059ff445d53f4a04217779cdf80812
BLAKE2b-256 3d56a2771ab8979a92db5dda7af3f2737f77f8e1242ab524619875eb4a853c19

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-win_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-win_arm64.whl
Algorithm Hash digest
SHA256 f6dd5340ca0b14e267a066e17e2d4a3fb55707838c939a9334fb8e34684faa26
MD5 4029374733d18542f67631f19d3b5d2c
BLAKE2b-256 e2bb053f141bbb004456af565ada4f3fc1f896cf3fb384cff954ea7bada8a486

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 9a122640d2d007982455bdff2c47ee929ffe7d195f064e52a308dfd1770fd8d9
MD5 8f5fce0e0a068b54af06805eb6dc75b2
BLAKE2b-256 444a5fd28fb177d57f92bb457393ec5d085fb12bfc62c14723fa0eede6979112

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp311-cp311-win32.whl
  • Upload date:
  • Size: 114.5 kB
  • Tags: CPython 3.11, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-win32.whl
Algorithm Hash digest
SHA256 c545d79165521565e512dbb857d204fd6a51ac63ab175f63bbfe6ef5a955fa13
MD5 963a470a75dc92b4830a8b603d43938c
BLAKE2b-256 d4ac7a73f491bc95ec1fbf7f8f39d4cf3603a4d9e3fe6875b2043687a68d8dc8

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 69c48f82a6f7616c52a31d189395249ae2ac88d2ece829ea02a005e856d6955f
MD5 f4e47e76e9fc9feb8c59fc67cc6d1596
BLAKE2b-256 c5adb708560a8541f8da92f317e825c7ec30cfa2067f4e05254d3af11c4c74fb

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 22f963af542eb7aa9aa2108e99950833a81067273621e6191355e97c3635569b
MD5 f63c04797d62b10f97057b33bfa4d47f
BLAKE2b-256 43ff4826a6ca44f68f34c43e6bd4ff03febadb781fcfec5051c8ca188217adae

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 906ae360c52375d6c0fc2ba7147f230e78fe10ceb55648a1c1c0faa4ef2ea832
MD5 5e297e2fa9a835fb1d3643ef55e96890
BLAKE2b-256 7e5e51c1ddb336b9ac4d2d7ff83f63102f098c11c5d50706e7d7410e934bca4a

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 229c8fd91cc40961adb2154c09cfa9f3e8aa2d40f05643b49564a438433ac539
MD5 ba9e6d25c76e55726bd66691e290e79e
BLAKE2b-256 6b40ff858cf7464d09258f6ecd19edfaa857f0f842f05c663fc500530e101a5e

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 37b52f122aeb0ddb1fc734a6a7280186de32a5e841c8d30f2ba4bdfe8de954aa
MD5 48977f90dc0c41baf7fb11af1a7b7889
BLAKE2b-256 c2ad6d6661a91629049cb118fe922494bcda2c5bae3b806926e641eb379c756c

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 425d0a0771b9d283bc46ef295bc99639a2cdb56bba9b17db5ef8f118b09ebb75
MD5 75a155ea44c7879456fff031edf7bfc8
BLAKE2b-256 2c06c9632b75e9f06b020885494b70438e844079f5d3f8ed927ffa49e6e5f997

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 5184a3755fd063dea0f2d3ea321aca88f88fa9216758233cfdb85b9cd3e98b0b
MD5 b338a2eb1c700ccdd54e65b984bf3c30
BLAKE2b-256 bce0a084975e1d36b145d082904eb2e63555894e4553595f229ad35ab3c43e0b

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 69582b9f6b44e08b39523581de20a95696406acfbc30e2835c892dc72ea02625
MD5 17451969cef26b3e66b54178c5063954
BLAKE2b-256 0c1e57aa264dd83adcad157bb9696cf28833eb605136060bb55d2653d1bf4400

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 cbac3652be1ed869918fcb2b0a120d200ac6064bed7b974a0a4215b5b0bd9602
MD5 4cc3c3379ac90b2146ede66968374aee
BLAKE2b-256 25cc0e5c311a60008901ce772f3a7bc430a6269c7d819bec916b4db395ef9cb8

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 381543420af673e9c6033b1c0158f322e346750a7853e0c8c35adbc9c08dcb11
MD5 0d93fbbb07c144e658b157b1830ec572
BLAKE2b-256 2448ce289efec08091c3930086243561aedc24dde34abea9dbd8d7ae34fc36ef

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 d40164582b093d8c3efd315d1ca5ae4ca3ff832120ad324d73989a67723935a3
MD5 2a02424a3ca8b228a34fbdb4e3d55f76
BLAKE2b-256 5cca7c2d4d392e2478b75debe1afac6fbb2e6c4c58f213fcc7aa94f746003375

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 585a9367e0acb96fd17396e5c8bacff5a74e2f0d7484c62e478bb7830656d196
MD5 3df1db4fe6d26609f788ef3ae7ee2537
BLAKE2b-256 55bfa859a34f31f47a64ef6f02619cdc990957635df7e4fe52a6407cf4423a61

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp311-cp311-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp311-cp311-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 fa4cf8c77f111c28caf27a8947577bcb7e12c88616eadee984f57bcd28c187df
MD5 8e9e8ba7199ca8ef81c05f6ccb10c0ea
BLAKE2b-256 591578d0ef4da3e06ff0773507feb166041a835694857597550d9d6fd4d9598d

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-win_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-win_arm64.whl
Algorithm Hash digest
SHA256 9c4ba58b4526be664b199808cc7972c33336e971255b63d266f4b71a1478f05c
MD5 5797b985eed04b777496b0ccded1fd7f
BLAKE2b-256 271212eb31e2cf3a8b841dcfec987b38f3da98ce01f32ff20dc1127bbab02c31

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 27c2e9e1c10bc59e99b8d82858b2dd3de5d186f2376a2d3066a005a6a10897dc
MD5 28a142f12d3ad6129429bfd957aa5614
BLAKE2b-256 cdb278a6b857f635f97739c79d022d4843b6d5fbfdc5163dc572529ff870106b

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp310-cp310-win32.whl
  • Upload date:
  • Size: 114.5 kB
  • Tags: CPython 3.10, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-win32.whl
Algorithm Hash digest
SHA256 c7f374328805058268ee3df189d9e61b8ae1ad9888f8c1e1d718c44ed87e8346
MD5 d8821acd007865b9efd74d8493a2bf8e
BLAKE2b-256 ad9173ef64b5f0bfe3165ea13c54c0896e1ef6e47f43c6aba0124ec9acebcff6

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 65b5f8a48604f9c11de551e45acb5dbf6fe878f2cd4599daaf84a412d48276c6
MD5 f8b9076b2a9a9645a3ab2f0d0a2bef1c
BLAKE2b-256 9e1f7b6c9ddcee514d14cce4cf42c58c7ec1d390d7707cef1d7ea684934662ec

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 6942d2b44cb97d3bb3683da109e4b305d4199fa7dd4633dfae47cf2a98107367
MD5 5b3c511f270dd47a00f31868d19c6b3c
BLAKE2b-256 be4074c558159d428f5b79c80157ead5454fe664d6269efa6b59e5ef518e06e7

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 f901b1aaf6a4c1ea7d8f53509c12c541320abd13525311766ca05618683147f4
MD5 ab7cd0a5f2c987a7828ff3c0f401eb0d
BLAKE2b-256 ed5665136f0319be3f7376a5f2cd6118f829b15ad901999f612035414b9cbcf0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 2a985849c8c10453a49fad881854761e0ea9d81177f18a3b3fa402b58772abf8
MD5 cbf515509ac8c5138b7160bcf901fbb4
BLAKE2b-256 a15e84223a79ba31bb64361214b0c620ad5e7ec46e1725017152c30b362f49c0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 563f326582f60615664f7312e498142152a265b587c0747e55f38c1b51a1fe96
MD5 76726aaa73484e69c10f8af75c2d7b6f
BLAKE2b-256 e4eaea8f24235a488995c9b3bf940480dccf34ad004878beb7d3f0d89a3f06ea

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 6055feafcda1587dfd6afcffb6cb9202bb164efd8da39165d0ad46a17f4eeb53
MD5 8526c00d26f97921766071e0a45d622b
BLAKE2b-256 e5b73610ebcc0bd40ce36cfa4cd889db681a1c9a9ad630ce80f39dee2d460e80

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8ab8705ff833835e1361b5ace0fb44ce0d15bc9794242134883caeabf7dda739
MD5 12dd404636591b68113a31367f21ddd7
BLAKE2b-256 c3959ae0176616cb169d1b69cea84841f5a462b1ffd8706a9c59d11f7c7be63a

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 dacfc2c4e134a40c4da020c7e0f9282eb0f087f75fb8fb88ead874fee75550ce
MD5 36f34c6360af16afcc7bdc311e2a30fa
BLAKE2b-256 59b5c3c8daccdacf914c4c057254dec48705c9e083dcdcd47def5549d1f9afa8

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 004f153653f279e1dfebf2e48db4d51844015f6881c3f32a96783cc27d56ee3f
MD5 3fb2208f343db4e91382a53745d42c7d
BLAKE2b-256 5ce286e82cac607017514028c1eb2b35b1e9b2b967b9931d0850f0b4e629c130

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 986ec0d146887fc80d078e22c43baa2f56f0baa2eff0222ddc164846b2b0f16c
MD5 d09bf0ac7f8f75211ac2b0dd2ed96b94
BLAKE2b-256 0f5ce6f43ca81ee5e2437521ecee48559aa8b5c5d17a182ea433fbd117865582

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 9c32813db61728e7261b1e94ee8d4d01962061b890428e673a6d2fdc640062bd
MD5 7d30679ca76162efcdcc3773e35d66c7
BLAKE2b-256 c36f629a530e1da0c5f23ba93cc0b70aa8ab9d39899292bc57cd00c203ec5632

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1df473186c6f6d1f2b4f6df8f5f2ab4a2e7a724e635f7911561b344bd54418db
MD5 b12b0820b0bb5608efb3596445a06dc1
BLAKE2b-256 ad7ebf14d9272821a48e477b2ecde531a75720067bfcb4c693689dfba4230828

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp310-cp310-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp310-cp310-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 ac0b5963915264b140b2e9a7de5e8d7e111836b04bd9a85a5819234b34abec19
MD5 92583c5f91551fc6f9e74c9e277a4c92
BLAKE2b-256 e615f6da24fe1d4b3cc657cd6ff2fb4e056f8be80beefb221786e9bbb40f858f

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-win_arm64.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp39-cp39-win_arm64.whl
  • Upload date:
  • Size: 123.1 kB
  • Tags: CPython 3.9, Windows ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-win_arm64.whl
Algorithm Hash digest
SHA256 98730ec8921298f66c3527e648c27cd839beae61dac51d718d0c284cca0cdb23
MD5 899f719cef5bed4bce9dcae7bfa5216d
BLAKE2b-256 5428fb6232dc3a31dfd78dfa004fd9249f84297fc0aa943df5b94ad026244a6e

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 162.5 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 5393cb4ec7257d215779e7e36ccf0878efac48d0348e8da31f481f6a571c6d9b
MD5 21aaa96d5e05125bd1cc680c3a3f884f
BLAKE2b-256 2ba7c1edd9c55dc14cb1bd764fba385bb4ad028a22d3cd536e18cd11023f2c9c

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp39-cp39-win32.whl
  • Upload date:
  • Size: 114.6 kB
  • Tags: CPython 3.9, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-win32.whl
Algorithm Hash digest
SHA256 c6adea3d9115f2967cc80a143bcbc59276f01cff2c602b7d10852a159035cabb
MD5 50cd9c6c5b6cce4d2717c2fb4cfd6cc1
BLAKE2b-256 aa64875131daab551eec37871f081d730ea528e8a49f528b66dd87cb9c35eee9

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 0dbd7ea4c08b6cade2c659366507318c78e71f034298d09e9ee2d2d5eb559f23
MD5 2379c48d273ebf05b6e5b38a896bb2a3
BLAKE2b-256 8f5b69d82521b5482642155433f1264688f66debffc20d4be7026215539c55fb

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 22f4b70960f340711fee97712a669aca61faf5d30e319055c07ad36633bb111c
MD5 364affdd943bd544f96407b599951411
BLAKE2b-256 6635f1d24a690c78681b376a2ca258cbce1f5f4d75238bae498d5c38cc05677d

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 f76219a7da24ed70a5febdf4e738ca41a38256847ac828ee12b25904a649c450
MD5 7115f7e62796e5a5009c251523f134eb
BLAKE2b-256 fecc814f92a82f9934c3238dc9112eea1a946808915b67982bf7a15f5dce9614

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 e8a57f62502c132cdb14a89afa51ea4d48eb7e5e29685106169f4c43a9adfee2
MD5 69828852c59134931283badb0d1d16a7
BLAKE2b-256 c539670ba2f817ff78bc82fabd5de9605a2e525a9682292dbbb19fac994a320d

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 28720e09d2ac9df565b39451cd7f3e3d6c7abb7a4ccbe4a75ee1fc7610ef0172
MD5 b577483fa9589ac669ce925f862cb39f
BLAKE2b-256 5ffc76e56cfee205c3d892aeb9b764c5f3e35cd559de6b1df5fb5e56d19e3d08

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 c92ede279acb00940a98c81cefc5cc3d82c00d2bdc1b3fb831a1d2ea7ef56a28
MD5 57b92eab99fa5eac5719c0711c73af55
BLAKE2b-256 7073afbe7fe36456c1796bf7dc22c7d089e6d0a29b1b0d8a7a8e61057f12fe20

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 53b29f5bf24d58b056bf8c986c73249398300276ee599d07c0720c4006f92a47
MD5 3c56ebee91bb6790390dc9cd063058bc
BLAKE2b-256 730afb0485a26caacbd271b201d0b76466a4656600fa806f1b150a542d7c7556

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 f2e35c25c838538b224b9b1e702961ea1144bfb4c70ea3e18914b31bb7d6c11b
MD5 c4af4b86addfc91432623bc137365518
BLAKE2b-256 a12d27ffe517d3b2697479e0e9dc04d89b9886293ec4a1fd452c991ab473c869

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 cf80404da112ccb087d00117bb1c5fe5fe13b1221ab06b1ad8494c83d9899d1d
MD5 8652692146fceccdbd1387580803f724
BLAKE2b-256 1dfb058930841821a71dc953172876e622ccf6a4dd1d83d59dc545865c87c3f0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 e97ee36a6b3261cec7a32b259e554e3794095affc988c2db7f02a5047ef697a7
MD5 7c4a7c1283f252c9ad1b95945cc1d345
BLAKE2b-256 277a3d37e9c97c45aeaaca48332f27e20aeb29ebed523ae9cd77da318fdf82d0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 877d32e8468d5e4a8b14861f6144ca6264f12604b582daf3e347beeb33de2c9c
MD5 f90c708427b113bb58f8bae8bbae2319
BLAKE2b-256 b6d036ebfa60bffce17e06382a6a20aaa117dca01ea9cf482dbab538171ab694

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f9974dadc6b9dd0281356769e7bd76d7c1fc1df0f58975918ebbd71dbe696471
MD5 b33d86b8d1e3a016297fbf46fd206ffe
BLAKE2b-256 4aab0172474804ea380f5d86242858242e180d48b0e0cc991c9ffc6ed8c12b3d

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp39-cp39-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp39-cp39-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 66c44bc120ab1092ca356693d2a7e92cf081bc2b305715df3d3d233740c35a23
MD5 d12831cc23a784da58e2d58cc7112bff
BLAKE2b-256 a1d09e5d481957174291833751560f86011050cd6fb59260be71927e10fd700f

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 162.4 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 ca2ee356bb03f252f3e0874860e584c5afeeb1589f9a012f6db7d47b7752170f
MD5 5b803aca01e5518ed138893cc26ba3a8
BLAKE2b-256 aac092d7b01756ed528f68f478473b2de0c2b1d1d0d035b6f44c1669d8fc0ef9

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-win32.whl.

File metadata

  • Download URL: stringzilla-4.6.1-cp38-cp38-win32.whl
  • Upload date:
  • Size: 114.5 kB
  • Tags: CPython 3.8, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-win32.whl
Algorithm Hash digest
SHA256 9def96d3c92fbd58d1a46cc340f563afe329cdee839368431ebf03a4abf82c8e
MD5 44d470d52cfc1c35f83a2cbab1ec55c9
BLAKE2b-256 d7d73ccc9c34fd3ef73c64971aeaec589192167fb8bd1f1c9c2aa8d636bace40

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 e55514684927318592a8fa57313a20bf39e0d8874f2d01488644d22d99dc12b4
MD5 f2aee1320a12deb5f864abb47540180b
BLAKE2b-256 3139245d11809245dac25fddf3aee4bd63fd7c2edee910245e8e956753ec0061

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_s390x.whl
Algorithm Hash digest
SHA256 22c4b14895f2e82ab91e20dfc82f4e5bb98712f914a80b2db50176b584b32378
MD5 abc0e4f1fce42b2ff7e513b676b5828f
BLAKE2b-256 7d740f61689bbc2596469d76d59092f4edbbfaaaa5458210e60f662eb8853e61

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_ppc64le.whl
Algorithm Hash digest
SHA256 4416e2209ad9efd4c89118fb59bcf96d30362a92dbb1e4e74739cfb6462afbe6
MD5 a1bf43c11628a28665c4776bd5e6f943
BLAKE2b-256 9eae92b0dcdc6f3c7de266f93bb07107a03e3d3117e299a0792ae46465ac76e0

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 b835ae9795ced5b87a150618932288883f66f9d99dd902140704a74842df76c9
MD5 6b9bfb52662c5476f567ac92e13b4229
BLAKE2b-256 26e56872918465e45027899b6c3142e5ec83302b2f24f6200281fabee3707600

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 65755d97e7f74ea77dc5b6f8ed12fb4c970cca2a39b05187b6170aace41394ae
MD5 688f522b3f12d17c036d389b9fb02139
BLAKE2b-256 a98ede112d2c67347aef36cdb007bb183506977fb0dd94b7002bbfffea988b85

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 5ed4e6b8d06b44df47c10b9bd338209d64d2baf3bf01b0dd60161c9b5aff81fd
MD5 a299bb4abd5bfac612530578c2a72ff6
BLAKE2b-256 13876e6ade2febfa1abc421c54d169b1aa6111377ba8aefeb79399a0ed6459f9

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2517c06db7d0f737ee85a9bcd2945586292818c6dfd38716e5259c0c901c11f5
MD5 a8eea106eacc349b2e86179740b9b03c
BLAKE2b-256 17cd86dae60a3d73f9865ffd0ee640f59f17687c9627d48863ea49b95b2c8815

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.manylinux_2_28_s390x.whl
Algorithm Hash digest
SHA256 abdc1a7021a1e7a80db7cfea19b01e1aa635b9fb3779fb2089de21e25b0db732
MD5 17975a17de636cafab6ec4e27a4d048b
BLAKE2b-256 fc2db0119aad5110f3f5716716cadf0d1d39c95268bc44f26efdb65cbb5da7b4

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.manylinux_2_28_ppc64le.whl
Algorithm Hash digest
SHA256 ab4ec2e1b38156605cbc16dfc5b6882bf8b0dcd9cb63f6d7662214d3bea67308
MD5 15cf9e098c5c776b3df777727e616a2f
BLAKE2b-256 464da932c400d05567103eb2268aef9f2140bf614d5b76b843e35dad7ca3c653

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 80088fc0d410f58b75ca523f3a3134cd0d813800790e45be96c5ecf2453d11dc
MD5 a30c5e94ffa2b9183631af95c69c207c
BLAKE2b-256 39ce6356aa0d4409f8d127c6b04c28cc906226bb361b4f5e1b7611138a8dba11

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 bd322f8c12801e8b6020272d7a1f93f730a7e236c8c33c64bb156d9d6b453b1b
MD5 88dc091e25a7b75c7bb73e1eb56dfa07
BLAKE2b-256 074817b736f7a94de0c5f4d1660ccc3cfb5c986878251b788f4a493258aa3caa

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0600a2f3cbe95a8673584e28b93c0c14c9c3e27d9f08eaf46d1ea803b0271049
MD5 fae2bfb2c8a68f5d8b6f5e9e36adae62
BLAKE2b-256 50f11267e189037a9f7f1e52a444c1e0c8aacc7fc781a449c8d8b5fddaef3b31

See more details on using hashes here.

File details

Details for the file stringzilla-4.6.1-cp38-cp38-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for stringzilla-4.6.1-cp38-cp38-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 68834ead4695ad71ccac05a800c7f8e7ef2ba3a7eb8dc6bb45d87d4bb287901b
MD5 38a133cbad4d01c67f740316300ddfbf
BLAKE2b-256 26498ae3af51c03e6bfe5e45976d91a683aa8062cefd8a8f44e5ee08e6c4c039

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page