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.
Matching the all-noexcept surface, the C++ sort takes any container plus a string-extractor callable and writes the permutation into a caller-owned span, returning a status_t:
The outputs are sized std::span<sz_size_t> views over caller-owned storage, and the scratch memory comes from allocator.
Size order to container.size() — the whole span receives a permutation, and a non-zero top_count only requests that the first top_count entries be fully sorted.
Size each position span to min(first.size(), second.size()); a shorter span returns unexpected_dimensions_k. intersect returns the number of matched pairs as an expected<size_t>, so there is no separate count out-parameter.
Without output spans, sz::argsort, sz::argsort_utf8_uncased and sz::intersect allocate their results from the allocator passed last and return them in an expected, never throwing.
In C++, sz::string_t is the owning, SSO container and sz::string_span_t is its mutable view; both rebuild the std::string surface, and nothing in them throws.
Mutating members (append, push_back, insert, erase, replace, resize, reserve, shrink_to_fit, clear, swap, assign) are present, alongside data/c_str/size/capacity and the relational operators.
Every allocating verb returns a sz::status_t, or an sz::expected beside an iterator, and insert_at, erase_slice and replace_slice take signed offsets counted from the end, like Python’s slices.
Copies and conversions come from copy and make, which return an sz::expected, and positions past the end clamp to it.
#include <stringzilla/stringzilla.hpp>namespace sz = ashvardanian::stringzilla;int main() { sz::expected<sz::string_t> made = sz::string_t::make("hello"); if (!made) return 1; sz::string_t greeting = std::move(made.value); if (sz::failed(greeting.append(", world"))) return 1; assert(greeting == "hello, world"); if (sz::failed(greeting.push_back('!'))) return 1; // allocating verbs return a status if (sz::failed(greeting.reserve(1024))) return 1; if (sz::failed(greeting.replace_all("l", "L"))) return 1; assert(greeting.starts_with("heLLo")); return 0;}
Collections pair the same way: sz::strings_view_t borrows an Arrow String or LargeString array from (data, offsets, count), a span of sz::string_view_t, or an owned column, and sz::strings_t owns one.
Its conversions, gather, escape, unescape, lookup and normalize, return a fresh sz::strings inside an sz::expected with the status, and leave nothing allocated on failure. find, hashes and to_rows write into spans the caller owns.
Every one takes a capability mask, sz::default_capabilities() unless given, and a stream, like the C dispatch points.
#include <stringzilla/stringzilla.hpp>namespace sz = ashvardanian::stringzilla;int main() { char const payload[] = "alphabetagamma"; sz_u32_t const offsets[] = {0, 5, 9, 14}; sz::strings_view_t const column(payload, offsets, 3); // an Arrow `String` array, no copies auto const [escaped, escape_status] = column.escape(sz_escape_json_k); if (sz::failed(escape_status)) return 1; std::size_t malformed_index = 0; auto const [unescaped, unescape_status] = escaped.view().unescape(sz_escape_json_k, malformed_index); assert(sz::succeeded(unescape_status) && unescaped[1] == "beta"); std::size_t positions[3]; if (sz::failed(column.find("a", positions))) return 1; // the first match in every string return 0;}
API
Functions
fill_randomOverwrites the string slice with random bytes.
The elements of the array must be convertible to a string_view_t with the given extractor. Unlike the sz_sort_order_best C interface, overwrites the output span.
Parameters
container
in
The array of string-like elements to sort.
extractor
in
The function object that extracts the string from the object.
order
out
The caller-owned output span of indices, populated with the permutation. Must hold at least container.size() entries; the whole span receives a permutation.
top_count
in
If non-zero, only the first top_count entries are guaranteed fully sorted (partial sort).
reverse
in
If true, sorts in descending order.
capabilities
in
Capabilities to pick from.
stream
in
Null on the CPU, or the stream of the device capabilities describes.
Extracts the string from each object of the first array.
second_container
in
The second array of string-like elements.
second_extractor
in
Extracts the string from each object of the second array.
seed
in
Randomizes the internal hash table to resist adversarial inputs.
first_positions
out
The caller-owned output span of indices from the first array.
second_positions
out
The caller-owned output span of indices from the second array. Each span must fit at least min(first.size(), second.size()) entries.
capabilities
in
Capabilities to pick from.
stream
in
Null on the CPU, or the stream of the device capabilities describes.
allocator
in
Serves the scratch memory of the hash table.
Returns
The number of matched pairs, valid in the first entries of each output span, with unexpected_dimensions_k if a span is too short, or the status of the kernel.