mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
Convert Buffers to and from latin1 and hex text a word at a time (#2183)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/8005-buffer-encoding-paths.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -1586,6 +1586,7 @@ static ScrStr *scr_bytes_encode_b64(const uint8_t *in, size_t n, bool url) {
|
||||
if (!url) out[o++] = '=';
|
||||
}
|
||||
out[outlen] = '\0';
|
||||
scr_str_note_ascii(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -2546,6 +2547,106 @@ static ScrStr *scr_bytes_decode_utf16le(const uint8_t *in, size_t n) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* The UTF-8 bytes of U+0000..U+00FF: the byte itself below 0x80 (the
|
||||
* second entry is overwritten by the next write), else a two-byte
|
||||
* sequence. */
|
||||
static const uint8_t scr_latin1_utf8[256][2] = {
|
||||
{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
|
||||
{0x08, 0x00}, {0x09, 0x00}, {0x0a, 0x00}, {0x0b, 0x00}, {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
|
||||
{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
|
||||
{0x18, 0x00}, {0x19, 0x00}, {0x1a, 0x00}, {0x1b, 0x00}, {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
|
||||
{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
|
||||
{0x28, 0x00}, {0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00},
|
||||
{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
|
||||
{0x38, 0x00}, {0x39, 0x00}, {0x3a, 0x00}, {0x3b, 0x00}, {0x3c, 0x00}, {0x3d, 0x00}, {0x3e, 0x00}, {0x3f, 0x00},
|
||||
{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
|
||||
{0x48, 0x00}, {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, {0x4c, 0x00}, {0x4d, 0x00}, {0x4e, 0x00}, {0x4f, 0x00},
|
||||
{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00}, {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
|
||||
{0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00}, {0x5c, 0x00}, {0x5d, 0x00}, {0x5e, 0x00}, {0x5f, 0x00},
|
||||
{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00}, {0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
|
||||
{0x68, 0x00}, {0x69, 0x00}, {0x6a, 0x00}, {0x6b, 0x00}, {0x6c, 0x00}, {0x6d, 0x00}, {0x6e, 0x00}, {0x6f, 0x00},
|
||||
{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00}, {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
|
||||
{0x78, 0x00}, {0x79, 0x00}, {0x7a, 0x00}, {0x7b, 0x00}, {0x7c, 0x00}, {0x7d, 0x00}, {0x7e, 0x00}, {0x7f, 0x00},
|
||||
{0xc2, 0x80}, {0xc2, 0x81}, {0xc2, 0x82}, {0xc2, 0x83}, {0xc2, 0x84}, {0xc2, 0x85}, {0xc2, 0x86}, {0xc2, 0x87},
|
||||
{0xc2, 0x88}, {0xc2, 0x89}, {0xc2, 0x8a}, {0xc2, 0x8b}, {0xc2, 0x8c}, {0xc2, 0x8d}, {0xc2, 0x8e}, {0xc2, 0x8f},
|
||||
{0xc2, 0x90}, {0xc2, 0x91}, {0xc2, 0x92}, {0xc2, 0x93}, {0xc2, 0x94}, {0xc2, 0x95}, {0xc2, 0x96}, {0xc2, 0x97},
|
||||
{0xc2, 0x98}, {0xc2, 0x99}, {0xc2, 0x9a}, {0xc2, 0x9b}, {0xc2, 0x9c}, {0xc2, 0x9d}, {0xc2, 0x9e}, {0xc2, 0x9f},
|
||||
{0xc2, 0xa0}, {0xc2, 0xa1}, {0xc2, 0xa2}, {0xc2, 0xa3}, {0xc2, 0xa4}, {0xc2, 0xa5}, {0xc2, 0xa6}, {0xc2, 0xa7},
|
||||
{0xc2, 0xa8}, {0xc2, 0xa9}, {0xc2, 0xaa}, {0xc2, 0xab}, {0xc2, 0xac}, {0xc2, 0xad}, {0xc2, 0xae}, {0xc2, 0xaf},
|
||||
{0xc2, 0xb0}, {0xc2, 0xb1}, {0xc2, 0xb2}, {0xc2, 0xb3}, {0xc2, 0xb4}, {0xc2, 0xb5}, {0xc2, 0xb6}, {0xc2, 0xb7},
|
||||
{0xc2, 0xb8}, {0xc2, 0xb9}, {0xc2, 0xba}, {0xc2, 0xbb}, {0xc2, 0xbc}, {0xc2, 0xbd}, {0xc2, 0xbe}, {0xc2, 0xbf},
|
||||
{0xc3, 0x80}, {0xc3, 0x81}, {0xc3, 0x82}, {0xc3, 0x83}, {0xc3, 0x84}, {0xc3, 0x85}, {0xc3, 0x86}, {0xc3, 0x87},
|
||||
{0xc3, 0x88}, {0xc3, 0x89}, {0xc3, 0x8a}, {0xc3, 0x8b}, {0xc3, 0x8c}, {0xc3, 0x8d}, {0xc3, 0x8e}, {0xc3, 0x8f},
|
||||
{0xc3, 0x90}, {0xc3, 0x91}, {0xc3, 0x92}, {0xc3, 0x93}, {0xc3, 0x94}, {0xc3, 0x95}, {0xc3, 0x96}, {0xc3, 0x97},
|
||||
{0xc3, 0x98}, {0xc3, 0x99}, {0xc3, 0x9a}, {0xc3, 0x9b}, {0xc3, 0x9c}, {0xc3, 0x9d}, {0xc3, 0x9e}, {0xc3, 0x9f},
|
||||
{0xc3, 0xa0}, {0xc3, 0xa1}, {0xc3, 0xa2}, {0xc3, 0xa3}, {0xc3, 0xa4}, {0xc3, 0xa5}, {0xc3, 0xa6}, {0xc3, 0xa7},
|
||||
{0xc3, 0xa8}, {0xc3, 0xa9}, {0xc3, 0xaa}, {0xc3, 0xab}, {0xc3, 0xac}, {0xc3, 0xad}, {0xc3, 0xae}, {0xc3, 0xaf},
|
||||
{0xc3, 0xb0}, {0xc3, 0xb1}, {0xc3, 0xb2}, {0xc3, 0xb3}, {0xc3, 0xb4}, {0xc3, 0xb5}, {0xc3, 0xb6}, {0xc3, 0xb7},
|
||||
{0xc3, 0xb8}, {0xc3, 0xb9}, {0xc3, 0xba}, {0xc3, 0xbb}, {0xc3, 0xbc}, {0xc3, 0xbd}, {0xc3, 0xbe}, {0xc3, 0xbf},
|
||||
};
|
||||
|
||||
/* Each byte is U+00XX ("binary" normalizes here upstream): a byte below
|
||||
* 0x80 is one UTF-8 byte and any other byte is two. Eight bytes without a
|
||||
* high bit copy as one word; other bytes take their two table bytes and
|
||||
* advance by their length, without branches (the terminator slot holds
|
||||
* the last spare byte). */
|
||||
/* The number of bytes from 0x80: eight per step, each step adding its
|
||||
* high bits into byte lanes that are summed every 255 steps. */
|
||||
static size_t scr_bytes_count_high(const uint8_t *in, size_t n) {
|
||||
const uint64_t ones = UINT64_C(0x0101010101010101);
|
||||
const uint64_t pairs = UINT64_C(0x00ff00ff00ff00ff);
|
||||
size_t high = 0, i = 0;
|
||||
while (n - i >= 8) {
|
||||
size_t steps = (n - i) / 8 < 255 ? (n - i) / 8 : 255;
|
||||
uint64_t lanes = 0;
|
||||
for (size_t end = i + steps * 8; i < end; i += 8) {
|
||||
uint64_t w;
|
||||
memcpy(&w, in + i, 8);
|
||||
lanes += (w >> 7) & ones;
|
||||
}
|
||||
lanes = (lanes & pairs) + ((lanes >> 8) & pairs);
|
||||
high += (size_t)((lanes * UINT64_C(0x0001000100010001)) >> 48);
|
||||
}
|
||||
for (; i < n; i++) high += in[i] >> 7;
|
||||
return high;
|
||||
}
|
||||
|
||||
static ScrStr *scr_bytes_latin1_text(const uint8_t *in, size_t n) {
|
||||
size_t high = scr_bytes_count_high(in, n);
|
||||
if (n > SIZE_MAX - high) scr_bytes_oom();
|
||||
ScrStr *s = scr_str_alloc_raw(n + high, n + high);
|
||||
uint8_t *out = (uint8_t *)s->data;
|
||||
if (high == 0) {
|
||||
memcpy(out, in, n);
|
||||
out[n] = '\0';
|
||||
scr_str_note_ascii(s);
|
||||
return s;
|
||||
}
|
||||
const uint64_t top = UINT64_C(0x8080808080808080);
|
||||
size_t i = 0, o = 0;
|
||||
while (n - i >= 8) {
|
||||
uint64_t w;
|
||||
memcpy(&w, in + i, 8);
|
||||
if (!(w & top)) {
|
||||
memcpy(out + o, &w, 8);
|
||||
i += 8;
|
||||
o += 8;
|
||||
continue;
|
||||
}
|
||||
for (size_t end = i + 8; i < end; i++) {
|
||||
memcpy(out + o, scr_latin1_utf8[in[i]], 2);
|
||||
o += 1 + (in[i] >> 7);
|
||||
}
|
||||
}
|
||||
for (; i < n; i++) {
|
||||
memcpy(out + o, scr_latin1_utf8[in[i]], 2);
|
||||
o += 1 + (in[i] >> 7);
|
||||
}
|
||||
out[o] = '\0';
|
||||
scr_str_note_utf16_len(s, n);
|
||||
return s;
|
||||
}
|
||||
|
||||
ScrStr *scr_bytes_to_str(const ScrBytes *b, const ScrStr *enc) {
|
||||
SCR_BYTES_SNAPSHOT(b);
|
||||
const uint8_t *in = b->data;
|
||||
@@ -2561,21 +2662,12 @@ ScrStr *scr_bytes_to_str(const ScrBytes *b, const ScrStr *enc) {
|
||||
out[i * 2 + 1] = scr_hex_digits[in[i] & 0xf];
|
||||
}
|
||||
out[n * 2] = '\0';
|
||||
scr_str_note_ascii(s);
|
||||
return s;
|
||||
}
|
||||
if (scr_enc_is(enc, "base64")) return scr_bytes_encode_b64(in, n, false);
|
||||
if (scr_enc_is(enc, "base64url")) return scr_bytes_encode_b64(in, n, true);
|
||||
if (scr_enc_is(enc, "latin1")) {
|
||||
/* Each byte is U+00XX ("binary" normalizes here upstream). */
|
||||
size_t high = 0;
|
||||
for (size_t i = 0; i < n; i++) high += in[i] >> 7;
|
||||
if (n > SIZE_MAX - high) scr_bytes_oom();
|
||||
ScrStr *s = scr_str_alloc_raw(n + high, n + high);
|
||||
size_t o = 0;
|
||||
for (size_t i = 0; i < n; i++) o = scr_bytes_put_cp(s->data, o, in[i]);
|
||||
s->data[o] = '\0';
|
||||
return s;
|
||||
}
|
||||
if (scr_enc_is(enc, "latin1")) return scr_bytes_latin1_text(in, n);
|
||||
if (scr_enc_is(enc, "ascii")) {
|
||||
/* Node's ascii decode masks the high bit: byte & 0x7f. */
|
||||
ScrStr *s = scr_str_alloc_raw(n, n);
|
||||
@@ -2670,12 +2762,25 @@ ScrStr *scr_bytes_to_str_checked_range(const ScrBytes *b, const ScrStr *enc,
|
||||
return out;
|
||||
}
|
||||
|
||||
static int scr_hex_val(uint8_t c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
/* Hex digit values, -1 for any other byte. */
|
||||
static const int8_t scr_hex_values[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
/* Both alphabets are accepted by both Buffer base64 encoding names. */
|
||||
static const int8_t scr_b64_values[256] = {
|
||||
@@ -2816,19 +2921,82 @@ static ScrBytes *scr_bytes_encode_str(ScrBytesSink *sink, const ScrStr *s, const
|
||||
size_t n = s->len;
|
||||
if (scr_enc_is(enc, "latin1") || scr_enc_is(enc, "ascii")) {
|
||||
/* Node writes charCodeAt(i) & 0xFF for BOTH spellings — an astral
|
||||
* code point contributes its two surrogates' low bytes. */
|
||||
* code point contributes its two surrogates' low bytes. The ASCII
|
||||
* prefix copies. Elsewhere eight ASCII bytes copy as one word. In eight bytes
|
||||
* holding two or more high bytes, all of them in two-byte sequences,
|
||||
* every continuation byte's output byte (the code point's low byte,
|
||||
* from the continuation and its lead) is computed in one word; each
|
||||
* byte is then stored without branches, a continuation replacing the
|
||||
* byte its lead wrote. A lead in a word's last byte stays pending: its
|
||||
* continuation, if the next byte is one, replaces it the same way. Any
|
||||
* other byte decodes with scr_bytes_next_cp. */
|
||||
ScrBytes *b = scr_bytes_conversion_storage_in(sink, n);
|
||||
/* A local copy: a byte store could otherwise change b->data. */
|
||||
uint8_t *dst = b->data;
|
||||
const uint64_t top = UINT64_C(0x8080808080808080);
|
||||
/* An ASCII prefix (all of an ASCII string) is its own encoding. */
|
||||
size_t o = s->ascii ? n : scr_bytes_ascii_prefix(in, n), i = o;
|
||||
memcpy(b->data, in, o);
|
||||
memcpy(dst, in, o);
|
||||
/* in[i - 1] is a two-byte lead (0xC2..0xDF) the word path wrote as
|
||||
* dst[o - 1], and in[i] is not yet known to continue it. */
|
||||
bool pending = false;
|
||||
while (i < n) {
|
||||
if (n - i >= 8) {
|
||||
uint64_t w;
|
||||
memcpy(&w, in + i, 8);
|
||||
uint64_t high = w & top;
|
||||
if (!high) {
|
||||
memcpy(dst + o, &w, 8);
|
||||
i += 8;
|
||||
o += 8;
|
||||
pending = false;
|
||||
continue;
|
||||
}
|
||||
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Byte k of the input is bits 8k..8k+7 of w; bit 8k+7 of w << j
|
||||
* is bit 7 - j of byte k. */
|
||||
uint64_t cont = w & ~(w << 1) & top; /* 10xxxxxx */
|
||||
uint64_t lead = w & (w << 1) & ~(w << 2) & top; /* 110xxxxx */
|
||||
/* 0xC0 and 0xC1 (bits 4..1 clear) only begin overlong forms. */
|
||||
uint64_t overlong = lead & ~((w << 3) | (w << 4) | (w << 5) | (w << 6));
|
||||
if ((high & (high - 1)) && high == (cont | lead) && !overlong &&
|
||||
cont == ((lead << 8) | (pending ? 0x80u : 0u))) {
|
||||
uint64_t prev = (w << 8) | (pending ? in[i - 1] : 0u);
|
||||
uint64_t low = ((prev & UINT64_C(0x0303030303030303)) << 6) |
|
||||
(w & UINT64_C(0x3f3f3f3f3f3f3f3f));
|
||||
uint64_t mask = (cont >> 7) * 0xff;
|
||||
uint64_t out = (w & ~mask) | (low & mask);
|
||||
for (unsigned k = 0; k < 8; k++) {
|
||||
o -= (size_t)(cont >> (8 * k + 7)) & 1;
|
||||
dst[o++] = (uint8_t)(out >> (8 * k));
|
||||
}
|
||||
i += 8;
|
||||
pending = (lead >> 63) != 0;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
unsigned c = in[i];
|
||||
if (pending && c >> 6 == 2) {
|
||||
/* The rest of the two-byte sequence whose lead ended a word. */
|
||||
dst[o - 1] = (uint8_t)(((in[i - 1] & 3u) << 6) | (c & 0x3f));
|
||||
i++;
|
||||
pending = false;
|
||||
continue;
|
||||
}
|
||||
pending = false;
|
||||
if (c < 0x80) {
|
||||
dst[o++] = (uint8_t)c;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
uint32_t cp = scr_bytes_next_cp(in, n, &i);
|
||||
if (cp > 0xffff) {
|
||||
cp -= 0x10000;
|
||||
b->data[o++] = (uint8_t)(0xd800 + (cp >> 10));
|
||||
b->data[o++] = (uint8_t)(0xdc00 + (cp & 0x3ff));
|
||||
dst[o++] = (uint8_t)(0xd800 + (cp >> 10));
|
||||
dst[o++] = (uint8_t)(0xdc00 + (cp & 0x3ff));
|
||||
} else {
|
||||
b->data[o++] = (uint8_t)cp;
|
||||
dst[o++] = (uint8_t)cp;
|
||||
}
|
||||
}
|
||||
b->len = o; /* never grows past n: multi-byte cps shrink */
|
||||
@@ -2871,11 +3039,12 @@ static ScrBytes *scr_bytes_encode_str(ScrBytesSink *sink, const ScrStr *s, const
|
||||
if (hex) {
|
||||
/* Stop at the first invalid pair, discarding an odd tail. */
|
||||
b = scr_bytes_conversion_storage_in(sink, n / 2);
|
||||
size_t o = 0;
|
||||
for (size_t i = 0; i + 1 < n; i += 2) {
|
||||
int hi = scr_hex_val(in[i]), lo = scr_hex_val(in[i + 1]);
|
||||
if (hi < 0 || lo < 0) break;
|
||||
b->data[o++] = (uint8_t)((hi << 4) | lo);
|
||||
uint8_t *dst = b->data;
|
||||
size_t pairs = n / 2, o = 0;
|
||||
for (; o < pairs; o++) {
|
||||
int hi = scr_hex_values[in[2 * o]], lo = scr_hex_values[in[2 * o + 1]];
|
||||
if ((hi | lo) < 0) break;
|
||||
dst[o] = (uint8_t)((hi << 4) | lo);
|
||||
}
|
||||
b->len = o;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Buffer latin1, ascii, hex and base64 conversions in both directions:
|
||||
// ASCII, binary and mixed contents at sizes around eight-byte words and the
|
||||
// pool threshold, strings with code points past U+00FF, invalid hex and
|
||||
// base64 text, and the lengths and characters of the strings produced.
|
||||
|
||||
let seed = 12345;
|
||||
function next(): number {
|
||||
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
||||
return seed >>> 8;
|
||||
}
|
||||
|
||||
function fill(size: number, kind: string): Buffer {
|
||||
const buf = Buffer.alloc(size);
|
||||
for (let i = 0; i < size; i++) {
|
||||
const r = next();
|
||||
if (kind === "ascii") buf[i] = 32 + (r % 95);
|
||||
else if (kind === "binary") buf[i] = r & 255;
|
||||
else if (kind === "high") buf[i] = 128 + (r % 128);
|
||||
else buf[i] = r % 23 === 0 ? 160 + (r % 96) : 97 + (r % 26);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
function digest(text: string): number {
|
||||
let h = 0;
|
||||
for (let i = 0; i < text.length; i++) h = (h * 31 + text.charCodeAt(i)) >>> 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
const sizes = [0, 1, 7, 8, 9, 15, 16, 17, 63, 64, 65, 4095, 4096, 4097, 70001];
|
||||
for (const kind of ["ascii", "binary", "high", "sparse"]) {
|
||||
const rows: string[] = [];
|
||||
for (const size of sizes) {
|
||||
const buf = fill(size, kind);
|
||||
const latin1 = buf.toString("latin1");
|
||||
const back = Buffer.from(latin1, "latin1");
|
||||
const ascii = Buffer.from(latin1, "ascii");
|
||||
const binary = Buffer.from(buf.toString("binary"), "binary");
|
||||
const hex = buf.toString("hex");
|
||||
const b64 = buf.toString("base64");
|
||||
const url = buf.toString("base64url");
|
||||
rows.push(
|
||||
[
|
||||
size,
|
||||
latin1.length,
|
||||
digest(latin1),
|
||||
back.equals(buf),
|
||||
ascii.equals(buf),
|
||||
binary.equals(buf),
|
||||
Buffer.from(hex, "hex").equals(buf),
|
||||
Buffer.from(b64, "base64").equals(buf),
|
||||
Buffer.from(url, "base64url").equals(buf),
|
||||
hex.length,
|
||||
b64.length,
|
||||
Buffer.byteLength(latin1, "latin1"),
|
||||
size > 2 ? latin1.charCodeAt(size - 2) : -1,
|
||||
size > 4 ? latin1.slice(1, 4).length : -1,
|
||||
].join(","),
|
||||
);
|
||||
}
|
||||
console.log(kind, rows.join(" "));
|
||||
}
|
||||
|
||||
// Strings with code points past U+00FF keep the low byte of each UTF-16
|
||||
// unit, and an astral code point gives its two surrogates' low bytes.
|
||||
const wide = [
|
||||
"abcéÿ",
|
||||
"ĀŁ߿",
|
||||
"xࠀ€y",
|
||||
"\u{1F600}a\u{10FFFF}",
|
||||
"é".repeat(9) + "€" + "è".repeat(9),
|
||||
"aaaaaaaébbbbbbbb中cccccccc\u{1F600}",
|
||||
"ÀÁÂÃÄÅÆÇࠀÈ",
|
||||
];
|
||||
for (const text of wide) {
|
||||
console.log(
|
||||
Buffer.from(text, "latin1").toString("hex"),
|
||||
Buffer.from(text, "ascii").toString("hex"),
|
||||
Buffer.byteLength(text, "latin1"),
|
||||
);
|
||||
}
|
||||
const mixed = "é".repeat(5000) + "中" + "z".repeat(5000) + "ÿ".repeat(3000);
|
||||
const mixedBytes = Buffer.from(mixed, "latin1");
|
||||
console.log(mixedBytes.length, mixedBytes[0], mixedBytes[5000], mixedBytes[5001], mixedBytes[13000]);
|
||||
|
||||
// Hex stops at the first invalid pair and drops an odd last digit.
|
||||
for (const text of ["0aFf", "0aF", "0a1g22", "zz00", "", "ABCDEF0123456789", "0aİ0b", "00a"]) {
|
||||
console.log(JSON.stringify(text), Buffer.from(text, "hex").toString("hex"), Buffer.byteLength(text, "hex"));
|
||||
}
|
||||
const longHex = "0123456789abcdef".repeat(600) + "0x" + "ff".repeat(10);
|
||||
console.log(Buffer.from(longHex, "hex").length);
|
||||
|
||||
// base64 skips characters outside both alphabets and stops at padding.
|
||||
for (const text of ["aGVsbG8=", "aGVsbG8", "aGV sbG8=", "aGV\nsbG8", "-_-_", "+/+/", "aGVsbG8=d29ybGQ=", "a", "ab", "abc", "aébc", "====", "YQ"]) {
|
||||
console.log(JSON.stringify(text), Buffer.from(text, "base64").toString("hex"), Buffer.from(text, "base64url").toString("hex"));
|
||||
}
|
||||
|
||||
// Strings produced by hex and base64 behave as ASCII text afterwards.
|
||||
const produced = fill(5000, "binary");
|
||||
const producedHex = produced.toString("hex");
|
||||
const joined = producedHex + "é" + produced.toString("base64");
|
||||
console.log(joined.length, joined.indexOf("é"), digest(joined.slice(9990, 10020)));
|
||||
const latinText = fill(9000, "high").toString("latin1");
|
||||
const appended = latinText + "€";
|
||||
console.log(latinText.length, appended.length, appended.charCodeAt(8999), appended.charCodeAt(9000), appended.slice(8998).length);
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2558, "san": 4160 }
|
||||
Reference in New Issue
Block a user