Skip to content

feat: [std] implement { Array, TypedArray, StaticArray, String }#at #1606

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 12 commits into from
Jan 30, 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
15 changes: 14 additions & 1 deletion std/assembly/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class Array<T> {

@operator("[]") private __get(index: i32): T {
if (<u32>index >= <u32>this.length_) throw new RangeError(E_INDEXOUTOFRANGE);
var value = this.__uget(index);
var value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
if (isReference<T>()) {
if (!isNullable<T>()) {
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
Expand Down Expand Up @@ -119,6 +119,19 @@ export class Array<T> {
}
}

at(index: i32): T {
var len = this.length_;
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
var value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
if (isReference<T>()) {
if (!isNullable<T>()) {
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
}
}
return value;
}

fill(value: T, start: i32 = 0, end: i32 = i32.MAX_VALUE): this {
var dataStart = this.dataStart;
var length = this.length_;
Expand Down
5 changes: 5 additions & 0 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,8 @@ declare abstract class TypedArray<T> implements ArrayBufferView {
readonly byteLength: i32;
/** The length (in elements). */
readonly length: i32;
/** Returns value using relative indexing. Index may be negative */
at(index: i32): T;
/** The includes() method determines whether a typed array includes a certain element, returning true or false as appropriate. */
includes(searchElement: T, fromIndex?: i32): bool;
/** The indexOf() method returns the first index at which a given element can be found in the typed array, or -1 if it is not present. */
Expand Down Expand Up @@ -1570,6 +1572,7 @@ declare class Array<T> {
/** Constructs a new array. */
constructor(capacity?: i32);

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;
Expand Down Expand Up @@ -1606,6 +1609,7 @@ declare class StaticArray<T> {
static slice<T>(source: StaticArray<T>, start?: i32, end?: i32): StaticArray<T>;
readonly length: i32;
constructor(length?: i32);
at(index: i32): T;
includes(searchElement: T, fromIndex?: i32): bool;
indexOf(searchElement: T, fromIndex?: i32): i32;
lastIndexOf(searchElement: T, fromIndex?: i32): i32;
Expand All @@ -1622,6 +1626,7 @@ declare class String {
static fromCodePoint(code: i32): string;
static fromCodePoints(arr: i32[]): string;
readonly length: i32;
at(index: i32): string;
charAt(index: i32): string;
charCodeAt(index: i32): i32;
codePointAt(index: i32): i32;
Expand Down
15 changes: 14 additions & 1 deletion std/assembly/staticarray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,22 @@ export class StaticArray<T> {
return changetype<OBJECT>(changetype<usize>(this) - TOTAL_OVERHEAD).rtSize >>> alignof<T>();
}

at(index: i32): T {
var len = this.length;
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
var value = load<T>(changetype<usize>(this) + (<usize>index << alignof<T>()));
if (isReference<T>()) {
if (!isNullable<T>()) {
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
}
}
return value;
}

@operator("[]") private __get(index: i32): T {
if (<u32>index >= <u32>this.length) throw new RangeError(E_INDEXOUTOFRANGE);
var value = this.__uget(index);
var value = load<T>(changetype<usize>(this) + (<usize>index << alignof<T>()));
if (isReference<T>()) {
if (!isNullable<T>()) {
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
Expand Down
11 changes: 10 additions & 1 deletion std/assembly/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { OBJECT, BLOCK_MAXSIZE, TOTAL_OVERHEAD } from "./rt/common";
import { compareImpl, strtol, strtod, isSpace, isAscii, isFinalSigma, toLower8, toUpper8 } from "./util/string";
import { SPECIALS_UPPER, casemap, bsearch } from "./util/casemap";
import { E_INVALIDLENGTH } from "./util/error";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH } from "./util/error";
import { idof } from "./builtins";
import { Array } from "./array";

Expand Down Expand Up @@ -48,6 +48,15 @@ import { Array } from "./array";
return changetype<OBJECT>(changetype<usize>(this) - TOTAL_OVERHEAD).rtSize >> 1;
}

at(pos: i32): String {
var len = this.length;
pos += select(0, len, pos >= 0);
if (<u32>pos >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
var out = __new(2, idof<String>());
store<u16>(out, load<u16>(changetype<usize>(this) + (<usize>pos << 1)));
return changetype<String>(out); // retains
}

@operator("[]") charAt(pos: i32): String {
if (<u32>pos >= <u32>this.length) return changetype<String>("");
var out = changetype<String>(__new(2, idof<String>()));
Expand Down
91 changes: 88 additions & 3 deletions std/assembly/typedarray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ export class Int8Array extends ArrayBufferView {
store<i8>(this.dataStart + <usize>index, value);
}

at(index: i32): i8 {
var len = this.byteLength;
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<i8>(this.dataStart + <usize>index);
}

includes(searchElement: i8, fromIndex: i32 = 0): bool {
return INCLUDES<Int8Array, i8>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -169,6 +176,13 @@ export class Uint8Array extends ArrayBufferView {
store<u8>(this.dataStart + <usize>index, value);
}

at(index: i32): u8 {
var len = this.byteLength;
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u8>(this.dataStart + <usize>index);
}

includes(searchElement: u8, fromIndex: i32 = 0): bool {
return INCLUDES<Uint8Array, u8>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -297,6 +311,13 @@ export class Uint8ClampedArray extends ArrayBufferView {
store<u8>(this.dataStart + <usize>index, ~(<i32>value >> 31) & (((255 - value) >> 31) | value));
}

at(index: i32): u8 {
var len = this.byteLength;
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u8>(this.dataStart + <usize>index);
}

includes(searchElement: u8, fromIndex: i32 = 0): bool {
return INCLUDES<Uint8ClampedArray, u8>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -425,6 +446,13 @@ export class Int16Array extends ArrayBufferView {
store<i16>(this.dataStart + (<usize>index << alignof<i16>()), value);
}

at(index: i32): i16 {
var len = this.byteLength >>> alignof<i16>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<i16>(this.dataStart + (<usize>index << alignof<i16>()));
}

includes(searchElement: i16, fromIndex: i32 = 0): bool {
return INCLUDES<Int16Array, i16>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -553,6 +581,13 @@ export class Uint16Array extends ArrayBufferView {
store<u16>(this.dataStart + (<usize>index << alignof<u16>()), value);
}

at(index: i32): u16 {
var len = this.byteLength >>> alignof<u16>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u16>(this.dataStart + (<usize>index << alignof<u16>()));
}

includes(searchElement: u16, fromIndex: i32 = 0): bool {
return INCLUDES<Uint16Array, u16>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -681,6 +716,13 @@ export class Int32Array extends ArrayBufferView {
store<i32>(this.dataStart + (<usize>index << alignof<i32>()), value);
}

at(index: i32): i32 {
var len = this.byteLength >>> alignof<i32>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<i32>(this.dataStart + (<usize>index << alignof<i32>()));
}

includes(searchElement: i32, fromIndex: i32 = 0): bool {
return INCLUDES<Int32Array, i32>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -809,6 +851,13 @@ export class Uint32Array extends ArrayBufferView {
store<u32>(this.dataStart + (<usize>index << alignof<u32>()), value);
}

at(index: i32): u32 {
var len = this.byteLength >>> alignof<u32>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u32>(this.dataStart + (<usize>index << alignof<u32>()));
}

includes(searchElement: u32, fromIndex: i32 = 0): bool {
return INCLUDES<Uint32Array, u32>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -937,6 +986,13 @@ export class Int64Array extends ArrayBufferView {
store<i64>(this.dataStart + (<usize>index << alignof<i64>()), value);
}

at(index: i32): i64 {
var len = this.byteLength >>> alignof<i64>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<i64>(this.dataStart + (<usize>index << alignof<i64>()));
}

includes(searchElement: i64, fromIndex: i32 = 0): bool {
return INCLUDES<Int64Array, i64>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -1065,6 +1121,13 @@ export class Uint64Array extends ArrayBufferView {
store<u64>(this.dataStart + (<usize>index << alignof<u64>()), value);
}

at(index: i32): u64 {
var len = this.byteLength >>> alignof<u64>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u64>(this.dataStart + (<usize>index << alignof<u64>()));
}

includes(searchElement: u64, fromIndex: i32 = 0): bool {
return INCLUDES<Uint64Array, u64>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -1193,6 +1256,13 @@ export class Float32Array extends ArrayBufferView {
store<f32>(this.dataStart + (<usize>index << alignof<f32>()), value);
}

at(index: i32): f32 {
var len = this.byteLength >>> alignof<f32>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<f32>(this.dataStart + (<usize>index << alignof<f32>()));
}

includes(searchElement: f32, fromIndex: i32 = 0): bool {
return INCLUDES<Float32Array, f32>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -1321,6 +1391,13 @@ export class Float64Array extends ArrayBufferView {
store<f64>(this.dataStart + (<usize>index << alignof<f64>()), value);
}

at(index: i32): f64 {
var len = this.byteLength >>> alignof<f64>();
index += select(0, len, index >= 0);
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
return load<f64>(this.dataStart + (<usize>index << alignof<f64>()));
}

includes(searchElement: f64, fromIndex: i32 = 0): bool {
return INCLUDES<Float64Array, f64>(this, searchElement, fromIndex);
}
Expand Down Expand Up @@ -1737,7 +1814,11 @@ function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {

// @ts-ignore: decorator
@inline
function WRAP<TArray extends ArrayBufferView, T>(buffer: ArrayBuffer, byteOffset: i32 = 0, length: i32 = -1): TArray {
function WRAP<TArray extends ArrayBufferView, T>(
buffer: ArrayBuffer,
byteOffset: i32 = 0,
length: i32 = -1
): TArray {
var byteLength: i32;
var bufferByteLength = buffer.byteLength;
const mask: u32 = sizeof<T>() - 1;
Expand Down Expand Up @@ -1769,7 +1850,11 @@ function WRAP<TArray extends ArrayBufferView, T>(buffer: ArrayBuffer, byteOffset

// @ts-ignore: decorator
@inline
function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView, U>(target: TArray, source: UArray, offset: i32 = 0): void {
function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView, U>(
target: TArray,
source: UArray,
offset: i32 = 0
): void {
// need to assert at compile time that U is not a reference or a function
if (isReference<U>()) {
ERROR(E_NOTIMPLEMENTED);
Expand Down Expand Up @@ -1798,7 +1883,7 @@ function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView,
let value = load<U>(sourceDataStart + (<usize>i << alignof<U>()));
store<T>(
targetDataStart + (<usize>i << alignof<T>()),
isFinite<U>(value) ? <T>max<U>(0, min<U>(255, value)) : 0
isFinite<U>(value) ? <T>max<U>(0, min<U>(255, value)) : <T>0
);
} else {
let value = load<U>(sourceDataStart + (<usize>i << alignof<U>()));
Expand Down
29 changes: 28 additions & 1 deletion std/portable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,23 @@ String["fromCodePoints"] = function fromCodePoints(arr) {
return parts;
};

if (!String.prototype.at) {
Object.defineProperty(String.prototype, "at", {
value: function at(index) {
return this.charAt(index >= 0 ? index : index + this.length);
},
configurable: true
});
}

if (!String.prototype.replaceAll) {
Object.defineProperty(String.prototype, "replaceAll", {
value: function replaceAll(search, replacment) {
var res = this.split(search).join(replacment);
if (!search.length) res = replacment + res + replacment;
return res;
}
},
configurable: true
});
}

Expand All @@ -267,6 +277,23 @@ Array.prototype.sort = function sort(comparator) {
return arraySort.call(this, comparator || defaultComparator);
};

[ Array,
Uint8ClampedArray,
Uint8Array, Int8Array,
Uint16Array, Int16Array,
Uint32Array, Int32Array,
Float32Array, Float64Array
].forEach(Ctr => {
if (!Ctr.prototype.at) {
Object.defineProperty(Ctr.prototype, "at", {
value: function at(index) {
return this[index >= 0 ? index : index + this.length];
},
configurable: true
});
}
});

globalScope["isInteger"] = Number.isInteger;

globalScope["isFloat"] = function isFloat(arg) {
Expand Down
Loading