///
import { OBJECT, BLOCK_MAXSIZE, TOTAL_OVERHEAD } from "./rt/common";
import { Runtime } from "shared/runtime";
import { COMPARATOR, SORT } from "./util/sort";
import { REVERSE, FILL } from "./util/bytes";
import { idof } from "./builtins";
import { Array } from "./array";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_HOLEYARRAY } from "./util/error";
import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, joinReferenceArray } from "./util/string";
@final
export class StaticArray {
[key: number]: T;
// Note that the interface of StaticArray instances must be a semantically
// compatible subset of Array in order for syntax highlighting to work
// properly, for instance when creating static arrays from array literals.
// The additionally provided static methods take care of dealing with static
// arrays exclusively, without having to convert to Array first.
static fromArray(source: Array): StaticArray {
let length = source.length;
let outSize = length << alignof();
let out = changetype>(__new(outSize, idof>()));
if (isManaged()) {
let sourcePtr = source.dataStart;
for (let i = 0; i < length; ++i) {
let off = i << alignof();
let ref = load(sourcePtr + off);
store(changetype(out) + off, ref);
__link(changetype(out), ref, true);
}
} else {
memory.copy(changetype(out), source.dataStart, outSize);
}
return out;
}
/** @deprecated Please use source.concat> instead. */
static concat(source: StaticArray, other: StaticArray): StaticArray {
return source.concat>(other);
}
/** @deprecated Please use source.slice> instead. */
static slice(source: StaticArray, start: i32 = 0, end: i32 = i32.MAX_VALUE): StaticArray {
return source.slice>(start, end);
}
constructor(length: i32) {
if (length > BLOCK_MAXSIZE >>> alignof()) throw new RangeError(E_INVALIDLENGTH);
let outSize = length << alignof();
let out = changetype>(__new(outSize, idof>()));
if (ASC_RUNTIME != Runtime.Incremental) {
memory.fill(changetype(out), 0, outSize);
}
return out;
}
get length(): i32 {
return changetype(changetype(this) - TOTAL_OVERHEAD).rtSize >>> alignof();
}
at(index: i32): T {
let len = this.length;
index += select(0, len, index >= 0);
if (index >= len) throw new RangeError(E_INDEXOUTOFRANGE);
let value = load(changetype(this) + (index << alignof()));
if (isReference()) {
if (!isNullable()) {
if (!changetype(value)) throw new Error(E_HOLEYARRAY);
}
}
return value;
}
@operator("[]") private __get(index: i32): T {
if (index >= this.length) throw new RangeError(E_INDEXOUTOFRANGE);
let value = load(changetype(this) + (index << alignof()));
if (isReference()) {
if (!isNullable()) {
if (!changetype(value)) throw new Error(E_HOLEYARRAY);
}
}
return value;
}
@unsafe @operator("{}") private __uget(index: i32): T {
return load(changetype(this) + (index << alignof()));
}
@operator("[]=") private __set(index: i32, value: T): void {
if (index >= this.length) throw new RangeError(E_INDEXOUTOFRANGE);
this.__uset(index, value);
}
@unsafe @operator("{}=") private __uset(index: i32, value: T): void {
store(changetype(this) + (index << alignof()), value);
if (isManaged()) {
__link(changetype(this), changetype(value), true);
}
}
fill(value: T, start: i32 = 0, end: i32 = i32.MAX_VALUE): StaticArray {
if (isManaged()) {
FILL(changetype(this), this.length, changetype(value), start, end);
__link(changetype(this), changetype(value), false);
} else {
FILL(changetype(this), this.length, value, start, end);
}
return this;
}
copyWithin(target: i32, start: i32, end: i32 = i32.MAX_VALUE): StaticArray {
let ptr = changetype(this);
let len = this.length;
end = min(end, len);
let to = target < 0 ? max(len + target, 0) : min(target, len);
let from = start < 0 ? max(len + start, 0) : min(start, len);
let last = end < 0 ? max(len + end, 0) : min(end, len);
let count = min(last - from, len - to);
memory.copy( // is memmove
ptr + (to << alignof()),
ptr + (from << alignof()),
count << alignof()
);
return this;
}
includes(value: T, fromIndex: i32 = 0): bool {
if (isFloat()) {
let length = this.length;
if (length == 0 || fromIndex >= length) return false;
if (fromIndex < 0) fromIndex = max(length + fromIndex, 0);
while (fromIndex < length) {
let elem = load(changetype(this) + (fromIndex << alignof()));
// @ts-ignore
if (elem == value || isNaN(elem) & isNaN(value)) return true;
++fromIndex;
}
return false;
} else {
return this.indexOf(value, fromIndex) >= 0;
}
}
indexOf(value: T, fromIndex: i32 = 0): i32 {
let length = this.length;
if (length == 0 || fromIndex >= length) return -1;
if (fromIndex < 0) fromIndex = max(length + fromIndex, 0);
while (fromIndex < length) {
if (load(changetype(this) + (fromIndex << alignof())) == value) return fromIndex;
++fromIndex;
}
return -1;
}
lastIndexOf(value: T, fromIndex: i32 = this.length): i32 {
let length = this.length;
if (length == 0) return -1;
if (fromIndex < 0) fromIndex = length + fromIndex;
else if (fromIndex >= length) fromIndex = length - 1;
while (fromIndex >= 0) {
if (load(changetype(this) + (fromIndex << alignof())) == value) return fromIndex;
--fromIndex;
}
return -1;
}
concat = Array>(other: U): U {
let sourceLen = this.length;
let otherLen = other.length;
let outLen = sourceLen + otherLen;
if (outLen > BLOCK_MAXSIZE >>> alignof()) {
throw new Error(E_INVALIDLENGTH);
}
let sourceSize = sourceLen << alignof();
let out = changetype(this); // FIXME: instanceof needs *some* value
if (out instanceof Array) {
out = changetype(__newArray(outLen, alignof(), idof>()));
// ^ FIXME: Function returns type U, but can't __newArray(U extends Array)
let outStart = changetype>(out).dataStart;
let otherStart = changetype>(other).dataStart;
let thisStart = changetype(this);
if (isManaged()) {
for (let offset: usize = 0; offset < sourceSize; offset += sizeof()) {
let ref = load(thisStart + offset);
store(outStart + offset, ref);
__link(changetype(out), ref, true);
}
outStart += sourceSize;
let otherSize = otherLen << alignof();
for (let offset: usize = 0; offset < otherSize; offset += sizeof()) {
let ref = load(otherStart + offset);
store(outStart + offset, ref);
__link(changetype(out), ref, true);
}
} else {
memory.copy(outStart, thisStart, sourceSize);
memory.copy(outStart + sourceSize, otherStart, otherLen << alignof());
}
} else if (out instanceof StaticArray) {
out = changetype(__new(outLen << alignof(), idof>()));
let outStart = changetype(out);
let otherStart = changetype(other);
let thisStart = changetype(this);
if (isManaged()) {
for (let offset: usize = 0; offset < sourceSize; offset += sizeof()) {
let ref = load(thisStart + offset);
store(outStart + offset, ref);
__link(changetype(out), ref, true);
}
outStart += sourceSize;
let otherSize = otherLen << alignof();
for (let offset: usize = 0; offset < otherSize; offset += sizeof()) {
let ref = load(otherStart + offset);
store(outStart + offset, ref);
__link(changetype(out), ref, true);
}
} else {
memory.copy(outStart, thisStart, sourceSize);
memory.copy(outStart + sourceSize, otherStart, otherLen << alignof());
}
} else {
ERROR("Only Array and StaticArray accept for 'U' parameter");
}
return out;
}
slice = Array>(start: i32 = 0, end: i32 = i32.MAX_VALUE): U {
let length = this.length;
start = start < 0 ? max(start + length, 0) : min(start, length);
end = end < 0 ? max(end + length, 0) : min(end, length);
length = max(end - start, 0);
let sourceStart = changetype(this) + (start << alignof());
let size = length << alignof();
let out = changetype(this); // FIXME: instanceof needs *some* value
if (out instanceof Array) {
// return Array
out = changetype(__newArray(length, alignof(), idof>()));
// ^ FIXME: Function returns type U, but can't __newArray(U extends Array)
let outStart = changetype>(out).dataStart;
if (isManaged()) {
let off: usize = 0;
while (off < size) {
let ref = load(sourceStart + off);
store(outStart + off, ref);
__link(changetype(out), ref, true);
off += sizeof();
}
} else {
memory.copy(outStart, sourceStart, size);
}
} else if (out instanceof StaticArray) {
// return StaticArray
out = changetype(__new(size, idof>()));
let outStart = changetype(out);
if (isManaged()) {
let off: usize = 0;
while (off < size) {
let ref = load(sourceStart + off);
store(outStart + off, ref);
__link(outStart, ref, true);
off += sizeof();
}
} else {
memory.copy(outStart, sourceStart, size);
}
} else {
ERROR("Only Array and StaticArray accept for 'U' parameter");
}
return out;
}
findIndex(fn: (value: T, index: i32, array: StaticArray) => bool): i32 {
for (let i = 0, len = this.length; i < len; ++i) {
if (fn(load(changetype(this) + (i << alignof())), i, this)) return i;
}
return -1;
}
findLastIndex(fn: (value: T, index: i32, array: StaticArray) => bool): i32 {
for (let i = this.length - 1; i >= 0; --i) {
if (fn(load(changetype(this) + (i << alignof())), i, this)) return i;
}
return -1;
}
forEach(fn: (value: T, index: i32, array: StaticArray) => void): void {
for (let i = 0, len = this.length; i < len; ++i) {
fn(load(changetype(this) + (i << alignof())), i, this);
}
}
map(fn: (value: T, index: i32, array: StaticArray) => U): Array {
let len = this.length;
let out = changetype>(__newArray(len, alignof(), idof>()));
let outStart = out.dataStart;
for (let i = 0; i < len; ++i) {
let result = fn(load(changetype(this) + (i << alignof())), i, this);
store(outStart + (i << alignof()), result);
if (isManaged()) {
__link(changetype(out), changetype(result), true);
}
}
return out;
}
filter(fn: (value: T, index: i32, array: StaticArray) => bool): Array {
let result = changetype>(__newArray(0, alignof(), idof>()));
for (let i = 0, len = this.length; i < len; ++i) {
let value = load(changetype(this) + (i << alignof()));
if (fn(value, i, this)) result.push(value);
}
return result;
}
reduce(
fn: (previousValue: U, currentValue: T, currentIndex: i32, array: StaticArray) => U,
initialValue: U
): U {
let acc = initialValue;
for (let i = 0, len = this.length; i < len; ++i) {
acc = fn(acc, load(changetype(this) + (i << alignof())), i, this);
}
return acc;
}
reduceRight(
fn: (previousValue: U, currentValue: T, currentIndex: i32, array: StaticArray) => U,
initialValue: U
): U {
let acc = initialValue;
for (let i = this.length - 1; i >= 0; --i) {
acc = fn(acc, load(changetype(this) + (i << alignof())), i, this);
}
return acc;
}
every(fn: (value: T, index: i32, array: StaticArray) => bool): bool {
for (let i = 0, len = this.length; i < len; ++i) {
if (!fn(load(changetype(this) + (i << alignof())), i, this)) return false;
}
return true;
}
some(fn: (value: T, index: i32, array: StaticArray) => bool): bool {
for (let i = 0, len = this.length; i < len; ++i) {
if (fn(load(changetype(this) + (i << alignof())), i, this)) return true;
}
return false;
}
sort(comparator: (a: T, b: T) => i32 = COMPARATOR()): StaticArray {
SORT(changetype(this), this.length, comparator);
return this;
}
join(separator: string = ","): string {
if (isBoolean()) return joinBooleanArray(changetype(this), this.length, separator);
if (isInteger()) return joinIntegerArray(changetype(this), this.length, separator);
if (isFloat