/// 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(); } } } }