Skip to content

Commit

Permalink
Change Array by copy (#537)
Browse files Browse the repository at this point in the history
* Add `Float16Array#withReversed`

* Add `Float16Array#withSorted`

* Add `Float16Array#withSpliced`

* Add `Float16Array#withAt`

* use 'to' prefix for method names

tc39/proposal-change-array-by-copy#50

* `.toSpliced` should throw a `TypeError` instead of `RangeError`

tc39/proposal-change-array-by-copy#70

* `.with` should convert `value` up front

tc39/proposal-change-array-by-copy#86

* Fix: `Float16Array#{toSorted, toSpliced}.length`

* Avoid userland re-entrance after a read in `Float16Array#spliced`

tc39/proposal-change-array-by-copy#89

* Remove `Float16Array#toSpliced`

tc39/proposal-change-array-by-copy#88 (comment)

* Optimize `Float16Array#toSorted`

* Add types of Change Array by Copy
  • Loading branch information
petamoriken committed Dec 6, 2022
1 parent f714553 commit 7d1f718
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 1 deletion.
20 changes: 20 additions & 0 deletions index.d.ts
Expand Up @@ -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.
*/
Expand Down
87 changes: 87 additions & 0 deletions src/Float16Array.mjs
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/_util/messages.mjs
Expand Up @@ -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";
147 changes: 146 additions & 1 deletion test/Float16Array.js
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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,
]);
});
});
Expand Down

0 comments on commit 7d1f718

Please sign in to comment.