Something went wrong. Try again.
An experimental ocaml library bindings for simdutf https://github.com/simdutf/simdutf
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833#include "simdutf_vendor.hh"#include <cstdint>#include <cstring>#include <new>#include <stdexcept>#include <vector>
extern "C" {#include <caml/alloc.h>#include <caml/bigarray.h>#include <caml/fail.h>#include <caml/memory.h>#include <caml/mlvalues.h>#include <caml/signals.h>}
namespace {// Keep these values in sync with Encoding.t in simdutf.ml.enum encoding { utf8, utf16le, utf16be, utf32le, utf32be, latin1 };// Status codes beyond upstream's error_code. Codes above incomplete_code_unit// become OCaml exceptions. Keep in sync with simdutf.ml.constexpr int incomplete_code_unit = 12;constexpr int overlapping_slices = 13;constexpr int out_of_memory = 14;constexpr int size_overflow = 15;constexpr size_t max_string_length = size_t(Max_wosize) * sizeof(value) - 1;
struct outcome { int code; size_t count; // Input byte offset on error; output size/count on success.};
struct output_size_overflow {};
size_t checked_output_bytes(size_t units, size_t width = 1) { if (units > size_t(Max_long) / width) throw output_size_overflow{}; return units * width;}
size_t unit_size(int enc) { if (enc == utf16le || enc == utf16be) return 2; if (enc == utf32le || enc == utf32be) return 4; return 1;}
char32_t swap32(char32_t x) { return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x >> 8) & 0xff00) | ((x >> 24) & 0xff);}
bool swap_utf32(int enc) { return (enc == utf32be) != bool(SIMDUTF_IS_BIG_ENDIAN);}
// Wide input is copied into properly aligned, typed storage. In particular,// bigstring slices may start at an arbitrary byte address. UTF-32 APIs operate// on native-endian values; UTF-16 APIs accept explicit byte order.struct input_view { int enc; const char *bytes; size_t size; std::vector<char16_t> u16; std::vector<char32_t> u32;
input_view(int encoding, const char *data, size_t length) : enc(encoding), bytes(data), size(length) { if (unit_size(enc) == 2) { u16.resize(length / 2); if (length) std::memcpy(u16.data(), data, length); } else if (unit_size(enc) == 4) { u32.resize(length / 4); if (length) std::memcpy(u32.data(), data, length); if (swap_utf32(enc)) { for (auto &c : u32) c = swap32(c); } } }
simdutf::result validate() const { if (size == 0) return {simdutf::SUCCESS, 0}; switch (enc) { case utf8: return simdutf::validate_utf8_with_errors(bytes, size); case utf16le: return simdutf::validate_utf16le_with_errors(u16.data(), u16.size()); case utf16be: return simdutf::validate_utf16be_with_errors(u16.data(), u16.size()); case utf32le: case utf32be: return simdutf::validate_utf32_with_errors(u32.data(), u32.size()); case latin1: return {simdutf::SUCCESS, size}; default: return {simdutf::OTHER, 0}; } }};
// No OCaml allocation or exception raising inside these native scopes. C++// buffers are destroyed before an OCaml allocation can trigger GC/longjmp.template <class F> auto native_call(F f) -> decltype(f()) { bool overflow = false; try { return f(); } catch (const std::bad_alloc &) { } catch (const std::length_error &) { } catch (const output_size_overflow &) { overflow = true; } if (overflow) caml_invalid_argument("Simdutf: output exceeds maximum buffer length"); caml_raise_out_of_memory();}
outcome validate_or_count(int enc, const char *data, size_t size, bool count) { const size_t unit = unit_size(enc); if (size % unit) return {incomplete_code_unit, size - size % unit}; if (size == 0) return {0, 0}; input_view input(enc, data, size); const auto r = input.validate(); if (r.error != simdutf::SUCCESS) return {int(r.error), r.count * unit}; if (!count) return {0, size}; switch (enc) { case utf8: return {0, simdutf::count_utf8(data, size)}; case utf16le: return {0, simdutf::count_utf16le(input.u16.data(), input.u16.size())}; case utf16be: return {0, simdutf::count_utf16be(input.u16.data(), input.u16.size())}; default: return {0, size / unit}; }}
bool valid(int enc, const char *data, size_t size) { // The common paths need no temporary allocation or diagnostic tuple. if (enc == utf8) return simdutf::validate_utf8(data, size); if (enc == latin1) return true; return validate_or_count(enc, data, size, false).code == 0;}
// Preflight both validity and output size before any write. In particular,// Latin-1 representability must be checked before calling an unbounded SIMD// conversion with an exact-sized destination.outcome measure_conversion(const input_view &input, int dst) { const int src = input.enc; const size_t size = input.size, units = size / unit_size(src); const auto r = input.validate(); if (r.error != simdutf::SUCCESS) return {int(r.error), r.count * unit_size(src)}; if (size == 0 || src == dst) return {0, size};
size_t count = 0; if (dst == latin1) { if (src == utf8) { for (size_t i = 0; i < size; ++count) { const auto c = static_cast<unsigned char>(input.bytes[i]); if (c >= 0xc4) return {simdutf::TOO_LARGE, i}; i += c < 0x80 ? 1 : 2; } } else if (unit_size(src) == 2) { const bool swap = (src == utf16be) != bool(SIMDUTF_IS_BIG_ENDIAN); for (size_t i = 0; i < units; ++i) { const char16_t c = input.u16[i]; const char16_t native = swap ? char16_t((c >> 8) | (c << 8)) : c; if (native > 0xff) return {simdutf::TOO_LARGE, i * 2}; } count = units; } else { for (size_t i = 0; i < units; ++i) { if (input.u32[i] > 0xff) return {simdutf::TOO_LARGE, i * 4}; } count = units; } return {0, count}; }
switch (src) { case utf8: count = unit_size(dst) == 2 ? simdutf::utf16_length_from_utf8(input.bytes, size) : simdutf::utf32_length_from_utf8(input.bytes, size); break; case latin1: count = dst == utf8 ? simdutf::utf8_length_from_latin1(input.bytes, size) : size; break; case utf16le: case utf16be: if (dst == utf8) count = src == utf16le ? simdutf::utf8_length_from_utf16le(input.u16.data(), units) : simdutf::utf8_length_from_utf16be(input.u16.data(), units); else if (unit_size(dst) == 4) count = src == utf16le ? simdutf::utf32_length_from_utf16le(input.u16.data(), units) : simdutf::utf32_length_from_utf16be(input.u16.data(), units); else count = units; break; case utf32le: case utf32be: if (dst == utf8) count = simdutf::utf8_length_from_utf32(input.u32.data(), units); else if (unit_size(dst) == 2) count = simdutf::utf16_length_from_utf32(input.u32.data(), units); else count = units; break; } return {0, checked_output_bytes(count, unit_size(dst))};}
outcome transcoded_length(int src, int dst, const char *data, size_t size) { const size_t unit = unit_size(src); if (size % unit) return {incomplete_code_unit, size - size % unit}; return measure_conversion(input_view(src, data, size), dst);}
outcome convert(int src, int dst, const char *data, size_t size, char *output, size_t capacity) { const size_t src_unit = unit_size(src), dst_unit = unit_size(dst); if (size % src_unit) return {incomplete_code_unit, size - size % src_unit}; if (size == 0) return {0, 0}; input_view input(src, data, size); const auto measured = measure_conversion(input, dst); if (measured.code != 0) return measured; if (measured.count > capacity) return {simdutf::OUTPUT_BUFFER_TOO_SMALL, 0}; if (src == dst) { std::memcpy(output, data, size); return {0, size}; }
const size_t units = size / src_unit; std::vector<char16_t> out16; std::vector<char32_t> out32; if (dst_unit == 2) out16.resize(measured.count / 2); if (dst_unit == 4) out32.resize(measured.count / 4);
simdutf::result r; switch (src) { case utf8: switch (dst) { case utf16le: r = simdutf::convert_utf8_to_utf16le_with_errors(data, size, out16.data()); break; case utf16be: r = simdutf::convert_utf8_to_utf16be_with_errors(data, size, out16.data()); break; case utf32le: case utf32be: r = simdutf::convert_utf8_to_utf32_with_errors(data, size, out32.data()); break; default: r = simdutf::convert_utf8_to_latin1_with_errors(data, size, output); break; } break; case latin1: switch (dst) { case utf8: r = {simdutf::SUCCESS, simdutf::convert_latin1_to_utf8(data, size, output)}; break; case utf16le: r = {simdutf::SUCCESS, simdutf::convert_latin1_to_utf16le(data, size, out16.data())}; break; case utf16be: r = {simdutf::SUCCESS, simdutf::convert_latin1_to_utf16be(data, size, out16.data())}; break; default: r = {simdutf::SUCCESS, simdutf::convert_latin1_to_utf32(data, size, out32.data())}; break; } break; case utf16le: case utf16be: switch (dst) { case utf8: r = src == utf16le ? simdutf::convert_utf16le_to_utf8_with_errors(input.u16.data(), units, output) : simdutf::convert_utf16be_to_utf8_with_errors(input.u16.data(), units, output); break; case latin1: r = src == utf16le ? simdutf::convert_utf16le_to_latin1_with_errors(input.u16.data(), units, output) : simdutf::convert_utf16be_to_latin1_with_errors(input.u16.data(), units, output); break; case utf32le: case utf32be: r = src == utf16le ? simdutf::convert_utf16le_to_utf32_with_errors( input.u16.data(), units, out32.data()) : simdutf::convert_utf16be_to_utf32_with_errors( input.u16.data(), units, out32.data()); break; default: simdutf::change_endianness_utf16(input.u16.data(), units, out16.data()); r = {simdutf::SUCCESS, units}; break; } break; case utf32le: case utf32be: switch (dst) { case utf8: r = simdutf::convert_utf32_to_utf8_with_errors(input.u32.data(), units, output); break; case latin1: r = simdutf::convert_utf32_to_latin1_with_errors(input.u32.data(), units, output); break; case utf16le: r = simdutf::convert_utf32_to_utf16le_with_errors(input.u32.data(), units, out16.data()); break; case utf16be: r = simdutf::convert_utf32_to_utf16be_with_errors(input.u32.data(), units, out16.data()); break; default: out32 = input.u32; r = {simdutf::SUCCESS, units}; break; } break; } if (r.error != simdutf::SUCCESS) return {int(r.error), r.count * src_unit}; if (dst_unit == 2) std::memcpy(output, out16.data(), r.count * 2); if (dst_unit == 4) { if (swap_utf32(dst)) { for (size_t i = 0; i < r.count; ++i) out32[i] = swap32(out32[i]); } std::memcpy(output, out32.data(), r.count * 4); } return {0, r.count * dst_unit};}
// Results cross into OCaml as one immediate integer, so that the hot stubs// neither allocate nor raise and can be declared [@@noalloc]: a count >= 0 on// success, or -(code + 16 * offset) on failure. Keep in sync with// Simdutf.status_error.intnat pack(outcome r) { if (r.code == 0) return intnat(r.count); // Only reachable with 31-bit OCaml integers and bigarrays beyond 64 MiB. constexpr size_t max_offset = size_t(Max_long) >> 4; const size_t offset = r.count < max_offset ? r.count : max_offset; return -(intnat(r.code) + intnat(offset << 4));}
// Like native_call, but reports C++ failures as status codes, since noalloc// stubs and blocking sections cannot raise OCaml exceptions.template <class F> outcome guarded(F f) noexcept { try { return f(); } catch (const std::bad_alloc &) { } catch (const std::length_error &) { } catch (const output_size_overflow &) { return {size_overflow, 0}; } return {out_of_memory, 0};}
// [release] stubs run without the runtime lock. They are only used on// bigarrays, whose data does not move. The input and output bigarrays are// rooted so that they cannot be finalized meanwhile, even if the caller drops// them. OCaml strings and bytes may move, so stubs reading or writing them// always hold the lock.template <bool release, class F> value run(value input, value output, F f) { if (!release) return Val_long(pack(guarded(f))); CAMLparam2(input, output); caml_enter_blocking_section(); const outcome r = guarded(f); caml_leave_blocking_section(); CAMLreturn(Val_long(pack(r)));}
// Slices are bounds-checked in OCaml before reaching these stubs.const char *string_data(value s, value off) { return String_val(s) + Long_val(off);}
char *bytes_data(value b, value off) { return reinterpret_cast<char *>(Bytes_val(b)) + Long_val(off);}
char *bigstring_data(value b, value off, value len) { // Empty bigarrays can have a null data pointer. static char empty[1]; if (Long_val(len) == 0) return empty; return static_cast<char *>(Caml_ba_data_val(b)) + Long_val(off);}
bool overlap(const char *input, size_t size, const char *output, size_t capacity) { if (size == 0 || capacity == 0) return false; const auto a = reinterpret_cast<uintptr_t>(input); const auto b = reinterpret_cast<uintptr_t>(output); return a <= b ? b - a < size : a - b < capacity;}
int detect_bigstring(const char *data, size_t size) { // Upstream detection may reinterpret input as char16_t/char32_t. A sub-view // of a bigarray need not have that alignment. if (reinterpret_cast<uintptr_t>(data) % alignof(char32_t) == 0 || size == 0) return simdutf::detect_encodings(data, size); std::vector<char32_t> aligned((size + 3) / 4); std::memcpy(aligned.data(), data, size); return simdutf::detect_encodings( reinterpret_cast<const char *>(aligned.data()), size);}
outcome is_valid_outcome(int enc, const char *data, size_t size) { return {0, valid(enc, data, size)};}
outcome validate_ascii(const char *data, size_t size) { const auto r = simdutf::validate_ascii_with_errors(data, size); return {int(r.error), r.count};}
outcome is_ascii(const char *data, size_t size) { return {0, simdutf::validate_ascii(data, size)};}
outcome transcode_into(int src, int dst, const char *data, size_t size, char *output, size_t capacity) { if (overlap(data, size, output, capacity)) return {overlapping_slices, 0}; return convert(src, dst, data, size, output, capacity);}
outcome base64_decoded_length(const char *data, size_t size) { return {0, simdutf::binary_length_from_base64(data, size)};}
outcome base64_encode_into(int options, const char *data, size_t size, char *output, size_t capacity) { if (overlap(data, size, output, capacity)) return {overlapping_slices, 0}; const auto opt = simdutf::base64_options(options); const size_t needed = checked_output_bytes(simdutf::base64_length_from_binary(size, opt)); if (needed > capacity) return {simdutf::OUTPUT_BUFFER_TOO_SMALL, 0}; return {0, simdutf::binary_to_base64(data, size, output, opt)};}
// [mode] packs the alphabet (bit 0), the last-chunk handling (bit 1), and// whether the caller guarantees the capacity (bit 2). Keep in sync with// Simdutf.Base64.decode_mode.outcome base64_decode_into(int mode, const char *data, size_t size, char *output, size_t capacity) { if (overlap(data, size, output, capacity)) return {overlapping_slices, 0}; // Upstream guarantees that binary_length_from_base64 can size the output for // any input. It is exact for valid input, including whitespace, but it scans // the input. The constant-time maximal length is never smaller, so capacity // at least that large needs no scan. const bool trusted = mode & 4; if (!trusted && capacity < simdutf::maximal_binary_length_from_base64(data, size) && simdutf::binary_length_from_base64(data, size) > capacity) return {simdutf::OUTPUT_BUFFER_TOO_SMALL, 0}; const auto r = simdutf::base64_to_binary_details( data, size, output, simdutf::base64_options(mode & 1), simdutf::last_chunk_handling_options((mode >> 1) & 1)); if (r.error != simdutf::SUCCESS) return {int(r.error), r.input_count}; return {0, r.output_count};}// Searches return the offset of the first match within the slice, or the slice// length when there is none, as upstream returns the end pointer.// Upstream scans byte by byte up to 64-byte alignment and after the last full// 64-byte block, which dominates short slices: at 1 KiB and below, the C// library's memchr measured two to three times faster on AVX2. Longer slices// use upstream, whose speed does not depend on the C library.constexpr size_t find_memchr_below = 1024;
outcome find_byte(const char *data, size_t size, char byte) { if (size < find_memchr_below) { const void *p = std::memchr(data, static_cast<unsigned char>(byte), size); return {0, p ? size_t(static_cast<const char *>(p) - data) : size}; } return {0, size_t(simdutf::find(data, data + size, byte) - data)};}
// [unit] is given in the byte order of [enc]. It is compared against code units// loaded in host order, so neither the input nor the result needs swapping.outcome find_code_unit(int enc, const char *data, size_t size, char16_t unit) { const bool swap = (enc == utf16be) != bool(SIMDUTF_IS_BIG_ENDIAN); const char16_t target = swap ? char16_t((unit >> 8) | (unit << 8)) : unit; const size_t units = size / 2; if (reinterpret_cast<uintptr_t>(data) % alignof(char16_t) == 0) { const auto *start = reinterpret_cast<const char16_t *>(data); return {0, size_t(simdutf::find(start, start + units, target) - start) * 2}; } // Unaligned input is searched through an aligned buffer, chunk by chunk. constexpr size_t chunk = 2048; char16_t buffer[chunk]; for (size_t done = 0; done < units;) { const size_t n = units - done < chunk ? units - done : chunk; std::memcpy(buffer, data + done * 2, n * 2); const size_t i = size_t(simdutf::find(buffer, buffer + n, target) - buffer); if (i < n) return {0, (done + i) * 2}; done += n; } return {0, units * 2};}} // namespace
// Each bigstring operation has a stub for [@@noalloc] use and a [_blocking]// variant that releases the runtime lock, for large inputs.#define BIGSTRING_STUBS(name, params, input, output, prologue, ...) \ CAMLprim value name params { \ prologue; \ return run<false>(input, output, [&] { return __VA_ARGS__; }); \ } \ CAMLprim value name##_blocking params { \ prologue; \ return run<true>(input, output, [&] { return __VA_ARGS__; }); \ }
#define BYTECODE_STUB(name, ...) \ CAMLprim value name##_bytecode(value *args, int) { \ return name(__VA_ARGS__); \ }
#define ARGS7 args[0], args[1], args[2], args[3], args[4], args[5], args[6]#define ARGS8 ARGS7, args[7]
extern "C" {CAMLprim value ocaml_simdutf_version(value unit) { CAMLparam1(unit); CAMLreturn(caml_copy_string(SIMDUTF_VERSION));}
CAMLprim value ocaml_simdutf_implementation(value unit) { CAMLparam1(unit); // Force lazy runtime dispatch before querying its name. const bool initialized = simdutf::validate_ascii("", 0); (void)initialized; const auto name = simdutf::get_active_implementation()->name(); CAMLreturn(caml_alloc_initialized_string(name.size(), name.data()));}
CAMLprim value ocaml_simdutf_detect(value input) { CAMLparam1(input); CAMLreturn(Val_int( simdutf::detect_encodings(String_val(input), caml_string_length(input))));}
/* String and bytes stubs. These are all [@@noalloc]: the OCaml heap cannot move while they run, so pointers taken from String_val stay valid. */
CAMLprim value ocaml_simdutf_validate(value enc, value input, value off, value len) { const char *data = string_data(input, off); return Val_long(pack(guarded([&] { return validate_or_count(Int_val(enc), data, Long_val(len), false); })));}
CAMLprim value ocaml_simdutf_is_valid(value enc, value input, value off, value len) { const char *data = string_data(input, off); return Val_long(pack( guarded([&] { return is_valid_outcome(Int_val(enc), data, Long_val(len)); })));}
CAMLprim value ocaml_simdutf_count(value enc, value input, value off, value len) { const char *data = string_data(input, off); return Val_long(pack(guarded([&] { return validate_or_count(Int_val(enc), data, Long_val(len), true); })));}
CAMLprim value ocaml_simdutf_validate_ascii(value input, value off, value len) { return Val_long(pack(validate_ascii(string_data(input, off), Long_val(len))));}
CAMLprim value ocaml_simdutf_is_ascii(value input, value off, value len) { return Val_bool(simdutf::validate_ascii(string_data(input, off), Long_val(len)));}
CAMLprim value ocaml_simdutf_transcode_into_bytes(value src, value dst, value input, value off, value len, value output, value output_off, value output_len) { const char *data = string_data(input, off); char *dest = bytes_data(output, output_off); return Val_long(pack(guarded([&] { return transcode_into(Int_val(src), Int_val(dst), data, Long_val(len), dest, Long_val(output_len)); })));}BYTECODE_STUB(ocaml_simdutf_transcode_into_bytes, ARGS8)
CAMLprim value ocaml_simdutf_base64_encode(value options, value input) { CAMLparam2(options, input); CAMLlocal1(output); const size_t size = caml_string_length(input); if ((size + 2) / 3 > max_string_length / 4) caml_invalid_argument("Simdutf: output exceeds maximum string length"); const auto opt = simdutf::base64_options(Int_val(options)); const size_t capacity = simdutf::base64_length_from_binary(size, opt); output = caml_alloc_string(capacity); // Reacquire all OCaml pointers after the allocation above. simdutf::binary_to_base64(String_val(input), size, reinterpret_cast<char *>(Bytes_val(output)), opt); CAMLreturn(output);}
CAMLprim value ocaml_simdutf_base64_decoded_length(value input, value off, value len) { return Val_long(simdutf::binary_length_from_base64(string_data(input, off), Long_val(len)));}
CAMLprim value ocaml_simdutf_base64_decode_into(value mode, value input, value off, value len, value output, value output_off, value output_len) { return Val_long(pack(base64_decode_into( Int_val(mode), string_data(input, off), Long_val(len), bytes_data(output, output_off), Long_val(output_len))));}BYTECODE_STUB(ocaml_simdutf_base64_decode_into, ARGS7)
CAMLprim value ocaml_simdutf_find(value input, value off, value len, value byte) { return Val_long(pack(find_byte(string_data(input, off), Long_val(len), char(Int_val(byte)))));}
CAMLprim value ocaml_simdutf_find_code_unit(value enc, value unit, value input, value off, value len) { return Val_long(pack(find_code_unit(Int_val(enc), string_data(input, off), Long_val(len), char16_t(Int_val(unit)))));}
/* Copies between strings and bigstrings, [@@noalloc]. */
CAMLprim value ocaml_simdutf_blit_string_to_bigstring(value src, value src_off, value dst, value dst_off, value len) { if (Long_val(len) > 0) std::memcpy(bigstring_data(dst, dst_off, len), string_data(src, src_off), Long_val(len)); return Val_unit;}
CAMLprim value ocaml_simdutf_blit_bigstring_to_bytes(value src, value src_off, value dst, value dst_off, value len) { if (Long_val(len) > 0) std::memcpy(bytes_data(dst, dst_off), bigstring_data(src, src_off, len), Long_val(len)); return Val_unit;}
/* Bigstring stubs. */
BIGSTRING_STUBS(ocaml_simdutf_bigstring_validate, (value enc, value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const int e = Int_val(enc); const size_t size = Long_val(len), validate_or_count(e, data, size, false))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_is_valid, (value enc, value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const int e = Int_val(enc); const size_t size = Long_val(len), is_valid_outcome(e, data, size))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_count, (value enc, value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const int e = Int_val(enc); const size_t size = Long_val(len), validate_or_count(e, data, size, true))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_validate_ascii, (value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const size_t size = Long_val(len), validate_ascii(data, size))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_is_ascii, (value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const size_t size = Long_val(len), is_ascii(data, size))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_transcoded_length, (value src, value dst, value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const int s = Int_val(src); const int d = Int_val(dst); const size_t size = Long_val(len), transcoded_length(s, d, data, size))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_transcode_into, (value src, value dst, value input, value off, value len, value output, value output_off, value output_len), input, output, const char *data = bigstring_data(input, off, len); char *dest = bigstring_data(output, output_off, output_len); const int s = Int_val(src); const int d = Int_val(dst); const size_t size = Long_val(len); const size_t capacity = Long_val(output_len), transcode_into(s, d, data, size, dest, capacity))BYTECODE_STUB(ocaml_simdutf_bigstring_transcode_into, ARGS8)BYTECODE_STUB(ocaml_simdutf_bigstring_transcode_into_blocking, ARGS8)
// Searches are short and retain the runtime lock, so there is no blocking// variant.CAMLprim value ocaml_simdutf_bigstring_find(value input, value off, value len, value byte) { return Val_long(pack(find_byte(bigstring_data(input, off, len), Long_val(len), char(Int_val(byte)))));}
BIGSTRING_STUBS(ocaml_simdutf_bigstring_find_code_unit, (value enc, value unit, value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const int e = Int_val(enc); const char16_t u = char16_t(Int_val(unit)); const size_t size = Long_val(len), find_code_unit(e, data, size, u))
CAMLprim value ocaml_simdutf_bigstring_detect(value input, value off, value len) { CAMLparam3(input, off, len); const char *data = bigstring_data(input, off, len); const int mask = native_call([&] { return detect_bigstring(data, Long_val(len)); }); CAMLreturn(Val_int(mask));}
CAMLprim value ocaml_simdutf_bigstring_base64_encoded_length(value options, value len) { CAMLparam2(options, len); if (Long_val(len) < 0) caml_invalid_argument("Simdutf.Bigstring.Base64: negative input length"); const size_t size = native_call([&] { return checked_output_bytes(simdutf::base64_length_from_binary( Long_val(len), simdutf::base64_options(Int_val(options)))); }); CAMLreturn(Val_long(size));}
BIGSTRING_STUBS(ocaml_simdutf_bigstring_base64_decoded_length, (value input, value off, value len), input, Val_unit, const char *data = bigstring_data(input, off, len); const size_t size = Long_val(len), base64_decoded_length(data, size))
BIGSTRING_STUBS(ocaml_simdutf_bigstring_base64_encode_into, (value options, value input, value off, value len, value output, value output_off, value output_len), input, output, const char *data = bigstring_data(input, off, len); char *dest = bigstring_data(output, output_off, output_len); const int opt = Int_val(options); const size_t size = Long_val(len); const size_t capacity = Long_val(output_len), base64_encode_into(opt, data, size, dest, capacity))BYTECODE_STUB(ocaml_simdutf_bigstring_base64_encode_into, ARGS7)BYTECODE_STUB(ocaml_simdutf_bigstring_base64_encode_into_blocking, ARGS7)
BIGSTRING_STUBS(ocaml_simdutf_bigstring_base64_decode_into, (value mode, value input, value off, value len, value output, value output_off, value output_len), input, output, const char *data = bigstring_data(input, off, len); char *dest = bigstring_data(output, output_off, output_len); const int m = Int_val(mode); const size_t size = Long_val(len); const size_t capacity = Long_val(output_len), base64_decode_into(m, data, size, dest, capacity))BYTECODE_STUB(ocaml_simdutf_bigstring_base64_decode_into, ARGS7)BYTECODE_STUB(ocaml_simdutf_bigstring_base64_decode_into_blocking, ARGS7)
// Writes OCaml bytes, so there is no blocking variant.CAMLprim value ocaml_simdutf_bigstring_base64_decode_to_bytes( value mode, value input, value off, value len, value output, value output_off, value output_len) { return Val_long(pack(base64_decode_into( Int_val(mode), bigstring_data(input, off, len), Long_val(len), bytes_data(output, output_off), Long_val(output_len))));}BYTECODE_STUB(ocaml_simdutf_bigstring_base64_decode_to_bytes, ARGS7)} // extern "C"