C · Case Folding and Normalization

Case Folding and Normalization

#include <stringzilla/types.h>#include <stringzilla/capabilities.h>#include <stringzilla/strings.h>#include <stringzilla/utf8_uncased_fold.h>#include <stringzilla/utf8_uncased.h>#include <stringzilla/utf8_norm.h> 6.0.0-rc.1 · 91a27bdd

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. utf8_uncased_search searches case-insensitively without pre-folding the haystack: it folds on the fly, so you never allocate a folded copy of a large document just to search it.

#include <stringzilla/stringzilla.hpp>
#include <cassert>
namespace sz = ashvardanian::stringzilla;

int main() {
    sz::string_view_t hay = "Take the STRAßE downtown";
    auto m = hay.utf8_uncased_search("strasse"); // no folded copy of `hay` is made
    assert(m.offset == 9);
    assert(hay.sub(m.offset, m.offset + m.length) == "STRAßE"); // matched the mixed-case ß form
    return 0;
}

The result carries both the offset and the matched length, since folding can make the matched span longer or shorter than the needle. To search many haystacks for one needle, prepare it once as a sz::utf8_uncased_needle_t, which borrows the needle’s bytes, and pass it to utf8_uncased_search or to utf8_uncased_matches, the lazy range of every non-overlapping match. Matches sit on codepoint boundaries, so an empty needle matches at every boundary, the end included. When you need the folded text itself, fold a sz::string_t in place:

sz::string_t greeting;
if (sz::failed(greeting.assign("Grüße"))) return 1;
if (sz::failed(greeting.utf8_uncased_fold())) return 1; // in place; `ß` expands to `ss`
assert(greeting == "grüsse");

In C, sz_utf8_uncased_needle_init_best analyses the needle once into a sz_utf8_uncased_needle_t, and sz_utf8_uncased_search_best only reads it, so any number of searches, under any capabilities, share one analysis. The prepared needle points at the needle’s bytes rather than copying them, so they must outlive it:

#include <stringzilla/stringzilla.h>

sz_utf8_uncased_needle_t needle;
sz_utf8_uncased_needle_init_best("café", 5, &needle, capabilities, NULL);
sz_cptr_t hit = NULL;
sz_size_t match_length = 0;
sz_utf8_uncased_search_best(haystack, haystack_length, &needle, &hit, &match_length, capabilities, NULL);
// `hit` points at the first case-insensitive match, or NULL; reuse `needle` for the next haystack.

An empty needle matches at the start of every haystack, with a zero match_length.

API

Types

sz_normal_form_tUnicode normalization form selector, see utf8_norm.h.
types.h:957 ↗
enum sz_normal_form_t

Values

sz_normal_form_nfd_k0Canonical decomposition.
sz_normal_form_nfc_k1Canonical decomposition followed by canonical composition.
sz_normal_form_nfkd_k2Compatibility decomposition.
sz_normal_form_nfkc_k3Compatibility decomposition followed by canonical composition.
sz_normal_form_nfkc_casefold_k4NFKC with full case folding and without default-ignorable codepoints, recomposed to NFC: the Unicode toNFKC_Casefold that caseless identifier and search matching rely on.
sz_case_sensitivity_tWhether a vocabulary matches needles byte-for-byte, or folds both sides to one case first.
types.h:987 ↗
enum sz_case_sensitivity_t

Values

sz_cased_k0Byte-exact matching; needles may be arbitrary bytes, including malformed UTF-8.
sz_uncased_k1Full Unicode case folding as CaseFolding.txt defines it; needles must be valid UTF-8.
sz_kernel_utf8_uncased_fold_tSignature of the kernels behind sz_utf8_uncased_fold_best.
capabilities.h:717 ↗
typedef sz_status_t(*) sz_kernel_utf8_uncased_fold_t(sz_cptr_t, sz_size_t, sz_ptr_t, sz_size_t *, sz_stream_t);
sz_kernel_utf8_norm_tSignature of the kernels behind sz_utf8_norm_best.
capabilities.h:720 ↗
typedef sz_status_t(*) sz_kernel_utf8_norm_t(sz_cptr_t, sz_size_t, sz_normal_form_t, sz_ptr_t, sz_size_t *, sz_stream_t);
sz_kernel_utf8_find_denormalized_tSignature of the kernels behind sz_utf8_find_denormalized_best.
capabilities.h:724 ↗
typedef sz_status_t(*) sz_kernel_utf8_find_denormalized_t(sz_cptr_t, sz_size_t, sz_normal_form_t, sz_cptr_t *, sz_stream_t);
sz_kernel_utf8_uncased_needle_init_tSignature of the kernels behind sz_utf8_uncased_needle_init_best.
capabilities.h:731 ↗
typedef sz_status_t(*) sz_kernel_utf8_uncased_needle_init_t(sz_cptr_t, sz_size_t, struct sz_utf8_uncased_needle_t *, sz_stream_t);
sz_kernel_utf8_uncased_search_tSignature of the kernels behind sz_utf8_uncased_search_best.
capabilities.h:735 ↗
typedef sz_status_t(*) sz_kernel_utf8_uncased_search_t(sz_cptr_t, sz_size_t, struct sz_utf8_uncased_needle_t const *, sz_cptr_t *, sz_size_t *, sz_stream_t);
sz_kernel_utf8_uncased_order_tSignature of the kernels behind sz_utf8_uncased_order_best.
capabilities.h:739 ↗
typedef sz_status_t(*) sz_kernel_utf8_uncased_order_t(sz_cptr_t, sz_size_t, sz_cptr_t, sz_size_t, sz_ordering_t *, sz_stream_t);
sz_kernel_utf8_find_cased_tSignature of the kernels behind sz_utf8_find_cased_best.
capabilities.h:743 ↗
typedef sz_status_t(*) sz_kernel_utf8_find_cased_t(sz_cptr_t, sz_size_t, sz_cptr_t *, sz_stream_t);
sz_kernel_strings_normalize_tSignature of the sz_strings_normalize_best kernels.
capabilities.h:848 ↗
typedef sz_status_t(*) sz_kernel_strings_normalize_t(sz_strings_view_t const *, sz_size_t const *, sz_size_t, sz_normal_form_t, sz_allocator_t *, sz_strings_span_t *, sz_stream_t);

Functions

sz_strings_normalize_bestBrings strings[order[i]], or the first count strings, into the Unicode form, writing them into a tape.
strings.h:804 ↗
sz_status_t sz_strings_normalize_best(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_normal_form_t form,
    sz_allocator_t *allocator,
    sz_strings_span_t *tape,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_strings_normalizeserialhaswellicelakeneonsvervvv128v128relaxedloongsonasxpowervsxcudarocmmetal

sz_normal_form_nfkc_casefold_k gives the keys search engines match on: NFKC with full case folding and without default-ignorable codepoints, the Unicode toNFKC_Casefold.

On the CPU every string is normalized once, into blocks of the call's own from allocator: a payload starting at the total input length, grown through reallocate when the allocator has one, and fitted to its exact size at the end. That needs an allocator even when tape holds blocks of exactly the final sizes, which are then filled rather than replaced. Like every collection verb it is atomic. GPUs measure first and read only tapes, a string to a thread.

Parameters

stringsinThe strings to normalize, in any layout.
orderinIndices into strings, possibly repeated, or null for the first count.
countinNumber of strings to write.
forminThe normalization form to bring every string into.
allocatorinWhere the blocks of tape come from and return to.
tapeinoutA zeroed span, one an earlier call produced, or the one strings views.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

See also

UAX #15 Unicode Normalization Forms: https://www.unicode.org/reports/tr15/

sz_utf8_uncased_fold_bestApply Unicode case folding to a UTF-8 string.
utf8_uncased_fold.h:70 ↗
sz_status_t sz_utf8_uncased_fold_best(
    sz_cptr_t source,
    sz_size_t source_length,
    sz_ptr_t target,
    sz_size_t *target_length,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_uncased_foldserialhaswellicelakeneonsve2rvvv128loongsonasxpowervsxcudarocmmetal

Case folding normalizes text for uncased comparisons by mapping uppercase letters to their lowercase equivalents and handling special expansions defined in Unicode CaseFolding.txt.

Buffer Sizing

The target buffer must be at least source_length * 3 bytes to guarantee sufficient space for worst-case expansion. The maximum expansion ratio is 3:1, which occurs with Greek characters that expand to three codepoints under case folding. For example, 'ΐ' (U+0390, CE 90) folds to "ΐ" (U+03B9 U+0308 U+0301, CE B9 CC 88 CC 81), so 2 bytes → 6 bytes, and a string of N such characters expands from 2N to 6N bytes.

Malformed UTF-8 is handled losslessly: any byte that does not begin a well-formed codepoint is copied through unchanged (it folds to itself) and folding resyncs at the next byte. Valid UTF-8 folds as usual. Basic usage:

char const *source = "HELLO";
char target[15]; // 5 * 3, a safe overestimate
sz_capability_t capabilities;
sz_capabilities_enabled_cpu(&capabilities);
sz_size_t target_length;
sz_utf8_uncased_fold_best(source, 5, target, &target_length, capabilities, NULL);
// target now contains "hello", target_length = 5

On CUDA, ROCm and Metal, folding queues on stream, which names the device: the source, the target and the length slot must all be memory that device reaches, and the length lands once stream is joined, the call itself allocating nothing and joining nothing.

Warnings

The caller must ensure the target buffer is large enough. No bounds checking is performed. Use source_length * 3 for safety.

Statuses

sz_utf8_uncased_fold_find_kernelFinds the UTF-8 case folding kernel of kind, from the best of capabilities.
utf8_uncased_fold.h:86 ↗
sz_status_t sz_utf8_uncased_fold_find_kernel(
    sz_kernel_kind_t kind,
    sz_capability_t capabilities,
    sz_kernel_punned_t *kernel,
    sz_capability_t *capability);

Parameters

kindinThe verb whose kernel to find.
capabilitiesinThe mask to pick the best capability from.
kerneloutThe kernel, or null when none of capabilities has it.
capabilityoutThe capability the kernel belongs to, or zero.

Statuses

sz_utf8_uncased_needle_init_bestAnalyses a needle once for any number of uncased searches, under any capabilities.
utf8_uncased.h:63 ↗
sz_status_t sz_utf8_uncased_needle_init_best(
    sz_cptr_t needle,
    sz_size_t needle_length,
    sz_utf8_uncased_needle_t *prepared,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_uncased_needle_initserial

Picks the per-script search kernel and the probe positions it matches on, so that sz_utf8_uncased_search_best never re-analyses the needle. The prepared needle points at needle rather than copying it, so the needle must outlive it:

sz_utf8_uncased_needle_t prepared;
sz_utf8_uncased_needle_init_best("STRASSE", 7, &prepared, capabilities, STRINGZILLA_NULL);
sz_cptr_t match;
sz_size_t match_length;
sz_utf8_uncased_search_best("die Straße", 11, &prepared, &match, &match_length, capabilities,
                            STRINGZILLA_NULL);
// match points at "Straße", match_length == 7

Parameters

needleinUTF-8 substring to search for, possibly empty.
needle_lengthinNumber of bytes in needle.
preparedoutThe prepared needle, every field filled.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Statuses

sz_utf8_uncased_search_bestUncased substring search in UTF-8 strings.
utf8_uncased.h:159 ↗
sz_status_t sz_utf8_uncased_search_best(
    sz_cptr_t haystack,
    sz_size_t haystack_length,
    sz_utf8_uncased_needle_t const *needle,
    sz_cptr_t *match,
    sz_size_t *match_length,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_uncased_searchserialhaswellicelakeneonsve2rvvv128loongsonasxpowervsx

In applications where the haystack remains largely static and memory/storage is cheap, it is recommended to pre-process the haystack into a case-folded version using Unicode Case Folding (e.g., via the ICU library) and subsequently use the simpler sz_find_best function for repeated searches, which avoids performing the full folding logic during every search.

This function applies full Unicode Case Folding as defined in the Unicode Standard (UAX #21 and CaseFolding.txt), covering all bicameral scripts, all offset-based one-to-one folds, all table-based one-to-one folds, and all normative one-to-many expansions. It doesn't however perform any normalization, like NFKC or NFC, so combining marks are treated as-is. StringZilla is intentionally locale-independent: case folding produces identical results regardless of runtime locale settings, ensuring deterministic behavior across platforms and simplifying use in multi-threaded and distributed systems.

The following character mappings are supported:

  • ASCII Latin letters A–Z (U+0041–U+005A) are folded to a–z (U+0061–U+007A) using a trivial +32 offset.
  • Fullwidth Latin letters A–Z (U+FF21–U+FF3A) are folded to a–z (U+FF41–U+FF5A) with the same +32 offset.
  • Cyrillic uppercase А–Я (U+0410–U+042F) are folded to а–я (U+0430–U+044F) using a +32 offset.
  • Armenian uppercase Ա–Ֆ (U+0531–U+0556) are folded to ա–ֆ (U+0561–U+0586) using a +48 offset.
  • Georgian Mtavruli letters Ა-Ჿ (U+1C90–U+1CBF, excluding 2) are folded to their Mkhedruli equivalents (U+10D0–U+10FF) using a fixed linear translation defined by the Unicode Standard.
  • Greek uppercase Α–Ω (U+0391–U+03A9) are folded to α–ω (U+03B1–U+03C9) via a +32 offset. Both Σ (U+03A3) and ς (U+03C2, final sigma) fold to σ (U+03C3) for consistent matching.
  • Latin Extended characters include numerous one-to-one folds, mixed-case digraphs and trigraphs normalized to lowercase sequences, and one-to-many expansions like:

    • 'ß' (U+00DF, C3 9F) → "ss" (U+0073 U+0073, 73 73)
    • 'ẞ' (U+1E9E, E1 BA 9E) → "ss" (U+0073 U+0073, 73 73)
  • Turkic dotted/dotless-I characters are handled per Unicode Case Folding (not locale-specific):

    • 'İ' (U+0130, C4 B0) → "i̇" (U+0069 U+0307, 69 CC 87) — Full case folding with combining dot
    • 'I' (U+0049, 49) → 'i' (U+0069, 69) — Standard folding (not Turkic 'I' (U+0049, 49) → 'ı' (U+0131, C4 B1))
    • 'ı' (U+0131, C4 B1) → 'ı' (U+0131, C4 B1) — Already lowercase, unchanged
  • Lithuanian accented I/J characters with combining dots are processed as multi-codepoint expansions per CaseFolding.txt.
  • Additional bicameral scripts, Cherokee, Deseret, Osage, Warang Citi, and Adlam, use their normative one-to-one uppercase-to-lowercase mappings defined in CaseFolding.txt.

Folding is applied during matching without rewriting the entire haystack. Multi-codepoint expansions, contextual folds, and combining-mark adjustments are handled at comparison time.

Algorithmic Considerations

Uncased search with full Unicode case folding is fundamentally harder than byte-level search because one-to-many expansions (e.g., 'ß' (U+00DF, C3 9F) → "ss" (U+0073 U+0073, 73 73)) break core assumptions of fast string algorithms:

  • Boyer-Moore/Horspool skip tables assume 1:1 character mapping
  • Two-Way critical factorization assumes fixed pattern length
  • Rabin-Karp rolling hash assumes fixed character widths
  • Volnitsky bigram hashing assumes consistent byte patterns

Industry approaches vary:

  • ICU abandoned Boyer-Moore for Unicode, reverting to linear search for correctness
  • ClickHouse uses Volnitsky with fallback to naive search for problematic characters
  • RipGrep uses simple case folding only (no expansion handling) leveraging the Rust RegEx engine

StringZilla implements several algorithms. Most importantly it first locates the longest expansion-free slice of the needle to locate against.

The references below cover ICU's abandonment of Boyer-Moore for Unicode, ClickHouse's hash-based Volnitsky search with UTF-8 case folding, glibc's O(n) time and O(1) space Two-Way matcher, and the uncased search of the uni-algo Unicode algorithms library.

Malformed UTF-8 is handled losslessly: any byte that does not begin a well-formed codepoint is matched byte-for-byte as a single literal byte (never as a Unicode codepoint) and processing resyncs at the next byte, so a search over invalid input is well-defined and identical across all backends.

See also

Statuses

sz_utf8_uncased_order_bestUncased lexicographic comparison of two UTF-8 strings.
utf8_uncased.h:204 ↗
sz_status_t sz_utf8_uncased_order_best(
    sz_cptr_t a,
    sz_size_t a_length,
    sz_cptr_t b,
    sz_size_t b_length,
    sz_ordering_t *ordering,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_uncased_orderserialhaswellicelakeneonsve2rvvv128loongsonasxpowervsx

Compares strings using Unicode case folding rules, producing consistent ordering regardless of letter case. Implements the same full Unicode Case Folding as sz_utf8_uncased_fold_best, including all one-to-many expansions (e.g. 'ß' (U+00DF, C3 9F) → "ss" (U+0073 U+0073, 73 73)) and bicameral script mappings.

Unlike simple byte comparison, this function correctly handles multi-byte UTF-8 sequences and expansion characters. Comparison is performed codepoint-by-codepoint after folding, not byte-by-byte, ensuring linguistically correct results.

Malformed UTF-8 is handled losslessly: any byte that does not begin a well-formed codepoint is treated as a single literal byte (folded to itself and compared byte-for-byte, never as a Unicode codepoint), and processing resyncs at the next byte. All uncased operations - find, order, and violation - share this contract, so results on invalid input are well-defined and consistent across backends. Basic usage:

sz_capability_t capabilities;
sz_capabilities_enabled_cpu(&capabilities);
sz_ordering_t ordering;
sz_utf8_uncased_order_best("Hello", 5, "HELLO", 5, &ordering, capabilities, STRINGZILLA_NULL);
// ordering == sz_equal_k

sz_utf8_uncased_order_best("straße", 7, "STRASSE", 7, &ordering, capabilities, STRINGZILLA_NULL);
// ordering == sz_equal_k ('ß' (U+00DF, C3 9F) → "ss" (U+0073 U+0073, 73 73))

Parameters

ainFirst UTF-8 string to compare.
a_lengthinNumber of bytes in the first string.
binSecond UTF-8 string to compare.
b_lengthinNumber of bytes in the second string.
orderingoutsz_less_k if a < b, sz_equal_k if a = b, sz_greater_k if a > b.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Statuses

sz_utf8_find_cased_bestLocate the first cased, or case-foldable, codepoint in a UTF-8 string.
utf8_uncased.h:274 ↗
sz_status_t sz_utf8_find_cased_best(
    sz_cptr_t text,
    sz_size_t length,
    sz_cptr_t *match,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_find_casedserialhaswellicelakeneonsve2rvvv128loongsonasxpowervsx

A codepoint is case-agnostic, or caseless, if all three conditions are true:

  1. Self-folding: case folding produces exactly the original codepoint.
  2. Not bicameral: it does not belong to any script with case distinctions.
  3. Not an expansion target: it appears in no multi-rune case fold expansion.

The third condition is subtle but critical. Consider 'ʾ' (U+02BE, CA BE):

  • It has no case variant and folds to itself.
  • However, 'ẚ' (U+1E9A, E1 BA 9A) → "aʾ" (U+0061 U+02BE, 61 CA BE).
  • A needle containing 'ʾ' must match at position 1 of the folded expansion of 'ẚ'.
  • Binary search cannot handle this, so 'ʾ' must not be case-agnostic.

Case-agnostic scripts include: CJK ideographs, Hangul, digits, punctuation, most symbols, Hebrew, Arabic, Thai, Hindi (Devanagari), and many other scripts without case distinctions.

The function is conservative: it reports any codepoint that participates in case folding, even if the specific instance wouldn't change. For example, lowercase 'a' is reported because it's a case-folding target.

Use Case

This function enables an important optimization: if both haystack and needle are fully case-agnostic, then sz_find_best can be used directly instead of the slower sz_utf8_uncased_search_best. This is particularly valuable for:

  • CJK text (Chinese, Japanese, Korean) - always caseless
  • Numeric data and punctuation-heavy content
  • Middle Eastern scripts (Arabic, Hebrew, Persian)
  • South/Southeast Asian scripts (Thai, Hindi, Vietnamese without Latin)

The optimization pattern:

sz_cptr_t haystack = "价格:¥1234";  // Chinese + punctuation + digits
sz_cptr_t needle = "¥1234";
sz_cptr_t haystack_cased, needle_cased, match;
sz_utf8_find_cased_best(haystack, haystack_length, &haystack_cased, capabilities, STRINGZILLA_NULL);
sz_utf8_find_cased_best(needle, needle_length, &needle_cased, capabilities, STRINGZILLA_NULL);

if (!haystack_cased && !needle_cased) {
    // Fast path: both strings are fully caseless, so use binary search
    sz_find_best(haystack, haystack_length, needle, needle_length, &match, capabilities, STRINGZILLA_NULL);
} else {
    // Slow path: full uncased search
    sz_utf8_uncased_needle_t prepared;
    sz_size_t match_length;
    sz_utf8_uncased_needle_init_best(needle, needle_length, &prepared, capabilities, STRINGZILLA_NULL);
    sz_utf8_uncased_search_best(haystack, haystack_length, &prepared, &match, &match_length, capabilities,
                                STRINGZILLA_NULL);
}

Statuses

sz_utf8_uncased_find_kernelFinds the UTF-8 uncased kernel of kind, from the best of capabilities.
utf8_uncased.h:288 ↗
sz_status_t sz_utf8_uncased_find_kernel(
    sz_kernel_kind_t kind,
    sz_capability_t capabilities,
    sz_kernel_punned_t *kernel,
    sz_capability_t *capability);

Parameters

kindinThe verb whose kernel to find.
capabilitiesinThe mask to pick the best capability from.
kerneloutThe kernel, or null when none of capabilities has it.
capabilityoutThe capability the kernel belongs to, or zero.

Statuses

sz_utf8_norm_bestTransform a UTF-8 string into a Unicode normalization form.
utf8_norm.h:66 ↗
sz_status_t sz_utf8_norm_best(
    sz_cptr_t source,
    sz_size_t source_length,
    sz_normal_form_t form,
    sz_ptr_t target,
    sz_size_t *target_length,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_normserialhaswellskylakeicelakeneonsvesve2rvvv128v128relaxedloongsonasxpowervsxcudarocmmetal

Buffer Sizing

Decomposition forms (NFD, NFKD) can expand the input; the target must hold up to source_length * 18 bytes for the worst single-codepoint compatibility decomposition. The composing forms (NFC, NFKC) never exceed the decomposed length, so the same bound is safe. NFKC_Casefold grows a byte into 11 at most, for U+FDFA, within the same bound.

Malformed UTF-8 is handled losslessly: any byte that does not begin a well-formed codepoint is an opaque 1-byte barrier - it is passed through unchanged, does not decompose, compose, or take part in canonical ordering, and processing resyncs at the next byte. NFKC_Casefold keeps the default ignorable right after such a byte, which erased would let it fuse with the bytes beyond.

On CUDA, ROCm and Metal normalization is enqueued on stream, which names the device: the source, target and length slot must be memory that device reaches, and the length lands once stream is joined. Calls allocate no output or scratch and join nothing. CUDA and ROCm copy the shared normalization tables to the device ahead of the kernel; Metal embeds them in its compiled library and requires buffers from sz_allocator_init_unified_metal.

Warnings

No bounds checking is performed on target.

Statuses

sz_utf8_find_denormalized_bestLocate the first byte that breaks a normalization form.
utf8_norm.h:88 ↗
sz_status_t sz_utf8_find_denormalized_best(
    sz_cptr_t source,
    sz_size_t source_length,
    sz_normal_form_t form,
    sz_cptr_t *match,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_utf8_find_denormalizedserialhaswellskylakeicelakeneonsvesve2rvvv128v128relaxedloongsonasxpowervsx

Malformed UTF-8 is treated losslessly: any byte that does not begin a well-formed codepoint is an opaque, inert 1-byte barrier (never a violation), and the scan resyncs at the next byte.

Parameters

sourceinUTF-8 string to test.
source_lengthinNumber of bytes in source.
forminA sz_normal_form_t: NFD, NFC, NFKD, NFKC or NFKC_Casefold.
matchoutSTRINGZILLA_NULL_CHAR if source is already in form, else a pointer to the first byte of the first codepoint breaking it: a non-Yes QC or out of canonical order.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Statuses

sz_utf8_norm_find_kernelFinds the UTF-8 normalization kernel of kind, from the best of capabilities.
utf8_norm.h:103 ↗
sz_status_t sz_utf8_norm_find_kernel(
    sz_kernel_kind_t kind,
    sz_capability_t capabilities,
    sz_kernel_punned_t *kernel,
    sz_capability_t *capability);

Parameters

kindinThe verb whose kernel to find.
capabilitiesinThe mask to pick the best capability from.
kerneloutThe kernel, or null when none of capabilities has it.
capabilityoutThe capability the kernel belongs to, or zero.

Statuses