import { defaultComparator, insertionSort, weakHeapSort } from "./internal/array"; export class Array { __memory: usize; __capacity: i32; // capped to [0, 0x7fffffff] __length: i32; // capped to [0, __capacity] private __grow(newCapacity: i32): void { var oldMemory = this.__memory; var oldCapacity = this.__capacity; assert(newCapacity > oldCapacity); var newMemory = allocate_memory(newCapacity * sizeof()); if (oldMemory) { move_memory(newMemory, oldMemory, oldCapacity * sizeof()); free_memory(oldMemory); } this.__memory = newMemory; this.__capacity = newCapacity; } constructor(capacity: i32 = 0) { if (capacity < 0) throw new RangeError("Invalid array length"); this.__memory = capacity ? allocate_memory(capacity * sizeof()) : 0; this.__capacity = capacity; this.__length = capacity; } every(callbackfn: (element: T, index: i32, array: Array) => bool): bool { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < this.__length) { if (!callbackfn(load(this.__memory + i * sizeof()), i, this)) { return false; } i += 1; } return true; } findIndex(predicate: (element: T, index: i32, array: Array) => bool): i32 { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < this.__length) { if (predicate(load(this.__memory + i * sizeof()), i, this)) { return i; } i += 1; } return -1; } get length(): i32 { return this.__length; } set length(length: i32) { if (length < 0) throw new RangeError("Invalid array length"); if (length > this.__capacity) this.__grow(max(length, this.__capacity << 1)); this.__length = length; } @operator("[]") private __get(index: i32): T { if (index >= this.__capacity) throw new Error("Index out of bounds"); return load(this.__memory + index * sizeof()); } @operator("[]=") private __set(index: i32, value: T): void { if (index < 0) throw new Error("Index out of bounds"); var capacity = this.__capacity; if (index >= capacity) this.__grow(max(index + 1, capacity << 1)); store(this.__memory + index * sizeof(), value); } includes(searchElement: T, fromIndex: i32 = 0): bool { var length = this.__length; if (length == 0 || fromIndex >= length) return false; if (fromIndex < 0) { fromIndex = length + fromIndex; if (fromIndex < 0) { fromIndex = 0; } } while (fromIndex < length) { if (load(this.__memory + fromIndex * sizeof()) == searchElement) return true; ++fromIndex; } return false; } indexOf(searchElement: T, fromIndex: i32 = 0): i32 { var length = this.__length; if (length == 0 || fromIndex >= length) { return -1; } if (fromIndex < 0) { fromIndex = length + fromIndex; if (fromIndex < 0) { fromIndex = 0; } } var memory = this.__memory; while (fromIndex < length) { if (load(memory + fromIndex * sizeof()) == searchElement) return fromIndex; ++fromIndex; } return -1; } lastIndexOf(searchElement: 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; } var memory = this.__memory; while (fromIndex >= 0) { if (load(memory + fromIndex * sizeof()) == searchElement) return fromIndex; --fromIndex; } return -1; } push(element: T): i32 { var capacity = this.__capacity; var length = this.__length; if (length == capacity) { this.__grow(capacity ? capacity << 1 : 1); } store(this.__memory + length * sizeof(), element); this.__length = ++length; return length; } pop(): T { var length = this.__length; if (length < 1) throw new RangeError("Array is empty"); var element = load(this.__memory + --length * sizeof()); this.__length = length; return element; } reduce( callbackfn: (previousValue: U, currentValue: T, currentIndex: i32, array: Array) => U, initialValue: U ): U { var accumulator: U = initialValue; var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < /* might change */ this.__length) { accumulator = callbackfn(accumulator, load(this.__memory + i * sizeof()), i, this); i += 1; } return accumulator; } shift(): T { var length = this.__length; if (length < 1) throw new RangeError("Array is empty"); var memory = this.__memory; var capacity = this.__capacity; var element = load(memory); move_memory( memory, memory + sizeof(), (capacity - 1) * sizeof() ); set_memory( memory + (capacity - 1) * sizeof(), 0, sizeof() ); this.__length = length - 1; return element; } some(callbackfn: (element: T, index: i32, array: Array) => bool): bool { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < /* might change */ this.__length) { if (callbackfn(load(this.__memory + i * sizeof()), i, this)) return true; i += 1; } return false; } unshift(element: T): i32 { var memory = this.__memory; var capacity = this.__capacity; var length = this.__length; if (this.__length == capacity) { // inlined __grow (avoids moving twice) let newCapacity: i32 = capacity ? capacity << 1 : 1; assert(newCapacity > capacity); let newMemory = allocate_memory(newCapacity * sizeof()); if (memory) { move_memory( newMemory + sizeof(), memory, capacity * sizeof() ); free_memory(memory); } this.__memory = newMemory; this.__capacity = newCapacity; memory = newMemory; } else { move_memory( memory + sizeof(), memory, capacity * sizeof() ); } store(memory, element); this.__length = ++length; return length; } slice(begin: i32 = 0, end: i32 = i32.MAX_VALUE): Array { var length = this.__length; if (begin < 0) { begin = length + begin; if (begin < 0) { begin = 0; } } else if (begin > length) { begin = length; } if (end < 0) { end = length + end; } else if (end > length) { end = length; } if (end < begin) { end = begin; } var capacity = end - begin; assert(capacity >= 0); var sliced = new Array(capacity); if (capacity) { move_memory( sliced.__memory, this.__memory + begin * sizeof(), capacity * sizeof() ); } return sliced; } splice(start: i32, deleteCount: i32 = i32.MAX_VALUE): void { if (deleteCount < 1) { return; } var length = this.__length; if (start < 0) { start = length + start; if (start < 0) { start = 0; } else if (start >= length) { return; } } else if (start >= length) { return; } deleteCount = min(deleteCount, length - start); var memory = this.__memory; move_memory( memory + start * sizeof(), memory + (start + deleteCount) * sizeof(), deleteCount * sizeof() ); this.__length = length - deleteCount; } reverse(): Array { var memory = this.__memory; for (let front: usize = 0, back: usize = this.__length - 1; front < back; ++front, --back) { let temp = load(memory + front * sizeof()); store(memory + front * sizeof(), load(memory + back * sizeof())); store(memory + back * sizeof(), temp); } return this; } sort(comparator: (a: T, b: T) => i32 = defaultComparator()): Array { var len = this.length; if (len <= 1) return this; if (len == 2) { let memory = this.__memory; let a = load(memory, sizeof()); // var a = arr[1]; let b = load(memory, 0); // var b = arr[0]; if (comparator(a, b) < 0) { store(memory, b, sizeof()); // arr[1] = b; store(memory, a, 0); // arr[0] = a; } return this; } return len <= 256 ? insertionSort(this, comparator) : weakHeapSort(this, comparator); } }