/// import { OBJECT, BLOCK_MAXSIZE, TOTAL_OVERHEAD } from "./rt/common"; import { Runtime } from "shared/runtime"; import { COMPARATOR, SORT } from "./util/sort"; import { REVERSE } 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 { var length = source.length; var outSize = length << alignof(); var 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; } 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>(__new(outLen << alignof(), idof>())); 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, ref); __link(changetype(out), ref, true); } outStart += sourceSize; let otherSize = otherLen << alignof(); for (let offset: usize = 0; offset < otherSize; offset += sizeof()) { let ref = load(changetype(other) + offset); store(outStart + offset, ref); __link(changetype(out), ref, true); } } 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>(__new(sliceSize, idof>())); var sourcePtr = changetype(source) + (start << alignof()); if (isManaged()) { let off: usize = 0; while (off < sliceSize) { let ref = load(sourcePtr + off); store(changetype(slice) + off, ref); __link(changetype(slice), ref, true); 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 = 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 { var len = this.length; index += select(0, len, index >= 0); if (index >= len) throw new RangeError(E_INDEXOUTOFRANGE); var 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); var 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): this { var ptr = changetype(this); var len = this.length; start = start < 0 ? max(len + start, 0) : min(start, len); end = end < 0 ? max(len + end, 0) : min(end, len); if (isManaged()) { for (; start < end; ++start) { store(ptr + (start << alignof()), changetype(value)); __link(changetype(this), changetype(value), true); } } else if (sizeof() == 1) { if (start < end) { memory.fill( ptr + start, u8(value), (end - start) ); } } else { for (; start < end; ++start) { store(ptr + (start << alignof()), value); } } return this; } copyWithin(target: i32, start: i32, end: i32 = i32.MAX_VALUE): this { var ptr = changetype(this); var len = this.length; end = min(end, len); var to = target < 0 ? max(len + target, 0) : min(target, len); var from = start < 0 ? max(len + start, 0) : min(start, len); var last = end < 0 ? max(len + end, 0) : min(end, len); var 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 { 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>(__newArray(outLen, alignof(), idof>())); 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, ref); __link(changetype(out), ref, true); } 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, ref); __link(changetype(out), ref, true); } } 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>(__newArray(length, alignof(), idof>())); 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, ref); __link(changetype(slice), ref, true); off += sizeof(); } } else { memory.copy(sliceBase, thisBase, length << alignof()); } return slice; } 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 { var len = this.length; var out = changetype>(__newArray(len, alignof(), idof>())); var 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 { var 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 { var 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 { var 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()): this { 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(): this { 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(); } } } }