It bundles:
- a precompiled static or shared library that compiles every capability once and picks one per call, for maximum portability,
- the C headers
<stringzilla/stringzilla.h>, which declare that library, or compile the kernels inline withSTRINGZILLA_HEADER_ONLY=1, - the C++ headers
<stringzilla/stringzilla.hpp>, built on the library.
The plain C ABI exposes each kernel family as a stable C 99 surface: substring and byte-set search, non-cryptographic hashing and checksums, lexicographic comparison, sorting and intersection of string collections, collection conversions from escaping and normalization to byte translation, and the stateful cross-product engines for edit distances, window overlap, and multi-pattern search.
The thin C++ binding rebuilds the STL <string> and <string_view> surface on top of those kernels, adding an owning Small-String-Optimized container, allocation-free splitting and partitioning views, and free functions for hashing, sorting, and translation.
Every verb has a dispatch point, like sz_find_best, which runs the best capability a mask shares with the verb’s list, and the mask to pass on the CPU is the one sz_capabilities_enabled_cpu reports.
There is no hidden global allocation and no thread pool: every function that may allocate takes an explicit sz_allocator_t *, and an engine holds the blocks its allocator handed it until you free it.
An engine is prepared by sz_levenshtein_engine_init, and its twins for overlap and substrings, for one device’s capabilities and a stream, which names the device, so choosing a device is choosing the mask and the stream an engine is built with rather than setting a global.
Its rounds, such as sz_levenshtein_distances, take a trailing stream: null on the CPU, while on a GPU they enqueue there and return, leaving the join to the caller.
A GPU engine’s init uses its stream for its own work alone and sizes a round’s device memory from the budgets it takes, so no compute verb allocates or joins, and a round carrying more haystacks than its budget, or on Metal more candidates, is refused with sz_unexpected_dimensions_k.
Overlap and Levenshtein engines keep no per-round state on a CUDA device, so several streams may score one engine at once, while the rounds of one substrings engine share its arena and its report, so the caller orders them.
The headers compile as freestanding C 99 — set STRINGZILLA_WITH_LIBC=0 to drop the libc dependency — and as C++20 or newer for the sz:: layer.
Per-family hubs find.h, hash.h, sort.h, compare.h, intersect.h, strings.h, small_string.h, levenshtein.h, overlap.h, and substrings.h declare the family’s dispatch points and its kernels, each capability’s under its STRINGZILLA_TARGET_* macro, from the c/ header root.
The kernels are defined under the matching subdirectories of the include/ root — find/haswell.h, hash/icelake.h, strings/neon.h, and so on — and the C++ headers live in the cpp/ root; a build passes all three as include paths.
The library compiles the kernels once, one capability per unit in src/capabilities/, and header-only builds include them into every translation unit.
Topics
| Installation | The headers declare the StringZilla library, so the simplest integration links it and includes the umbrella header. |
|---|---|
| Types169 | The C++ layer lives in namespace ashvardanian::stringzilla, conventionally aliased as sz, and is built on the types below plus a 256-bit byteset. |
| Searching and Counting13 | The view and the owning string expose find/rfind for sub-strings, single characters, and byte-sets, plus contains, startswith, endswith, and the full findfirstof / findlastof / findfirstnotof / findlastnotof family taking either a byteset or a view. |
| Comparison and Equality2 | |
| Splitting and Partitioning16 | This is a C++-only convenience layer; the C ABI provides the underlying scans szfindbest and szfindbytesetbest that you compose by hand. |
| Trimming and Translating2 | Trimming is exposed in C++ on the view: strip(byteset), lstrip(byteset), and rstrip(byteset) return a narrowed view with the matching leading/trailing bytes removed — no copy is made. |
| Unicode Segmentation5 | Splitting Unicode text into the units a human actually perceives is deceptively hard, and getting it wrong corrupts data. |
| Case Folding and Normalization1 | Case-insensitive matching across full Unicode is more than ASCII tolower — ß matches ss, accents fold, and the matched byte-length can differ from the needle's. |
| Hashing and Checksums1 | In C++, hashing is exposed as methods — view.hash(seed = 0), view.bytesum(), and view.hashmultiseed(seeds, hashes) — and as the function object sz::hash{} together with sz::equalto{}, which are convenient as template arguments for std::unorderedmap/std::unorderedset. |
| Sorting and Sets4 | These operate on a read-only szstringsviewt in any layout and never reorder the underlying data — they fill an order permutation array instead. |
| Memory Operations2 | Most operations in StringZilla don't assume any memory ownership. |
| Edit Distances1 | |
| Runtime Dispatch11 | Every kernel exists in a serial form plus one or more per-ISA forms, named with a backend suffix (serial, westmere, goldmont, haswell, skylake, icelake, neon, neonaes, neonsha, sve, sve2, sve2aes, v128, v128relaxed, rvv, rvvcrypto, loongsonasx, powervsx), and the engines add one per GPU vendor, cuda, rocm and metal. |
| Platform Notes1 | StringZilla is not a drop-in replacement for the C Standard Library. |