Skip to content

Commit

Permalink
Add Float16Array#withAt
Browse files Browse the repository at this point in the history
  • Loading branch information
petamoriken committed Sep 10, 2021
1 parent 4340868 commit 140a6b5
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 10 deletions.
43 changes: 41 additions & 2 deletions src/Float16Array.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { wrapInArrayIterator } from "./helper/arrayIterator.mjs";
import { convertToNumber, roundToFloat16Bits } from "./helper/converter.mjs";
import { isArrayBuffer, isCanonicalIntegerIndexString, isIterable, isObject, isObjectLike, isOrdinaryArray, isOrdinaryTypedArray, isSharedArrayBuffer, isTypedArray, isUint16Array } from "./helper/is.mjs";
import { isArrayBuffer, isCanonicalIntegerIndexString, isIntegralNumber, isIterable, isObject, isObjectLike, isOrdinaryArray, isOrdinaryTypedArray, isSharedArrayBuffer, isTypedArray, isUint16Array } from "./helper/is.mjs";
import { createPrivateStorage } from "./helper/private.mjs";
import { LengthOfArrayLike, SpeciesConstructor, ToIntegerOrInfinity, defaultCompare } from "./helper/spec.mjs";
import { CanonicalNumericIndexString, LengthOfArrayLike, SpeciesConstructor, ToIntegerOrInfinity, defaultCompare } from "./helper/spec.mjs";

const brand = Symbol.for("__Float16Array__");

Expand Down Expand Up @@ -399,6 +399,45 @@ export class Float16Array extends Uint16Array {
return convertToNumber(this[k]);
}

withAt(index, value) {
assertFloat16BitsArray(this);

const length = this.length;

/** @type {number} */
let numericIndex;

if (typeof index === "string") {
numericIndex = CanonicalNumericIndexString(index);
if (numericIndex === undefined) {
throw TypeError("Invalid index type");
}

} else if (typeof index === "number") {
numericIndex = index;

} else {
throw TypeError("Invalid index type");
}

if (!isIntegralNumber(numericIndex)) {
throw RangeError("Invalid index");
}

const actualIndex = numericIndex >= 0 ? numericIndex : length + numericIndex;
if (!isIntegralNumber(actualIndex) || actualIndex < 0 || actualIndex >= length) {
throw RangeError("Invalid index");
}

const uint16 = new Uint16Array(this.buffer, this.byteOffset, this.length);
const proxy = new Float16Array(uint16.slice().buffer);
const float16bitsArray = getFloat16BitsArrayFromFloat16Array(proxy);

float16bitsArray[actualIndex] = roundToFloat16Bits(value);

return proxy;
}

/**
* @see https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
*/
Expand Down
28 changes: 20 additions & 8 deletions src/helper/is.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,37 @@ export function isOrdinaryTypedArray(value) {

/**
* @param {unknown} value
* @returns {value is string}
* @returns {value is number}
*/
export function isCanonicalIntegerIndexString(value) {
if (typeof value !== "string") {
export function isIntegralNumber(value) {
if (typeof value !== "number") {
return false;
}

const number = Number(value);
if (value !== number + "") {
if (!Number.isFinite(value)) {
return false;
}

if (!Number.isFinite(number)) {
if (value !== Math.trunc(value)) {
return false;
}

return true;
}

/**
* @param {unknown} value
* @returns {value is string}
*/
export function isCanonicalIntegerIndexString(value) {
if (typeof value !== "string") {
return false;
}

if (number !== Math.trunc(number)) {
const number = Number(value);
if (value !== number + "") {
return false;
}

return true;
return isIntegralNumber(number);
}
18 changes: 18 additions & 0 deletions src/helper/spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ function ToLength(target) {
return length < Number.MAX_SAFE_INTEGER ? length : Number.MAX_SAFE_INTEGER;
}

/**
* @see https://tc39.es/ecma262/#sec-canonicalnumericindexstring
* @param {string} target
* @return {number|undefined}
*/
export function CanonicalNumericIndexString(target) {
if (target === "-0") {
return -0;
}

const num = Number(target);
if (target !== num + "") {
return undefined;
}

return num;
}

/**
* @see https://tc39.es/ecma262/#sec-lengthofarraylike
* @param {object} arrayLike
Expand Down
46 changes: 46 additions & 0 deletions test/Float16Array.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,53 @@ describe("Float16Array", () => {
assert.throws(() => { float16.at(BigInt(0), 0); }, TypeError);
}
});
});

describe("#withAt()", () => {

it("property `name` is 'withAt'", () => {
assert( Float16Array.prototype.withAt.name === "withAt" );
});

it("property `length` is 1", () => {
assert( Float16Array.prototype.withAt.length === 2 );
});

it("create Array", () => {
const float16_1 = new Float16Array([1, 2, 3]);
const float16_2 = float16_1.withAt(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.withAt(-1, 4);

assert( float16_1.buffer !== float16_3.buffer );
assert.equalFloat16ArrayValues( float16_1, [1, 2, 3] );
assert.equalFloat16ArrayValues( float16_3, [1, 2, 4] );
});

it("throw Error with invalid index", () => {
const float16 = new Float16Array([1, 2, 3]);

// out of range
assert.throws(() => { float16.withAt(5, 0); }, RangeError);
assert.throws(() => { float16.withAt(-5, 0); }, RangeError);

// not integer number
assert.throws(() => { float16.withAt(0.5, 0); }, RangeError);
assert.throws(() => { float16.withAt(NaN, 0); }, RangeError);
assert.throws(() => { float16.withAt(Infinity, 0); }, RangeError);

assert.throws(() => { float16.withAt("", 0); }, TypeError);
assert.throws(() => { float16.withAt(Symbol(), 0); }, TypeError);

// Safari 13 doesn't have BigInt
if (typeof BigInt !== "undefined") {
assert.throws(() => { float16.withAt(BigInt(0), 0); }, TypeError);
}
});
});

describe("#map()", () => {
Expand Down

0 comments on commit 140a6b5

Please sign in to comment.