C · Types

Types

#include <stringzilla/types.h> 6.0.0-rc.1 · 91a27bdd

The C API is built around length-bounded raw pointers, so most functions take a sz_cptr_t, an alias of char const *, or a sz_ptr_t, an alias of char *, together with a sz_size_t length, and return either a sz_cptr_t into the input or one of the small enums below.

Scalar and result types from types.h:

  • sz_cptr_t, sz_ptr_t — immutable and mutable byte pointers.
  • sz_size_t, sz_ssize_t — unsigned and signed size types.
  • sz_u8_t, sz_u32_t, sz_u64_t — fixed-width integers; sz_rune_t is a 32-bit Unicode scalar value.
  • sz_bool_t — { sz_false_k = 0, sz_true_k = 1 }.
  • sz_ordering_t — { sz_less_k = -1, sz_equal_k = 0, sz_greater_k = 1 }, the C analog of std::strong_ordering.
  • sz_status_t — sz_success_k = 0, sz_bad_alloc_k, sz_invalid_utf8_k, sz_contains_duplicates_k, sz_overflow_risk_k, and friends.
  • sz_pgram_t — a pointer-sized N-gram, an alias of sz_size_t, which also indexes every permutation.

sz_byteset_t is a 256-bit set of byte values used by the byte-set search functions:

sz_byteset_t set;
sz_byteset_init(&set);                 // all bits cleared
sz_byteset_add(&set, '\n');            // include a byte
int has = sz_byteset_contains(&set, '\n');
sz_byteset_invert(&set);               // complement the set

sz_allocator_t is the explicit allocator, a { allocate, free, handle } triple, handed to any function that may allocate:

sz_allocator_t allocator;
sz_status_t status = sz_allocator_init_heap(&allocator); // libc malloc/free
if (status != sz_success_k) return status;
// or a fixed arena with no dynamic allocation:
char arena[4096];
status = sz_allocator_init_arena(&allocator, arena, sizeof(arena), 64);
if (status != sz_success_k) return status;

sz_strings_view_t is the read-only collection of strings every collection family takes, in one of three layouts: an Apache Arrow tape of 32-bit or 64-bit offsets, which GPUs read directly, or an accessor writing up to 64 views per call over any other shape. Helpers wire it to plain arrays of C strings, of views, of records, and of NUL-padded rows:

char const *words[] = {"banana", "apple", "cherry"};
sz_strings_view_t strings;
sz_strings_from_null_terminated_strings(words, 3, &strings);

There is no dedicated sz_string_view_t struct in the C ABI — views are passed as the (pointer, length) pairs described above. The one owning C string type is sz_string_t, documented under String Collections and Containers below.

API

Types

sz_i8_tEven when LibC is not available, compiler macros let us infer the size of integer types.
types.h:750 ↗
typedef signed char sz_i8_t;

The C standard doesn't specify the signedness of char. On x86 char is signed by default while on Arm it is unsigned by default. That's why we don't define sz_char_t and generally use explicit sz_i8_t and sz_u8_t.

See also

sz_size_tNow we need to redefine the size_t.
types.h:808 ↗
typedef sz_u32_t sz_size_t;

Microsoft Visual C++ (MSVC) typically follows the LLP64 data model on 64-bit platforms, where integers, pointers, and longs differ in size.

GCC and Clang on 64-bit Unix-like systems typically follow the LP64 model instead:

LLP64 (MSVC)LP64 (GCC, Clang)
32 bits32 bits
32 bits64 bits
64 bits64 bits
64 bits64 bits

See also

sz_u1x8_tEight booleans in one byte, item 0 in the least significant bit, the order of Arrow and cuDF validity masks and the reverse of NumKong's nk_u1x8_t.
types.h:879 ↗
typedef sz_u8_t sz_u1x8_t;
sz_bool_tSimple boolean type, until _Bool in C 99 and true and false in C 23.
types.h:900 ↗
enum sz_bool_t

Values

sz_false_k0
sz_true_k1

See also

sz_status_tOutcome of every StringZilla call that can fail: zero on success, negative when the call failed and its outputs hold no result.
types.h:1059 ↗
enum sz_status_t

Positive values are reserved for results with a caveat.

Statuses fall into four categories, with no interruptions to report. Resources: sz_bad_alloc_k, sz_missing_gpu_k, sz_device_code_mismatch_k, sz_missing_kernel_k and sz_missing_library_k. Input: sz_invalid_utf8_k, sz_contains_duplicates_k, sz_unexpected_dimensions_k, sz_device_memory_mismatch_k, sz_authentication_failed_k and sz_invalid_syntax_k. State: sz_overflow_risk_k. Defects: sz_status_unknown_k. Any verb that allocates, binds a device or touches a file may also return a Resources status. A verb's own block lists only what its contract adds.

Values

sz_success_k0For algorithms that return a status, indicates that the operation was successful.
sz_bad_alloc_k-10For algorithms that require memory allocation, indicates that the allocation failed.
sz_invalid_utf8_k-12For algorithms that require UTF8 input, indicates that the input is invalid.
sz_contains_duplicates_k-13For algorithms taking collections of unique elements, reports the presence of duplicates.
sz_overflow_risk_k-14For algorithms on large inputs, reports the need to upcast the logic to larger types.
sz_unexpected_dimensions_k-15For algorithms with multi-stage pipelines indicates input/output size mismatch.
sz_missing_gpu_k-16GPU support is missing in the library.
sz_device_code_mismatch_k-17Backend-device mismatch: e.g., GPU kernel with CPU/default executor or vice versa.
sz_device_memory_mismatch_k-18Device memory mismatch: e.g., GPU kernel requires unified/device-accessible memory.
sz_authentication_failed_k-19An authenticated decryption saw a tag that does not match the ciphertext it accompanies.
sz_missing_kernel_k-20No capability in the capability mask has this kernel.
sz_missing_library_k-21A dispatch point or finder called from a header-only build, which links no library.
sz_invalid_syntax_k-22A pattern, path or document breaking its grammar, or using a construct no automaton runs.
sz_status_unknown_k-1A sink-hole status for unknown errors.

Spellings

char const * sz_status_name(sz_status_t status);
sz_rune_length_tDescribes the length of a UTF-8 rune / character / codepoint in bytes, from 1 to 4.
types.h:1144 ↗
enum sz_rune_length_t

Values

sz_rune_invalid_k0Invalid UTF8 character.
sz_rune_1byte_k11-byte UTF8 character.
sz_rune_2bytes_k22-byte UTF8 character.
sz_rune_3bytes_k33-byte UTF8 character.
sz_rune_4bytes_k44-byte UTF8 character.

See also

sz_rune_tStores a single UTF-8 rune / character / codepoint unpacked into UTF-32.
types.h:1176 ↗
typedef sz_u32_t sz_rune_t;

The underlying numeric type holds 4 bytes, with over 4 billion possible states, but:

  • UTF-8 in its largest 4-byte form has only 3 + 6 + 6 + 6 = 21 bits of usable space, for 2 million states.
  • Unicode, in turn, has only 1'114'112 possible code points from U+0000 to U+10FFFF.
  • Of those, in Unicode 16, only 155'063 are assigned characters, just over 17 bits' worth.

That's 0.004% of the 32-bit space, so sparse data-structures suit UTF-8 oriented algorithms.

See also

sz_string_view_tTiny string-view structure.
types.h:1192 ↗
struct sz_string_view_t

It's a Plain-Old Datatype (POD) type, unlike the std::string_view.

Fields

startsz_cptr_t
lengthsz_size_t

See also

sz_byteset_tSemi-opaque bit-set for 256 possible byte values, useful for filtering and search.
types.h:1218 ↗
union sz_byteset_t

It can be filled and probed like this:

#include <stringzilla/types.h>
int main() {
    char const *alphabet = "abcdefghijklmnopqrstuvwxyz";
    sz_byteset_t byteset;
    sz_byteset_init(&byteset);
    for (sz_size_t i = 0; i < 26; ++i)
        sz_byteset_add(&byteset, alphabet[i]);
    return sz_byteset_contains(&byteset, 'a') && !sz_byteset_contains(&byteset, 'A') ? 0 : 1;
}

Fields

_u64ssz_u64_t
_u32ssz_u32_t
_u16ssz_u16_t
_u8ssz_u8_t

See also

sz_u16_vec_tHelper structure to simplify work with 16-bit words.
types.h:1343 ↗
union sz_u16_vec_t

Fields

u16sz_u16_t
u8ssz_u8_t

See also

sz_u32_vec_tHelper structure to simplify work with 32-bit words.
types.h:1352 ↗
union sz_u32_vec_t

Fields

u32sz_u32_t
i32sz_i32_t
u16ssz_u16_t
i16ssz_i16_t
u8ssz_u8_t
i8ssz_i8_t

See also

sz_u64_vec_tHelper structure to simplify work with 64-bit words.
types.h:1365 ↗
union sz_u64_vec_t

Fields

u64sz_u64_t
i64sz_i64_t
u32ssz_u32_t
i32ssz_i32_t
u16ssz_u16_t
i16ssz_i16_t
u8ssz_u8_t
i8ssz_i8_t

See also

sz_u128_vec_tHelper structure to simplify work with 128-bit registers.
types.h:1378 ↗
union sz_u128_vec_t

It can help view the contents as 8-bit, 16-bit, 32-bit, or 64-bit integers, as well as 1x XMM register.

Fields

f64ssz_f64_t
f32ssz_f32_t
u64ssz_u64_t
i64ssz_i64_t
u32ssz_u32_t
i32ssz_i32_t
u16ssz_u16_t
i16ssz_i16_t
u8ssz_u8_t
i8ssz_i8_t
sz_u256_vec_tHelper structure to simplify work with 256-bit registers.
types.h:1418 ↗
union sz_u256_vec_t

It can help view the contents as 8-bit, 16-bit, 32-bit, or 64-bit integers, as well as 2x XMM registers or 1x YMM register.

Fields

f64ssz_f64_t
f32ssz_f32_t
u64ssz_u64_t
i64ssz_i64_t
u32ssz_u32_t
i32ssz_i32_t
u16ssz_u16_t
i16ssz_i16_t
u8ssz_u8_t
i8ssz_i8_t
sz_u512_vec_tHelper structure to simplify work with 512-bit registers.
types.h:1454 ↗
union sz_u512_vec_t

It can help view the contents as 8-bit, 16-bit, 32-bit, or 64-bit integers, as well as 4x XMM registers or 2x YMM registers or 1x ZMM register.

Fields

f64ssz_f64_t
f32ssz_f32_t
u64ssz_u64_t
i64ssz_i64_t
u32ssz_u32_t
i32ssz_i32_t
u16ssz_u16_t
i16ssz_i16_t
u8ssz_u8_t
i8ssz_i8_t
u128ssz_u128_vec_t
u256ssz_u128_vec_t
sz_strings_get_tSignature of the sz_strings_view_t accessor, writing the view of every string indices[i].
types.h:1534 ↗
typedef void(*) sz_strings_get_t(void const *handle, sz_size_t const *indices, sz_size_t count, sz_string_view_t *views);

Kernels call it once per block of up to 64 indices, and it must accept any count.

sz_strings_layout_tHow a collection holds its strings, naming the member of its data union to read.
types.h:1538 ↗
enum sz_strings_layout_t

Values

sz_strings_u32_tape_k0Apache Arrow String, where string i spans [offsets[i], offsets[i + 1]) of the bytes.
sz_strings_u64_tape_k1Apache Arrow LargeString, the same with 64-bit offsets.
sz_strings_accessor_k2Strings found through a sz_strings_get_t accessor, which only the host can call.
sz_strings_view_tA read-only, ordered collection of borrowed strings, in one of three layouts.
types.h:1560 ↗
struct sz_strings_view_t

Tapes are what Apache Arrow, the Python Strings class and GPU kernels read directly. The accessor covers every other shape, from arrays of views to the nodes of a parsed tree, at one indirect call per block of strings. A slice of a tape keeps its parent's offsets, so its first offset may be above zero.

Fields

layoutsz_strings_layout_tWhich member of data describes the strings.
countsz_size_tNumber of strings.
dataunion sz_strings_view_t::@305030033365211305011023036247110352205365160207

See also

sz_strings_span_tA writable tape, the output of every collection conversion, in place or not.
types.h:1609 ↗
struct sz_strings_span_t

Its payload and offsets are two blocks of exactly the sizes they need, so both sizes follow from the offsets, which the host can always read. A zeroed span is an empty tape holding no blocks.

Fields

layoutsz_strings_layout_tEither sz_strings_u32_tape_k or sz_strings_u64_tape_k, never the accessor.
countsz_size_tNumber of strings.
dataunion sz_strings_span_t::@077141061375231343114166315312256034361144020235

Functions

sz_byteset_initInitializes a bit-set to an empty collection, meaning - all characters are banned.
types.h:1226 ↗
void sz_byteset_init(sz_byteset_t *s);
sz_byteset_init_asciiInitializes a bit-set to all ASCII characters.
types.h:1231 ↗
void sz_byteset_init_ascii(sz_byteset_t *s);
sz_byteset_add_u8Adds a character to the set and accepts unsigned integers.
types.h:1237 ↗
void sz_byteset_add_u8(sz_byteset_t *s, sz_u8_t c);
sz_byteset_addAdds a character to the set.
types.h:1240 ↗
void sz_byteset_add(sz_byteset_t *s, char c);

Consider sz_byteset_add_u8.

sz_byteset_contains_u8Checks if the set contains a given character and accepts unsigned integers.
types.h:1243 ↗
sz_bool_t sz_byteset_contains_u8(sz_byteset_t const *s, sz_u8_t c);
sz_byteset_containsChecks if the set contains a given character.
types.h:1253 ↗
sz_bool_t sz_byteset_contains(sz_byteset_t const *s, char c);

Consider sz_byteset_contains_u8.

sz_byteset_invertInverts the contents of the set, so allowed characters get disallowed, and vice versa.
types.h:1258 ↗
void sz_byteset_invert(sz_byteset_t *s);
sz_u64_bits_reverseReverse the 64 bits of value, so bit i moves to bit 63 - i: swap adjacent bits, then bit-pairs within nibbles, then nibbles within bytes, then the bytes.
types.h:2003 ↗
sz_u64_t sz_u64_bits_reverse(sz_u64_t value);

Lets an ascending-only byte-compress, like vpcompressb, pack lanes in descending order.

sz_u64_nth_set_bitBit index of the n-th (0-based) set bit of bits; clears the n lowest set bits, then ctz.
types.h:2013 ↗
int sz_u64_nth_set_bit(sz_u64_t bits, sz_size_t n);

bits must hold more than n set bits. One tested home for the per-ISA SIMD "n-th lane" locate.

sz_u64_blendSelect bits from either a or b depending on the value of mask bits.
types.h:2038 ↗
sz_u64_t sz_u64_blend(sz_u64_t a, sz_u64_t b, sz_u64_t mask);

Similar to the _mm_blend_epi16 intrinsic on x86.

See also

sz_i32_min_of_twoBranchless minimum function for two signed 32-bit integers.
types.h:2087 ↗
sz_i32_t sz_i32_min_of_two(sz_i32_t x, sz_i32_t y);
sz_i32_max_of_twoBranchless maximum function for two signed 32-bit integers.
types.h:2090 ↗
sz_i32_t sz_i32_max_of_two(sz_i32_t x, sz_i32_t y);
sz_ssize_clamp_interval_checkedClamps signed offsets to a Python-style [offset, offset + length) slice and reports whether the requested window was non-degenerate.
types.h:2155 ↗
sz_bool_t sz_ssize_clamp_interval_checked(
    sz_size_t length,
    sz_ssize_t start,
    sz_ssize_t end,
    sz_size_t *normalized_offset,
    sz_size_t *normalized_length);

Mirrors CPython's ADJUST_INDICES: negative indices count from the end, end is clamped into the string, and a window with start past end, out of range or inverted, is "empty" - exactly when str.find("") and str.rfind("") return -1.

sz_ssize_clamp_intervalClamps signed offsets in a string to a valid range.
types.h:2174 ↗
void sz_ssize_clamp_interval(
    sz_size_t length,
    sz_ssize_t start,
    sz_ssize_t end,
    sz_size_t *normalized_offset,
    sz_size_t *normalized_length);

Used for Pythonic-style slicing.

Notes

Thin wrapper over sz_ssize_clamp_interval_checked for callers ignoring validity.

sz_size_log2i_nonzeroCompute the logarithm base 2 of a positive integer, rounding down.
types.h:2183 ↗
sz_size_t sz_size_log2i_nonzero(sz_size_t x);

Preconditions

Input must be a positive number, as the logarithm of zero is undefined.

sz_size_divide_round_upComputes the ceiling of x divided by divisor - the number of chunks of that size needed to cover x.
types.h:2191 ↗
sz_size_t sz_size_divide_round_up(sz_size_t x, sz_size_t divisor);

Assumes a non-zero divisor and no overflow on x + divisor.

sz_u32_divide_round_upDivides rounding up in 32 bits, for device code where the sz_size_t form costs registers.
types.h:2196 ↗
sz_u32_t sz_u32_divide_round_up(sz_u32_t x, sz_u32_t divisor);
sz_size_bit_ceilCompute the smallest power of two greater than or equal to x.
types.h:2206 ↗
sz_size_t sz_size_bit_ceil(sz_size_t x);

Notes

Uses LZCNT/CLZ for efficient computation on modern CPUs. Edge cases: bit_ceil(0) = 0, bit_ceil(1) = 1.

See also

Rounding up to a power of two: https://stackoverflow.com/a/10143264

sz_u64_transposeTransposes an 8×8 bit matrix packed in a sz_u64_t.
types.h:2240 ↗
sz_u64_t sz_u64_transpose(sz_u64_t x);

There is a well known SWAR sequence for that known to chess programmers, willing to flip a bit-matrix of pieces along the main A1-H8 diagonal.

See also

sz_strings_from_null_terminated_stringsDescribes an array of NUL-terminated strings, like char const *argv[].
types.h:2634 ↗
void sz_strings_from_null_terminated_strings(
    sz_cptr_t const *start,
    sz_size_t count,
    sz_strings_view_t *strings);

Parameters

startinThe array of strings, which must outlive strings.
countinNumber of strings in the array.
stringsoutThe accessor-backed collection to initialize.
sz_strings_from_string_viewsDescribes an array of pointer-length pairs, like a list of borrowed string views.
types.h:2642 ↗
void sz_strings_from_string_views(
    sz_string_view_t const *views,
    sz_size_t count,
    sz_strings_view_t *strings);

Parameters

viewsinThe array of views, which must outlive strings.
countinNumber of views in the array.
stringsoutThe accessor-backed collection to initialize.
sz_strings_sliceSelects the count strings at start + i * step, copying no string bytes.
types.h:2674 ↗
void sz_strings_slice(
    sz_strings_view_t const *strings,
    sz_size_t start,
    sz_ssize_t step,
    sz_size_t count,
    sz_string_view_t *views,
    sz_strings_view_t *slice);

A step of one over a tape yields a tape over the same bytes and offsets, leaving views untouched. Every other selection fills views and points the accessor of slice at them.

Parameters

stringsinThe collection to slice, which must outlive slice.
startinIndex of the first selected string.
stepinDistance between selected indices, negative to walk backward.
countinNumber of strings to select.
viewsoutRoom for count views, or null for a step of one over a tape.
sliceoutThe selected strings.
sz_strings_from_matchesViews the spans a substrings, regex or subdocuments search located, copying no bytes.
types.h:2699 ↗
void sz_strings_from_matches(
    sz_strings_view_t const *haystacks,
    sz_match_t const *matches,
    sz_size_t count,
    sz_string_view_t *views,
    sz_strings_view_t *strings);

Parameters

haystacksinThe collection the matches were found in, which must outlive strings.
matchesinThe located matches, in any order.
countinNumber of matches.
viewsoutReceives the bytes of matches[i] inside its haystack for each match.
stringsoutPoints its accessor at views.
sz_strings_from_spanViews the tape of span, so the output of one conversion can feed the next.
types.h:2715 ↗
void sz_strings_from_span(
    sz_strings_span_t const *span,
    sz_strings_view_t *strings);

Parameters

spaninThe writable tape to read, which must outlive strings.
stringsoutThe read-only collection to initialize.
sz_strings_span_freeReturns both blocks of span to the allocator they came from, leaving it zeroed.
types.h:2726 ↗
void sz_strings_span_free(
    sz_strings_span_t *span,
    sz_allocator_t *allocator,
    sz_stream_t stream);

Parameters

spaninoutThe tape to release, whose offsets the host reads to size both blocks.
allocatorinThe allocator both blocks came from.
streaminNull on the CPU, or the GPU stream the release waits behind.

Macros

STRINGZILLA_U8_MAXLargest value that fits into 8 bits.
types.h:334 ↗
#define STRINGZILLA_U8_MAX (255u)
STRINGZILLA_U16_MAXLargest value that fits into 16 bits.
types.h:337 ↗
#define STRINGZILLA_U16_MAX (65535u)
STRINGZILLA_U16_MAX_PRIMELargest prime number that fits into 16 bits.
types.h:340 ↗
#define STRINGZILLA_U16_MAX_PRIME (65521u)
STRINGZILLA_U32_MAX_PRIMELargest prime number that fits into 31 bits.
types.h:343 ↗
#define STRINGZILLA_U32_MAX_PRIME (2147483647u)
STRINGZILLA_U64_MAX_PRIMELargest prime number that fits into 64 bits.
types.h:356 ↗
#define STRINGZILLA_U64_MAX_PRIME (18446744073709551557ull)
2⁶⁴= 18,446,744,073,709,551,616
this= 18,446,744,073,709,551,557
diff= 59

See also

STRINGZILLA_NULLDefines STRINGZILLA_NULL, analogous to NULL.
types.h:831 ↗
#define STRINGZILLA_NULL ((void *)0)

The default often comes from locale.h, stddef.h, stdio.h, stdlib.h, string.h, time.h, or wchar.h.

sz_min_of_twoEfficiently computing the minimum and maximum of two or three values can be tricky.
types.h:2057 ↗
#define sz_min_of_two(x, y) ((x) <(y) ? (x) : (y))

A branchless approach is well known for signed integers, but it doesn't apply to unsigned ones. The simple branching baseline comes first, then the shift-only form for signed integers, then two forms for any integers, with and without multiplication:

x < y ? x : ycan replace with 1 conditional move
y + ((x - y) & (x - y) >> 31)4 unique operations
(x > y) * y + (x <= y) * x5 operations
x & ~((x < y) - 1) + y & ((x < y) - 1)6 unique operations

See also