# Installation

> StringZilla for C++

The headers declare the StringZilla library, so the simplest integration links it and includes the umbrella header.
Its dispatch points pick the best capability at runtime, so one binary runs optimally on any CPU.

```c
#include <stringzilla/stringzilla.h> // C
```

```cpp
#include <stringzilla/stringzilla.hpp> // C++
namespace sz = ashvardanian::stringzilla;
```

Each family can also be included on its own when you only need a slice of the API and want to keep compile times down:

```c
#include <stringzilla/find.h> // `sz_find_best`, `sz_rfind_best`, byte-set scans
#include <stringzilla/hash.h> // `sz_hash_best`, `sz_bytesum_best`, incremental state
#include <stringzilla/sort.h> // `sz_sort_order_best`
#include <stringzilla/compare.h> // `sz_equal_best`, `sz_order_best`
#include <stringzilla/intersect.h> // `sz_intersect_best`
#include <stringzilla/strings.h> // `sz_strings_to_tape_best`, `sz_strings_lookup_best`
#include <stringzilla/small_string.h> // `sz_string_t` SSO container
#include <stringzilla/levenshtein.h> // `sz_levenshtein_engine_init`, `sz_levenshtein_distances`
#include <stringzilla/overlap.h> // `sz_overlap_engine_init`, `sz_overlap_scores`
#include <stringzilla/substrings.h> // `sz_substrings_engine_init`, `sz_substrings_counts`
```

The three engine families prepare a batch of queries once and score it against many batches of candidates, writing into a strided block the caller supplies rather than allocating one per round.
Each init takes the mask, the allocator and a stream last, and each round is a single verb with no `_best` twin, as the engine already holds the capability its init picked:

```c
sz_status_t sz_levenshtein_engine_init(sz_levenshtein_engine_t *engine, sz_strings_view_t const *queries,
                                       sz_levenshtein_symbol_t symbol, sz_capability_t capabilities,
                                       sz_allocator_t *allocator, sz_stream_t stream);
sz_status_t sz_levenshtein_distances(sz_levenshtein_engine_t *engine, sz_strings_view_t const *candidates,
                                     sz_size_t const *symbol_counts, sz_size_t const *order, sz_size_t bound,
                                     sz_size_t *distances, sz_size_t distances_stride, sz_stream_t stream);
void sz_levenshtein_engine_free(sz_levenshtein_engine_t *engine, sz_stream_t stream);
```

Their per-tier building blocks and their design notes live beside the kernels, in [`levenshtein/README.md`](https://github.com/ashvardanian/StringZilla/blob/main/include/stringzilla/levenshtein/README.md), [`overlap/README.md`](https://github.com/ashvardanian/StringZilla/blob/main/include/stringzilla/overlap/README.md), and [`substrings/README.md`](https://github.com/ashvardanian/StringZilla/blob/main/include/stringzilla/substrings/README.md).

### CMake, Precompiled Library

With CMake 3.14 or newer, pull the project in with `FetchContent` and link the static target `stringzilla::static`, which compiles every capability the toolchain can build.

```cmake
include(FetchContent)
FetchContent_Declare(
    stringzilla
    GIT_REPOSITORY https://github.com/ashvardanian/StringZilla.git
    GIT_TAG main)
FetchContent_MakeAvailable(stringzilla)

target_link_libraries(your_app PRIVATE stringzilla::static)
```

`add_subdirectory(stringzilla)` works the same way.
Pulled in as a subproject, the static library is compiled only once something links it, and `STRINGZILLA_BUILD_SHARED` defaults to off.
Turn that on for the shared target `stringzilla::shared`:

```cmake
set(STRINGZILLA_BUILD_SHARED ON)
FetchContent_MakeAvailable(stringzilla)

target_link_libraries(your_app PRIVATE stringzilla::shared)
```

Either library's dispatch points pick a capability by the mask each call passes, so one artifact runs optimally on any CPU.

### CMake, Header Only

C code that wants no compiled dependency links the interface target `stringzilla::header`, which adds `include/` to your search path and defines `STRINGZILLA_HEADER_ONLY=1`:

```cmake
target_link_libraries(your_app PRIVATE stringzilla::header)
```

The kernels your compiler flags enable then compile into your translation units, and you call them by name, like `sz_find_haswell`.
Every dispatch point and finder reports `sz_missing_library_k` there, so the C++ layer runs the serial kernels over its default mask, which is empty in these builds.

### CMake, Installed Package

Configure with `-DSTRINGZILLA_INSTALL=ON` to install the headers and libraries system-wide, then find them from an unrelated project:

```cmake
find_package(stringzilla REQUIRED)

target_link_libraries(your_app PRIVATE stringzilla::shared)
```

`stringzilla::shared` joins the package whenever `STRINGZILLA_BUILD_SHARED` was on for the install.
`stringzilla::header` always does, as it needs no submodules and compiles nothing, so it works straight out of a release tarball.

A shared-library install also writes `lib/pkgconfig/stringzilla.pc`, for build systems that read pkg-config rather than CMake:

```sh
cc main.c $(pkg-config --cflags --libs stringzilla) -o main
```

### Build Knobs

The behavior of the core is controlled entirely by preprocessor macros, so you can tune it without touching the sources.
The most important ones, mirrored from `stringzilla.h`:

| Macro                                |  Default | Effect                                   |
| :----------------------------------- | -------: | :--------------------------------------- |
| `STRINGZILLA_HEADER_ONLY`            |      `0` | Inline kernels instead of the library    |
| `STRINGZILLA_DEBUG`                  |      `0` | Debug assertions and logging             |
| `STRINGZILLA_WITH_LIBC`              |      `1` | `0` builds freestanding, without libc    |
| `STRINGZILLA_ALLOW_MISALIGNED_LOADS` | platform | Unaligned word loads in SWAR fallbacks   |
| `STRINGZILLA_SWAR_THRESHOLD`         |     `24` | Length below which scalar loops are used |
| `STRINGZILLA_DEFAULT_ALIGNMENT`      | platform | Default alignment and padding policy     |

`STRINGZILLA_HEADER_ONLY` chooses between the two builds: with `0`, the default, the dispatch points live in the library, which compiles every capability and picks one by the mask each call passes; with `1` the kernels your compiler flags enable inline into your translation unit, and the dispatch points report `sz_missing_library_k`.
`STRINGZILLA_WITH_LIBC=0` builds freestanding without the C standard library, which disables the default `malloc`-based allocator and the `offsetof` static checks.
`STRINGZILLA_ALLOW_MISALIGNED_LOADS` allows unaligned word loads in the SWAR fallbacks where the platform permits.

Per-ISA backends are toggled with their own macros; if left undefined they are auto-detected from the compiler's target flags:
`STRINGZILLA_TARGET_WESTMERE` for SSE4.2 + AES-NI, `STRINGZILLA_TARGET_GOLDMONT` for SHA-NI, `STRINGZILLA_TARGET_HASWELL` for AVX2, `STRINGZILLA_TARGET_SKYLAKE` for AVX-512 F/BW/VL, `STRINGZILLA_TARGET_ICELAKE` for AVX-512 VBMI + VAES, then `STRINGZILLA_TARGET_NEON`, `STRINGZILLA_TARGET_NEONAES`, `STRINGZILLA_TARGET_NEONSHA`, `STRINGZILLA_TARGET_SVE`, `STRINGZILLA_TARGET_SVE2`, and `STRINGZILLA_TARGET_SVE2AES` on ARM, `STRINGZILLA_TARGET_V128` and `STRINGZILLA_TARGET_V128RELAXED` for WebAssembly SIMD128, `STRINGZILLA_TARGET_RVV` and `STRINGZILLA_TARGET_RVVCRYPTO` for the RISC-V Vector extension, `STRINGZILLA_TARGET_LOONGSONASX` for LoongArch, and `STRINGZILLA_TARGET_POWERVSX` for IBM Power.

The umbrella header also exposes the version triple as `STRINGZILLA_VERSION_MAJOR`/`_MINOR`/`_PATCH`, with matching `sz_version_major()`, `sz_version_minor()`, and `sz_version_patch()` accessors.

### Compilers and Platforms

The headers require only C 99 and C++ 20, so any reasonably modern compiler builds the serial and SWAR baseline.
The per-ISA SIMD kernels and the project's own tests are CI-validated with these toolchains:

| Toolchain | Recommended Versions |
| :-------- | :------------------- |
| GCC       | 13 or newer          |
| Clang     | 18 or newer          |
| MSVC      | Visual Studio 2022   |
| NVCC      | CUDA 13+             |

The RVV kernels need Clang 21 or newer, as older Clang hides the 64-bit vector types from a unit compiled without `v`, and an older compiler builds the libraries without them.
On macOS, prefer Homebrew Clang over Apple Clang; on Windows, MinGW with GCC works alongside MSVC.
NVCC with CUDA 13 builds the device backends of the engine families, reached by building an engine with the mask `sz_capabilities_enabled_cuda` reports for a device.
HIP-Clang builds the same sources for AMD GPUs, reached through `sz_capabilities_enabled_rocm`.
Built with `STRINGZILLA_METAL_ARCH`, the three engines also run on Apple GPUs of the Apple7 family and newer, through the same constructors and verbs.

A device round reads memory the device reaches, and strings only as a tape, never through an accessor, which is host code. `strings.h` declares allocation and gathering into tapes; `capabilities.h` declares stream creation, synchronization and release. Their `_best` dispatch points take a mask, with `_serial`, `_cuda`, `_rocm` and `_metal` twins, the GPU ones exported only where built:

- `sz_allocator_init_unified_best` fills a stateless `sz_allocator_t` whose blocks the host and the device share: the heap on the CPU, managed memory on CUDA and ROCm, shared buffers on Metal, each made on the device of the stream it is called with.
- `sz_strings_to_tape_best` gathers strings of any layout into a `sz_strings_span_t` tape of payload and offsets, two blocks from the allocator it is given, and `sz_strings_span_free` returns both.
- `sz_stream_synchronize_best` waits for one stream, after which what a round wrote is readable from the host.

```c
sz_capabilities_enabled_cuda(0, &caps);
sz_capabilities_enabled_cpu(&cpu_caps);
sz_allocator_init_unified_best(&unified, caps);
sz_strings_span_t tape = {0};
sz_strings_to_tape_best(&host_candidates, NULL, host_candidates.count, &unified, &tape, cpu_caps, NULL);
sz_strings_view_t candidates;
sz_strings_from_span(&tape, &candidates);
sz_levenshtein_engine_init(&engine, &queries, sz_levenshtein_bytes_k, caps, NULL, stream);
sz_levenshtein_distances(&engine, &candidates, NULL, NULL, STRINGZILLA_SIZE_MAX, distances, candidates.count, stream);
sz_stream_synchronize_best(caps, stream);
sz_levenshtein_engine_free(&engine, stream);
sz_strings_span_free(&tape, &unified, stream);
```

A null stream is the default stream of the default device, and a vendor's devices are only numbered by its producers, like `sz_capabilities_enabled_cuda(ordinal, &caps)`.

StringZilla also __compiles to WebAssembly__: the `wasm32` toolchain targets `wasm32-wasip1`, and `STRINGZILLA_MARCH` names the module's one SIMD capability, `serial`, `v128` or the default `v128relaxed`, which enables the `STRINGZILLA_TARGET_V128` and `STRINGZILLA_TARGET_V128RELAXED` kernels listed above.

