///
import { BLOCK, BLOCK_MAXSIZE, BLOCK_OVERHEAD } from "./rt/common";
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 {
var length = source.length;
var outSize = length << alignof();
var out = __alloc(outSize, idof>());
if (isManaged()) {
let sourcePtr = source.dataStart;
for (let i = 0; i < length; ++i) {
let off = i << alignof();
store(out + off, __retain(load(sourcePtr + off)));
}
} else {
memory.copy(out, source.dataStart, outSize);
}
return changetype>(out);
}
static concat(source: StaticArray, other: StaticArray): StaticArray {
var sourceLen = source.length;
var otherLen = select(0, other.length, other === null);
var outLen = sourceLen + otherLen;
if (outLen > BLOCK_MAXSIZE >>> alignof()) throw new Error(E_INVALIDLENGTH);
var out = changetype>(__alloc(outLen << alignof(), idof>())); // retains
var outStart = changetype(out);
var sourceSize = sourceLen << alignof();
if (isManaged()) {
for (let offset: usize = 0; offset < sourceSize; offset += sizeof()) {
let ref = load(changetype(source) + offset);
store(outStart + offset, __retain(ref));
}
outStart += sourceSize;
let otherSize = otherLen << alignof();
for (let offset: usize = 0; offset < otherSize; offset += sizeof()) {
let ref = load(changetype(other) + offset);
store(outStart + offset, __retain(ref));
}
} else {
memory.copy(outStart, changetype(source), sourceSize);
memory.copy(outStart + sourceSize, changetype(other), otherLen << alignof());
}
return out;
}
static slice(source: StaticArray, start: i32 = 0, end: i32 = i32.MAX_VALUE): StaticArray {
var length = source.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);
var sliceSize = length << alignof();
var slice = changetype>(__alloc(sliceSize, idof>())); // retains
var sourcePtr = changetype(source) + (start << alignof());
if (isManaged()) {
let off: usize = 0;
while (off < sliceSize) {
let ref = load(sourcePtr + off);
store(changetype(slice) + off, __retain(ref));
off += sizeof();
}
} else {
memory.copy(changetype(slice), sourcePtr, sliceSize);
}
return slice;
}
constructor(length: i32) {
if (length > BLOCK_MAXSIZE >>> alignof()) throw new RangeError(E_INVALIDLENGTH);
var outSize = length << alignof();
var out = __alloc(outSize, idof>());
memory.fill(out, 0, outSize);
return changetype>(out); // retains
}
get length(): i32 {
return changetype(changetype(this) - BLOCK_OVERHEAD).rtSize >>> alignof();
}
@operator("[]") private __get(index: i32): T {
if (index >= this.length) throw new RangeError(E_INDEXOUTOFRANGE);
var value = this.__uget(index);
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 {
if (isManaged()) {
let offset = changetype(this) + (index << alignof());
let oldRef = load(offset);
if (changetype(value) != oldRef) {
store(offset, __retain(changetype(value)));
__release(changetype(oldRef));
}
} else {
store(changetype(this) + (index << alignof()), value);
}
}
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 {
var 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 {
var 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(other: Array): Array {
var thisLen = this.length;
var otherLen = select(0, other.length, other === null);
var outLen = thisLen + otherLen;
if (outLen > BLOCK_MAXSIZE >>> alignof()) throw new Error(E_INVALIDLENGTH);
var out = changetype>(__allocArray(outLen, alignof(), idof>())); // retains
var outStart = out.dataStart;
var thisSize = thisLen << alignof();
if (isManaged()) {
let thisStart = changetype(this);
for (let offset: usize = 0; offset < thisSize; offset += sizeof()) {
let ref = load(thisStart + offset);
store(outStart + offset, __retain(ref));
}
outStart += thisSize;
let otherStart = other.dataStart;
let otherSize = otherLen << alignof();
for (let offset: usize = 0; offset < otherSize; offset += sizeof()) {
let ref = load(otherStart + offset);
store(outStart + offset, __retain(ref));
}
} else {
memory.copy(outStart, changetype(this), thisSize);
memory.copy(outStart + thisSize, other.dataStart, otherLen << alignof());
}
return out;
}
slice(start: i32 = 0, end: i32 = i32.MAX_VALUE): Array {
var 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);
var slice = changetype>(__allocArray(length, alignof(), idof>())); // retains
var sliceBase = slice.dataStart;
var thisBase = changetype(this) + (start << alignof());
if (isManaged()) {
let off = 0;
let end = length << alignof();
while (off < end) {
let ref = load(thisBase + off);
store(sliceBase + off, __retain(ref));
off += sizeof();
}
} else {
memory.copy(sliceBase, thisBase, length << alignof());
}
return slice;
}
join(separator: string = ","): string {
if (isBoolean()) return joinBooleanArray(changetype(this), this.length, separator);
if (isInteger()) return joinIntegerArray(changetype(this), this.length, separator);
if (isFloat()) return joinFloatArray(changetype(this), this.length, separator);
if (ASC_SHRINK_LEVEL < 1) {
if (isString()) return joinStringArray(changetype(this), this.length, separator);
}
if (isReference()) return joinReferenceArray(changetype(this), this.length, separator);
ERROR("unspported element type");
return unreachable();
}
toString(): string {
return this.join();
}
// RT integration
@unsafe private __visit_impl(cookie: u32): void {
if (isManaged()) {
let cur = changetype(this);
let end = cur + changetype(changetype(this) - BLOCK_OVERHEAD).rtSize;
while (cur < end) {
let val = load(cur);
if (val) __visit(val, cookie);
cur += sizeof();
}
}
}
}