Skip to content

feat: Add findLastIndex for Array and TypedArray (Stage 3) #1981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 115 additions & 108 deletions std/assembly/array.ts

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,8 @@ declare abstract class TypedArray<T> implements ArrayBufferView {
fill(value: T, start?: i32, end?: i32): this;
/** The findIndex() method returns an index in the typed array, if an element in the typed array satisfies the provided testing function. Otherwise -1 is returned. See also the find() [not implemented] method, which returns the value of a found element in the typed array instead of its index. */
findIndex(callbackfn: (value: T, index: i32, self: this) => bool): i32;
/** The findLastIndex() method returns an index start searching from the end in the typed array, if an element in the typed array satisfies the provided testing function. Otherwise -1 is returned. See also the find() [not implemented] method, which returns the value of a found element in the typed array instead of its index. */
findLastIndex(callbackfn: (value: T, index: i32, self: this) => bool): i32;
/** The every() method tests whether all elements in the typed array pass the test implemented by the provided function. This method has the same algorithm as Array.prototype.every(). */
every(callbackfn: (value: T, index: i32, self: this) => bool): bool;
/** The forEach() method executes a provided function once per array element. This method has the same algorithm as Array.prototype.forEach().*/
Expand Down Expand Up @@ -1688,7 +1690,8 @@ declare class Array<T> {
at(index: i32): T;
fill(value: T, start?: i32, end?: i32): this;
every(callbackfn: (element: T, index: i32, array?: Array<T>) => bool): bool;
findIndex(predicate: (element: T, index: i32, array?: Array<T>) => bool): i32;
findIndex(callbackfn: (element: T, index: i32, array?: Array<T>) => bool): i32;
findLastIndex(callbackfn: (element: T, index: i32, array?: Array<T>) => bool): i32;
includes(searchElement: T, fromIndex?: i32): bool;
indexOf(searchElement: T, fromIndex?: i32): i32;
lastIndexOf(searchElement: T, fromIndex?: i32): i32;
Expand Down
135 changes: 96 additions & 39 deletions std/assembly/typedarray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ export class Int8Array extends ArrayBufferView {
return FIND_INDEX<Int8Array, i8>(this, fn);
}

findLastIndex(fn: (value: i8, index: i32, self: Int8Array) => bool): i32 {
return FIND_LAST_INDEX<Int8Array, i8>(this, fn);
}

some(fn: (value: i8, index: i32, self: Int8Array) => bool): bool {
return SOME<Int8Array, i8>(this, fn);
}
Expand Down Expand Up @@ -243,6 +247,10 @@ export class Uint8Array extends ArrayBufferView {
return FIND_INDEX<Uint8Array, u8>(this, fn);
}

findLastIndex(fn: (value: u8, index: i32, self: Uint8Array) => bool): i32 {
return FIND_LAST_INDEX<Uint8Array, u8>(this, fn);
}

some(fn: (value: u8, index: i32, self: Uint8Array) => bool): bool {
return SOME<Uint8Array, u8>(this, fn);
}
Expand Down Expand Up @@ -379,6 +387,10 @@ export class Uint8ClampedArray extends ArrayBufferView {
return FIND_INDEX<Uint8ClampedArray, u8>(this, fn);
}

findLastIndex(fn: (value: u8, index: i32, self: Uint8ClampedArray) => bool): i32 {
return FIND_LAST_INDEX<Uint8ClampedArray, u8>(this, fn);
}

some(fn: (value: u8, index: i32, self: Uint8ClampedArray) => bool): bool {
return SOME<Uint8ClampedArray, u8>(this, fn);
}
Expand Down Expand Up @@ -515,6 +527,10 @@ export class Int16Array extends ArrayBufferView {
return FIND_INDEX<Int16Array, i16>(this, fn);
}

findLastIndex(fn: (value: i16, index: i32, self: Int16Array) => bool): i32 {
return FIND_LAST_INDEX<Int16Array, i16>(this, fn);
}

some(fn: (value: i16, index: i32, self: Int16Array) => bool): bool {
return SOME<Int16Array, i16>(this, fn);
}
Expand Down Expand Up @@ -651,6 +667,10 @@ export class Uint16Array extends ArrayBufferView {
return FIND_INDEX<Uint16Array, u16>(this, fn);
}

findLastIndex(fn: (value: u16, index: i32, self: Uint16Array) => bool): i32 {
return FIND_LAST_INDEX<Uint16Array, u16>(this, fn);
}

some(fn: (value: u16, index: i32, self: Uint16Array) => bool): bool {
return SOME<Uint16Array, u16>(this, fn);
}
Expand Down Expand Up @@ -787,6 +807,10 @@ export class Int32Array extends ArrayBufferView {
return FIND_INDEX<Int32Array, i32>(this, fn);
}

findLastIndex(fn: (value: i32, index: i32, self: Int32Array) => bool): i32 {
return FIND_LAST_INDEX<Int32Array, i32>(this, fn);
}

some(fn: (value: i32, index: i32, self: Int32Array) => bool): bool {
return SOME<Int32Array, i32>(this, fn);
}
Expand Down Expand Up @@ -923,6 +947,10 @@ export class Uint32Array extends ArrayBufferView {
return FIND_INDEX<Uint32Array, u32>(this, fn);
}

findLastIndex(fn: (value: u32, index: i32, self: Uint32Array) => bool): i32 {
return FIND_LAST_INDEX<Uint32Array, u32>(this, fn);
}

some(fn: (value: u32, index: i32, self: Uint32Array) => bool): bool {
return SOME<Uint32Array, u32>(this, fn);
}
Expand Down Expand Up @@ -1059,6 +1087,10 @@ export class Int64Array extends ArrayBufferView {
return FIND_INDEX<Int64Array, i64>(this, fn);
}

findLastIndex(fn: (value: i64, index: i32, self: Int64Array) => bool): i32 {
return FIND_LAST_INDEX<Int64Array, i64>(this, fn);
}

some(fn: (value: i64, index: i32, self: Int64Array) => bool): bool {
return SOME<Int64Array, i64>(this, fn);
}
Expand Down Expand Up @@ -1195,6 +1227,10 @@ export class Uint64Array extends ArrayBufferView {
return FIND_INDEX<Uint64Array, u64>(this, fn);
}

findLastIndex(fn: (value: u64, index: i32, self: Uint64Array) => bool): i32 {
return FIND_LAST_INDEX<Uint64Array, u64>(this, fn);
}

some(fn: (value: u64, index: i32, self: Uint64Array) => bool): bool {
return SOME<Uint64Array, u64>(this, fn);
}
Expand Down Expand Up @@ -1331,6 +1367,10 @@ export class Float32Array extends ArrayBufferView {
return FIND_INDEX<Float32Array, f32>(this, fn);
}

findLastIndex(fn: (value: f32, index: i32, self: Float32Array) => bool): i32 {
return FIND_LAST_INDEX<Float32Array, f32>(this, fn);
}

some(fn: (value: f32, index: i32, self: Float32Array) => bool): bool {
return SOME<Float32Array, f32>(this, fn);
}
Expand Down Expand Up @@ -1467,6 +1507,10 @@ export class Float64Array extends ArrayBufferView {
return FIND_INDEX<Float64Array, f64>(this, fn);
}

findLastIndex(fn: (value: f64, index: i32, self: Float64Array) => bool): i32 {
return FIND_LAST_INDEX<Float64Array, f64>(this, fn);
}

some(fn: (value: f64, index: i32, self: Float64Array) => bool): bool {
return SOME<Float64Array, f64>(this, fn);
}
Expand Down Expand Up @@ -1508,15 +1552,15 @@ function FILL<TArray extends ArrayBufferView, T extends number>(
start: i32,
end: i32
): TArray {
var dataStart = array.dataStart;
var ptr = array.dataStart;
var len = array.length;
start = start < 0 ? max(len + start, 0) : min(start, len);
end = end < 0 ? max(len + end, 0) : min(end, len);
if (sizeof<T>() == 1) {
if (start < end) memory.fill(dataStart + <usize>start, <u8>value, <usize>(end - start));
if (start < end) memory.fill(ptr + <usize>start, <u8>value, <usize>(end - start));
} else {
for (; start < end; ++start) {
store<T>(dataStart + (<usize>start << alignof<T>()), value);
store<T>(ptr + (<usize>start << alignof<T>()), value);
}
}
return array;
Expand Down Expand Up @@ -1572,7 +1616,7 @@ function COPY_WITHIN<TArray extends ArrayBufferView, T>(
end: i32
): TArray {
var len = array.length;
var dataStart = array.dataStart;
var ptr = array.dataStart;

end = min<i32>(end, len);
var to = target < 0 ? max(len + target, 0) : min(target, len);
Expand All @@ -1581,8 +1625,8 @@ function COPY_WITHIN<TArray extends ArrayBufferView, T>(
var count = min(last - from, len - to);

memory.copy(
dataStart + (<usize>to << alignof<T>()),
dataStart + (<usize>from << alignof<T>()),
ptr + (<usize>to << alignof<T>()),
ptr + (<usize>from << alignof<T>()),
<usize>count << alignof<T>()
);
return array;
Expand All @@ -1595,9 +1639,9 @@ function REDUCE<TArray extends ArrayBufferView, T, TRet>(
fn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
initialValue: TRet
): TRet {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = 0, k = array.length; i < k; i++) {
initialValue = fn(initialValue, load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
initialValue = fn(initialValue, load<T>(ptr + (<usize>i << alignof<T>())), i, array);
}
return initialValue;
}
Expand All @@ -1609,9 +1653,9 @@ function REDUCE_RIGHT<TArray extends ArrayBufferView, T, TRet>(
fn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
initialValue: TRet
): TRet {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = array.length - 1; i >= 0; i--) {
initialValue = fn(initialValue, load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
initialValue = fn(initialValue, load<T>(ptr + (<usize>i << alignof<T>())), i, array);
}
return initialValue;
}
Expand All @@ -1623,15 +1667,15 @@ function MAP<TArray extends ArrayBufferView, T>(
fn: (value: T, index: i32, self: TArray) => T,
): TArray {
var len = array.length;
var dataStart = array.dataStart;
var ptr = array.dataStart;

var byteLength = len << alignof<T>();
var out = changetype<TArray>(__new(offsetof<TArray>(), idof<TArray>()));
var buf = changetype<ArrayBuffer>(__new(byteLength, idof<ArrayBuffer>()));
for (let i = 0; i < len; i++) {
store<T>(
changetype<usize>(buf) + (<usize>i << alignof<T>()),
fn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)
fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array)
);
}
store<usize>(changetype<usize>(out), changetype<usize>(buf), offsetof<TArray>("buffer"));
Expand Down Expand Up @@ -1677,9 +1721,22 @@ function FIND_INDEX<TArray extends ArrayBufferView, T>(
array: TArray,
fn: (value: T, index: i32, array: TArray) => bool,
): i32 {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = 0, k = array.length; i < k; i++) {
if (fn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) return i;
if (fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array)) return i;
}
return -1;
}

// @ts-ignore: decorator
@inline
function FIND_LAST_INDEX<TArray extends ArrayBufferView, T>(
array: TArray,
fn: (value: T, index: i32, array: TArray) => bool,
): i32 {
var ptr = array.dataStart;
for (let i = array.length - 1; i >= 0; --i) {
if (fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array)) return i;
}
return -1;
}
Expand All @@ -1693,11 +1750,11 @@ function INCLUDES<TArray extends ArrayBufferView, T>(
): bool {
if (isFloat<T>()) {
let index: isize = fromIndex;
let length: isize = array.length;
if (length == 0 || index >= length) return false;
if (index < 0) index = max(length + index, 0);
let len: isize = array.length;
if (len == 0 || index >= len) return false;
if (index < 0) index = max(len + index, 0);
let dataStart = array.dataStart;
while (index < length) {
while (index < len) {
let elem = load<T>(dataStart + (index << alignof<T>()));
// @ts-ignore
if (elem == searchElement || isNaN(elem) & isNaN(searchElement)) return true;
Expand All @@ -1717,11 +1774,11 @@ function INDEX_OF<TArray extends ArrayBufferView, T>(
fromIndex: i32,
): i32 {
var index: isize = fromIndex;
var length: isize = array.length;
if (length == 0 || index >= length) return -1;
if (index < 0) index = max(length + index, 0);
var len: isize = array.length;
if (len == 0 || index >= len) return -1;
if (index < 0) index = max(len + index, 0);
var dataStart = array.dataStart;
while (index < length) {
while (index < len) {
if (load<T>(dataStart + (index << alignof<T>())) == searchElement) return <i32>index;
++index;
}
Expand All @@ -1736,10 +1793,10 @@ function LAST_INDEX_OF<TArray extends ArrayBufferView, T>(
fromIndex: i32,
): i32 {
var index: isize = fromIndex;
var length: isize = array.length;
if (length == 0) return -1;
if (index < 0) index = length + index; // no need to clamp
else if (index >= length) index = length - 1;
var len: isize = array.length;
if (len == 0) return -1;
if (index < 0) index = len + index; // no need to clamp
else if (index >= len) index = len - 1;
var dataStart = array.dataStart;
while (index >= 0) {
if (load<T>(dataStart + (index << alignof<T>())) == searchElement) return <i32>index;
Expand All @@ -1754,9 +1811,9 @@ function SOME<TArray extends ArrayBufferView, T>(
array: TArray,
fn: (value: T, index: i32, array: TArray) => bool,
): bool {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = 0, k = array.length; i < k; i++) {
if (fn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) return true;
if (fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array)) return true;
}
return false;
}
Expand All @@ -1767,9 +1824,9 @@ function EVERY<TArray extends ArrayBufferView, T>(
array: TArray,
fn: (value: T, index: i32, array: TArray) => bool,
): bool {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = 0, k = array.length; i < k; i++) {
if (fn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) continue;
if (fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array)) continue;
return false;
}
return true;
Expand All @@ -1781,19 +1838,19 @@ function FOREACH<TArray extends ArrayBufferView, T>(
array: TArray,
fn: (value: T, index: i32, array: TArray) => void,
): void {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let i = 0, k = array.length; i < k; i++) {
fn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
fn(load<T>(ptr + (<usize>i << alignof<T>())), i, array);
}
}

// @ts-ignore: decorator
@inline
function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {
var dataStart = array.dataStart;
var ptr = array.dataStart;
for (let front: usize = 0, back: usize = array.length - 1; front < back; ++front, --back) {
let frontPtr = dataStart + (front << alignof<T>());
let backPtr = dataStart + (back << alignof<T>());
let frontPtr = ptr + (front << alignof<T>());
let backPtr = ptr + (back << alignof<T>());
let temp = load<T>(frontPtr);
store<T>(frontPtr, load<T>(backPtr));
store<T>(backPtr, temp);
Expand All @@ -1806,16 +1863,16 @@ function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {
function WRAP<TArray extends ArrayBufferView, T>(
buffer: ArrayBuffer,
byteOffset: i32 = 0,
length: i32 = -1
len: i32 = -1
): TArray {
var byteLength: i32;
var bufferByteLength = buffer.byteLength;
const mask: u32 = sizeof<T>() - 1;
if (i32(<u32>byteOffset > <u32>bufferByteLength) | (byteOffset & mask)) {
throw new RangeError(E_INDEXOUTOFRANGE);
}
if (length < 0) {
if (length == -1) {
if (len < 0) {
if (len == -1) {
if (bufferByteLength & mask) {
throw new RangeError(E_INVALIDLENGTH);
}
Expand All @@ -1824,7 +1881,7 @@ function WRAP<TArray extends ArrayBufferView, T>(
throw new RangeError(E_INVALIDLENGTH);
}
} else {
byteLength = length << alignof<T>();
byteLength = len << alignof<T>();
if (byteOffset + byteLength > bufferByteLength) {
throw new RangeError(E_INVALIDLENGTH);
}
Expand Down
Loading