From 7d1f71862b4c03fc05b871ba9a17aeb68ceebee9 Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Tue, 6 Dec 2022 20:29:04 +0900 Subject: [PATCH] Change Array by copy (#537) * Add `Float16Array#withReversed` * Add `Float16Array#withSorted` * Add `Float16Array#withSpliced` * Add `Float16Array#withAt` * use 'to' prefix for method names https://github.com/tc39/proposal-change-array-by-copy/pull/50 * `.toSpliced` should throw a `TypeError` instead of `RangeError` https://github.com/tc39/proposal-change-array-by-copy/pull/70 * `.with` should convert `value` up front https://github.com/tc39/proposal-change-array-by-copy/pull/86 * Fix: `Float16Array#{toSorted, toSpliced}.length` * Avoid userland re-entrance after a read in `Float16Array#spliced` https://github.com/tc39/proposal-change-array-by-copy/pull/89 * Remove `Float16Array#toSpliced` https://github.com/tc39/proposal-change-array-by-copy/issues/88#issuecomment-1190709637 * Optimize `Float16Array#toSorted` * Add types of Change Array by Copy --- index.d.ts | 20 ++++++ src/Float16Array.mjs | 87 ++++++++++++++++++++++++ src/_util/messages.mjs | 2 + test/Float16Array.js | 147 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 255 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index c9d66ab7..7885948e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -330,6 +330,26 @@ export interface Float16Array { */ subarray(begin?: number, end?: number): Float16Array; + /** + * Copies the array and returns the copy with the elements in reverse order. + */ + toReversed(): Float16Array; + + /** + * Copies and sorts the array. + * @param compareFn Function used to determine the order of the elements. It is expected to return + * a negative value if first argument is less than second argument, zero if they're equal and a positive + * value otherwise. If omitted, the elements are sorted in ascending. + */ + toSorted(compareFn?: (a: number, b: number) => number): Float16Array; + + /** + * Copies the array and replaces the element at the given index with the provided value. + * @param index The zero-based location in the array for which to replace an element. + * @param value Element to insert into the array in place of the replaced element. + */ + with(index: number, value: number): Float16Array; + /** * Converts a number to a string by using the current locale. */ diff --git a/src/Float16Array.mjs b/src/Float16Array.mjs index feb25d95..8a652a78 100644 --- a/src/Float16Array.mjs +++ b/src/Float16Array.mjs @@ -20,6 +20,7 @@ import { OFFSET_IS_OUT_OF_BOUNDS, REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE, SPECIES_CONSTRUCTOR_DIDNT_RETURN_TYPEDARRAY_OBJECT, + THE_COMPARISON_FUNCTION_MUST_BE_EITHER_A_FUNCTION_OR_UNDEFINED, THIS_CONSTRUCTOR_IS_NOT_A_SUBCLASS_OF_FLOAT16ARRAY, THIS_IS_NOT_A_FLOAT16ARRAY_OBJECT, } from "./_util/messages.mjs"; @@ -524,6 +525,39 @@ export class Float16Array { return convertToNumber(float16bitsArray[k]); } + /** @see https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.with */ + with(index, value) { + assertFloat16Array(this); + const float16bitsArray = getFloat16BitsArray(this); + + const length = TypedArrayPrototypeGetLength(float16bitsArray); + const relativeIndex = ToIntegerOrInfinity(index); + const k = relativeIndex >= 0 ? relativeIndex : length + relativeIndex; + + const number = +value; + + if (k < 0 || k >= length) { + throw NativeRangeError(OFFSET_IS_OUT_OF_BOUNDS); + } + + // don't use SpeciesConstructor + const uint16 = new NativeUint16Array( + TypedArrayPrototypeGetBuffer(float16bitsArray), + TypedArrayPrototypeGetByteOffset(float16bitsArray), + TypedArrayPrototypeGetLength(float16bitsArray) + ); + const cloned = new Float16Array( + TypedArrayPrototypeGetBuffer( + TypedArrayPrototypeSlice(uint16) + ) + ); + const array = getFloat16BitsArray(cloned); + + array[k] = roundToFloat16Bits(number); + + return cloned; + } + /** @see https://tc39.es/ecma262/#sec-%typedarray%.prototype.map */ map(callback, ...opts) { assertFloat16Array(this); @@ -839,6 +873,29 @@ export class Float16Array { return this; } + /** @see https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.toReversed */ + toReversed() { + assertFloat16Array(this); + const float16bitsArray = getFloat16BitsArray(this); + + // don't use SpeciesConstructor + const uint16 = new NativeUint16Array( + TypedArrayPrototypeGetBuffer(float16bitsArray), + TypedArrayPrototypeGetByteOffset(float16bitsArray), + TypedArrayPrototypeGetLength(float16bitsArray) + ); + const cloned = new Float16Array( + TypedArrayPrototypeGetBuffer( + TypedArrayPrototypeSlice(uint16) + ) + ); + + const clonedFloat16bitsArray = getFloat16BitsArray(cloned); + TypedArrayPrototypeReverse(clonedFloat16bitsArray); + + return cloned; + } + /** @see https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill */ fill(value, ...opts) { assertFloat16Array(this); @@ -876,6 +933,36 @@ export class Float16Array { return this; } + /** @see https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.toSorted */ + toSorted(compareFn) { + assertFloat16Array(this); + const float16bitsArray = getFloat16BitsArray(this); + + if (compareFn !== undefined && typeof compareFn !== "function") { + throw new NativeTypeError(THE_COMPARISON_FUNCTION_MUST_BE_EITHER_A_FUNCTION_OR_UNDEFINED); + } + const sortCompare = compareFn !== undefined ? compareFn : defaultCompare; + + // don't use SpeciesConstructor + const uint16 = new NativeUint16Array( + TypedArrayPrototypeGetBuffer(float16bitsArray), + TypedArrayPrototypeGetByteOffset(float16bitsArray), + TypedArrayPrototypeGetLength(float16bitsArray) + ); + const cloned = new Float16Array( + TypedArrayPrototypeGetBuffer( + TypedArrayPrototypeSlice(uint16) + ) + ); + + const clonedFloat16bitsArray = getFloat16BitsArray(cloned); + TypedArrayPrototypeSort(clonedFloat16bitsArray, (x, y) => { + return sortCompare(convertToNumber(x), convertToNumber(y)); + }); + + return cloned; + } + /** @see https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice */ slice(start, end) { assertFloat16Array(this); diff --git a/src/_util/messages.mjs b/src/_util/messages.mjs index 9786dd5c..c43508ff 100644 --- a/src/_util/messages.mjs +++ b/src/_util/messages.mjs @@ -17,4 +17,6 @@ export const CANNOT_MIX_BIGINT_AND_OTHER_TYPES = export const ITERATOR_PROPERTY_IS_NOT_CALLABLE = "@@iterator property is not callable"; export const REDUCE_OF_EMPTY_ARRAY_WITH_NO_INITIAL_VALUE = "Reduce of empty array with no initial value"; +export const THE_COMPARISON_FUNCTION_MUST_BE_EITHER_A_FUNCTION_OR_UNDEFINED = + "The comparison function must be either a function or undefined"; export const OFFSET_IS_OUT_OF_BOUNDS = "Offset is out of bounds"; diff --git a/test/Float16Array.js b/test/Float16Array.js index 6ebb064b..ae1a1798 100644 --- a/test/Float16Array.js +++ b/test/Float16Array.js @@ -826,6 +826,60 @@ describe("Float16Array", () => { }); }); + describe("#with()", () => { + it("property `name` is 'with'", () => { + assert(Float16Array.prototype.with.name === "with"); + }); + + it("property `length` is 1", () => { + assert(Float16Array.prototype.with.length === 2); + }); + + it("with", () => { + const float16_1 = new Float16Array([1, 2, 3]); + const float16_2 = float16_1.with(1, 4); + + assert(float16_1.buffer !== float16_2.buffer); + assert.equalFloat16ArrayValues(float16_1, [1, 2, 3]); + assert.equalFloat16ArrayValues(float16_2, [1, 4, 3]); + + const float16_3 = float16_1.with(-1, 4); + + assert(float16_1.buffer !== float16_3.buffer); + assert.equalFloat16ArrayValues(float16_1, [1, 2, 3]); + assert.equalFloat16ArrayValues(float16_3, [1, 2, 4]); + + const float16_4 = float16_1.with(0, "aaa"); + + assert(float16_1.buffer !== float16_4.buffer); + assert.equalFloat16ArrayValues(float16_1, [1, 2, 3]); + assert.equalFloat16ArrayValues(float16_4, [NaN, 2, 3]); + }); + + it("throw Error with invalid index", () => { + const float16 = new Float16Array([1, 2, 3]); + + // out of range + assert.throws(() => { + float16.with(5, 0); + }, RangeError); + assert.throws(() => { + float16.with(-5, 0); + }, RangeError); + + assert.throws(() => { + float16.with(Symbol(), 0); + }, TypeError); + + // Safari 13 doesn't have BigInt + if (typeof BigInt !== "undefined") { + assert.throws(() => { + float16.with(BigInt(0), 0); + }, TypeError); + } + }); + }); + describe("#map()", () => { it("property `name` is 'map'", () => { assert(Float16Array.prototype.map.name === "map"); @@ -1436,6 +1490,25 @@ describe("Float16Array", () => { }); }); + describe("#toReversed()", () => { + it("property `name` is 'reverse'", () => { + assert(Float16Array.prototype.toReversed.name === "toReversed"); + }); + + it("property `length` is 0", () => { + assert(Float16Array.prototype.toReversed.length === 0); + }); + + it("toReversed", () => { + const float16_1 = new Float16Array([1, 2, 3]); + const float16_2 = float16_1.toReversed(); + + assert(float16_1.buffer !== float16_2.buffer); + assert.equalFloat16ArrayValues(float16_1, [1, 2, 3]); + assert.equalFloat16ArrayValues(float16_2, [3, 2, 1]); + }); + }); + describe("#fill()", () => { it("property `name` is 'fill'", () => { assert(Float16Array.prototype.fill.name === "fill"); @@ -1526,14 +1599,86 @@ describe("Float16Array", () => { it("check custom compare", () => { const float16 = new Float16Array([1, 2, -1, -2, Infinity, -Infinity]); - assert(float16.sort((x, y) => x - y) === float16); + assert(float16.sort((x, y) => y - x) === float16); assert.equalFloat16ArrayValues(float16, [ + Infinity, + 2, + 1, + -1, + -2, + -Infinity, + ]); + }); + }); + + describe("#toSorted()", () => { + it("property `name` is 'toSorted'", () => { + assert(Float16Array.prototype.toSorted.name === "toSorted"); + }); + + it("property `length` is 1", () => { + assert(Float16Array.prototype.toSorted.length === 1); + }); + + it("check default compare", () => { + const float16_1 = new Float16Array([ + 1, + 2, + -1, + -2, + 0, + -0, + NaN, + Infinity, + -Infinity, + ]); + const float16_2 = float16_1.toSorted(); + + assert(float16_1.buffer !== float16_2.buffer); + assert.equalFloat16ArrayValues(float16_1, [ + 1, + 2, + -1, + -2, + 0, + -0, + NaN, + Infinity, + -Infinity, + ]); + assert.equalFloat16ArrayValues(float16_2, [ -Infinity, -2, -1, + -0, + 0, 1, 2, Infinity, + NaN, + ]); + }); + + it("check custom compare", () => { + const float16_1 = new Float16Array([1, 2, -1, -2, Infinity, -Infinity]); + const float16_2 = float16_1.toSorted((x, y) => y - x); + + assert(float16_1.buffer !== float16_2.buffer); + assert.equalFloat16ArrayValues(float16_1, [ + 1, + 2, + -1, + -2, + Infinity, + -Infinity, + ]); + assert.equalFloat16ArrayValues(float16_2, [ + Infinity, + 2, + 1, + -1, + -2, + -Infinity, ]); }); });