mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-06 08:23:19 +08:00
Address review comments on Buffer methods for checked-dynamic values
This commit is contained in:
@@ -1479,6 +1479,8 @@ function lowerBufferInstanceMethod(
|
||||
if (encName === "utf16le") align = 2;
|
||||
} else if (vT?.kind === "bytes" && vT.elem === "u8") {
|
||||
needle = lowerer.lowerExpr(args[0]!);
|
||||
// A Buffer needle under utf16le also searches whole 2-byte units.
|
||||
if (encName === "utf16le") align = 2;
|
||||
} else {
|
||||
lowerer.noLowering(
|
||||
`.${name} of '${lowerer.checker.typeToString(lowerer.typeOf(args[0]!))}' values`,
|
||||
|
||||
@@ -1489,8 +1489,10 @@ ScrStr *scr_strdec_end(const ScrStr *enc, double pending) {
|
||||
static void scr_bytes_to_str_bounds(const ScrBytes *b, double start, double end,
|
||||
size_t *s0_out, size_t *e0_out) {
|
||||
size_t len = b->len;
|
||||
size_t s0 = start <= 0 ? 0 : ((size_t)start > len ? len : (size_t)start);
|
||||
size_t e0 = end <= 0 ? 0 : ((size_t)end > len ? len : (size_t)end);
|
||||
/* Clamped before the conversion: NaN and values at or below 0 are 0,
|
||||
* values at or past the length (Infinity included) are the length. */
|
||||
size_t s0 = !(start > 0) ? 0 : start >= (double)len ? len : (size_t)start;
|
||||
size_t e0 = !(end > 0) ? 0 : end >= (double)len ? len : (size_t)end;
|
||||
if (e0 < s0) e0 = s0;
|
||||
*s0_out = s0;
|
||||
*e0_out = e0;
|
||||
@@ -3204,15 +3206,61 @@ double scr_bytes_compare(const ScrBytes *src, const ScrBytes *target, double nar
|
||||
return sn < tn ? -1 : 1;
|
||||
}
|
||||
|
||||
/* node_buffer.cc's IndexOfBuffer/IndexOfString for the UCS2 encoding over
|
||||
* an encoded needle: buffer.js clamps the byteOffset to the int32 range,
|
||||
* IndexOfOffset picks the start, the search runs over whole 2-byte units of
|
||||
* the haystack (an odd trailing byte of either side is ignored), and a match
|
||||
* is reported at its byte position. */
|
||||
static double scr_bytes_index_of_ucs2(const ScrBytes *haystack, const ScrBytes *needle, double at, bool fwd) {
|
||||
int64_t length = (int64_t)haystack->len, needle_length = (int64_t)needle->len;
|
||||
/* NaN (an omitted or non-numeric byteOffset) searches everything. */
|
||||
if (at > 2147483647.0) at = 2147483647.0;
|
||||
else if (at < -2147483648.0) at = -2147483648.0;
|
||||
int64_t offset_i64 = at != at ? (fwd ? 0 : length) : (int64_t)trunc(at);
|
||||
int64_t opt_offset;
|
||||
if (offset_i64 < 0) {
|
||||
if (offset_i64 + length >= 0) opt_offset = length + offset_i64;
|
||||
else opt_offset = fwd || needle_length == 0 ? 0 : -1;
|
||||
} else if (offset_i64 + needle_length <= length) {
|
||||
opt_offset = offset_i64;
|
||||
} else if (needle_length == 0) {
|
||||
opt_offset = length;
|
||||
} else {
|
||||
opt_offset = fwd ? -1 : length - 1;
|
||||
}
|
||||
size_t search_end = haystack->len & ~(size_t)1;
|
||||
if (needle_length == 0) return (double)(opt_offset < (int64_t)search_end ? opt_offset : (int64_t)search_end);
|
||||
if (length == 0 || opt_offset <= -1) return -1;
|
||||
size_t offset = (size_t)opt_offset;
|
||||
if (offset >= search_end) {
|
||||
if (fwd || search_end == 0) return -1;
|
||||
offset = search_end - 1;
|
||||
}
|
||||
if ((fwd && needle->len + offset > search_end) || needle->len > search_end) return -1;
|
||||
if (search_end < 2 || needle->len < 2) return -1;
|
||||
size_t units = search_end / 2, needle_units = needle->len / 2, start = offset / 2;
|
||||
if (units < needle_units) return -1;
|
||||
size_t last = units - needle_units;
|
||||
if (fwd) {
|
||||
for (size_t p = start; p <= last; p++)
|
||||
if (!memcmp(haystack->data + 2 * p, needle->data, 2 * needle_units)) return (double)(2 * p);
|
||||
return -1;
|
||||
}
|
||||
for (size_t p = start < last ? start : last;; p--) {
|
||||
if (!memcmp(haystack->data + 2 * p, needle->data, 2 * needle_units)) return (double)(2 * p);
|
||||
if (p == 0) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The shared search core: needle bytes, a coerced byteOffset (trunc; NaN
|
||||
* means 'search everything' — 0 forward, the end backward; negatives
|
||||
* count from the end, and a still-negative backward search answers -1),
|
||||
* and the utf16le alignment stride. */
|
||||
* and the alignment: 2 searches utf16le units (scr_bytes_index_of_ucs2). */
|
||||
double scr_bytes_index_of(const ScrBytes *b, const ScrBytes *needle, double off, double align, bool fwd) {
|
||||
SCR_BYTES_SNAPSHOT(b);
|
||||
SCR_BYTES_SNAPSHOT(needle);
|
||||
if (align == 2) return scr_bytes_index_of_ucs2(b, needle, off, fwd);
|
||||
size_t len = b->len, nlen = needle->len;
|
||||
size_t step = align == 2 ? 2 : 1;
|
||||
double o = (off != off) ? (fwd ? 0 : (double)len) : trunc(off);
|
||||
if (o < 0) {
|
||||
o += (double)len;
|
||||
@@ -3225,17 +3273,15 @@ double scr_bytes_index_of(const ScrBytes *b, const ScrBytes *needle, double off,
|
||||
if (nlen > len) return -1;
|
||||
if (fwd) {
|
||||
size_t start = o > (double)len ? len : (size_t)o;
|
||||
if (step == 2) start += start % 2;
|
||||
for (size_t i = start; i + nlen <= len; i += step) {
|
||||
for (size_t i = start; i + nlen <= len; i++) {
|
||||
if (memcmp(b->data + i, needle->data, nlen) == 0) return (double)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
size_t start = o > (double)(len - nlen) ? len - nlen : (size_t)o;
|
||||
if (step == 2) start -= start % 2;
|
||||
for (size_t i = start;; i -= step) {
|
||||
for (size_t i = start;; i--) {
|
||||
if (memcmp(b->data + i, needle->data, nlen) == 0) return (double)i;
|
||||
if (i < step) break;
|
||||
if (i == 0) break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,91 @@ static ScrStr *scr_bytes_dyn_quoted(const ScrStr *text) {
|
||||
return scr_jb_finish(&jb);
|
||||
}
|
||||
|
||||
/* Appends Node's inspect of an ArrayBuffer at indentation level `indent`:
|
||||
* the first 100 bytes as hex pairs, on one line when it fits the 80-column
|
||||
* break length (reduceToSingleString), one entry per line otherwise. */
|
||||
static void scr_bytes_dyn_inspect_array_buffer(ScrJsonBuf *jb, const ScrBytes *owner, size_t indent) {
|
||||
static const char digits[] = "0123456789abcdef";
|
||||
size_t len = owner->len * scr_bytes_elem_size(owner->elem);
|
||||
size_t shown = len < 100 ? len : 100;
|
||||
ScrJsonBuf contents;
|
||||
scr_jb_init(&contents);
|
||||
scr_jb_puts(&contents, "[Uint8Contents]: <");
|
||||
for (size_t i = 0; i < shown; i++) {
|
||||
uint8_t byte;
|
||||
scr_bytes_read(owner, i, &byte, 1);
|
||||
if (i) scr_jb_putc(&contents, ' ');
|
||||
scr_jb_putc(&contents, digits[byte >> 4]);
|
||||
scr_jb_putc(&contents, digits[byte & 15]);
|
||||
}
|
||||
if (len > shown) {
|
||||
char more[48];
|
||||
snprintf(more, sizeof more, " ... %zu more byte%s", len - shown, len - shown > 1 ? "s" : "");
|
||||
scr_jb_puts(&contents, more);
|
||||
}
|
||||
scr_jb_putc(&contents, '>');
|
||||
ScrStr *first = scr_jb_finish(&contents);
|
||||
char second[40];
|
||||
int second_len = snprintf(second, sizeof second, "[byteLength]: %zu", len);
|
||||
/* isBelowBreakLength with two entries, start = 2 + indentation + "ArrayBuffer {" + 10. */
|
||||
size_t total = 2 + (2 + indent + 13 + 10) + first->len + (size_t)second_len;
|
||||
if (total <= 80) {
|
||||
scr_jb_puts(jb, "ArrayBuffer { ");
|
||||
scr_jb_put_str(jb, first);
|
||||
scr_jb_puts(jb, ", ");
|
||||
scr_jb_puts(jb, second);
|
||||
scr_jb_puts(jb, " }");
|
||||
} else {
|
||||
scr_jb_puts(jb, "ArrayBuffer {\n");
|
||||
for (size_t i = 0; i < indent + 2; i++) scr_jb_putc(jb, ' ');
|
||||
scr_jb_put_str(jb, first);
|
||||
scr_jb_puts(jb, ",\n");
|
||||
for (size_t i = 0; i < indent + 2; i++) scr_jb_putc(jb, ' ');
|
||||
scr_jb_puts(jb, second);
|
||||
scr_jb_putc(jb, '\n');
|
||||
for (size_t i = 0; i < indent; i++) scr_jb_putc(jb, ' ');
|
||||
scr_jb_putc(jb, '}');
|
||||
}
|
||||
scr_str_release(first);
|
||||
}
|
||||
|
||||
/* ERR_INVALID_ARG_VALUE for an empty ArrayBufferView fill value: Node's
|
||||
* inspect of the view (a Buffer, an empty typed array, or a DataView with
|
||||
* its buffer), cut to 128 characters followed by "...". */
|
||||
static ScrStr *scr_bytes_dyn_empty_view_message(const ScrDyn *value) {
|
||||
const ScrBytes *view = value->v.bytes;
|
||||
ScrJsonBuf jb;
|
||||
scr_jb_init(&jb);
|
||||
if (value->buffer) {
|
||||
scr_jb_puts(&jb, "<Buffer >");
|
||||
} else if (!view->is_data_view) {
|
||||
scr_jb_puts(&jb, scr_bytes_elem_name(view->elem));
|
||||
scr_jb_puts(&jb, "(0) []");
|
||||
} else {
|
||||
const ScrBytes *owner = view->backing ? view->backing : view;
|
||||
char line[64];
|
||||
snprintf(line, sizeof line, "DataView {\n [byteLength]: 0,\n [byteOffset]: %zu,\n [buffer]: ",
|
||||
(size_t)(view->data - owner->data));
|
||||
scr_jb_puts(&jb, line);
|
||||
scr_bytes_dyn_inspect_array_buffer(&jb, owner, 2);
|
||||
scr_jb_puts(&jb, "\n}");
|
||||
}
|
||||
ScrStr *inspected = scr_jb_finish(&jb);
|
||||
ScrJsonBuf out;
|
||||
scr_jb_init(&out);
|
||||
scr_jb_puts(&out, "The argument 'value' is invalid. Received ");
|
||||
if (inspected->len > 128) {
|
||||
ScrStr *head = scr_str_new(inspected->data, 128);
|
||||
scr_jb_put_str(&out, head);
|
||||
scr_str_release(head);
|
||||
scr_jb_puts(&out, "...");
|
||||
} else {
|
||||
scr_jb_put_str(&out, inspected);
|
||||
}
|
||||
scr_str_release(inspected);
|
||||
return scr_jb_finish(&out);
|
||||
}
|
||||
|
||||
/* buf.fill(value[, offset[, end]][, encoding]): buffer.js's _fill. A string
|
||||
* takes its encoding from the first string among offset and end; numbers and
|
||||
* other non-view values fill with ToUint32(value) & 255. Answers the receiver. */
|
||||
@@ -244,9 +329,23 @@ static ScrDyn *scr_bytes_dyn_fill(ScrDyn *recv, ScrDyn *const *args, size_t argc
|
||||
/* An empty string fills with zeros. */
|
||||
out = text->len == 0 ? scr_bytes_fill_num(b, 0, 2, from, to) : scr_bytes_fill(b, pattern, 2, from, to);
|
||||
scr_bytes_release(pattern);
|
||||
} else if (value->kind == SCR_DYN_BYTES && !value->v.bytes->is_data_view &&
|
||||
value->v.bytes->elem == SCR_BYTES_U8) {
|
||||
out = scr_bytes_fill(b, value->v.bytes, 2, from, to);
|
||||
} else if (value->kind == SCR_DYN_BYTES) {
|
||||
/* Any ArrayBufferView repeats its raw bytes: a byte view over the
|
||||
* value's storage (a typed array of wider elements, or a DataView). */
|
||||
const ScrBytes *view = value->v.bytes;
|
||||
if (view->len == 0) {
|
||||
ScrStr *message = scr_bytes_dyn_empty_view_message(value);
|
||||
scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, "ERR_INVALID_ARG_VALUE");
|
||||
scr_str_release(message);
|
||||
return NULL;
|
||||
}
|
||||
ScrBytes pattern = { .rc = 1,
|
||||
.len = view->len * scr_bytes_elem_size(view->elem),
|
||||
.elem = SCR_BYTES_U8,
|
||||
.data = view->data,
|
||||
.backing = NULL,
|
||||
.shared = view->shared };
|
||||
out = scr_bytes_fill(b, &pattern, 2, from, to);
|
||||
} else {
|
||||
double n;
|
||||
if (!scr_dyn_number_coerce_js(value, &n)) return NULL;
|
||||
@@ -256,21 +355,27 @@ static ScrDyn *scr_bytes_dyn_fill(ScrDyn *recv, ScrDyn *const *args, size_t argc
|
||||
scr_bytes_release(out);
|
||||
return scr_dyn_retain(recv);
|
||||
}
|
||||
/* buf.toString(encoding, start, end): the window clamps to
|
||||
* [0, length] with NaN as 0, an empty window answers "" before the
|
||||
* encoding resolves, and an undefined encoding is utf8. */
|
||||
/* buf.toString(encoding, start, end): the window clamps to [0, length]
|
||||
* with NaN as 0 (infinite and out-of-range values included), an empty
|
||||
* window answers "" before the encoding resolves, and an undefined encoding
|
||||
* is utf8. */
|
||||
static ScrDyn *scr_bytes_dyn_to_string(ScrBytes *b, ScrDyn *const *args, size_t argc) {
|
||||
const ScrDyn *encoding = scr_bytes_dyn_arg(args, argc, 0);
|
||||
const ScrDyn *start = scr_bytes_dyn_arg(args, argc, 1);
|
||||
const ScrDyn *end = scr_bytes_dyn_arg(args, argc, 2);
|
||||
double from = 0, to = (double)b->len;
|
||||
const double len = (double)b->len;
|
||||
double from = NAN, to = len;
|
||||
if (!scr_bytes_dyn_undef(start) && !scr_dyn_number_coerce_js(start, &from)) return NULL;
|
||||
if (!scr_bytes_dyn_undef(end) && !scr_dyn_number_coerce_js(end, &to)) return NULL;
|
||||
if (from != from) from = 0;
|
||||
if (to != to) to = 0;
|
||||
from = trunc(from);
|
||||
to = trunc(to);
|
||||
if (from >= (double)b->len || to <= from || to <= 0) return scr_bytes_dyn_str("", 0);
|
||||
/* `if (start <= 0) start = 0; else if (start >= len) return ''; else
|
||||
* start = MathTrunc(start) || 0`, then `end > len` clamps to the length
|
||||
* and other ends truncate (NaN as 0), so both stay within [0, len]. */
|
||||
if (from <= 0) from = 0;
|
||||
else if (from >= len) return scr_bytes_dyn_str("", 0);
|
||||
else from = from == from ? trunc(from) : 0;
|
||||
if (to > len) to = len;
|
||||
else to = to == to ? trunc(to) : 0;
|
||||
if (to <= from) return scr_bytes_dyn_str("", 0);
|
||||
ScrStr *enc = scr_bytes_dyn_undef(encoding) ? scr_str_new("utf8", 4) : scr_bytes_dyn_encoding(encoding);
|
||||
if (!enc) return NULL;
|
||||
ScrStr *text = scr_bytes_to_str_range(b, enc, from, to);
|
||||
@@ -313,6 +418,23 @@ static ScrDyn *scr_bytes_dyn_copy(const ScrBytes *b, ScrDyn *const *args, size_t
|
||||
return scr_dyn_new_num(scr_bytes_copy_into(b, target->v.bytes, 3, offsets[0], offsets[1], offsets[2]));
|
||||
}
|
||||
|
||||
/* Whether an encoding name is one of the utf16le spellings, in any ASCII case. */
|
||||
static bool scr_bytes_dyn_is_ucs2(const ScrStr *raw) {
|
||||
static const char *const names[] = { "utf16le", "utf-16le", "ucs2", "ucs-2" };
|
||||
for (size_t i = 0; i < sizeof names / sizeof names[0]; i++) {
|
||||
size_t n = strlen(names[i]);
|
||||
if (raw->len != n) continue;
|
||||
size_t k = 0;
|
||||
for (; k < n; k++) {
|
||||
char c = raw->data[k];
|
||||
if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A'));
|
||||
if (c != names[i][k]) break;
|
||||
}
|
||||
if (k == n) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* buf.indexOf/lastIndexOf/includes(value[, byteOffset][, encoding]):
|
||||
* buffer.js's bidirectionalIndexOf. A string byteOffset is the encoding;
|
||||
* the offset clamps to the int32 range, and NaN searches everything. */
|
||||
@@ -333,25 +455,30 @@ static ScrDyn *scr_bytes_dyn_index_of(const ScrBytes *b, ScrDyn *const *args, si
|
||||
if (value->kind == SCR_DYN_NUM) {
|
||||
found = scr_bytes_index_of_num(b, value->v.num, at, fwd);
|
||||
} else {
|
||||
ScrStr *enc = NULL;
|
||||
ScrStr *raw = NULL;
|
||||
if (!scr_bytes_dyn_undef(encoding)) {
|
||||
ScrStr *raw = scr_dyn_string_coerce_js(encoding);
|
||||
raw = scr_dyn_string_coerce_js(encoding);
|
||||
if (!raw) return NULL;
|
||||
/* An unknown encoding throws for a string needle only. */
|
||||
enc = value->kind == SCR_DYN_STR ? scr_bytes_normalize_encoding(raw) : NULL;
|
||||
scr_str_release(raw);
|
||||
if (value->kind == SCR_DYN_STR && !enc) return NULL;
|
||||
}
|
||||
if (value->kind == SCR_DYN_STR) {
|
||||
if (!enc) enc = scr_str_new("utf8", 4);
|
||||
/* An unknown encoding throws for a string needle. */
|
||||
ScrStr *enc = raw ? scr_bytes_normalize_encoding(raw) : scr_str_new("utf8", 4);
|
||||
if (raw) scr_str_release(raw);
|
||||
if (!enc) return NULL;
|
||||
bool ucs2 = enc->len == 7 && !memcmp(enc->data, "utf16le", 7);
|
||||
ScrBytes *needle = scr_bytes_from_str(value->v.str, enc);
|
||||
found = scr_bytes_index_of(b, needle, at, enc->len == 7 && !memcmp(enc->data, "utf16le", 7) ? 2 : 1, fwd);
|
||||
found = scr_bytes_index_of(b, needle, at, ucs2 ? 2 : 1, fwd);
|
||||
scr_bytes_release(needle);
|
||||
scr_str_release(enc);
|
||||
} else if (value->kind == SCR_DYN_BYTES && !value->v.bytes->is_data_view &&
|
||||
value->v.bytes->elem == SCR_BYTES_U8) {
|
||||
found = scr_bytes_index_of(b, value->v.bytes, at, 1, fwd);
|
||||
/* A Buffer needle searches 2-byte units under a utf16le encoding and
|
||||
* bytes under any other, an unknown name included. */
|
||||
bool ucs2 = raw && scr_bytes_dyn_is_ucs2(raw);
|
||||
if (raw) scr_str_release(raw);
|
||||
found = scr_bytes_index_of(b, value->v.bytes, at, ucs2 ? 2 : 1, fwd);
|
||||
} else {
|
||||
if (raw) scr_str_release(raw);
|
||||
scr_dyn_arg_type_fail("value", "one of type number or string or an instance of Buffer or Uint8Array", value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -143,3 +143,125 @@ console.log(box.text.indexOf("an"), box.text.lastIndexOf("an"), box.text.include
|
||||
console.log(box.list.indexOf(2), box.list.includes(4), box.list.fill(0, 1).join(","), box.list.toString());
|
||||
console.log(Array.from(box.bytes.fill(9, 2)), box.bytes.toString());
|
||||
console.log(box.sink.write("x"), box.sink.written, box.sink.toString(1, 2));
|
||||
|
||||
// fill with other ArrayBufferViews repeats their raw bytes; an empty view is
|
||||
// ERR_INVALID_ARG_VALUE with Node's inspect of the view.
|
||||
const tm = (name, f) => {
|
||||
try {
|
||||
const v = f();
|
||||
console.log(name, Buffer.isBuffer(v) ? `<${v.toString("hex")}>` : JSON.stringify(v));
|
||||
} catch (e) {
|
||||
console.log(name, e.name, e.code, JSON.stringify(e.message));
|
||||
}
|
||||
};
|
||||
function Held(bytes) {
|
||||
this.b = bytes;
|
||||
}
|
||||
const held = (bytes) => new Held(bytes).b;
|
||||
const filled = (n) => held(Buffer.alloc(n));
|
||||
tm("fill Uint16Array", () => filled(6).fill(new Uint16Array([0x0102])));
|
||||
tm("fill DataView", () => filled(6).fill(new DataView(new Uint8Array([7, 8, 9]).buffer)));
|
||||
tm("fill DataView window", () => filled(6).fill(new DataView(new Uint8Array([1, 2, 3, 4]).buffer, 1, 2)));
|
||||
tm("fill Float64Array", () => filled(10).fill(new Float64Array([1])));
|
||||
tm("fill subarray", () => filled(6).fill(new Uint16Array([1, 2, 3]).subarray(1)));
|
||||
tm("fill Int8Array range", () => filled(6).fill(new Int8Array([-1, 2]), 1, 4));
|
||||
tm("fill empty Uint16Array", () => filled(6).fill(new Uint16Array(0)));
|
||||
tm("fill empty Uint8Array", () => filled(6).fill(new Uint8Array(0)));
|
||||
for (const [size, offset] of [[0, 0], [1, 1], [6, 6], [7, 7], [8, 8], [120, 3], [101, 101]])
|
||||
tm(`fill empty DataView ${size}/${offset}`, () => filled(6).fill(new DataView(new ArrayBuffer(size), offset, 0)));
|
||||
tm("fill empty DataView over bytes", () => filled(6).fill(new DataView(new Uint8Array(200).fill(171).buffer, 7, 0)));
|
||||
|
||||
// Buffer needles under utf16le search whole 2-byte units.
|
||||
const units = held(Buffer.from("abcabc", "utf16le"));
|
||||
const split = held(Buffer.from([0x61, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x63]));
|
||||
const N = (bytes) => Buffer.from(bytes);
|
||||
tm("indexOf utf16le needle", () => [split.indexOf(N([0x62, 0x00]), 0, "ucs2"), split.indexOf(N([0x62, 0x00]))]);
|
||||
tm("indexOf utf16le odd position", () => [
|
||||
split.indexOf(N([0x00, 0x62]), 0, "ucs2"),
|
||||
split.indexOf(N([0x00, 0x62]), 0, "UTF-16LE"),
|
||||
split.indexOf(N([0x00, 0x62])),
|
||||
]);
|
||||
tm("lastIndexOf utf16le needle", () => [
|
||||
units.lastIndexOf(N([0x62, 0x00]), undefined, "ucs2"),
|
||||
split.lastIndexOf(N([0x00, 0x62]), undefined, "ucs2"),
|
||||
...[5, 3, 2, 1, -100, 100].map((at) => split.lastIndexOf(N([0x62, 0x00]), at, "ucs2")),
|
||||
]);
|
||||
tm("indexOf utf16le offsets", () => [1, 2, 3, -5, 7, 2.9].map((at) => split.indexOf(N([0x62, 0x00]), at, "ucs2")));
|
||||
tm("indexOf utf16le short needles", () => [
|
||||
split.indexOf(N([0x62]), 0, "ucs2"),
|
||||
held(N([0x62])).indexOf(N([0x62]), 0, "ucs2"),
|
||||
split.indexOf(Buffer.alloc(0), 3, "ucs2"),
|
||||
split.indexOf(Buffer.alloc(0), 30, "ucs2"),
|
||||
split.indexOf(N([0x62, 0, 0]), 0, "ucs2"),
|
||||
]);
|
||||
tm("indexOf utf16le odd lengths", () => [
|
||||
held(N([1, 0, 2, 0, 3])).indexOf(N([2, 0, 3]), 0, "ucs2"),
|
||||
held(N([1, 0, 2, 0, 3, 0])).indexOf(N([2, 0, 3]), 0, "ucs2"),
|
||||
held(N([1, 2, 1, 2, 1, 2])).indexOf(N([2, 1, 2]), 0, "ucs2"),
|
||||
held(N([1, 0, 2, 0, 3])).lastIndexOf(N([3]), undefined, "ucs2"),
|
||||
held(N([0, 2, 0, 2, 0])).lastIndexOf(N([2, 0]), undefined, "ucs2"),
|
||||
]);
|
||||
tm("indexOf buffer needle other encodings", () => [
|
||||
split.indexOf(N([0x62, 0x00]), 0, "nope"),
|
||||
split.indexOf(N([0x00, 0x62]), 0, "nope"),
|
||||
split.indexOf(N([0x00, 0x62]), 0, "latin1"),
|
||||
]);
|
||||
tm("indexOf utf16le strings", () => [
|
||||
split.indexOf("b", 0, "ucs2"),
|
||||
units.lastIndexOf("b", undefined, "ucs2"),
|
||||
units.indexOf("b", 3, "ucs2"),
|
||||
units.lastIndexOf("b", 3, "ucs2"),
|
||||
units.indexOf("", 3, "ucs2"),
|
||||
units.indexOf("", 30, "ucs2"),
|
||||
units.lastIndexOf("", 30, "ucs2"),
|
||||
split.indexOf("戀", 0, "ucs2"),
|
||||
]);
|
||||
tm("includes utf16le needle", () => [split.includes(N([0x00, 0x62]), 0, "ucs2"), split.includes(N([0x62, 0x00]), 3, "ucs2")]);
|
||||
|
||||
// toString windows with infinite and out-of-range bounds.
|
||||
const word = held(Buffer.from("hello"));
|
||||
tm("toString infinite bounds", () => [
|
||||
word.toString("utf8", 1, Infinity),
|
||||
word.toString("utf8", -Infinity, 2),
|
||||
word.toString("utf8", Infinity),
|
||||
word.toString("utf8", NaN, 3),
|
||||
word.toString("utf8", 1e300, 2),
|
||||
word.toString("utf8", 1, 1e300),
|
||||
word.toString("utf8", -1e300, 1e300),
|
||||
word.toString("utf8", 0, -Infinity),
|
||||
word.toString("utf8", 2, NaN),
|
||||
word.toString("utf8", 0.5, 4.5),
|
||||
word.toString("hex", -0.5, -0),
|
||||
word.toString("latin1", 4.9),
|
||||
word.toString(undefined, undefined, Infinity),
|
||||
]);
|
||||
|
||||
// The same searches and windows on Buffers the compiler types statically.
|
||||
const typed = Buffer.from([0x61, 0x00, 0x62, 0x00, 0x00, 0x62, 0x00, 0x63]);
|
||||
const typedUnits = Buffer.from("abcabc", "utf16le");
|
||||
console.log(
|
||||
"typed utf16le",
|
||||
typed.indexOf(Buffer.from([0x00, 0x62]), 0, "ucs2"),
|
||||
Buffer.from([0x62]).indexOf(Buffer.from([0x62]), 0, "ucs2"),
|
||||
typed.lastIndexOf(Buffer.from([0x62, 0x00]), 5, "ucs2"),
|
||||
typed.indexOf(Buffer.from([0x62, 0x00]), 3, "ucs2"),
|
||||
typed.includes(Buffer.from([0x00, 0x62]), 0, "utf16le"),
|
||||
typed.indexOf("b", 0, "ucs2"),
|
||||
typedUnits.lastIndexOf("b", 100, "ucs2"),
|
||||
typedUnits.lastIndexOf("b", 3, "ucs2"),
|
||||
typedUnits.indexOf("", 30, "ucs2"),
|
||||
typedUnits.indexOf("b", 1e300, "ucs2"),
|
||||
typedUnits.indexOf("c", -1e300, "ucs2"),
|
||||
);
|
||||
const typedWord = Buffer.from("hello");
|
||||
console.log(
|
||||
"typed windows",
|
||||
JSON.stringify([
|
||||
typedWord.toString("utf8", 1, Infinity),
|
||||
typedWord.toString("utf8", -Infinity, 2),
|
||||
typedWord.toString("utf8", Infinity),
|
||||
typedWord.toString("utf8", NaN, 3),
|
||||
typedWord.toString("utf8", 1, 1e300),
|
||||
typedWord.toString("hex", 0, -Infinity),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "plain": 7800, "san": 39000 }
|
||||
{ "plain": 12000, "san": 39000 }
|
||||
|
||||
Reference in New Issue
Block a user