diff --git a/src/CharWidth.ts b/src/CharWidth.ts index d099b109..341b347a 100644 --- a/src/CharWidth.ts +++ b/src/CharWidth.ts @@ -1,3 +1,5 @@ +import { fill } from './core/TypedArrayUtils'; + /** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT @@ -91,26 +93,26 @@ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: nu // create lookup table for BMP plane const table = new Uint8Array(65536); - table.fill(1); + fill(table, 1); table[0] = opts.nul; // control chars - table.subarray(1, 32).fill(opts.control); - table.subarray(0x7f, 0xa0).fill(opts.control); + fill(table, opts.control, 1, 32); + fill(table, opts.control, 0x7f, 0xa0); // apply wide char rules first // wide chars - table.subarray(0x1100, 0x1160).fill(2); + fill(table, 2, 0x1100, 0x1160); table[0x2329] = 2; table[0x232a] = 2; - table.subarray(0x2e80, 0xa4d0).fill(2); + fill(table, 2, 0x2e80, 0xa4d0); table[0x303f] = 1; // wrongly in last line - table.subarray(0xac00, 0xd7a4).fill(2); - table.subarray(0xf900, 0xfb00).fill(2); - table.subarray(0xfe10, 0xfe1a).fill(2); - table.subarray(0xfe30, 0xfe70).fill(2); - table.subarray(0xff00, 0xff61).fill(2); - table.subarray(0xffe0, 0xffe7).fill(2); + fill(table, 2, 0xac00, 0xd7a4); + fill(table, 2, 0xf900, 0xfb00); + fill(table, 2, 0xfe10, 0xfe1a); + fill(table, 2, 0xfe30, 0xfe70); + fill(table, 2, 0xff00, 0xff61); + fill(table, 2, 0xffe0, 0xffe7); // apply combining last to ensure we overwrite // wrongly wide set chars: @@ -118,7 +120,7 @@ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: nu // through to wide check so we simply do here the opposite // combining 0 for (let r = 0; r < COMBINING_BMP.length; ++r) { - table.subarray(COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1).fill(0); + fill(table, 0, COMBINING_BMP[r][0], COMBINING_BMP[r][1] + 1); } return function (num: number): number { diff --git a/src/core/TypedArrayUtils.test.ts b/src/core/TypedArrayUtils.test.ts new file mode 100644 index 00000000..2e45d371 --- /dev/null +++ b/src/core/TypedArrayUtils.test.ts @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { assert } from 'chai'; +import { fill } from './TypedArrayUtils'; + +type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray + | Int8Array | Int16Array | Int32Array + | Float32Array | Float64Array; + +function loopFill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray { + if (start >= array.length) { + return array; + } + start = (array.length + start) % array.length; + if (end === undefined) { + end = array.length; + } + if (end >= array.length) { + end = array.length; + } else { + end = (array.length + end) % array.length; + } + for (let i = start; i < end; ++i) { + array[i] = value; + } + return array; +} + +function deepEquals(a: TypedArray, b: TypedArray) { + assert.equal(a.length, b.length); + for (let i = 0; i < a.length; ++i) { + assert.equal(a[i], b[i]); + } +} + + +describe('polyfill conformance tests', function(): void { + describe('TypedArray.fill', function(): void { + it('should work with all typed array types', function(): void { + const u8_1 = new Uint8Array(5); + const u8_2 = new Uint8Array(5); + deepEquals(fill(u8_1, 2), u8_2.fill(2)); + deepEquals(fill(u8_1, -1), u8_2.fill(-1)); + const u16_1 = new Uint16Array(5); + const u16_2 = new Uint16Array(5); + deepEquals(fill(u16_1, 2), u16_2.fill(2)); + deepEquals(fill(u16_1, 65535), u16_2.fill(65535)); + deepEquals(fill(u16_1, -1), u16_2.fill(-1)); + const u32_1 = new Uint32Array(5); + const u32_2 = new Uint32Array(5); + deepEquals(fill(u32_1, 2), u32_2.fill(2)); + deepEquals(fill(u32_1, 65537), u32_2.fill(65537)); + deepEquals(fill(u32_1, -1), u32_2.fill(-1)); + const i8_1 = new Int8Array(5); + const i8_2 = new Int8Array(5); + deepEquals(fill(i8_1, 2), i8_2.fill(2)); + deepEquals(fill(i8_1, -1), i8_2.fill(-1)); + const i16_1 = new Int16Array(5); + const i16_2 = new Int16Array(5); + deepEquals(fill(i16_1, 2), i16_2.fill(2)); + deepEquals(fill(i16_1, 65535), i16_2.fill(65535)); + deepEquals(fill(i16_1, -1), i16_2.fill(-1)); + const i32_1 = new Int32Array(5); + const i32_2 = new Int32Array(5); + deepEquals(fill(i32_1, 2), i32_2.fill(2)); + deepEquals(fill(i32_1, 65537), i32_2.fill(65537)); + deepEquals(fill(i32_1, -1), i32_2.fill(-1)); + const f32_1 = new Float32Array(5); + const f32_2 = new Float32Array(5); + deepEquals(fill(f32_1, 1.2345), f32_2.fill(1.2345)); + const f64_1 = new Float64Array(5); + const f64_2 = new Float64Array(5); + deepEquals(fill(f64_1, 1.2345), f64_2.fill(1.2345)); + const u8Clamped_1 = new Uint8ClampedArray(5); + const u8Clamped_2 = new Uint8ClampedArray(5); + deepEquals(fill(u8Clamped_1, 2), u8Clamped_2.fill(2)); + deepEquals(fill(u8Clamped_1, 257), u8Clamped_2.fill(257)); + }); + it('should work with all typed array types - explicit looping', function(): void { + const u8_1 = new Uint8Array(5); + const u8_2 = new Uint8Array(5); + deepEquals(loopFill(u8_1, 2), u8_2.fill(2)); + deepEquals(loopFill(u8_1, -1), u8_2.fill(-1)); + const u16_1 = new Uint16Array(5); + const u16_2 = new Uint16Array(5); + deepEquals(loopFill(u16_1, 2), u16_2.fill(2)); + deepEquals(loopFill(u16_1, 65535), u16_2.fill(65535)); + deepEquals(loopFill(u16_1, -1), u16_2.fill(-1)); + const u32_1 = new Uint32Array(5); + const u32_2 = new Uint32Array(5); + deepEquals(loopFill(u32_1, 2), u32_2.fill(2)); + deepEquals(loopFill(u32_1, 65537), u32_2.fill(65537)); + deepEquals(loopFill(u32_1, -1), u32_2.fill(-1)); + const i8_1 = new Int8Array(5); + const i8_2 = new Int8Array(5); + deepEquals(loopFill(i8_1, 2), i8_2.fill(2)); + deepEquals(loopFill(i8_1, -1), i8_2.fill(-1)); + const i16_1 = new Int16Array(5); + const i16_2 = new Int16Array(5); + deepEquals(loopFill(i16_1, 2), i16_2.fill(2)); + deepEquals(loopFill(i16_1, 65535), i16_2.fill(65535)); + deepEquals(loopFill(i16_1, -1), i16_2.fill(-1)); + const i32_1 = new Int32Array(5); + const i32_2 = new Int32Array(5); + deepEquals(loopFill(i32_1, 2), i32_2.fill(2)); + deepEquals(loopFill(i32_1, 65537), i32_2.fill(65537)); + deepEquals(loopFill(i32_1, -1), i32_2.fill(-1)); + const f32_1 = new Float32Array(5); + const f32_2 = new Float32Array(5); + deepEquals(loopFill(f32_1, 1.2345), f32_2.fill(1.2345)); + const f64_1 = new Float64Array(5); + const f64_2 = new Float64Array(5); + deepEquals(loopFill(f64_1, 1.2345), f64_2.fill(1.2345)); + const u8Clamped_1 = new Uint8ClampedArray(5); + const u8Clamped_2 = new Uint8ClampedArray(5); + deepEquals(loopFill(u8Clamped_1, 2), u8Clamped_2.fill(2)); + deepEquals(loopFill(u8Clamped_1, 257), u8Clamped_2.fill(257)); + }); + it('start offset', function(): void { + for (let i = -2; i < 10; ++i) { + const u8_1 = new Uint8Array(5); + const u8_2 = new Uint8Array(5); + const u8_3 = new Uint8Array(5); + deepEquals(fill(u8_1, 2, i), u8_3.fill(2, i)); + deepEquals(fill(u8_1, -1, i), u8_3.fill(-1, i)); + deepEquals(loopFill(u8_2, 2, i), u8_3.fill(2, i)); + deepEquals(loopFill(u8_2, -1, i), u8_3.fill(-1, i)); + } + }); + it('end offset', function(): void { + for (let i = -2; i < 10; ++i) { + const u8_1 = new Uint8Array(5); + const u8_2 = new Uint8Array(5); + const u8_3 = new Uint8Array(5); + deepEquals(fill(u8_1, 2, 0, i), u8_3.fill(2, 0, i)); + deepEquals(fill(u8_1, -1, 0, i), u8_3.fill(-1, 0, i)); + deepEquals(loopFill(u8_2, 2, 0, i), u8_3.fill(2, 0, i)); + deepEquals(loopFill(u8_2, -1, 0, i), u8_3.fill(-1, 0, i)); + } + }); + it('start/end offset', function(): void { + for (let i = -2; i < 10; ++i) { + for (let j = -2; j < 10; ++j) { + const u8_1 = new Uint8Array(5); + const u8_2 = new Uint8Array(5); + const u8_3 = new Uint8Array(5); + deepEquals(fill(u8_1, 2, i, j), u8_3.fill(2, i, j)); + deepEquals(fill(u8_1, -1, i, j), u8_3.fill(-1, i, j)); + deepEquals(loopFill(u8_2, 2, i, j), u8_3.fill(2, i, j)); + deepEquals(loopFill(u8_2, -1, i, j), u8_3.fill(-1, i, j)); + } + } + }); + }); +}); diff --git a/src/core/TypedArrayUtils.ts b/src/core/TypedArrayUtils.ts new file mode 100644 index 00000000..56e9d7b0 --- /dev/null +++ b/src/core/TypedArrayUtils.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * polyfill for TypedArray.fill + * This is needed to support .fill in all safari versions and IE 11. + */ + +type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray + | Int8Array | Int16Array | Int32Array + | Float32Array | Float64Array; + +export function fill(array: TypedArray, value: number, start: number = 0, end?: number | undefined): TypedArray { + // all modern engines that support .fill + if (array.fill) { + return array.fill(value, start, end); + } + // safari and IE 11 + // since IE 11 does not support Array.prototype.fill either + // we cannot use the suggested polyfill from MDN + // instead we simply fall back to looping + if (start >= array.length) { + return array; + } + start = (array.length + start) % array.length; + if (end === undefined) { + end = array.length; + } + if (end >= array.length) { + end = array.length; + } else { + end = (array.length + end) % array.length; + } + for (let i = start; i < end; ++i) { + array[i] = value; + } + return array; +}