Files
scriptc/packages/runtime/src/scr_bytes.h
T

584 lines
35 KiB
C

/* scriptc runtime — typed arrays and Buffer (scr_bytes.c). Included by
* scr_runtime.h, which says where new declarations go. */
#ifndef SCR_BYTES_H
#define SCR_BYTES_H
#include "scr_events.h"
/* ── typed arrays / Buffer (scr_bytes.c) ──────────────────────────────
* ONE runtime representation for the supported numeric typed arrays,
* Node's Buffer (a Uint8Array subclass), and DataView: a refcounted,
* MUTABLE, fixed-length element buffer. An ScrBytes either OWNS its
* storage (backing == NULL, byteOffset 0) or is a VIEW: its `data` points
* INTO an owner's storage and its `backing` retains that owner (chain
* depth is always exactly 1 — views over views resolve to the owner at
* construction), so reads/writes through the view alias the source
* JS-exactly. Views come from DataView (scr_dataview_new) and from
* subarray()/Buffer-slice() (scr_bytes_subarray — Buffer's slice is
* subarray's deprecated alias in Node); only the plain typed arrays'
* slice() copies (scr_bytes_slice, matching JS).
* Elements are scalars only and the backing edge is acyclic by
* construction (owners point at nothing): never part of a cycle, no
* trace. Element reads widen to double; writes coerce JS-exactly (ToUint8
* / ToUint32 modular truncation, double→float rounding for f32, one
* double→binary16 rounding for f16).
* Out-of-bounds typed ELEMENT reads trap like arrays; invalid writes are
* ignored as in JavaScript. The Node-shaped operations
* (construction lengths, set(), the read/write numeric families, the
* DataView constructor and getters) THROW catchable RangeErrors exactly
* where Node throws. */
typedef enum ScrBytesElem {
SCR_BYTES_U8, /* Uint8Array / Buffer */
SCR_BYTES_U32, /* Uint32Array */
SCR_BYTES_F32, /* Float32Array */
SCR_BYTES_I32, /* Int32Array (reads sign-extend; writes ToInt32-wrap) */
SCR_BYTES_F64, /* Float64Array */
SCR_BYTES_I8, /* Int8Array */
SCR_BYTES_U16, /* Uint16Array */
SCR_BYTES_I16, /* Int16Array */
SCR_BYTES_U8C, /* Uint8ClampedArray */
SCR_BYTES_F16, /* Float16Array (IEEE 754 binary16 storage) */
} ScrBytesElem;
#define SCR_BYTES_ELEM_COUNT 10
/* Float16Array's element conversions: one round-to-nearest-even step from
* the double (Float16Array stores, Math.f16round and DataView.setFloat16
* all round this way), and the exact widening back. NaN keeps its sign and
* top payload bits with the quiet bit set. */
uint16_t scr_f16_from_f64(double value);
double scr_f16_to_f64(uint16_t bits);
typedef struct ScrSharedBytes ScrSharedBytes;
typedef struct ScrBytes {
size_t rc;
size_t len; /* ELEMENT count, fixed at construction */
ScrBytesElem elem;
uint8_t *data; /* len * elem_size bytes; owned unless backing is set */
/* NULL for owners. A view (DataView, subarray, Buffer-slice) sets this
* to the retained OWNER it aliases (chain depth is always exactly 1:
* views over views resolve to the owner at construction) and its `data`
* points into backing->data — released, never freed. */
struct ScrBytes *backing;
bool is_buffer; /* Buffer brand belongs to the view, not its backing. */
bool is_data_view; /* DataView is distinct from a numeric typed array. */
bool external; /* Root aliases foreign memory; releasing it never frees data. */
bool untransferable; /* A Buffer pool slab: its ArrayBuffer cannot be transferred. */
/* The cycle collector's last trace of `properties` from the one checked
* value that held this view (see scr_dyn_trace_v). In padding, so the
* offsets compiled code uses hold. */
bool properties_traced;
ScrSharedBytes *shared; /* Neutral backing, borrowed by views from their local owner. */
/* Named properties a program adds to the view (`ta.foo = 1`,
* Object.defineProperty): a checked object, NULL until the first one.
* Runtime-only and last, so compiled code's field indices hold. */
struct ScrDyn *properties;
} ScrBytes;
/* Copies out of and into shared storage, made of relaxed atomic accesses
* (aligned 8-byte words, single bytes at unaligned edges). Lock-free
* Atomics operations and element accesses on the same bytes stay free of
* data races; a copy that overlaps a concurrent write may observe either
* value of each word or byte, which ECMA-262 permits for non-atomic
* accesses. */
void scr_shared_load(void *dst, const void *src, size_t n);
void scr_shared_store(void *dst, const void *src, size_t n);
ScrSharedBytes *scr_shared_retain(ScrSharedBytes *);
void scr_shared_release(ScrSharedBytes *);
void scr_bytes_make_shared(ScrBytes *);
ScrBytes *scr_shared_wrap(ScrSharedBytes *);
void scr_bytes_read(const ScrBytes *, size_t, void *, size_t);
void scr_bytes_write(ScrBytes *, size_t, const void *, size_t);
ScrStr *scr_bytes_string(const ScrBytes *, size_t, size_t);
ScrBytes *scr_bytes_snapshot(const ScrBytes *);
void scr_bytes_snapshot_release(ScrBytes **);
ScrBytes *scr_bytes_local_copy(ScrBytes *); /* +1; shared inputs are copied */
void scr_bytes_require_unshared(const ScrBytes *);
/* Snapshot before reading shared bytes in bulk or passing byte pointers to
* I/O or callback-capable consumers. The copy is local and read with
* scr_shared_load, so no shared raw memory escapes. */
#if defined(SCR_WORKERS) || (defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES))
#define SCR_BYTES_SNAPSHOT(name) \
ScrBytes *scr_snapshot_##name __attribute__((cleanup(scr_bytes_snapshot_release))) = scr_bytes_snapshot(name); \
if (scr_snapshot_##name) name = scr_snapshot_##name
#else
#define SCR_BYTES_SNAPSHOT(name) ((void)0)
#endif
ScrDyn *scr_shared_array_buffer_new(ScrDyn *);
ScrDyn *scr_worker_data(void);
ScrDyn *scr_worker_parent_port(void);
/* new MessageChannel() ({ port1, port2 }) and receiveMessageOnPort(port)
* (scr_worker_events.c). */
ScrDyn *scr_worker_message_channel(void);
ScrDyn *scr_worker_receive_message_on_port(ScrDyn *port);
/* new BroadcastChannel(...args) (scr_worker_events.c): args is the argument
* vector as one array (borrowed); may throw. */
ScrDyn *scr_worker_broadcast_channel(ScrDyn *args);
/* setEnvironmentData(key, value) and getEnvironmentData(key) (+1); both
* borrow their arguments. */
void scr_worker_set_environment_data(ScrDyn *key, ScrDyn *value);
ScrDyn *scr_worker_get_environment_data(ScrDyn *key);
ScrDyn *scr_worker_resource_limits(void);
ScrDyn *scr_worker_thread_name_current(void);
/* markAsUntransferable(value) and isMarkedAsUntransferable(value)
* (scr_worker_events.c); both borrow the value. */
void scr_worker_mark_untransferable(ScrDyn *value);
bool scr_worker_is_marked_untransferable(ScrDyn *value);
/* worker.stdout, worker.stderr and worker.stdin streams: main installs the
* stream unit's operations and the Readable and Writable boxes (the
* Writable one may be NULL) when the program reads them. */
struct ScrChildStreamOps;
void scr_worker_stdio_install(const struct ScrChildStreamOps *ops, ScrDyn *(*readable_box)(void *),
ScrDyn *(*writable_box)(void *));
void scr_worker_stdio_default(const struct ScrChildStreamOps *ops, ScrDyn *(*view)(void *));
ScrDyn *scr_worker_new(double root, ScrStr *filename, ScrDyn *options);
bool scr_shared_array_buffer_is(const ScrDyn *);
bool scr_buffer_storage_is(const ScrDyn *);
ScrBytes *scr_bytes_from_external(void *data, size_t length);
ScrDyn *scr_ffi_memory_module(ScrDyn *catalog);
ScrDyn *scr_ffi_argument(ScrDyn *value, ScrStr *type);
size_t scr_bytes_elem_size(ScrBytesElem elem); /* 1, 2, 4, or 8 */
const char *scr_bytes_elem_name(ScrBytesElem elem);
double scr_bytes_to_u8_clamp(double value);
/* node:string_decoder's StringDecoder (scr_bytes.c, beside the decoders
* it shares): the decoder value is a record holding the CANONICAL
* encoding name and an f64 packing the pending partial sequence (count +
* up to 3 raw bytes — every encoding buffers at most 3); the compiler's
* interned %strdec helpers thread both through these pure functions.
* write answers the decoded complete prefix of pending+chunk, next the
* packed new pending, end the buffered partial's flush (utf8 replacement
* chars, base64's padded remainder, utf16le's held bytes; the stateless
* latin1/ascii/hex flush nothing). Node-exact per encoding — utf8's
* incomplete-tail walk, base64's mod-3 groups, utf16le's odd-byte and
* trailing-lead-surrogate holds. None throws. */
ScrStr *scr_strdec_write(const ScrStr *enc, double pending, const ScrBytes *chunk);
double scr_strdec_next(const ScrStr *enc, double pending, const ScrBytes *chunk);
ScrStr *scr_strdec_end(const ScrStr *enc, double pending);
ScrStr *scr_strdec_encoding(const ScrDyn *enc); /* +1, or NULL with ERR_UNKNOWN_ENCODING pending */
/* A Buffer encoding name known only at run time, Node's getEncodingOps: the
* canonical name of an encoding in any ASCII case (+1), or NULL with
* ERR_UNKNOWN_ENCODING ("Unknown encoding: <raw>") pending. Borrows raw. */
ScrStr *scr_bytes_normalize_encoding(const ScrStr *raw);
/* `new Uint8Array(n)` / Buffer.alloc(n): zero-filled. n goes through
* ToIndex like JS (NaN → 0, truncate toward zero; negative or > 2^53-1
* THROWS Node's "Invalid typed array length" RangeError catchably and
* returns NULL with the exception pending). */
ScrBytes *scr_bytes_new(ScrBytesElem elem, double n); /* +1 */
/* Shared u8 view over exactly the source view's raw bytes. Borrows; +1. */
ScrBytes *scr_bytes_raw_view(ScrBytes *bytes);
ScrBytes *scr_bytes_as_buffer(ScrBytes *bytes); /* borrows; +1, Buffer factory only */
/* Buffer.isBuffer over a typed view: the view's Buffer brand. Borrows. */
bool scr_bytes_is_buffer(const ScrBytes *bytes);
/* A fresh Uint8Array result of a Buffer's map or filter takes the Buffer
* brand (Node's species). Borrows both. */
void scr_bytes_copy_brand(ScrBytes *dst, const ScrBytes *src);
/* Views retain the root allocation; offsets are relative to its complete
* byte storage, including when src is itself a subarray. */
ScrBytes *scr_bytes_buffer_view(ScrBytes *src, ScrBytesElem elem,
double offset, bool has_len, double length);
ScrDyn *scr_array_buffer_constructor(void);
ScrDyn *scr_array_buffer_new(ScrDyn *length);
ScrDyn *scr_array_buffer_from_bytes(ScrBytes *view);
bool scr_array_buffer_is(const ScrDyn *value);
bool scr_array_buffer_is_view(const ScrDyn *value);
double scr_array_buffer_byte_length_getter(void);
ScrDyn *scr_array_buffer_byte_length_descriptor(ScrDyn *getter);
ScrBytes *scr_array_buffer_view(ScrBytesElem elem, const ScrDyn *buffer,
const ScrDyn *offset, const ScrDyn *length);
ScrBytes *scr_array_buffer_view_u8c(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_i8(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_u16(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_i16(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_u8(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_u32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_i32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f16(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f32(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_f64(ScrDyn *, ScrDyn *, ScrDyn *);
ScrBytes *scr_array_buffer_view_dv(ScrDyn *, ScrDyn *, ScrDyn *);
/* Native callback boundary: exact owned u8 copy. NULL with len == 0 is an
* empty span. */
ScrBytes *scr_bytes_from_data(const uint8_t *data, size_t len); /* +1 */
ScrBytes *scr_bytes_take_data(uint8_t *data, size_t len); /* +1, consumes malloc storage */
/* Same-element-kind copy, preserving the raw representation. Cross-kind
* construction uses scr_bytes_convert. Borrows src. Never throws. */
ScrBytes *scr_bytes_copy(const ScrBytes *src); /* +1 */
/* `new Uint8Array([1, 2, 3])` / Buffer.from(number[]): each f64 element
* coerces per the element kind (ToUint8/ToUint32/float). Borrows arr
* (SCR_ELEM_F64). Never throws. */
ScrBytes *scr_bytes_from_arr(ScrBytesElem elem, const ScrArr *arr); /* +1 */
static inline ScrBytes *scr_bytes_retain(ScrBytes *b) {
if (b->rc != SIZE_MAX) b->rc++;
return b;
}
void scr_bytes_release(ScrBytes *b); /* NULL-tolerant */
void *scr_bytes_retain_v(void *b);
void scr_bytes_release_v(void *b);
#ifdef SCR_RC_AUDIT
long scr_bytes_live_count(void);
#endif
double scr_bytes_len(const ScrBytes *b); /* element count */
double scr_bytes_byte_len(const ScrBytes *b); /* len * elem size */
/* `.byteOffset`: 0 for owners (scriptc typed arrays always own their
* whole storage — SEMANTICS.md notes the divergence from Node's Buffer
* pooling), the view's offset into its owner's storage for a DataView. */
double scr_bytes_byte_offset(const ScrBytes *b);
/* `new DataView(src.buffer, byteOffset?, byteLength?)` — src is the bytes
* value whose `.buffer` the program named (any elem kind; a DataView src
* resolves to ITS owner, so offsets stay relative to the one buffer,
* exactly like JS). Both indices go through ToIndex (NaN → 0, truncate
* toward zero); a bad offset THROWS Node's "Start offset %s is outside
* the bounds of the buffer" and a bad/overflowing length "Invalid
* DataView length %s" (catchable RangeErrors, NULL result with the
* exception pending). has_len distinguishes an omitted byteLength (view
* to the end of the buffer) from an explicit one. Borrows src; the
* result retains the owner. */
ScrBytes *scr_dataview_new(ScrBytes *src, double byte_off, bool has_len, double byte_len); /* +1 */
/* DataView getters. LE flag false = big-endian (the JS default). The
* BIGU64/BIGI64 kinds answer the composed Number(view.getBigUint64/
* getBigInt64(...)) lowering: the 8-byte integer converted to double
* (round-to-nearest-even — exactly Number(bigint)). Offsets go through
* ToIndex; any invalid or out-of-range offset THROWS Node's "Offset is
* outside the bounds of the DataView" RangeError catchably (0 result
* with the exception pending). */
typedef enum ScrDataViewGet {
SCR_DV_U8, SCR_DV_I8, SCR_DV_U16, SCR_DV_I16, SCR_DV_U32, SCR_DV_I32,
SCR_DV_F32, SCR_DV_F64, SCR_DV_BIGU64, SCR_DV_BIGI64, SCR_DV_F16,
} ScrDataViewGet;
double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le);
/* DataView setters (the integer/float kinds only — the BIG kinds never
* lower: bigint arguments have no representation). Values coerce
* JS-exactly: the integer kinds by modular truncation (ToUint32's residue
* — the narrower widths store its low bytes, the same 2^width residue),
* F32 by double→float round-to-nearest-even. Offsets go through ToIndex
* with the getters' one RangeError. */
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le);
/* Element read/write. Invalid typed reads trap; invalid writes are ignored.
* Checked reads preserve JavaScript's undefined result. Valid writes coerce
* JS-exactly: u8/u32 by modular truncation (NaN/±Infinity → 0, truncate
* toward zero, wrap mod 2^8/2^32), f32 by double→float rounding. */
double scr_bytes_get(const ScrBytes *b, double i);
double scr_bytes_get_or_nan(const ScrBytes *b, double i); /* NaN when the index is invalid */
void scr_bytes_set(ScrBytes *b, double i, double v);
/* Shared-view element access at an index the caller has checked against
* the length (0 <= i < len). Each accesses the element as one relaxed
* atomic access of its width. Integer elements read as their value in 32 bits, sign- or
* zero-extended, with Uint32 elements as their bit pattern; float elements
* read as a double. Writes coerce like scr_bytes_set. Compiled code calls
* these only from a view's shared branch. */
int32_t scr_bytes_shared_get_int(const ScrBytes *b, size_t i);
double scr_bytes_shared_get_f64(const ScrBytes *b, size_t i);
void scr_bytes_shared_set(ScrBytes *b, size_t i, double v);
/* Replace a live typed-array capsule's fixed-size payload from its dyn
* snapshot. Source and target have the same compiler-checked bytes type. */
void scr_bytes_copy_contents(ScrBytes *dst, const ScrBytes *src);
/* TypedArray.prototype.slice(start, end): relative indices clamp like
* string/array slice (ToIntegerOrInfinity, negatives from the end); the
* result is a fresh same-kind copy. Never throws. */
ScrBytes *scr_bytes_slice(const ScrBytes *b, double start, double end); /* +1 */
/* Independent element-coerced copy; same-kind copies preserve raw bits. */
ScrBytes *scr_bytes_convert(ScrBytesElem elem, const ScrBytes *src); /* +1 */
/* Checked native constructor/from input; no JavaScript engine required.
* `from` selects iterable/array-like semantics, otherwise primitives are
* converted to lengths. Borrows source, returns +1 or NULL+pending. */
ScrBytes *scr_bytes_from_dyn(ScrBytesElem elem, const ScrDyn *value, bool from);
/* In-place, overlapping copy with relative/clamped element indices;
* returns the retained receiver, including for a shared subarray view. */
ScrBytes *scr_bytes_copy_within(ScrBytes *b, double target, double start, double end); /* +1 */
/* ES2023 typed-array copying methods. Both preserve the receiver's element
* kind and return a fresh +1 owner. with() raises Node's catchable
* "Invalid typed array index" RangeError for an invalid relative index. */
ScrBytes *scr_bytes_to_reversed(const ScrBytes *b); /* +1 */
/* In-place numeric sort of a typed array: order 0 is the default
* TypedArray order (NaN last, -0 before +0); 1 or -1 is a recognized
* numeric comparator's, refused (false, nothing written) when a float
* element is NaN or -0. Runs no script code. */
bool scr_bytes_sort_numeric(ScrBytes *b, int32_t order);
/* Comparator sort of a typed array with the comparison schedule Node
* uses; `f` is a borrowed closure of `arity` (0..2) number parameters.
* A comparator exception stays pending and nothing is written back. */
void scr_bytes_sort_compare(ScrBytes *b, struct ScrClosure *f, uint32_t arity);
ScrBytes *scr_bytes_with(const ScrBytes *b, double index, double value); /* +1 */
/* Numeric typed-array iteration drained into number[], and Uint8Array.join.
* Inputs borrowed; results fresh +1. */
ScrArr *scr_bytes_to_arr(const ScrBytes *b); /* +1 */
ScrStr *scr_bytes_join(const ScrBytes *b, const ScrStr *separator); /* +1 */
/* TypedArray.prototype.fill on non-u8 receivers: per-element fill with
* the element write's coercion, slice-clamped relative indices; answers
* the receiver +1 (chaining). Never throws. */
ScrBytes *scr_bytes_fill_elem(ScrBytes *b, double v, double start, double end); /* +1 */
/* TypedArray.prototype.subarray(start, end) — and Buffer's slice(), its
* deprecated alias: a same-elem VIEW aliasing the receiver's storage
* (mutations visible both ways, JS-exactly). The view retains the OWNER
* (chain depth exactly 1, the DataView rule) and its byteOffset composes.
* Same index clamping as slice; never throws. */
ScrBytes *scr_bytes_subarray(ScrBytes *b, double start, double end); /* +1 */
/* dst.set(src, offset): overlapping-safe, element-coerced copy.
* offset goes through ToIntegerOrInfinity; a negative offset or
* src.len + offset > dst.len THROWS Node's "offset is out of bounds"
* RangeError catchably. */
void scr_bytes_set_from(ScrBytes *dst, const ScrBytes *src, double offset);
/* Array-like source with sequential element coercion and catchable errors.
* Earlier writes remain visible when a later coercion throws. */
void scr_bytes_set_from_dyn(ScrBytes *dst, const ScrDyn *src, double offset);
/* buf.toString(enc) on u8 bytes: "utf8" decodes with WHATWG per-maximal-
* subpart U+FFFD replacement (Node-exact for invalid sequences), "hex" is
* lowercase pairs, "base64"/"base64url" the standard/url alphabets (url
* unpadded), "latin1" maps bytes to U+00XX, "ascii" masks to & 0x7f, and
* "utf16le" decodes LE code units (surrogate pairs combine; a LONE
* surrogate stays lone; an odd tail byte drops). Aliases arrive NORMALIZED (the compiler folds "binary",
* "ucs2", "utf-8", ...); NULL selects the default UTF-8 encoding for
* implicit native coercions. Borrows both; +1 result. Never throws (the
* compiler fences other encodings). */
ScrStr *scr_bytes_to_str(const ScrBytes *b, const ScrStr *enc);
ScrStr *scr_bytes_to_str_range(const ScrBytes *b, const ScrStr *enc, double start, double end);
/* Runtime-valued Buffer.toString encoding: aliases/case canonicalize;
* unknown names throw ERR_UNKNOWN_ENCODING. +1, or NULL when throwing. */
ScrStr *scr_bytes_to_str_checked(const ScrBytes *b, const ScrStr *enc);
ScrStr *scr_bytes_to_str_checked_range(const ScrBytes *b, const ScrStr *enc, double start, double end);
/* WHATWG TextDecoder.decode over u8 bytes (utf-8, default options): the
* same replacement decode as toString("utf8") with the leading BOM
* stripped. Borrows; +1; never throws. */
ScrStr *scr_text_decode(const ScrBytes *b);
double scr_text_decoder_encoding(const ScrDyn *label);
ScrStr *scr_text_decoder_name(double encoding);
ScrDyn *scr_text_encode_into(const ScrDyn *source, const ScrDyn *destination);
/* TextEncoder and TextDecoder records as checked-dynamic handles
* (scr_text_codec_dyn.c, linked by the textCodecDyn runtime feature). The
* boxes borrow the record and retain it through `retain`; a decoder also
* names the byte offsets of its encoding (f64), fatal and ignoreBOM (i8)
* and state (dyn) fields. from_dyn answers the retained record or throws
* the path-annotated TypeError and answers NULL. */
ScrDyn *scr_text_encoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *));
ScrDyn *scr_text_decoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *),
size_t encoding, size_t fatal, size_t ignore_bom, size_t state);
void *scr_text_codec_from_dyn(const ScrDyn *d, bool decoder, const struct ScrDynPath *path,
const char *want);
bool scr_text_codec_is(const ScrDyn *d, bool decoder);
/* StringDecoder records as checked-dynamic handles (the same unit): the
* byte offsets of the encoding (string) and pending (f64) fields. */
ScrDyn *scr_string_decoder_to_dyn(void *rec, void *(*retain)(void *), void (*release)(void *),
size_t encoding, size_t pending);
void *scr_string_decoder_from_dyn(const ScrDyn *d, const struct ScrDynPath *path,
const char *want);
bool scr_buffer_is_ascii(const ScrDyn *input);
bool scr_buffer_is_utf8(const ScrDyn *input);
ScrBytes *scr_buffer_transcode(const ScrDyn *source, const ScrDyn *from, const ScrDyn *to);
ScrBytes *scr_bytes_buffer_source(const ScrDyn *value);
ScrStr *scr_text_decode_buffer_source(const ScrDyn *value);
ScrStr *scr_text_decode_options(const ScrBytes *b, bool fatal, bool ignore_bom);
ScrStr *scr_text_decode_stream(ScrDyn *state, const ScrBytes *b, double encoding, bool fatal, bool ignore_bom, bool stream);
ScrStr *scr_text_decode_legacy_options(const ScrBytes *b, double encoding, bool fatal, bool ignore_bom);
/* TextDecoder with a compile-time WHATWG legacy-encoding id. The frontend
* owns label canonicalization and emits only the ids understood by
* scr_bytes.c; keeping this separate from scr_text_decode means default
* UTF-8 users do not retain the legacy mapping tables. Borrows; +1. */
ScrStr *scr_text_decode_legacy(const ScrBytes *b, double encoding);
/* Buffer.from(string, enc): "utf8" copies the bytes; "hex" parses pairs
* and stops at the first invalid/odd tail (Node-lenient); "base64" and
* "base64url" decode the standard AND url-safe alphabets, skipping
* invalid bytes (Node-lenient); "latin1"/"ascii" write each UTF-16
* unit's low byte (astral code points contribute their two surrogates');
* "utf16le" writes LE code units, surrogate pairs included. Borrows
* both; +1 u8 result. Never throws. */
ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc);
/* The Buffer pool (Node's lib/buffer.js allocate/fromStringFast/
* fromArrayLike): Buffer.allocUnsafe, Buffer.from of strings, arrays and
* array-likes, and Buffer.concat return views into a per-thread slab of
* Buffer.poolSize bytes when the size is below poolSize >>> 1. Views start
* at 8-byte aligned offsets; a request that does not fit starts a new
* slab. A slab lives while any of its views does. All return +1 Buffers. */
double scr_buffer_pool_size(void);
void scr_buffer_set_pool_size(double size);
/* The setting without publishing it (scr_buffer_pool_refresh_hook). */
void scr_buffer_pool_apply(double size);
/* Set while a Buffer function value exists (scr_builtin_values.c): its
* poolSize property is the setting, which the refresh hook reads before
* each pool decision and the publish hook receives from static writes. */
extern SCR_TL void (*scr_buffer_pool_refresh_hook)(void);
extern SCR_TL void (*scr_buffer_pool_publish_hook)(double size);
ScrBytes *scr_buffer_alloc(double size); /* zeroed, never pooled */
ScrBytes *scr_buffer_alloc_unsafe(double size); /* pooled */
ScrBytes *scr_buffer_alloc_unsafe_slow(double size); /* never pooled */
ScrBytes *scr_buffer_from_str(const ScrStr *s, const ScrStr *enc);
ScrBytes *scr_buffer_from_str_checked(const ScrStr *s, const ScrStr *enc);
ScrBytes *scr_buffer_from_bytes(const ScrBytes *src); /* a copy of any typed array's values */
ScrBytes *scr_buffer_from_arr(const ScrArr *arr); /* number[] */
ScrBytes *scr_buffer_from_dyn_pooled(const ScrDyn *value, const ScrStr *encoding);
ScrBytes *scr_buffer_from_dyn_ref_pooled(const ScrDyn *value, const ScrStr *encoding);
ScrBytes *scr_buffer_from_dyn_offset_pooled(const ScrDyn *value, const ScrDyn *second);
/* Buffer.from(value, byteOffset, length): an ArrayBuffer's view with
* Node's fromArrayBuffer checks; other values as the two-argument form. */
ScrBytes *scr_buffer_from_dyn_range_pooled(const ScrDyn *value, const ScrDyn *second,
const ScrDyn *third);
/* The deprecated Buffer constructor's DEP0005 state (scr_bytes_io.c): a
* call from node_modules counts toward Node's 10000-call check. */
extern SCR_TL bool scr_buffer_ctor_warned;
extern SCR_TL uint64_t scr_buffer_ctor_checks;
void scr_buffer_deprecation_count(void);
ScrBytes *scr_buffer_concat(const ScrArr *list);
ScrBytes *scr_buffer_concat_len(const ScrArr *list, double total);
/* The untyped-list forms: Node's ERR_INVALID_ARG_TYPE checks on the list
* and each element, then the typed concat. NULL with the exception pending. */
ScrBytes *scr_buffer_concat_dyn(const struct ScrDyn *list);
ScrBytes *scr_buffer_alloc_value(const struct ScrDyn *size, const struct ScrDyn *fill,
const struct ScrDyn *encoding);
struct ScrDyn *scr_fs_realpath_value(const struct ScrDyn *path, const struct ScrDyn *options, bool native);
/* The Buffer function as a value (scr_builtin_values.c), one per thread. +1. */
struct ScrDyn *scr_buffer_constructor_value(void);
/* Installs that function as a Buffer's `.constructor` (the program's
* %main calls it when it reads the value): scr_bytes_buffer_constructor_fn. */
void scr_buffer_install_value(void);
extern struct ScrDyn *(*scr_bytes_buffer_constructor_fn)(void);
/* A static of that value by name (`Buffer.alloc` read as a value). +1. */
struct ScrDyn *scr_buffer_static_value(const ScrStr *name);
/* `Buffer.prototype[key]` (scr_bytes_dyn.c). +1, or NULL with the
* exception pending. */
struct ScrDyn *scr_buffer_prototype_member(const struct ScrDyn *key);
ScrBytes *scr_buffer_concat_len_dyn(const struct ScrDyn *list, double total);
/* Drops the pool's slab reference and restores Buffer.poolSize (process
* exit audits and library session resets). */
void scr_buffer_pool_reset(void);
/* A pooled copy of an unpooled u8 Buffer below the pool threshold; others
* are returned as they are. Consumes `b`. */
ScrBytes *scr_buffer_pool_adopt(ScrBytes *b);
ScrBytes *scr_bytes_from_str_checked(const ScrStr *s, const ScrStr *enc);
/* Buffer.byteLength(string, enc) — enc NORMALIZED like from_str — and
* Buffer.isEncoding(name) (case-insensitive over Node's alias set).
* Borrow; never throw. */
double scr_bytes_byte_length_str(ScrStr *s, const ScrStr *enc);
double scr_buffer_byte_length_dyn(ScrDyn *value, ScrStr *enc);
double scr_buffer_byte_length_value(ScrDyn *value, ScrDyn *encoding);
bool scr_bytes_is_encoding(const ScrStr *s);
/* equals / compare / indexOf-lastIndexOf / fill / copy / swap / write on
* u8 bytes, Node-exact (corpus 1663). compare and copy take nargs — the
* count of PRESENT index args (defaults skip validation like Node);
* their ladders and fill/write's mirror validateOffset ('an integer',
* then '>= 0 && <= max' — Node spells '&&' here) and copy's C++ ladder
* (fractionals truncate without an error; targetStart/sourceEnd check only
* >= 0). indexOf coerces byteOffset (NaN = search everything) and steps
* by `align` (2 for utf16le needles — matches land on even offsets).
* fill repeats the pattern over [offset, end) and answers the RECEIVER
* (+1, chaining); the string form zero-fills on an empty pattern where
* the bytes form throws Node's TypeError. swap reverses w-byte groups in
* place (+1 receiver; ERR_INVALID_BUFFER_SIZE off the width). write
* encodes and truncates at unit boundaries (whole UTF-8 code points,
* even utf16le lengths), returning the bytes written. concat_len
* truncates/zero-pads the concatenation to the validated total. */
bool scr_bytes_equals(const ScrBytes *a, const ScrBytes *b);
double scr_bytes_compare(const ScrBytes *src, const ScrBytes *target, double nargs,
double ts, double te, double ss, double se);
/* Node's validateOffset ladder (buffer.js): non-integers (±Infinity and
* NaN included) throw the 'an integer' ERR_OUT_OF_RANGE RangeError, the
* rest the '>= 0 && <= max' render; max < 0 drops the upper bound.
* Returns false after arming the pending throw. */
bool scr_bytes_validate_off(const char *name, double value, double max);
/* The checked-dynamic compare/equals validators (scr_bytes_io.c): Node's
* argument ladder over dyn-boxed arguments — a non-bytes value throws
* ERR_INVALID_ARG_TYPE with the API's own argument name ("buf1"/"buf2",
* "otherBuffer", "target"), non-number offsets ERR_INVALID_ARG_TYPE
* "of type number", out-of-range numbers the validateOffset RangeError;
* an undefined offset takes its Node default. All arguments BORROWED. */
double scr_buffer_compare_chk(const ScrDyn *a, const ScrDyn *b);
ScrBytes *scr_buffer_from_dyn(const ScrDyn *value, const ScrStr *encoding); /* +1 or NULL+pending; borrowed args */
bool scr_bytes_equals_chk(const ScrBytes *recv, const ScrDyn *other);
double scr_bytes_compare_chk(const ScrBytes *src, const ScrDyn *target,
const ScrDyn *ts, const ScrDyn *te,
const ScrDyn *ss, const ScrDyn *se);
/* new Buffer(number, encoding)'s string-arm type error (always throws;
* borrowed). */
ScrBytes *scr_buffer_new_string_fail(const ScrDyn *got);
/* fs._toUnixTimestamp over a dyn time value: numeric strings and finite
* numbers coerce (negatives answer now/1000), the rest throw Node's
* ERR_INVALID_ARG_TYPE. Borrowed. */
double scr_fs_to_unix_timestamp(const ScrDyn *t);
double scr_fs_timestamp_value(const ScrDyn *t, const char *name);
void scr_fs_utimes_checked(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime);
void scr_fs_lutimes_checked(const ScrDyn *path, const ScrDyn *atime, const ScrDyn *mtime);
void scr_fs_futimes_checked(const ScrDyn *fd, const ScrDyn *atime, const ScrDyn *mtime);
/* The fs argument-validation ladders (the fs.*Chk libCalls): Node-order
* validation over dyn values with Node's exact typed errors; a pass
* meets the real operation where one exists (mkdtempSync, macOS
* lchmodSync) or the compiler-rendered fence. All borrowed; the Chk
* forms without results always leave an exception pending. */
ScrDyn *scr_fs_exists_async(const ScrDyn *path, const ScrDyn *cb);
void scr_fs_mkdtemp_chk(const ScrDyn *prefix, const ScrDyn *cb, const ScrStr *fence);
ScrStr *scr_fs_mkdtemp_sync_chk(const ScrDyn *prefix, const ScrDyn *opts, const ScrStr *fence);
void scr_fs_read_file_chk(const ScrDyn *path, const ScrDyn *opts, const ScrDyn *cb, const ScrStr *fence);
void scr_fs_opendir_chk(const ScrDyn *path, const ScrDyn *opts, const ScrStr *fence);
void scr_fs_watch_file_chk(const ScrDyn *path, const ScrDyn *listener, const ScrStr *fence);
void scr_fs_lchmod_chk(const ScrDyn *path, const ScrDyn *mode, const ScrDyn *cb, const ScrStr *fence);
ScrDyn *scr_fs_lchmod_sync_chk(const ScrDyn *path, const ScrDyn *mode);
ScrPromise *scr_fsp_lchmod_chk(const ScrDyn *path, const ScrDyn *mode);
void scr_fs_read_chk(const ScrDyn *fd, const ScrDyn *buffer, const ScrDyn *offset,
const ScrDyn *length, const ScrDyn *position, const ScrStr *fence);
double scr_fs_read_sync_dyn(const ScrDyn *fd, const ScrDyn *buffer, const ScrDyn *offset,
const ScrDyn *length, const ScrDyn *position);
void scr_fs_stream_opts_chk(const ScrDyn *path, const ScrDyn *opts, const ScrStr *fence);
/* The fs streams' option pieces (scr_fs_stream.c): the getOptions,
* options.fd and path checks above without the fence; the checked path
* (+1, URLs converted); string flags as native open(2) bits (-1 with the
* exception pending); parseFileMode with the 0o666 default. */
bool scr_fs_stream_opts_valid(const ScrDyn *path, const ScrDyn *opts);
ScrStr *scr_fs_stream_path(const ScrDyn *value);
int scr_fs_stream_flags(const ScrDyn *flag, const char *fallback);
bool scr_fs_stream_mode(const ScrDyn *value, double *mode);
/* The checked-dynamic max-listeners ladders (scr_events_emitter.c). */
ScrEmitter *scr_emitter_set_max_chk(ScrEmitter *em, const ScrDyn *n);
void scr_emitter_set_default_max_chk(const ScrDyn *n, const ScrStr *name);
double scr_bytes_index_of(const ScrBytes *b, const ScrBytes *needle, double off, double align, bool fwd);
double scr_bytes_index_of_num(const ScrBytes *b, double v, double off, bool fwd);
/* TypedArray indexOf (last false) and lastIndexOf (last true) over any
* element kind, with strict equality: NaN is never found and -0 matches 0.
* `from` is the fromIndex argument (ToIntegerOrInfinity applies); the
* caller passes 0, or +Infinity for lastIndexOf, when it is omitted. A
* Buffer (a Uint8Array receiver can be one) searches as Buffer's
* indexOf and lastIndexOf do. */
double scr_bytes_index_of_elem(const ScrBytes *b, double x, double from, bool last);
/* TypedArray includes: indexOf's search from `from` with SameValueZero,
* so NaN is found. A Buffer searches as Buffer's includes does. */
bool scr_bytes_includes_elem(const ScrBytes *b, double x, double from);
ScrBytes *scr_bytes_fill(ScrBytes *b, const ScrBytes *pattern, double nargs, double offset, double end);
ScrBytes *scr_bytes_fill_num(ScrBytes *b, double v, double nargs, double offset, double end);
ScrBytes *scr_bytes_fill_str(ScrBytes *b, const ScrStr *s, const ScrStr *enc,
double nargs, double offset, double end);
double scr_bytes_copy_into(const ScrBytes *src, ScrBytes *dst, double nargs,
double ts, double ss, double se);
ScrBytes *scr_bytes_swap(ScrBytes *b, double width);
double scr_bytes_write_str(ScrBytes *b, const ScrStr *s, const ScrStr *enc,
double offset, double len, bool has_len);
ScrBytes *scr_bytes_concat_len(const ScrArr *list, double total);
/* Buffer.concat(list): list is a SCR_ELEM_BYTES array of u8 bytes values;
* the result is one fresh copy of their concatenation. Borrows the list.
* Never throws. */
ScrBytes *scr_bytes_concat(const ScrArr *list); /* +1 */
#endif /* SCR_BYTES_H */