'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' }); });