C · Sorting and Sets

Sorting and Sets

#include <stringzilla/types.h>#include <stringzilla/capabilities.h>#include <stringzilla/strings.h>#include <stringzilla/hash.h>#include <stringzilla/sort.h>#include <stringzilla/intersect.h> 6.0.0-rc.1 · 91a27bdd

These operate on a read-only sz_strings_view_t in any layout and never reorder the underlying data — they fill an order permutation array instead. Sorting is stable, so equal elements keep their input order, supports descending order via a reverse flag, and supports partial / top-K sorting via top_count — pass 0 to sort everything. Every allocation these kernels need is routed through the sz_allocator_t you supply — pass NULL for the default malloc-based one — so, unlike std::sort or thrust::sort, no scratch memory is ever allocated behind your back.

The C API fills a caller-owned order array and reports success through a sz_status_t:

sz_status_t sz_sort_order_best(
    sz_strings_view_t const *strings, sz_size_t top_count, sz_bool_t reverse,
    sz_allocator_t *allocator, sz_size_t *order,
    sz_capability_t capabilities, sz_stream_t stream);

sz_status_t sz_sort_uncased_order_best(
    sz_strings_view_t const *strings, sz_size_t top_count, sz_bool_t reverse,
    sz_allocator_t *allocator, sz_size_t *order,
    sz_capability_t capabilities, sz_stream_t stream);

sz_sort_order_best orders byte-lexicographically; sz_sort_uncased_order_best orders under Unicode case-folding, folding small chunks on the fly, and malformed UTF-8 sorts by raw byte value so the order stays total and deterministic. The allocator argument may be NULL to use the default allocator.

#include <stringzilla/stringzilla.h>

int main(void) {
    sz_capability_t capabilities = sz_cap_serial_k;
    sz_capabilities_enabled_cpu(&capabilities);

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

    sz_size_t order[3];
    sz_status_t status = sz_sort_order_best(&strings, /*top_count*/ 0, sz_false_k, NULL, order, capabilities, NULL);
    assert(status == sz_success_k);
    assert(order[0] == 1 && order[1] == 0 && order[2] == 2); // apple, banana, cherry
    return 0;
}

§ Set Intersection

sz_intersect_best computes the intersection of two deduplicated binary string collections using a hash table, writing matched positions from each side and leaving unmatched slots as STRINGZILLA_SIZE_MAX:

sz_status_t sz_intersect_best(
    sz_strings_view_t const *first_strings, sz_strings_view_t const *second_strings,
    sz_allocator_t *allocator, sz_u64_t seed, sz_size_t *intersection_count,
    sz_size_t *first_positions, sz_size_t *second_positions,
    sz_capability_t capabilities, sz_stream_t stream);

The seed randomizes the hash table to resist adversarial inputs; the position arrays must each fit at least min(first->count, second->count) entries. The companion sz_join_semantics_t enum documents the SQL-style JOIN modes the family is designed around, namely sz_join_inner_strict_k, sz_join_inner_k, sz_join_left_outer_k, sz_join_right_outer_k, sz_join_full_outer_k, and sz_join_cross_k.

char const *first[]  = {"banana", "apple", "cherry"};
char const *second[] = {"cherry", "orange", "pineapple", "banana"};
sz_strings_view_t a, b;
sz_strings_from_null_terminated_strings(first, 3, &a);
sz_strings_from_null_terminated_strings(second, 4, &b);

sz_size_t intersection_count;
sz_size_t a_positions[3], b_positions[3];  // sized to the smaller collection
sz_status_t status = sz_intersect_best(&a, &b, NULL, /*seed*/ 0, &intersection_count, a_positions, b_positions,
                                       capabilities, NULL);
assert(intersection_count == 2); // "banana" and "cherry"

§ String Collections and Containers

Collections are read through sz_strings_view_t in one of three layouts: an Apache Arrow String tape with 32-bit offsets, a LargeString tape with 64-bit offsets, or an accessor that writes the views of up to 64 strings per call. Accessors describe arrays of views, NUL-terminated pointers, struct members, record pointers and NUL-padded rows without copying a byte, while tapes are what Arrow and the GPU kernels read directly. Every conversion takes a view, an optional array of indices that reorders, samples or repeats it, and a count, and writes a writable sz_strings_span_t tape:

sz_status_t sz_strings_to_tape_best(sz_strings_view_t const *strings, sz_size_t const *order, sz_size_t count,
                                    sz_allocator_t *allocator, sz_strings_span_t *tape,
                                    sz_capability_t capabilities, sz_stream_t stream);
sz_status_t sz_strings_escape_best(sz_strings_view_t const *strings, sz_size_t const *order, sz_size_t count,
                                   sz_escape_kind_t kind, sz_allocator_t *allocator, sz_strings_span_t *tape,
                                   sz_capability_t capabilities, sz_stream_t stream);
sz_status_t sz_strings_unescape_best(sz_strings_view_t const *strings, sz_size_t const *order, sz_size_t count,
                                     sz_escape_kind_t kind, sz_allocator_t *allocator, sz_strings_span_t *tape,
                                     sz_size_t *malformed_index, sz_capability_t capabilities, sz_stream_t stream);
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);
sz_status_t sz_strings_lookup_best(sz_strings_view_t const *strings, sz_size_t const *order, sz_size_t count,
                                   char const lut[256], sz_allocator_t *allocator, sz_strings_span_t *tape,
                                   sz_capability_t capabilities, sz_stream_t stream);
sz_status_t sz_strings_to_rows_best(sz_strings_view_t const *strings, sz_size_t const *order, sz_size_t count,
                                    sz_strings_rows_t const *rows, sz_size_t *longest,
                                    sz_capability_t capabilities, sz_stream_t stream);

Every conversion is atomic: it succeeds with the whole result in the tape, or fails and leaves the tape, its blocks and their bytes exactly as they were, whether an allocation was refused or a string was malformed. A zeroed span receives fresh blocks from the allocator, blocks that already have the result’s sizes are written in place, and any other span’s old blocks go back only once the result is written. The allocator’s reallocate is an optional fast path; without it, a block that must change size moves through allocate, memcpy and free. Escaping covers JSON, JSON in ASCII the way Python’s json.dumps writes it, RFC 4180 CSV, hexadecimal, HTML, URL percent-encoding, and terminal-safe logs, and unescaping reports the first malformed string. Normalization brings every string into NFC, NFD, NFKC, NFKD or NFKC_Casefold, and NUL-padded rows serve NumPy S arrays and fixed-width record fields. A single buffer is a tape of one string, so lowercasing in place looks like this:

#include <stringzilla/stringzilla.h>

int main(void) {
    sz_capability_t capabilities = sz_cap_serial_k;
    sz_capabilities_enabled_cpu(&capabilities);

    char to_lower[256];
    sz_strings_lookup_init_lower(to_lower);
    char buffer[3] = {'A', 'B', 'C'};
    sz_u32_t offsets[2] = {0, 3};
    sz_strings_span_t tape;
    tape.layout = sz_strings_u32_tape_k, tape.count = 1;
    tape.data.u32_tape.data = buffer, tape.data.u32_tape.offsets = offsets;
    sz_strings_view_t strings;
    sz_strings_from_span(&tape, &strings);
    sz_strings_lookup_best(&strings, NULL, 1, to_lower, NULL, &tape, capabilities, NULL);
    assert(buffer[0] == 'a' && buffer[1] == 'b' && buffer[2] == 'c'); // "abc"
    return 0;
}

§ The sz_string_t SSO Container

sz_string_t is a tiny memory-owning string with Small-String-Optimization: short strings, up to 22 bytes plus the terminator on 64-bit, live inline with no heap allocation, while longer strings spill to the heap. Its length can be read branchlessly, and many length changes avoid branches entirely. Every mutating call takes an explicit sz_allocator_t *.

Lifecycle and read-only access:

void sz_string_init(sz_string_t *string);
sz_ptr_t sz_string_init_length(sz_string_t *string, sz_size_t length, sz_allocator_t *allocator);
sz_bool_t sz_string_is_on_stack(sz_string_t const *string);
void sz_string_unpack(sz_string_t const *string, sz_ptr_t *start, sz_size_t *length, sz_size_t *space, sz_bool_t *is_external);
void sz_string_range(sz_string_t const *string, sz_ptr_t *start, sz_size_t *length);
sz_size_t sz_string_length(sz_string_t const *string);
sz_bool_t sz_string_equal(sz_string_t const *a, sz_string_t const *b);
sz_ordering_t sz_string_order(sz_string_t const *a, sz_string_t const *b);

Growth and mutation, where all but sz_string_erase may allocate and return STRINGZILLA_NULL_CHAR on failure:

sz_ptr_t sz_string_reserve(sz_string_t *string, sz_size_t new_capacity, sz_allocator_t *allocator);
sz_ptr_t sz_string_expand(sz_string_t *string, sz_size_t offset, sz_size_t added_length, sz_allocator_t *allocator);
sz_size_t sz_string_erase(sz_string_t *string, sz_size_t offset, sz_size_t length);
sz_ptr_t sz_string_shrink_to_fit(sz_string_t *string, sz_allocator_t *allocator);
void sz_string_free(sz_string_t *string, sz_allocator_t *allocator);

sz_string_expand opens an uninitialized gap of added_length at offset that you then populate, often via memcpy; sz_string_erase removes a range without ever allocating and cannot fail.

#include <string.h>
#include <stringzilla/stringzilla.h>

int main(void) {
    sz_allocator_t allocator;
    if (sz_allocator_init_heap(&allocator) != sz_success_k) return 1;

    sz_string_t s;
    sz_string_init(&s);

    sz_ptr_t room = sz_string_expand(&s, 0, 5, &allocator);  // 5-byte gap at offset 0
    if (!room) return 1;
    memcpy(room, "hello", 5);

    sz_ptr_t start; sz_size_t length;
    sz_string_range(&s, &start, &length);
    assert(length == 5);

    sz_string_free(&s, &allocator);
    return 0;
}

API

Types

sz_strings_crecords_tWhere every record of an array keeps its string, the handle of sz_strings_from_records and sz_strings_from_record_pointers.
types.h:1644 ↗
struct sz_strings_crecords_t

Fields

recordsvoid const *The first record, or the first pointer to a record.
stridesz_size_tBytes between consecutive records, or between consecutive record pointers.
start_membersz_size_tByte offset of the sz_cptr_t member pointing at the string inside a record.
length_membersz_size_tByte offset of the sz_size_t member holding the string length inside a record.
sz_strings_crows_tRead-only NUL-padded rows, like NumPy S arrays or char fields of structs, each string ending before its row's trailing NUL bytes.
types.h:1661 ↗
struct sz_strings_crows_t

Fields

datasz_cptr_tThe first row.
stridesz_size_tBytes between the starts of consecutive rows, at least width.
widthsz_size_tBytes in every row.
sz_strings_rows_tWritable NUL-padded rows, laid out like sz_strings_crows_t.
types.h:1674 ↗
struct sz_strings_rows_t

Fields

datasz_ptr_tThe first row.
stridesz_size_tBytes between the starts of consecutive rows, at least width.
widthsz_size_tBytes in every row.
sz_kernel_fill_random_tSignature of the sz_fill_random_best kernels.
capabilities.h:756 ↗
typedef sz_status_t(*) sz_kernel_fill_random_t(sz_ptr_t, sz_size_t, sz_u64_t, sz_stream_t);
sz_kernel_strings_to_tape_tSignature of the sz_strings_to_tape_best kernels.
capabilities.h:836 ↗
typedef sz_status_t(*) sz_kernel_strings_to_tape_t(sz_strings_view_t const *, sz_size_t const *, sz_size_t, sz_allocator_t *, sz_strings_span_t *, sz_stream_t);
sz_kernel_strings_to_rows_tSignature of the sz_strings_to_rows_best kernels.
capabilities.h:840 ↗
typedef sz_status_t(*) sz_kernel_strings_to_rows_t(sz_strings_view_t const *, sz_size_t const *, sz_size_t, sz_strings_rows_t const *, sz_size_t *, sz_stream_t);
sz_kernel_sort_order_tSignature of the sz_sort_order_best and sz_sort_uncased_order_best kernels.
capabilities.h:866 ↗
typedef sz_status_t(*) sz_kernel_sort_order_t(sz_strings_view_t const *, sz_size_t, sz_bool_t, sz_allocator_t *, sz_size_t *, sz_stream_t);
sz_kernel_intersect_tSignature of the sz_intersect_best kernels.
capabilities.h:873 ↗
typedef sz_status_t(*) sz_kernel_intersect_t(sz_strings_view_t const *, sz_strings_view_t const *, sz_allocator_t *, sz_u64_t, sz_size_t *, sz_size_t *, sz_size_t *, sz_stream_t);
sz_escape_kind_tThe text formats that the escape and unescape families convert strings to and from.
strings.h:43 ↗
enum sz_escape_kind_t

Values

sz_escape_json_k0RFC 8259 string bodies: \", \\, short forms like \n, and \u00XX for other bytes below 0x20.
sz_escape_hex_k1Two lowercase hexadecimal digits per byte, decoding either case.
sz_escape_html_k2HTML and XML text and attribute values: &amp;, &lt;, &gt;, &quot; and &#39;.
sz_escape_url_k3RFC 3986 percent-encoding of every byte outside A-Z a-z 0-9 - _ ~ and the dot.
sz_escape_console_k4Terminal-safe logs: short forms like \n, \\ and \", \xHH for other C0 control bytes, DEL and ill-formed UTF-8, and \uXXXX for C1 controls and the bidirectional overrides U+202A to U+202E and U+2066 to U+2069, which reorder what a reader sees.
sz_escape_csv_k5RFC 4180 fields, quoted only when holding a comma, a quote, CR or LF, with every inner quote doubled.
sz_escape_json_ascii_k6RFC 8259 string bodies in ASCII alone, as Python's json.dumps writes them by default: every codepoint outside printable ASCII without a short form, DEL included, becomes \uXXXX, a surrogate pair beyond the Basic Multilingual Plane, and every ill-formed byte becomes the escape of U+FFFD.
sz_kernel_strings_escape_tSignature of the sz_strings_escape_best kernels, kept beside the enum it takes.
strings.h:75 ↗
typedef sz_status_t(*) sz_kernel_strings_escape_t(sz_strings_view_t const *, sz_size_t const *, sz_size_t, sz_escape_kind_t, sz_allocator_t *, sz_strings_span_t *, sz_stream_t);
sz_kernel_strings_unescape_tSignature of the sz_strings_unescape_best kernels, kept beside the enum it takes.
strings.h:79 ↗
typedef sz_status_t(*) sz_kernel_strings_unescape_t(sz_strings_view_t const *, sz_size_t const *, sz_size_t, sz_escape_kind_t, sz_allocator_t *, sz_strings_span_t *, sz_size_t *, sz_stream_t);
sz_join_semantics_tDefines various JOIN semantics for string collections, including handling of duplicates.
intersect.h:85 ↗
enum sz_join_semantics_t

Values

sz_join_inner_strict_k0Strict inner join with uniqueness enforcement.
sz_join_inner_k1Conventional inner join allowing duplicate entries.
sz_join_left_outer_k2Left outer join preserving all entries from the first sequence.
sz_join_right_outer_k3Right outer join preserving all entries from the second sequence.
sz_join_full_outer_k4Full outer join combining all entries from both sequences.
sz_join_cross_k5Cross join (Cartesian product) of two sequences.

See also

Functions

sz_strings_from_recordsDescribes the string members of fixed-stride records, like an array of structs that each hold a pointer and a length.
types.h:2650 ↗
void sz_strings_from_records(
    sz_strings_crecords_t const *records,
    sz_size_t count,
    sz_strings_view_t *strings);

Parameters

recordsinWhere the members sit, which must outlive strings.
countinNumber of records.
stringsoutThe accessor-backed collection to initialize.
sz_strings_from_record_pointersDescribes the string members of records reached through an array of pointers, like the nodes a query over a parsed tree returns.
types.h:2658 ↗
void sz_strings_from_record_pointers(
    sz_strings_crecords_t const *records,
    sz_size_t count,
    sz_strings_view_t *strings);

Parameters

recordsinWhere the pointers and members sit, which must outlive strings.
countinNumber of record pointers.
stringsoutThe accessor-backed collection to initialize.
sz_strings_from_rowsDescribes NUL-padded rows, trimming the trailing NUL bytes of every row.
types.h:2666 ↗
void sz_strings_from_rows(
    sz_strings_crows_t const *rows,
    sz_size_t count,
    sz_strings_view_t *strings);

Parameters

rowsinWhere the rows sit, which must outlive strings.
countinNumber of rows.
stringsoutThe accessor-backed collection to initialize.
sz_strings_permuteReorders strings by the order sz_sort_order_best emits, copying no bytes.
types.h:2693 ↗
void sz_strings_permute(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_string_view_t *views,
    sz_strings_view_t *permuted);

Parameters

stringsinThe collection to reorder, which must outlive permuted.
orderinIndices into strings, possibly fewer than all of them.
countinNumber of indices in order.
viewsoutReceives the view of strings[order[i]] for each index.
permutedoutPoints its accessor at views.
sz_strings_to_tape_bestGathers strings[order[i]], or the first count strings without order, into a tape.
strings.h:130 ↗
sz_status_t sz_strings_to_tape_best(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_allocator_t *allocator,
    sz_strings_span_t *tape,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_strings_to_tapeserialhaswellicelakeneonsvervvv128v128relaxedloongsonasxpowervsxcudarocmmetal

Reads any layout, so it turns accessor-backed strings, slices and permutations into a tape that GPU kernels and Apache Arrow read directly. The payload and the offsets are two blocks, exactly as large as they must be, with 32-bit offsets while the payload fits in 4 GiB and 64-bit beyond.

Every collection verb is atomic: it succeeds and leaves tape holding the whole result, or it fails and leaves tape, its blocks and their bytes as they were. Blocks of exactly the sizes the result needs are written in place, so a borrowed span of the right sizes needs no allocator; any other tape receives fresh blocks from allocator, and its old ones go back only once the result is written. A whole tape gathered onto itself is left as it is.

Parameters

stringsinThe strings to gather, in any layout.
orderinIndices into strings, possibly repeated, or null for the first count.
countinNumber of strings to write.
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.

Notes

A GPU call reads the payload size back before allocating, synchronizing stream once.

sz_strings_to_rows_bestWrites strings[order[i]], or the first count strings, into NUL-padded rows.
strings.h:248 ↗
sz_status_t sz_strings_to_rows_best(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_strings_rows_t const *rows,
    sz_size_t *longest,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_strings_to_rowsserialhaswellicelakeneonsvervvv128v128relaxedloongsonasxpowervsxcudarocmmetal

Parameters

stringsinThe strings to write, in any layout.
orderinIndices into strings, possibly repeated, or null for the first count.
countinNumber of rows to write.
rowsinRoom for count rows, each truncating a longer string at its width.
longestoutLength of the longest string, to retry with wider rows.
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_strings_escape_bestEscapes strings[order[i]], or the first count strings, for kind into a tape.
strings.h:369 ↗
sz_status_t sz_strings_escape_best(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_escape_kind_t kind,
    sz_allocator_t *allocator,
    sz_strings_span_t *tape,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_strings_escapeserialhaswellicelakeneonsvervvv128v128relaxedloongsonasxpowervsxcudarocmmetal

Allocates and reuses tape like sz_strings_to_tape_best. When strings views all of tape, its payload block grows first, through reallocate when the allocator has one, then its bytes move to the end and escape front to back, the writer never overtaking unread bytes.

Parameters

stringsinThe strings to escape, in any layout.
orderinIndices into strings, possibly repeated, or null for the first count.
countinNumber of strings to write.
kindinThe format to escape for.
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.
sz_strings_unescape_bestUnescapes strings[order[i]], or the first count strings, from kind into a tape.
strings.h:497 ↗
sz_status_t sz_strings_unescape_best(
    sz_strings_view_t const *strings,
    sz_size_t const *order,
    sz_size_t count,
    sz_escape_kind_t kind,
    sz_allocator_t *allocator,
    sz_strings_span_t *tape,
    sz_size_t *malformed_index,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_strings_unescapeserialhaswellicelakeneonsvervvv128v128relaxedloongsonasxpowervsxcudarocmmetal

Allocates and reuses tape like sz_strings_to_tape_best. Every string is checked before anything is written, so one malformed string leaves tape as it was.

Parameters

stringsinThe strings to unescape, in any layout.
orderinIndices into strings, possibly repeated, or null for the first count.
countinNumber of strings to write.
kindinThe format to unescape from.
allocatorinWhere the blocks of tape come from and return to.
tapeinoutA zeroed span, one an earlier call produced, or the one strings views.
malformed_indexoutThe first string holding a malformed sequence, on failure.
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_strings_find_kernelFinds the strings kernel of kind, from the best of capabilities.
strings.h:1060 ↗
sz_status_t sz_strings_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_fill_random_bestA Pseudorandom Number Generator (PRNG), inspired by the AES-CTR-128 algorithm, but using only one round of AES mixing as opposed to "NIST SP 800-90A".
hash.h:285 ↗
sz_status_t sz_fill_random_best(
    sz_ptr_t target,
    sz_size_t length,
    sz_u64_t nonce,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_fill_randomserialwestmereskylakeicelakeneonaessve2aesrvvrvvcryptov128v128relaxedloongsonasxpowervsx

CTR_DRBG (CounTeR mode Deterministic Random Bit Generator) appears secure and indistinguishable from a true random source when AES is used as the underlying block cipher and 112 bits are taken from this PRNG. When AES is used as the underlying block cipher and 128 bits are taken from each instantiation, the required security level is delivered with the caveat that a 128-bit cipher's output in counter mode can be distinguished from a true RNG.

In this case, it doesn't apply, as we only use one round of AES mixing. We also don't expose a separate "key", only a "nonce", to keep the API simple, but we mix it with 512 bits of Pi constants to increase randomness.

Filling two buffers under the same nonce:

#include <stringzilla/stringzilla.h>
int main() {
    sz_capability_t capabilities;
    char first_buffer[5], second_buffer[5];
    sz_u64_t first_checksum, second_checksum;
    sz_capabilities_enabled_cpu(&capabilities);
    sz_fill_random_best(first_buffer, 5, 0, capabilities, NULL);
    sz_fill_random_best(second_buffer, 5, 0, capabilities, NULL); // ? Same nonce, same output
    sz_bytesum_best(first_buffer, 5, &first_checksum, capabilities, NULL);
    sz_bytesum_best(second_buffer, 5, &second_checksum, capabilities, NULL);
    return first_checksum == second_checksum ? 0 : 1;
}

Parameters

targetoutOutput string buffer to be populated.
lengthinNumber of bytes in the string.
noncein"Number used once" to ensure uniqueness of produced blocks.
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

sz_fill_random_serial, sz_fill_random_westmere, sz_fill_random_skylake, sz_fill_random_icelake, sz_fill_random_neonaes, sz_fill_random_sve2aes, sz_fill_random_rvv, sz_fill_random_rvvcrypto, sz_fill_random_v128, sz_fill_random_v128relaxed, sz_fill_random_loongsonasx, sz_fill_random_powervsx

Statuses

sz_sort_order_bestFaster stable arg-sort for an arbitrary string collection, using QuickSort.
sort.h:81 ↗
sz_status_t sz_sort_order_best(
    sz_strings_view_t const *strings,
    sz_size_t top_count,
    sz_bool_t reverse,
    sz_allocator_t *allocator,
    sz_size_t *order,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_sort_orderserialhaswellskylakesveneonrvv

Outputs the order of elements in the immutable strings, that would sort them.

For example, sorting three fruit names:

#include <stringzilla/stringzilla.h>
int main() {
    char const *fruits[] = {"banana", "apple", "cherry"};
    sz_strings_view_t strings;
    sz_strings_from_null_terminated_strings(fruits, 3, &strings);
    sz_capability_t capabilities;
    sz_capabilities_enabled_cpu(&capabilities);
    sz_size_t order[3];
    sz_status_t status =
        sz_sort_order_best(&strings, 0, sz_false_k, NULL, order, capabilities, NULL);
    return status == sz_success_k && order[0] == 1 && order[1] == 0 && order[2] == 2 ? 0 : 1;
}

Parameters

stringsinImmutable collection of strings to sort, in any layout.
top_countinNumber of leading elements to fully order, or 0 to sort all of them.
reverseinWhether to sort in descending order.
allocatorinOptional memory allocator for temporary storage.
orderoutOutput permutation that sorts the elements.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Preconditions

The order array must fit at least as many integers as strings holds.

Postconditions

  • The order array will contain a valid permutation of all indices of strings.
  • If top_count is non-zero and smaller than the count, only the first top_count entries are sorted; the rest are an arbitrary permutation of the leftover indices.

Notes

  • Takes linear memory, quadratic worst-case and log-linear average time.
  • This algorithm is stable: equal elements keep their relative order, ascending by index.

See also

Statuses

sz_sort_uncased_order_bestFaster stable uncased arg-sort for a UTF-8 string collection, using QuickSort.
sort.h:113 ↗
sz_status_t sz_sort_uncased_order_best(
    sz_strings_view_t const *strings,
    sz_size_t top_count,
    sz_bool_t reverse,
    sz_allocator_t *allocator,
    sz_size_t *order,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_sort_uncased_orderserialhaswellskylakesveneonrvv

Orders strings under Unicode case-folding, equivalent to folding every string and sorting the folded bytes lexicographically - but folding only small chunks on the fly, never materializing a fully pre-folded copy of the collection.

Malformed UTF-8 is handled losslessly: any byte that does not begin a well-formed codepoint is compared and sorted by its raw byte value as a single one-byte unit, and decoding resyncs at the next byte. The result stays a total, deterministic order; valid input sorts byte-identically.

Parameters

stringsinImmutable collection of UTF-8 strings to sort, in any layout.
top_countinNumber of leading elements to fully order, or 0 to sort all of them.
reverseinWhether to sort in descending order.
allocatorinOptional memory allocator for temporary storage.
orderoutOutput permutation that sorts the elements.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Preconditions

The order array must fit at least as many integers as strings holds.

Postconditions

The order array will contain a valid permutation of all indices of strings.

Notes

This algorithm is stable: case-folded-equal elements keep their input order.

See also

  • sz_utf8_uncased_fold, sz_utf8_uncased_order
  • sz_sort_uncased_order_serial

Statuses

sz_sort_find_kernelFinds the sorting kernel of kind, from the best of capabilities.
sort.h:205 ↗
sz_status_t sz_sort_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_intersect_bestIntersects two binary string collections, using a hash table.
intersect.h:74 ↗
sz_status_t sz_intersect_best(
    sz_strings_view_t const *first_strings,
    sz_strings_view_t const *second_strings,
    sz_allocator_t *allocator,
    sz_u64_t seed,
    sz_size_t *intersection_count,
    sz_size_t *first_positions,
    sz_size_t *second_positions,
    sz_capability_t capabilities,
    sz_stream_t stream);

Backends

  • sz_intersectserialwestmereicelakeneonaes

Outputs the first_positions from the first_strings and second_positions from the second_strings, that contain matched strings. Missing matches are represented as STRINGZILLA_SIZE_MAX. Tolerates duplicate strings within either collection: each distinct shared value is emitted exactly once, a distinct-set intersection, so intersection_count never exceeds the smaller of the two counts and can't overflow the output arrays.

For example, intersecting two small collections of fruit names:

#include <stringzilla/stringzilla.h>
int main() {
    char const *first[] = {"banana", "apple", "cherry"};
    char const *second[] = {"cherry", "orange", "pineapple", "banana"};
    sz_strings_view_t first_strings, second_strings;
    sz_strings_from_null_terminated_strings(first, 3, &first_strings);
    sz_strings_from_null_terminated_strings(second, 4, &second_strings);
    sz_capability_t capabilities;
    sz_capabilities_enabled_cpu(&capabilities);
    sz_size_t intersection_count;
    sz_size_t first_positions[3], second_positions[3]; //? The smaller collection's size
    sz_status_t status = sz_intersect_best(&first_strings, &second_strings, NULL, 0,
        &intersection_count, first_positions, second_positions, capabilities, NULL);
    return status == sz_success_k && intersection_count == 2 ? 0 : 1;
}

Parameters

first_stringsinFirst immutable collection of strings to intersect, in any layout.
second_stringsinSecond immutable collection of strings to intersect, in any layout.
allocatorinOptional memory allocator for temporary storage.
seedinOptional seed for the hash table to avoid attacks.
intersection_countoutNumber of matching strings in both collections.
first_positionsoutOffset positions of the matching strings from the first_strings.
second_positionsoutOffset positions of the matching strings from second_strings.
capabilitiesinOne device's capabilities, like sz_capabilities_enabled_cpu reports.
streaminNull on the CPU, or the GPU stream of that device to queue on.

Preconditions

  • The first_positions array must fit as many items as the smaller collection holds.
  • The second_positions array must fit as many items as the smaller collection holds.

Notes

The algorithm has linear memory complexity and linear time complexity.

See also

Statuses

sz_intersect_find_kernelFinds the intersection kernel of kind, from the best of capabilities.
intersect.h:286 ↗
sz_status_t sz_intersect_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