Skip to content

Commit 0e851ca

Browse files
authored
feat: Implement Array/TypedArray/String#at (#1606)
1 parent 2a35b82 commit 0e851ca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+16414
-13999
lines changed

std/assembly/array.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class Array<T> {
9090

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

122+
at(index: i32): T {
123+
var len = this.length_;
124+
index += select(0, len, index >= 0);
125+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
126+
var value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
127+
if (isReference<T>()) {
128+
if (!isNullable<T>()) {
129+
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
130+
}
131+
}
132+
return value;
133+
}
134+
122135
fill(value: T, start: i32 = 0, end: i32 = i32.MAX_VALUE): this {
123136
var dataStart = this.dataStart;
124137
var length = this.length_;

std/assembly/index.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,8 @@ declare abstract class TypedArray<T> implements ArrayBufferView {
14601460
readonly byteLength: i32;
14611461
/** The length (in elements). */
14621462
readonly length: i32;
1463+
/** Returns value using relative indexing. Index may be negative */
1464+
at(index: i32): T;
14631465
/** The includes() method determines whether a typed array includes a certain element, returning true or false as appropriate. */
14641466
includes(searchElement: T, fromIndex?: i32): bool;
14651467
/** 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. */
@@ -1570,6 +1572,7 @@ declare class Array<T> {
15701572
/** Constructs a new array. */
15711573
constructor(capacity?: i32);
15721574

1575+
at(index: i32): T;
15731576
fill(value: T, start?: i32, end?: i32): this;
15741577
every(callbackfn: (element: T, index: i32, array?: Array<T>) => bool): bool;
15751578
findIndex(predicate: (element: T, index: i32, array?: Array<T>) => bool): i32;
@@ -1606,6 +1609,7 @@ declare class StaticArray<T> {
16061609
static slice<T>(source: StaticArray<T>, start?: i32, end?: i32): StaticArray<T>;
16071610
readonly length: i32;
16081611
constructor(length?: i32);
1612+
at(index: i32): T;
16091613
includes(searchElement: T, fromIndex?: i32): bool;
16101614
indexOf(searchElement: T, fromIndex?: i32): i32;
16111615
lastIndexOf(searchElement: T, fromIndex?: i32): i32;
@@ -1622,6 +1626,7 @@ declare class String {
16221626
static fromCodePoint(code: i32): string;
16231627
static fromCodePoints(arr: i32[]): string;
16241628
readonly length: i32;
1629+
at(index: i32): string;
16251630
charAt(index: i32): string;
16261631
charCodeAt(index: i32): i32;
16271632
codePointAt(index: i32): i32;

std/assembly/staticarray.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,22 @@ export class StaticArray<T> {
9696
return changetype<OBJECT>(changetype<usize>(this) - TOTAL_OVERHEAD).rtSize >>> alignof<T>();
9797
}
9898

99+
at(index: i32): T {
100+
var len = this.length;
101+
index += select(0, len, index >= 0);
102+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
103+
var value = load<T>(changetype<usize>(this) + (<usize>index << alignof<T>()));
104+
if (isReference<T>()) {
105+
if (!isNullable<T>()) {
106+
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);
107+
}
108+
}
109+
return value;
110+
}
111+
99112
@operator("[]") private __get(index: i32): T {
100113
if (<u32>index >= <u32>this.length) throw new RangeError(E_INDEXOUTOFRANGE);
101-
var value = this.__uget(index);
114+
var value = load<T>(changetype<usize>(this) + (<usize>index << alignof<T>()));
102115
if (isReference<T>()) {
103116
if (!isNullable<T>()) {
104117
if (!changetype<usize>(value)) throw new Error(E_HOLEYARRAY);

std/assembly/string.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { OBJECT, BLOCK_MAXSIZE, TOTAL_OVERHEAD } from "./rt/common";
44
import { compareImpl, strtol, strtod, isSpace, isAscii, isFinalSigma, toLower8, toUpper8 } from "./util/string";
55
import { SPECIALS_UPPER, casemap, bsearch } from "./util/casemap";
6-
import { E_INVALIDLENGTH } from "./util/error";
6+
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH } from "./util/error";
77
import { idof } from "./builtins";
88
import { Array } from "./array";
99

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

51+
at(pos: i32): String {
52+
var len = this.length;
53+
pos += select(0, len, pos >= 0);
54+
if (<u32>pos >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
55+
var out = __new(2, idof<String>());
56+
store<u16>(out, load<u16>(changetype<usize>(this) + (<usize>pos << 1)));
57+
return changetype<String>(out); // retains
58+
}
59+
5160
@operator("[]") charAt(pos: i32): String {
5261
if (<u32>pos >= <u32>this.length) return changetype<String>("");
5362
var out = changetype<String>(__new(2, idof<String>()));

std/assembly/typedarray.ts

+88-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ export class Int8Array extends ArrayBufferView {
4141
store<i8>(this.dataStart + <usize>index, value);
4242
}
4343

44+
at(index: i32): i8 {
45+
var len = this.byteLength;
46+
index += select(0, len, index >= 0);
47+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
48+
return load<i8>(this.dataStart + <usize>index);
49+
}
50+
4451
includes(searchElement: i8, fromIndex: i32 = 0): bool {
4552
return INCLUDES<Int8Array, i8>(this, searchElement, fromIndex);
4653
}
@@ -169,6 +176,13 @@ export class Uint8Array extends ArrayBufferView {
169176
store<u8>(this.dataStart + <usize>index, value);
170177
}
171178

179+
at(index: i32): u8 {
180+
var len = this.byteLength;
181+
index += select(0, len, index >= 0);
182+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
183+
return load<u8>(this.dataStart + <usize>index);
184+
}
185+
172186
includes(searchElement: u8, fromIndex: i32 = 0): bool {
173187
return INCLUDES<Uint8Array, u8>(this, searchElement, fromIndex);
174188
}
@@ -297,6 +311,13 @@ export class Uint8ClampedArray extends ArrayBufferView {
297311
store<u8>(this.dataStart + <usize>index, ~(<i32>value >> 31) & (((255 - value) >> 31) | value));
298312
}
299313

314+
at(index: i32): u8 {
315+
var len = this.byteLength;
316+
index += select(0, len, index >= 0);
317+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
318+
return load<u8>(this.dataStart + <usize>index);
319+
}
320+
300321
includes(searchElement: u8, fromIndex: i32 = 0): bool {
301322
return INCLUDES<Uint8ClampedArray, u8>(this, searchElement, fromIndex);
302323
}
@@ -425,6 +446,13 @@ export class Int16Array extends ArrayBufferView {
425446
store<i16>(this.dataStart + (<usize>index << alignof<i16>()), value);
426447
}
427448

449+
at(index: i32): i16 {
450+
var len = this.byteLength >>> alignof<i16>();
451+
index += select(0, len, index >= 0);
452+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
453+
return load<i16>(this.dataStart + (<usize>index << alignof<i16>()));
454+
}
455+
428456
includes(searchElement: i16, fromIndex: i32 = 0): bool {
429457
return INCLUDES<Int16Array, i16>(this, searchElement, fromIndex);
430458
}
@@ -553,6 +581,13 @@ export class Uint16Array extends ArrayBufferView {
553581
store<u16>(this.dataStart + (<usize>index << alignof<u16>()), value);
554582
}
555583

584+
at(index: i32): u16 {
585+
var len = this.byteLength >>> alignof<u16>();
586+
index += select(0, len, index >= 0);
587+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
588+
return load<u16>(this.dataStart + (<usize>index << alignof<u16>()));
589+
}
590+
556591
includes(searchElement: u16, fromIndex: i32 = 0): bool {
557592
return INCLUDES<Uint16Array, u16>(this, searchElement, fromIndex);
558593
}
@@ -681,6 +716,13 @@ export class Int32Array extends ArrayBufferView {
681716
store<i32>(this.dataStart + (<usize>index << alignof<i32>()), value);
682717
}
683718

719+
at(index: i32): i32 {
720+
var len = this.byteLength >>> alignof<i32>();
721+
index += select(0, len, index >= 0);
722+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
723+
return load<i32>(this.dataStart + (<usize>index << alignof<i32>()));
724+
}
725+
684726
includes(searchElement: i32, fromIndex: i32 = 0): bool {
685727
return INCLUDES<Int32Array, i32>(this, searchElement, fromIndex);
686728
}
@@ -809,6 +851,13 @@ export class Uint32Array extends ArrayBufferView {
809851
store<u32>(this.dataStart + (<usize>index << alignof<u32>()), value);
810852
}
811853

854+
at(index: i32): u32 {
855+
var len = this.byteLength >>> alignof<u32>();
856+
index += select(0, len, index >= 0);
857+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
858+
return load<u32>(this.dataStart + (<usize>index << alignof<u32>()));
859+
}
860+
812861
includes(searchElement: u32, fromIndex: i32 = 0): bool {
813862
return INCLUDES<Uint32Array, u32>(this, searchElement, fromIndex);
814863
}
@@ -937,6 +986,13 @@ export class Int64Array extends ArrayBufferView {
937986
store<i64>(this.dataStart + (<usize>index << alignof<i64>()), value);
938987
}
939988

989+
at(index: i32): i64 {
990+
var len = this.byteLength >>> alignof<i64>();
991+
index += select(0, len, index >= 0);
992+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
993+
return load<i64>(this.dataStart + (<usize>index << alignof<i64>()));
994+
}
995+
940996
includes(searchElement: i64, fromIndex: i32 = 0): bool {
941997
return INCLUDES<Int64Array, i64>(this, searchElement, fromIndex);
942998
}
@@ -1065,6 +1121,13 @@ export class Uint64Array extends ArrayBufferView {
10651121
store<u64>(this.dataStart + (<usize>index << alignof<u64>()), value);
10661122
}
10671123

1124+
at(index: i32): u64 {
1125+
var len = this.byteLength >>> alignof<u64>();
1126+
index += select(0, len, index >= 0);
1127+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
1128+
return load<u64>(this.dataStart + (<usize>index << alignof<u64>()));
1129+
}
1130+
10681131
includes(searchElement: u64, fromIndex: i32 = 0): bool {
10691132
return INCLUDES<Uint64Array, u64>(this, searchElement, fromIndex);
10701133
}
@@ -1193,6 +1256,13 @@ export class Float32Array extends ArrayBufferView {
11931256
store<f32>(this.dataStart + (<usize>index << alignof<f32>()), value);
11941257
}
11951258

1259+
at(index: i32): f32 {
1260+
var len = this.byteLength >>> alignof<f32>();
1261+
index += select(0, len, index >= 0);
1262+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
1263+
return load<f32>(this.dataStart + (<usize>index << alignof<f32>()));
1264+
}
1265+
11961266
includes(searchElement: f32, fromIndex: i32 = 0): bool {
11971267
return INCLUDES<Float32Array, f32>(this, searchElement, fromIndex);
11981268
}
@@ -1321,6 +1391,13 @@ export class Float64Array extends ArrayBufferView {
13211391
store<f64>(this.dataStart + (<usize>index << alignof<f64>()), value);
13221392
}
13231393

1394+
at(index: i32): f64 {
1395+
var len = this.byteLength >>> alignof<f64>();
1396+
index += select(0, len, index >= 0);
1397+
if (<u32>index >= <u32>len) throw new RangeError(E_INDEXOUTOFRANGE);
1398+
return load<f64>(this.dataStart + (<usize>index << alignof<f64>()));
1399+
}
1400+
13241401
includes(searchElement: f64, fromIndex: i32 = 0): bool {
13251402
return INCLUDES<Float64Array, f64>(this, searchElement, fromIndex);
13261403
}
@@ -1737,7 +1814,11 @@ function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {
17371814

17381815
// @ts-ignore: decorator
17391816
@inline
1740-
function WRAP<TArray extends ArrayBufferView, T>(buffer: ArrayBuffer, byteOffset: i32 = 0, length: i32 = -1): TArray {
1817+
function WRAP<TArray extends ArrayBufferView, T>(
1818+
buffer: ArrayBuffer,
1819+
byteOffset: i32 = 0,
1820+
length: i32 = -1
1821+
): TArray {
17411822
var byteLength: i32;
17421823
var bufferByteLength = buffer.byteLength;
17431824
const mask: u32 = sizeof<T>() - 1;
@@ -1769,7 +1850,11 @@ function WRAP<TArray extends ArrayBufferView, T>(buffer: ArrayBuffer, byteOffset
17691850

17701851
// @ts-ignore: decorator
17711852
@inline
1772-
function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView, U>(target: TArray, source: UArray, offset: i32 = 0): void {
1853+
function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView, U>(
1854+
target: TArray,
1855+
source: UArray,
1856+
offset: i32 = 0
1857+
): void {
17731858
// need to assert at compile time that U is not a reference or a function
17741859
if (isReference<U>()) {
17751860
ERROR(E_NOTIMPLEMENTED);
@@ -1798,7 +1883,7 @@ function SET<TArray extends ArrayBufferView, T, UArray extends ArrayBufferView,
17981883
let value = load<U>(sourceDataStart + (<usize>i << alignof<U>()));
17991884
store<T>(
18001885
targetDataStart + (<usize>i << alignof<T>()),
1801-
isFinite<U>(value) ? <T>max<U>(0, min<U>(255, value)) : 0
1886+
isFinite<U>(value) ? <T>max<U>(0, min<U>(255, value)) : <T>0
18021887
);
18031888
} else {
18041889
let value = load<U>(sourceDataStart + (<usize>i << alignof<U>()));

std/portable/index.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,23 @@ String["fromCodePoints"] = function fromCodePoints(arr) {
239239
return parts;
240240
};
241241

242+
if (!String.prototype.at) {
243+
Object.defineProperty(String.prototype, "at", {
244+
value: function at(index) {
245+
return this.charAt(index >= 0 ? index : index + this.length);
246+
},
247+
configurable: true
248+
});
249+
}
250+
242251
if (!String.prototype.replaceAll) {
243252
Object.defineProperty(String.prototype, "replaceAll", {
244253
value: function replaceAll(search, replacment) {
245254
var res = this.split(search).join(replacment);
246255
if (!search.length) res = replacment + res + replacment;
247256
return res;
248-
}
257+
},
258+
configurable: true
249259
});
250260
}
251261

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

280+
[ Array,
281+
Uint8ClampedArray,
282+
Uint8Array, Int8Array,
283+
Uint16Array, Int16Array,
284+
Uint32Array, Int32Array,
285+
Float32Array, Float64Array
286+
].forEach(Ctr => {
287+
if (!Ctr.prototype.at) {
288+
Object.defineProperty(Ctr.prototype, "at", {
289+
value: function at(index) {
290+
return this[index >= 0 ? index : index + this.length];
291+
},
292+
configurable: true
293+
});
294+
}
295+
});
296+
270297
globalScope["isInteger"] = Number.isInteger;
271298

272299
globalScope["isFloat"] = function isFloat(arg) {

0 commit comments

Comments
 (0)