# Types

> StringZilla for Python

The `stringzilla` module exposes `String`, `Strings`, and `File`, the incremental hashers `Hasher`, `Sha256` and `Sha256s`, the batch engines `LevenshteinEngine`, `OverlapEngine` and `SubstringsEngine`, plus a family of UTF-8 iterator types.

### `String`

`String(source)` is an immutable, zero-copy view over a `str`, `bytes`, `bytearray`, another `String`, or a memory-mapped `File`.
It implements the read-only half of the `str`/`bytes` protocol — indexing, slicing, iteration, `len`, `in`, rich comparison, hashing, and the buffer protocol — without materializing new Python objects.
Slicing returns a new `String` view into the same backing buffer rather than copying bytes.

Two read-only properties expose its memory layout for zero-copy interop with PyArrow, `ctypes`, and similar:

- `String.address` — integer memory address of the first byte.
- `String.nbytes` — length of the view in bytes.

```python
import stringzilla as sz

text = sz.String("the quick brown fox")
assert str(text[4:9]) == "quick"   # a zero-copy view
assert str(text[10:15]) == "brown"
assert "quick" in text
assert len(text) == 19
assert text.nbytes == 19
assert isinstance(text.address, int)
```

### `Strings`

`Strings(sequence=())` is an immutable, indexable, sliceable sequence of byte strings, like a `tuple` of `bytes`, kept on one tape: the bytes back to back and an offsets array, the Apache Arrow `string` or `large_string` layout.
A `pyarrow.Array` of strings or binaries is borrowed in place, while a `list`, `tuple`, generator, or any other iterable of `str` and bytes-like objects is copied once.
The eager splits return one too, with the parts copied onto a tape of their own.
`strings.copy(capabilities, stream=None)` copies the strings into one tape in the unified memory of `capabilities`, as [Engines on a GPU](https://www.unum.cloud/docs/stringzilla/python/edit-distances.md#engines-on-a-gpu) describes; the host reads it as a normal read-only `Strings`.

Indexing yields `String` views into the tape, and slicing with a step of one borrows it, so neither copies any bytes.
Any other step, `sorted`, `shuffled`, and `sample` gather into a new tape, while `argsort` and `intersect` return indices and copy nothing.
One tape is also dramatically more memory-efficient than a Python `list` of `str`.
N strings cost two allocations, whereas a `list` of `str` holds N separately heap-allocated objects, each carrying a PyObject header, a hash cache, and per-object allocator overhead.
Splitting a large document into millions of tokens therefore stays compact and cache-friendly instead of fragmenting the heap.
The lazy `split_iter`, `rsplit_iter`, and `*_byteset_iter` forms go a step further and build no collection at all, yielding one `String` view at a time.

Five read-only properties expose the tape for Apache Arrow and StringTape interop:

- `Strings.tape_address` — address of the tape's first byte, which the offsets count from.
- `Strings.tape_nbytes` — bytes from that address to the end of the last string.
- `Strings.offsets_address` — address of the first offset.
- `Strings.offsets_nbytes` — length of the offsets array in bytes, one offset more than there are strings.
- `Strings.offsets_are_large` — `True` for the 64-bit offsets a tape past 4 GiB needs, `False` for 32-bit ones.

A borrowed slice shares `tape_address` with its source, while its offsets start past zero.

```python
import stringzilla as sz

names = sz.Strings(["banana", "apple", "cherry", "date"])
assert len(names) == 4
assert str(names[0]) == "banana"
assert [str(x) for x in names[1:3]] == ["apple", "cherry"]   # a borrowed slice
assert names[1:3].tape_address == names.tape_address
```

### `File`

`File(path, mode='r')` memory-maps a path so a `String` can view data larger than RAM without reading it into memory.
The default `mode` is `'r'`, for read-only access.

```python
import os, tempfile, pathlib
import stringzilla as sz

path = os.path.join(tempfile.mkdtemp(), "log.txt")
pathlib.Path(path).write_text("error: disk full\nok\nerror: timeout\n")

mapped = sz.String(sz.File(path))   # scan in place, no copy into RAM
assert mapped.count("error") == 2
```

`from stringzilla import Strings, String`

## Strings

An immutable sequence of byte strings on one tape - their bytes back to back and an array of offsets, the Apache Arrow `string` or `large_string` layout - like a `tuple` of `bytes` that costs two allocations rather than one per element.

```python
class Strings
```

Slicing with a step of one borrows the tape, while any other step, [`sorted`](#sorted), [`shuffled`](#shuffled) and
[`sample`](#sample) gather into a new one, and [`argsort`](#argsort) returns the order without copying, as [`find`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#find),
[`hashes`](#hashes) and [`utf8_count`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#utf8_count) return one number per string in a single pass over the tape.

Examples: ```
>>> names = sz.Strings(['banana', 'apple', 'cherry', 'date'])
>>> [str(x) for x in names.sorted()]
['apple', 'banana', 'cherry', 'date']
>>> [str(x) for x in names.sorted(top=2)]  # partial top-k is cheaper than a full sort
['apple', 'banana']
```

## Strings

```python
Strings(sequence: Any = ())
```

## shuffled

shuffled(seed=None) -> Strings

```python
def shuffled(seed: int | None = None) -> Strings
```

Return a new Strings with the elements randomly permuted, like [`random.sample`](#sample) drawing all
of them; the original is unchanged.

Returns:
Strings: A new shuffled collection on a tape of its own.
Example:

sorted(map(str, sz.Strings(['a', 'b', 'c']).shuffled(seed=42))) == ['a', 'b', 'c']
True

- `seed`: Seed for reproducible shuffling. Defaults to a random seed.

## sorted

sorted(*, reverse=False, uncased=False, top=None, capabilities=None) -> Strings

```python
def sorted(
    *,
    reverse: bool = False,
    uncased: bool = False,
    top: int | None = None,
    capabilities: Capability | None = None,
) -> Strings
```

Return a new, stably sorted Strings, like the built-in [`sorted`](#sorted); the original is unchanged.

Returns:
Strings: A new, sorted collection on a tape of its own.
Example:

list(map(str, sz.Strings(['banana', 'apple', 'cherry']).sorted()))
['apple', 'banana', 'cherry']

- `reverse`: Sort in descending order. Defaults to False.
- `uncased`: Order by Unicode case-folding. Defaults to False.
- `top`: Keep only the `top` smallest elements, or largest if reversed. Defaults to None, keeping all.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## argsort

argsort(*, reverse=False, uncased=False, top=None, out=None, capabilities=None) -> tuple | buffer

```python
def argsort(
    *,
    reverse: bool = False,
    uncased: bool = False,
    top: int | None = None,
    out: _Buffer | None = None,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | _Buffer
```

Return the stable permutation of indices that sorts the Strings, like
`sorted(range(len(strings)), key=strings.__getitem__)`, copying no strings.

Returns:
tuple[int, ...]: The sorting permutation, or `out` itself when an `out` buffer is given.
Example:

sz.Strings(['banana', 'apple', 'cherry']).argsort()
(1, 0, 2)

- `reverse`: Sort in descending order. Defaults to False.
- `uncased`: Order by Unicode case-folding. Defaults to False.
- `top`: Keep only the `top` leading indices. Defaults to None, keeping all.
- `out`: Writable, C-contiguous buffer of pointer-width unsigned integers like numpy.uintp to receive the indices with zero allocation. Defaults to None.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## find

find(needle, *, out=None, capabilities=None) -> tuple | buffer

```python
def find(
    needle: _StringLike,
    *,
    out: _Buffer | None = None,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | _Buffer
```

Find the first occurrence of `needle` in every string, like [`str.find`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#find) over each, counting bytes
as [`bytes.find`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#find) does, in one pass over the whole tape.

Returns:
tuple[int, ...]: The byte offset of the first match in each string, or -1 where it holds none,
or `out` itself when an `out` buffer is given.
Example:

sz.Strings(['hello', 'world', 'help']).find('el')
(1, -1, 1)

- `needle`: The substring to find; an empty one matches at 0.
- `out`: Writable, C-contiguous buffer of pointer-width signed integers like
numpy.intp to receive the offsets with zero allocation. Defaults to None.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## hashes

hashes(seed=0, *, out=None, capabilities=None) -> tuple | buffer

```python
def hashes(
    seed: int = 0,
    *,
    out: _Buffer | None = None,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | _Buffer
```

Hash every string under one seed, each equal to `sz.hash(text, seed)`, like the built-in [`hash`](https://www.unum.cloud/docs/stringzilla/python/hashing-and-checksums.md#hash)
over each but stable across runs and platforms, for group-by keys, joins and sharding.

Returns:
tuple[int, ...]: One unsigned 64-bit hash per string, or `out` itself when an `out` buffer is
given.
Example:

sz.Strings(['a', 'b']).hashes(7) == (sz.hash('a', 7), sz.hash('b', 7))
True

- `seed`: The seed every string is hashed under, defaulting to 0.
- `out`: Writable, C-contiguous buffer of 64-bit unsigned integers like
numpy.uint64 to receive the hashes with zero allocation. Defaults to None.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## utf8_count

utf8_count(*, out=None, capabilities=None) -> tuple | buffer

```python
def utf8_count(
    *,
    out: _Buffer | None = None,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | _Buffer
```

Count the codepoints of every string, like `len(text.decode())` over each, validating every one
as UTF-8 in the same pass over the whole tape.

Returns:
tuple[int, ...]: The codepoints of each string, or `out` itself when an `out` buffer is given.
Raises:
ValueError: Naming the index of the first string holding ill-formed UTF-8.
Example:

sz.Strings(['hello', 'caf\u00e9', '']).utf8_count()
(5, 4, 0)

- `out`: Writable, C-contiguous buffer of pointer-width unsigned integers like
numpy.uintp to receive the counts with zero allocation. Defaults to None.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## sample

sample(size, seed=None) -> Strings

```python
def sample(size: int, seed: int | None = None) -> Strings
```

Return a new Strings with `size` elements drawn at random with replacement, like
`random.choices(strings, k=size)`.

Returns:
Strings: A new collection of the sampled elements on a tape of its own.
Example:

list(map(bytes, sz.Strings(['a', 'a', 'a']).sample(2)))
[b'a', b'a']

- `size`: Number of elements to draw.
- `seed`: Seed for reproducible sampling. Defaults to a random seed.

## matched

matched(matches) -> Strings

```python
def matched(matches: _Buffer) -> Strings
```

Return a new Strings holding the bytes each match row spans, in row order, gathering what
a [`find`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#find) of [`SubstringsEngine`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#SubstringsEngine) or [`SubdocumentsEngine`](https://www.unum.cloud/docs/stringzilla/python/searching-and-counting.md#SubdocumentsEngine) located in this collection.

Returns:
Strings: A new collection of the matched spans on a tape of its own.
Raises:
IndexError: When a row reaches outside this collection.
Example:

documents = sz.Strings(['{"a": [1, "x"]}'])
engine = sz.SubdocumentsEngine(sz.Strings(['$.a[*]']))
matches = memoryview(bytearray(2 * 4 * 8)).cast('Q', (2, 4))
offsets = memoryview(bytearray(2 * 8)).cast('Q')
engine.find(documents, matches, offsets)
list(map(bytes, documents.matched(matches)))
[b'1', b'"x"']

- `matches`: Contiguous 2-D buffer of pointer-width unsigned integers shaped
`(count, 4)` - haystack index, needle index, byte offset, byte length.

## intersect

intersect(other, *, seed=0, capabilities=None) -> tuple[tuple[int, ...], tuple[int, ...]]

```python
def intersect(
    other: Strings,
    *,
    seed: int = 0,
    capabilities: Capability | None = None,
) -> tuple[tuple[int, ...], tuple[int, ...]]
```

Return the positions of strings present in both collections.
Each distinct shared value is matched exactly once, even if either side has duplicates.

Returns:
tuple[tuple[int, ...], tuple[int, ...]]: Parallel position tuples - `result[0][i]` in
this collection and `result[1][i]` in `other` point to equal strings.
Example:

ours, theirs = sz.Strings(['banana', 'apple', 'cherry']).intersect(
...     sz.Strings(['cherry', 'banana']))
sorted(ours)
[0, 2]

- `other`: The collection to intersect with.
- `seed`: Seed for the hash table to avoid attacks. Defaults to 0.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## copy

copy(capabilities, stream=None) -> Strings

```python
def copy(
    capabilities: int,
    stream: int | None = None,
) -> Strings
```

Copy the strings into a tape in the unified memory of `capabilities`, which engines of those
capabilities read in place and the host reads as a normal read-only Strings.

Returns:
Strings: A tape owning its unified memory, freed with it.
Example:

tape = sz.Strings(['kitten', 'sitting']).copy(sz.cpu_capabilities_enabled())
[str(text) for text in tape]
['kitten', 'sitting']

- `capabilities`: A producer's mask, like sz.cuda_capabilities_enabled(0).
- `stream`: A stream of that vendor as an integer, naming the device the tape is
made on, or None for the default.

## translate

translate(table, *, capabilities=None) -> Strings

```python
def translate(
    table: str | bytes | dict[str, str],
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Map every byte of every string through a look-up table, like [`bytes.translate`](https://www.unum.cloud/docs/stringzilla/python/trimming-and-translating.md#translate) over each.

Returns:
Strings: A new collection, each string as long as its source.
Raises:
ValueError: If the table is not 256 bytes long, or a dict names a character above U+00FF.
Example:

[str(text) for text in sz.Strings(['cab', 'bee']).translate({'a': 'A', 'b': 'B'})]
['cAB', 'Bee']

- `table`: A 256-byte table, or a dict mapping characters below U+0100.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## *

escape(kind='json', *, capabilities=None) -> Strings

```python
def escape(
    kind: str = 'json',
    *,
    capabilities: Capability | None = None,
) -> Strings
```

- `escape`
- `unescape`

Escape every string for a text format, in one pass over the whole collection.

Returns:
Strings: A new collection of the escaped strings.
Example:

[str(text) for text in sz.Strings(['<b>', 'Tom & Jerry']).escape('html')]
['<b>', 'Tom & Jerry']

Reverse [`escape`](#escape) for every string, in one pass over the whole collection. The json kinds
decode what `json.loads` decodes in a string body, html what [`html.unescape`](#unescape) decodes of the
five XML entities and numeric references, url what `urllib.parse.unquote_to_bytes` decodes,
and csv one field as `csv.reader` reads it.

Returns:
Strings: A new collection of the unescaped strings.
Raises:
ValueError: Naming the index of the first string holding a malformed escape.
Example:

[str(text) for text in sz.Strings(['a%20b', '100%25']).unescape('url')]
['a b', '100%']

- `kind`: One of the kinds above, defaulting to 'json'.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Kinds: json: A JSON string body, as `json.dumps(text, ensure_ascii=False)` writes it unquoted.
json_ascii: The same with every non-ASCII codepoint as `\uXXXX`, like `json.dumps(text)`.
hex: Two lowercase digits per byte, as `bytes.hex` writes them.
html: `&`, `<`, `>`, `"` and `'` as entities, like [`html.escape`](#escape), with `&#39;` for `'`.
url: Percent-encoding, as `urllib.parse.quote(text, safe='')` writes it.
console: Controls, ill-formed UTF-8 and bidirectional overrides as `\n`, `\xHH` or `\uXXXX`.
csv: A field quoted only around a comma, a quote, CR or LF, as `csv.writer` writes it.

## normalize

normalize(form='NFC', *, capabilities=None) -> Strings

```python
def normalize(
    form: str = 'NFC',
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Bring every string into a Unicode normalization form, in one pass over the whole collection,
like [`unicodedata.normalize`](#normalize) over each. 'NFKC_Casefold' is the Unicode toNFKC_Casefold, the
keys search engines match on: NFKC with full case folding and no default-ignorable codepoints.

Returns:
Strings: A new collection of the normalized strings.
Example:

[str(text) for text in sz.Strings(['\ufb01', 'Stra\u00dfe']).normalize('NFKC_Casefold')]
['fi', 'strasse']

- `form`: 'NFC', 'NFD', 'NFKC', 'NFKD' or 'NFKC_Casefold', defaulting to 'NFC'.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## to_json

to_json(ensure_ascii=True, *, capabilities=None) -> bytes

```python
def to_json(
    ensure_ascii: bool = True,
    *,
    capabilities: Capability | None = None,
) -> bytes
```

Dump the strings as one compact JSON array, the bytes of
`json.dumps(strings, ensure_ascii=ensure_ascii, separators=(',', ':')).encode()`.

Returns:
bytes: The UTF-8 JSON text.
Example:

sz.Strings(['Ada', 'Grace']).to_json()
b'["Ada","Grace"]'

- `ensure_ascii`: Write every non-ASCII codepoint as `\uXXXX`, defaulting to True.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## tape_address

Memory address of the first byte of the tape, which the offsets count from, as an integer.

```python
tape_address: int
```

## tape_nbytes

Bytes from the start of the tape to the end of its last string.

```python
tape_nbytes: int
```

## offsets_address

Memory address of the first offset, as an integer.

```python
offsets_address: int
```

## offsets_nbytes

Length of the offsets array in bytes, one offset per string and one past the last.

```python
offsets_nbytes: int
```

## offsets_are_large

True for 64-bit offsets, the Arrow `large_string` layout, and False for the 32-bit `string` one.

```python
offsets_are_large: bool
```

## String

Immutable byte-string/slice class with SIMD and SWAR-accelerated operations.

```python
class String
```

Provides high-performance byte-string operations using modern CPU instructions.

Functions: - `find(), rfind()`: Fast substring search with SIMD acceleration

- `count()`: Count occurrences with optional overlap support

- `split(), rsplit()`: String splitting with various separators

- `contains()`: Fast membership testing

- `translate()`: Byte-level translations with lookup tables

Examples: ```
>>> text = sz.String('the quick brown fox')
>>> text.find('brown')        # SIMD-accelerated substring search
10
>>> str(text[10:15])          # slicing returns a zero-copy view, not a new buffer
'brown'
```

## String

```python
String(source: Any)
```

## contains

Check if a string contains a substring.

```python
def contains(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> bool
```

Returns:
bool: True if the substring is found, False otherwise.

- `substring`: The substring to search for.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').contains('ell')
True
```

## count

Count the occurrences of a substring.

```python
def count(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    allowoverlap: bool = False,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The number of occurrences of the substring.

- `substring`: The substring to count.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `allowoverlap`: Count overlapping occurrences, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('banana').count('a')
3
```

## splitlines

Split a string by line breaks, like [`str.splitlines`](https://www.unum.cloud/docs/stringzilla/python/splitting-and-partitioning.md#splitlines).

```python
def splitlines(
    keeplinebreaks: bool = False,
    maxsplit: int = -1,
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Returns:
Strings: The lines, copied onto a tape of their own.

- `keeplinebreaks`: Include line breaks in the results, defaulting to False.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(map(str, sz.String('a\nb\nc').splitlines()))
['a', 'b', 'c']
```

## startswith

Check if a string starts with a given prefix.

```python
def startswith(
    prefix: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> bool
```

Returns:
bool: True if the string starts with the prefix, False otherwise.

- `prefix`: The prefix to check.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').startswith('he')
True
```

## endswith

Check if a string ends with a given suffix.

```python
def endswith(
    suffix: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> bool
```

Returns:
bool: True if the string ends with the suffix, False otherwise.

- `suffix`: The suffix to check.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').endswith('lo')
True
```

## decode

Decode the bytes into a Unicode string with a given encoding.

```python
def decode(
    encoding: str = 'utf-8',
    errors: str = 'strict',
) -> str
```

Returns:
str: The decoded Unicode string.
Raises:
UnicodeDecodeError: If decoding fails.

- `encoding`: The encoding to use, defaulting to 'utf-8'.
- `errors`: Error handling scheme, defaulting to 'strict'.

Examples: ```
>>> sz.String('abc').decode() == 'abc'
True
```

## hash

Compute the hash value of the string.

```python
def hash(
    seed: int = 0,
    /,
    *,
    capabilities: Capability | None = None,
) -> int
```

This function can be called as a method on a String object or as a standalone function.
Args:
text (String or str or bytes): The string to hash, positional-only when standalone.
seed (int, optional): The seed value for hashing. Defaults to 0. Can be positional or keyword.
capabilities (Capability, optional): Capabilities to run, defaulting to the CPU's enabled ones.
Returns:
int: The hash value as an unsigned 64-bit integer. This differs from Python's
built-in `hash()` which returns a `Py_hash_t` and may be platform-dependent.
Raises:
TypeError: If the argument is not string-like or incorrect number of arguments is provided.
Signature:

def hash(text, seed=0, /, *, capabilities=None) -> int: ...

## hash_multiseed

Hash one string under many seeds at once.

```python
def hash_multiseed(
    seeds: _Buffer,
    /,
    out: _Buffer | None = None,
    *,
    capabilities: Capability | None = None,
) -> tuple[int, ...] | None
```

Equivalent to `tuple(hash(text, s) for s in seeds)`, but normalizes the input into AES
blocks once and replays the cheap per-seed rounds - markedly faster for short strings under
many seeds (feature hashing, Count-Min sketches, Bloom/cuckoo filters, MinHash/LSH).

Returns:
tuple[int, ...] | None: Tuple of 64-bit hashes, or None when writing into `out`.

- `seeds`: Contiguous 64-bit seeds, e.g. numpy.uint64 array or array('Q', ...).
Plain int lists are not accepted - wrap them in array('Q', seeds) or numpy.
- `out`: A contiguous buffer of at least len(seeds) elements.
If given, it is filled in place and None returned; otherwise a tuple of ints is returned.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> def hash_multiseed(text, seeds, /, out=None, *, capabilities=None) -> tuple | None: ...
```

## sha256

Compute SHA256 cryptographic hash of the input data.

```python
def sha256(*, capabilities: Capability | None = None) -> bytes
```

This function can be called as a method on a String object or as a standalone function.
Args:
text (String or str or bytes): The input data to hash.
capabilities (Capability, optional): Capabilities to run, defaulting to the CPU's enabled ones.
Returns:
bytes: The 32-byte (256-bit) SHA256 digest.
Raises:
TypeError: If the argument is not string-like or incorrect number of arguments is provided.

Examples: ```
>>> digest = sz.String('abc').sha256().hex()
>>> digest[:8]
'ba7816bf'
```

## lstrip

Remove leading characters from a string.

```python
def lstrip(
    chars: _StringLike | None = None,
    *,
    capabilities: Capability | None = None,
) -> String
```

Returns:
String: A new string with leading characters removed.

- `chars`: Characters to remove, defaulting to whitespace.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('  hi').lstrip() == 'hi'
True
```

## rstrip

Remove trailing characters from a string.

```python
def rstrip(
    chars: _StringLike | None = None,
    *,
    capabilities: Capability | None = None,
) -> String
```

Returns:
String: A new string with trailing characters removed.

- `chars`: Characters to remove, defaulting to whitespace.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hi  ').rstrip() == 'hi'
True
```

## strip

Remove leading and trailing characters from a string.

```python
def strip(
    chars: _StringLike | None = None,
    *,
    capabilities: Capability | None = None,
) -> String
```

Returns:
String: A new string with leading and trailing characters removed.

- `chars`: Characters to remove, defaulting to whitespace.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('  hi  ').strip() == 'hi'
True
```

## join

join(strings, /, *, capabilities=None) -> String

```python
def join(
    strings: Strings | Iterable[str | bytes],
    /,
    *,
    capabilities: Capability | None = None,
) -> String
```

Concatenate the strings with this one between every two, like [`str.join`](#join), measuring the result
first and writing it once.

Returns:
String: The joined bytes.
Example:

str(sz.String(', ').join(['Ada', 'Grace', 'Linus']))
'Ada, Grace, Linus'

- `strings`: The strings to join; a [`Strings`](#Strings) is read in place.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

## find

Find the first occurrence of a substring.

```python
def find(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The index of the first occurrence, or -1 if not found.

- `substring`: The substring to find.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').find('l')
2
```

## index

Find the first occurrence of a substring or raise an error if not found.

```python
def index(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The index of the first occurrence.
Raises:
ValueError: If the substring is not found.

- `substring`: The substring to find.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').index('l')
2
```

## partition

Split the string into a 3-tuple around the first occurrence of a separator.

```python
def partition(
    separator: _StringLike,
    *,
    capabilities: Capability | None = None,
) -> tuple[String, String, String]
```

Returns:
tuple: A 3-tuple (head, separator, tail), or `(self, '', '')` if the separator is not found.

- `separator`: The separator to partition by.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> tuple(map(str, sz.String('a=b=c').partition('=')))
('a', '=', 'b=c')
```

## split

Split a string by a separator, like [`str.split`](https://www.unum.cloud/docs/stringzilla/python/splitting-and-partitioning.md#split).

```python
def split(
    separator: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Returns:
Strings: The parts, copied onto a tape of their own.

- `separator`: The separator to split by; an empty one leaves the text whole.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include the separator in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(map(str, sz.String('a,b,c').split(',')))
['a', 'b', 'c']
```

## rfind

Find the last occurrence of a substring.

```python
def rfind(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The index of the last occurrence, or -1 if not found.

- `substring`: The substring to find.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').rfind('l')
3
```

## rindex

Find the last occurrence of a substring or raise an error if not found.

```python
def rindex(
    substring: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The index of the last occurrence.
Raises:
ValueError: If the substring is not found.

- `substring`: The substring to find.
- `start`: The starting index, defaulting to 0.
- `end`: The ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').rindex('l')
3
```

## rpartition

Split the string into a 3-tuple around the last occurrence of a separator.

```python
def rpartition(
    separator: _StringLike,
    *,
    capabilities: Capability | None = None,
) -> tuple[String, String, String]
```

Returns:
tuple: A 3-tuple (head, separator, tail). If the separator is not found, returns ('', '', self).

- `separator`: The separator to partition by.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> tuple(map(str, sz.String('a=b=c').rpartition('=')))
('a=b', '=', 'c')
```

## rsplit

Split a string by a separator starting from the end, like [`str.rsplit`](https://www.unum.cloud/docs/stringzilla/python/splitting-and-partitioning.md#rsplit).

```python
def rsplit(
    separator: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Returns:
Strings: The parts, copied onto a tape of their own.

- `separator`: The separator to split by; an empty one leaves the text whole.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include the separator in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(map(str, sz.String('a,b,c').rsplit(',')))
['a', 'b', 'c']
```

## find_*_of

Find the index of the first occurrence of any character from another string.

```python
def find_first_of(
    chars: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

- `find_first_of`
- `find_last_of`

Returns:
int: Index of the first matching character, or -1 if none found.

Returns:
int: Index of the last matching character, or -1 if none found.

- `chars`: A string containing characters to search for.
- `start`: Starting index, defaulting to 0.
- `end`: Ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').find_first_of('aeiou')
1
```

Examples: ```
>>> sz.String('hello').find_last_of('aeiou')
4
```

## find_*_not_of

Find the index of the first character not in another string.

```python
def find_first_not_of(
    chars: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

- `find_first_not_of`
- `find_last_not_of`

Returns:
int: Index of the first non-matching character, or -1 if all match.

Returns:
int: Index of the last non-matching character, or -1 if all match.

- `chars`: A string containing characters to exclude.
- `start`: Starting index, defaulting to 0.
- `end`: Ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello').find_first_not_of('he')
2
```

Examples: ```
>>> sz.String('hello').find_last_not_of('lo')
1
```

## count_byteset

Count the occurrences of any character from a set of characters.

```python
def count_byteset(
    chars: _StringLike,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> int
```

Returns:
int: The number of occurrences of any character from the set.

- `chars`: A string containing characters to count.
- `start`: Starting index, defaulting to 0.
- `end`: Ending index, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('hello world').count_byteset('lo')
5
```

## split_byteset

Split a string by a set of character separators.

```python
def split_byteset(
    separators: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Returns:
Strings: The parts, copied onto a tape of their own.

- `separators`: A string containing separator characters.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separators in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(map(str, sz.String('a,b;c').split_byteset(',;')))
['a', 'b', 'c']
```

## rsplit_byteset

Split a string by a set of character separators in reverse order.

```python
def rsplit_byteset(
    separators: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Strings
```

Returns:
Strings: The parts, copied onto a tape of their own.

- `separators`: A string containing separator characters.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separators in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(map(str, sz.String('a,b;c').rsplit_byteset(',;')))
['a', 'b', 'c']
```

## split_iter

Create an iterator for splitting a string by a separator.

```python
def split_iter(
    separator: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> FindSplits
```

Returns:
iterator: An iterator yielding split substrings.

- `separator`: The separator to split by; an empty one leaves the text whole.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separator in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> # Stream parts lazily instead of materializing a list (that is what split() is for):
>>> sum(1 for _ in sz.String('a,b,c').split_iter(','))
3
```

## rsplit_iter

Create an iterator for splitting a string by a separator in reverse order.

```python
def rsplit_iter(
    separator: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> FindSplits
```

Returns:
iterator: An iterator yielding split substrings in reverse.

- `separator`: The separator to split by; an empty one leaves the text whole.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separator in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> # Iterates from the end; the first yielded part is the last field:
>>> str(next(iter(sz.String('a/b/c').rsplit_iter('/'))))
'c'
```

## split_byteset_iter

Create an iterator for splitting a string by a set of character separators.

```python
def split_byteset_iter(
    separators: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> FindSplits
```

Returns:
iterator: An iterator yielding split substrings.

- `separators`: A string containing separator characters.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separators in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> # Splits on ANY byte in the set, streamed lazily:
>>> str(next(iter(sz.String('a,b;c').split_byteset_iter(',;'))))
'a'
```

## rsplit_byteset_iter

Create an iterator for splitting a string by a set of character separators in reverse order.

```python
def rsplit_byteset_iter(
    separators: _StringLike,
    maxsplit: int = -1,
    keepseparator: bool = False,
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> FindSplits
```

Returns:
iterator: An iterator yielding split substrings in reverse.

- `separators`: A string containing separator characters.
- `maxsplit`: Maximum number of splits, defaulting to no limit.
- `keepseparator`: Include separators in results, defaulting to False.
- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> # Reverse byteset split; first yielded part is the last field:
>>> str(next(iter(sz.String('a,b;c').rsplit_byteset_iter(',;'))))
'c'
```

## utf8_count

Count the number of UTF-8 characters in a string.

```python
def utf8_count(
    *,
    capabilities: Capability | None = None,
) -> int
```

Unlike len() which returns bytes, this counts actual Unicode characters,
handling multi-byte UTF-8 sequences correctly.

Returns:
int: Number of UTF-8 characters in the string.

- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.utf8_count('hello')  # 5 ASCII chars = 5
5
>>> sz.utf8_count('\xc3\xa9')  # 1 e-acute char = 1
1
```

## utf8_split_newlines

Create an iterator over the lines, the content between Unicode newlines.

```python
def utf8_split_newlines(
    skip_empty: bool = False,
    with_separators: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8SplitNewlines
```

Uses SIMD-accelerated detection of all 7 Unicode newline characters plus CRLF.
Unlike splitlines(), this returns an iterator for memory-efficient processing.

Returns:
iterator: An iterator yielding the lines as String objects.

- `skip_empty`: Skip empty lines, defaulting to False.
- `with_separators`: Interleave the separators losslessly, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Recognized newlines: LF (\n), VT (\v), FF (\f), CR (\r), NEL (U+0085),
LINE SEPARATOR (U+2028), PARAGRAPH SEPARATOR (U+2029), CRLF (\r\n)

Examples: ```
>>> sum(1 for _ in sz.String('a\nb').utf8_split_newlines())
2
```

## utf8_newlines

Create an iterator over the Unicode newline separators, one per codepoint, CR+LF kept together.

```python
def utf8_newlines(
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8Newlines
```

Uses SIMD-accelerated detection of all 7 Unicode newline characters plus CRLF.
Yields each newline separator; utf8_split_newlines() yields the lines between them.

Returns:
iterator: An iterator yielding the newline separators as String objects.

- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Recognized newlines: LF (\n), VT (\v), FF (\f), CR (\r), NEL (U+0085),
LINE SEPARATOR (U+2028), PARAGRAPH SEPARATOR (U+2029), CRLF (\r\n)

Examples: ```
>>> sum(1 for _ in sz.String('a\nb').utf8_newlines())
1
```

## utf8_split_whitespaces

Create an iterator over the tokens, the content between Unicode whitespace.

```python
def utf8_split_whitespaces(
    skip_empty: bool = False,
    with_separators: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8SplitWhitespaces
```

Uses SIMD-accelerated detection of all 25 Unicode White_Space characters.
Splits on each whitespace codepoint; pass skip_empty=True for str.split()-style tokens.

Returns:
iterator: An iterator yielding the non-whitespace tokens as String objects.

- `skip_empty`: Skip empty segments, defaulting to False.
- `with_separators`: Interleave the separators losslessly, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Recognized whitespace: ASCII: TAB, LF, VT, FF, CR, SPACE
Latin-1: NEXT LINE, NO-BREAK SPACE
General Punctuation: EN/EM QUAD/SPACE, THIN SPACE, etc.
CJK: IDEOGRAPHIC SPACE (U+3000)

Examples: ```
>>> sum(1 for _ in sz.String('foo bar baz').utf8_split_whitespaces())
3
```

## utf8_whitespaces

Create an iterator over Unicode whitespace separators, one per codepoint, CR+LF kept together.

```python
def utf8_whitespaces(
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8Whitespaces
```

Uses SIMD-accelerated detection of all 25 Unicode White_Space characters.
Yields each whitespace codepoint; utf8_split_whitespaces() yields the tokens between them.

Returns:
iterator: An iterator yielding the whitespace separators as String objects.

- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Recognized whitespace: ASCII: TAB, LF, VT, FF, CR, SPACE
Latin-1: NEXT LINE, NO-BREAK SPACE
General Punctuation: EN/EM QUAD/SPACE, THIN SPACE, etc.
CJK: IDEOGRAPHIC SPACE (U+3000)

Examples: ```
>>> sum(1 for _ in sz.String('foo bar').utf8_whitespaces())
1
```

## utf8_split_delimiters

Create an iterator over the fields, the content between Unicode delimiters.

```python
def utf8_split_delimiters(
    skip_empty: bool = False,
    with_separators: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8SplitDelimiters
```

Uses SIMD-accelerated detection of every punctuation (P*), symbol (S*), and
separator/whitespace (Z*) codepoint - the superset of utf8_split_whitespaces().
Splits on delimiters, one separator per codepoint, and yields the segments between them.

Returns:
iterator: An iterator yielding the non-delimiter segments as String objects.

- `skip_empty`: Skip empty segments, defaulting to False.
- `with_separators`: Interleave the separators losslessly, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(str(t) for t in sz.String('Hi, world').utf8_split_delimiters(skip_empty=True))
['Hi', 'world']
```

## utf8_delimiters

Create an iterator over Unicode delimiter separators, one per codepoint, CR+LF kept together.

```python
def utf8_delimiters(
    skip_empty: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8Delimiters
```

Uses SIMD-accelerated detection of every punctuation (P*), symbol (S*), and
separator/whitespace (Z*) codepoint. Yields each delimiter codepoint;
utf8_split_delimiters() yields the fields between them.

Returns:
iterator: An iterator yielding the delimiter separators as String objects.

- `skip_empty`: Skip empty segments, defaulting to False.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> list(str(d) for d in sz.String('a.b').utf8_delimiters())
['.']
```

## utf8_wordbreaks

Return an iterator yielding words per Unicode UAX-29 word boundary rules.

```python
def utf8_wordbreaks(
    *,
    capabilities: Capability | None = None,
) -> Utf8Wordbreaks
```

Unlike str.split(), this is UAX-29 compliant and supports all Unicode scripts.

- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> # Stream UAX-29 word tokens lazily:
>>> 'world' in (str(w) for w in sz.utf8_wordbreaks('Hi, world'))
True
```

## utf8_codepoints

Return an iterator yielding Unicode code points as int, decoded from UTF-8.

```python
def utf8_codepoints(
    *,
    capabilities: Capability | None = None,
) -> Utf8Codepoints
```

Ill-formed bytes decode to U+FFFD, the replacement character, so iteration is total and never raises on malformed input.

- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> list(sz.utf8_codepoints('AB'))
[65, 66]
```

## utf8_graphemes

Return an iterator yielding grapheme clusters per Unicode UAX-29 rules.

```python
def utf8_graphemes(
    *,
    capabilities: Capability | None = None,
) -> Utf8Graphemes
```

A grapheme cluster is a user-perceived character (e.g. a base plus combining marks, or an emoji ZWJ sequence) and may span several code points.

- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> # Stream UAX-29 grapheme clusters lazily:
>>> [str(g) for g in sz.utf8_graphemes('abc')]
['a', 'b', 'c']
```

## utf8_sentences

Return an iterator yielding sentences per Unicode UAX-29 sentence boundary rules.

```python
def utf8_sentences(
    *,
    capabilities: Capability | None = None,
) -> Utf8Sentences
```

UAX-29 compliant and Unicode-script aware, unlike naive period splitting.

- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> # Stream UAX-29 sentences lazily:
>>> len(list(sz.utf8_sentences('Hi. Bye.'))) >= 2
True
```

## utf8_linebreaks

Return an iterator yielding segments at line-break opportunities per UAX-14.

```python
def utf8_linebreaks(
    *,
    capabilities: Capability | None = None,
) -> Utf8Linebreaks
```

Each segment ends at a line-break opportunity, a soft wrap point. For hard-line splitting (str.splitlines()), use utf8_split_newlines().

- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> # Stream UAX-14 line-break opportunities lazily:
>>> len(list(sz.utf8_linebreaks('a\nb'))) >= 2
True
```

## utf8_uncased_fold

Apply Unicode case folding to a UTF-8 string.

```python
def utf8_uncased_fold(
    validate: bool = False,
    *,
    capabilities: Capability | None = None,
) -> bytes
```

Case folding normalizes text for uncased comparisons,
handling one-to-many expansions, like German sharp S to 'ss'.

- `validate`: If True, validate UTF-8 before processing. Default: False.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> sz.utf8_uncased_fold('HELLO')
b'hello'
>>> sz.utf8_uncased_fold('Stra\u00dfe')  # German sharp S
b'strasse'
```

## utf8_norm

Normalize a UTF-8 string to a Unicode normalization form.

```python
def utf8_norm(
    form: str,
    validate: bool = False,
    *,
    capabilities: Capability | None = None,
) -> bytes
```

Transforms the input into NFD, NFC, NFKD, or NFKC, mirroring [`unicodedata.normalize`](#normalize) but
returning raw UTF-8 bytes, or into NFKC_Casefold, the Unicode toNFKC_Casefold: NFKC with full
case folding and no default-ignorable codepoints.

- `form`: One of 'NFC', 'NFD', 'NFKC', 'NFKD', 'NFKC_Casefold'.
- `validate`: If True, validate UTF-8 before processing. Default: False.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> sz.utf8_norm('caf\u00e9', 'NFD')  # decompose
b'cafe\xcc\x81'
>>> sz.utf8_norm('\ufb01', 'NFKD')  # fi ligature
b'fi'
>>> sz.utf8_norm('Stra\u00dfe\u00ad', 'NFKC_Casefold')  # folded, soft hyphen dropped
b'strasse'
```

## utf8_find_denormalized

Return the byte offset of the first normalization violation, or None.

```python
def utf8_find_denormalized(
    form: str,
    *,
    capabilities: Capability | None = None,
) -> int | None
```

Scans the UTF-8 string and returns the byte offset of the first codepoint
that breaks the given normalization form (first non-Yes Quick-Check result
or canonical-ordering violation). Returns None when the string is already
fully normalized.

- `form`: One of 'NFC', 'NFD', 'NFKC', 'NFKD', 'NFKC_Casefold'.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> sz.utf8_find_denormalized('cafe\u0301', 'NFC')  # 'e' + combining acute
3
>>> sz.utf8_find_denormalized('caf\u00e9', 'NFC') is None  # precomposed, already NFC
True
```

## utf8_uncased_search

Find substring using Unicode uncased matching.

```python
def utf8_uncased_search(
    needle: _StringLike,
    start: int = 0,
    end: int | None = None,
    validate: bool = False,
    *,
    capabilities: Capability | None = None,
) -> int
```

Performs a uncased search using Unicode case folding rules,
correctly handling one-to-many expansions, like 'ß' matching 'SS'.

- `needle`: The substring to find.
- `start`: Starting index, defaulting to 0.
- `end`: Ending index, defaulting to length.
- `validate`: If True, validate UTF-8 before processing. Default: False.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

IMPORTANT - Type-dependent behavior: - str input:   start/end are CODEPOINT offsets, returns CODEPOINT offset

- bytes input: start/end are BYTE offsets, returns BYTE offset

Examples: ```
>>> sz.utf8_uncased_search('Hello World', 'WORLD')  # str: codepoint offset
6
>>> sz.utf8_uncased_search('Straße', 'STRASSE')  # 'ß' = 1 codepoint
0
>>> sz.utf8_uncased_search(b'Stra\xc3\x9fe', b'STRASSE')  # 'ß' = 2 bytes
0
```

## utf8_uncased_matches

Iterate over all uncased matches of needle in haystack.

```python
def utf8_uncased_matches(
    needle: _StringLike,
    /,
    include_overlapping: bool = False,
    *,
    capabilities: Capability | None = None,
) -> Utf8UncasedMatches
```

This function uses Unicode case folding for proper handling of
international text. The matched region length may differ from the
needle length due to case folding expansions, like 'ß' matching 'SS'.
An empty needle matches at every codepoint boundary, the end included.

- `needle`: The pattern to find.
- `include_overlapping`: Allow overlapping matches, defaulting to False.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Yields: - [`Utf8UncasedMatches`](https://www.unum.cloud/docs/stringzilla/python/case-folding-and-normalization.md#Utf8UncasedMatches): Each matched region as a view into the original haystack.

Examples: ```
>>> list(sz.utf8_uncased_matches('Hello HELLO hello', 'hello'))
[sz.String('Hello'), sz.String('HELLO'), sz.String('hello')]
>>> list(sz.utf8_uncased_matches('Straße STRASSE', 'strasse'))
[sz.String('Straße'), sz.String('STRASSE')]
```

## utf8_uncased_order

Compare two UTF-8 strings uncasedly.

```python
def utf8_uncased_order(
    other: _StringLike,
    validate: bool = False,
    *,
    capabilities: Capability | None = None,
) -> int
```

Performs lexicographical comparison using Unicode case folding, correctly handling
one-to-many expansions, like 'Straße' equaling 'STRASSE'.

- `other`: The string to compare with.
- `validate`: If True, validate UTF-8 before processing. Default: False.
- `capabilities`: Capabilities to run, by default the CPU's enabled ones.

Examples: ```
>>> sz.utf8_uncased_order('hello', 'HELLO')
0
>>> sz.utf8_uncased_order('apple', 'BANANA')
-1
```

## offset_within

Return the raw byte offset of this StringZilla string within a larger StringZilla string.

```python
def offset_within(larger: String) -> int
```

Returns:
int: The byte offset where 'self' is found within 'larger', or -1 if not found.

- `larger`: The larger string to search within.

Examples: ```
>>> big = sz.String('hello world')
>>> big[6:].offset_within(big)
6
```

## write_to

Write the string to a file.

```python
def write_to(filename: str) -> None
```

Returns:
None.

- `filename`: The file path to write to.

Examples: ```
>>> import os, tempfile
>>> path = os.path.join(tempfile.gettempdir(), 'sz_demo.bin')
>>> sz.String('payload').write_to(path)
```

## translate

Perform transformation of a string using a look-up table.

```python
def translate(
    table: str | bytes | dict[str, str],
    inplace: bool = False,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> None | str | bytes
```

Returns:
Union[None, str, bytes]: If inplace is False, a translated copy of the [start, end) slice is
returned, otherwise None.
Raises:
ValueError: If the table is not 256 bytes long, or a dict names a character above U+00FF.
UnicodeDecodeError: If a str input translates into malformed UTF-8.
TypeError: If the table is not a string or dictionary.

- `table`: A 256-byte string, or a dict mapping characters below U+0100 to such.
- `inplace`: If True, the string is modified in place, defaulting to False.
- `start`: The starting index for translation, defaulting to 0.
- `end`: The ending index for translation, defaulting to the string length.
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> sz.String('abc').translate({'a': 'A'}) == b'Abc'
True
```

## fill_random

Fill a string-like buffer in place with pseudo-random bytes.

```python
def fill_random(
    nonce: int = 0,
    alphabet: str | bytes | None = None,
    start: int = 0,
    end: int | None = None,
    *,
    capabilities: Capability | None = None,
) -> None
```

Returns:
None: Mutates the buffer slice in place.

- `nonce`: Seed ensuring reproducible output for the same inputs, defaulting to 0.
- `alphabet`: If provided, remaps random bytes to its characters.
- `start`: Starting index, defaulting to 0.
- `end`: Ending index, defaulting to len(buffer).
- `capabilities`: Capabilities to run, defaulting to the CPU's enabled ones.

Examples: ```
>>> buf = bytearray(16)
>>> sz.fill_random(buf)
>>> len(buf)
16
```

## address

Memory address of the first byte of the string, as an integer.

```python
address: int
```

Enables zero-copy interop, e.g. with PyArrow or ctypes.

Examples: ```
>>> isinstance(sz.String('abc').address, int)
True
```

## nbytes

Length of the string in bytes.

```python
nbytes: int
```

Examples: ```
>>> sz.String('abc').nbytes
3
```

