/// 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()) 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(); } reverse(): StaticArray { REVERSE(changetype(this), this.length); return this; } toString(): string { return this.join(); } // RT integration @unsafe private __visit(cookie: u32): void { if (isManaged()) { let cur = changetype(this); let end = cur + changetype(changetype(this) - TOTAL_OVERHEAD).rtSize; while (cur < end) { let val = load(cur); if (val) __visit(val, cookie); cur += sizeof(); } } } }