mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
Implements a fast check to determine if a string is a valid byte string (only chars <= 0xff). Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/66298 Reviewed-By: René <contact.9a5d6388@renegade334.me.uk> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Robert Nagy <ronagy@icloud.com>
86 lines
3.0 KiB
JavaScript
86 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
require('../common');
|
|
const assert = require('assert');
|
|
const { isLatin1 } = require('buffer');
|
|
|
|
function reference(str) {
|
|
for (let i = 0; i < str.length; i++) {
|
|
if (str.charCodeAt(i) > 0xFF) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Basic cases.
|
|
assert.strictEqual(isLatin1(''), true);
|
|
assert.strictEqual(isLatin1('hello'), true);
|
|
assert.strictEqual(isLatin1('\x00'), true);
|
|
assert.strictEqual(isLatin1('\x7f\x80'), true);
|
|
assert.strictEqual(isLatin1('caf\u00e9'), true);
|
|
assert.strictEqual(isLatin1('\u00ff'), true);
|
|
assert.strictEqual(isLatin1('\u0100'), false);
|
|
assert.strictEqual(isLatin1('\u20ac'), false);
|
|
assert.strictEqual(isLatin1('\uffff'), false);
|
|
// Surrogate pairs and lone surrogates are > 0xFF.
|
|
assert.strictEqual(isLatin1('\ud83d\ude00'), false);
|
|
assert.strictEqual(isLatin1('\ud800'), false);
|
|
assert.strictEqual(isLatin1('\udfff'), false);
|
|
|
|
// Position of the offending code unit must not matter, and long strings must
|
|
// exercise the vectorized paths.
|
|
for (const len of [1, 7, 8, 15, 16, 31, 32, 33, 63, 64, 65, 1000, 4099]) {
|
|
const base = 'a\u00ff'.repeat(len).slice(0, len);
|
|
assert.strictEqual(isLatin1(base), true);
|
|
for (const pos of [0, len >> 1, len - 1]) {
|
|
for (const ch of ['\u0100', '\u1234', '\ud800', '\uffff']) {
|
|
const str = base.slice(0, pos) + ch + base.slice(pos + 1);
|
|
assert.strictEqual(isLatin1(str), false, `len=${len} pos=${pos}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Strings stored with a two-byte representation that only contain code units
|
|
// <= 0xFF must still be reported as latin1.
|
|
{
|
|
const twoByte = '\u0100' + 'abc\u00e9\u00ff'.repeat(100);
|
|
const sliced = twoByte.slice(1);
|
|
assert.strictEqual(isLatin1(twoByte), false);
|
|
assert.strictEqual(isLatin1(sliced), true);
|
|
assert.strictEqual(isLatin1(twoByte.substring(1, 20)), true);
|
|
}
|
|
|
|
// Cons strings (results of concatenation) with mixed representations.
|
|
{
|
|
let cons = '';
|
|
for (let i = 0; i < 100; i++) cons += `x${i}\u00e9`;
|
|
assert.strictEqual(isLatin1(cons), true);
|
|
assert.strictEqual(isLatin1(cons + '\u0100'), false);
|
|
assert.strictEqual(isLatin1('\u0100' + cons), false);
|
|
assert.strictEqual(isLatin1(cons + '\u0100'.slice(1) + cons), true);
|
|
}
|
|
|
|
// Randomized comparison with the reference implementation.
|
|
for (let i = 0; i < 1000; i++) {
|
|
const len = Math.floor(Math.random() * 100);
|
|
const max = Math.random() < 0.5 ? 0x100 : 0x10000;
|
|
let str = '';
|
|
for (let j = 0; j < len; j++) {
|
|
// Keep the probability of producing a code unit > 0xFF low so that
|
|
// both outcomes are exercised.
|
|
const code = Math.random() < 0.98 ?
|
|
Math.floor(Math.random() * 0x100) :
|
|
Math.floor(Math.random() * max);
|
|
str += String.fromCharCode(code);
|
|
}
|
|
assert.strictEqual(isLatin1(str), reference(str), JSON.stringify(str));
|
|
}
|
|
|
|
// Invalid argument types.
|
|
[
|
|
undefined, null, 1, 1n, true, {}, [], Symbol('a'),
|
|
Buffer.from('a'), new Uint8Array(1), new ArrayBuffer(1),
|
|
new String('a'),
|
|
].forEach((input) => {
|
|
assert.throws(() => isLatin1(input), { code: 'ERR_INVALID_ARG_TYPE' });
|
|
});
|