/// import { OBJECT, BLOCK_MAXSIZE, TOTAL_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 = 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>())); 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); } } 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; } 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(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(); } } } }