import { defaultComparator, insertionSort, weakHeapSort } from "./internal/array"; export class Array<T> { __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(<usize>newCapacity * sizeof<T>()); if (oldMemory) { move_memory(newMemory, oldMemory, <usize>oldCapacity * sizeof<T>()); 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(<usize>capacity * sizeof<T>()) : 0; this.__capacity = capacity; this.__length = capacity; } every(callbackfn: (element: T, index: i32, array: Array<T>) => bool): bool { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < this.__length) { if (!callbackfn(load<T>(this.__memory + <usize>i * sizeof<T>()), i, this)) { return false; } i += 1; } return true; } findIndex(predicate: (element: T, index: i32, array: Array<T>) => bool): i32 { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < this.__length) { if (predicate(load<T>(this.__memory + <usize>i * sizeof<T>()), 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 (<u32>index >= <u32>this.__capacity) throw new Error("Index out of bounds"); return load<T>(this.__memory + <usize>index * sizeof<T>()); } @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<T>(this.__memory + <usize>index * sizeof<T>(), 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<T>(this.__memory + <usize>fromIndex * sizeof<T>()) == 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<T>(memory + <usize>fromIndex * sizeof<T>()) == 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<T>(memory + <usize>fromIndex * sizeof<T>()) == 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<T>(this.__memory + <usize>length * sizeof<T>(), element); this.__length = ++length; return length; } pop(): T { var length = this.__length; if (length < 1) throw new RangeError("Array is empty"); var element = load<T>(this.__memory + <usize>--length * sizeof<T>()); this.__length = length; return element; } reduce<U>( callbackfn: (previousValue: U, currentValue: T, currentIndex: i32, array: Array<T>) => 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<T>(this.__memory + <usize>i * sizeof<T>()), 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<T>(memory); move_memory( memory, memory + sizeof<T>(), <usize>(capacity - 1) * sizeof<T>() ); set_memory( memory + <usize>(capacity - 1) * sizeof<T>(), 0, sizeof<T>() ); this.__length = length - 1; return element; } some(callbackfn: (element: T, index: i32, array: Array<T>) => bool): bool { var toIndex: i32 = this.__length; var i: i32 = 0; while (i < toIndex && i < /* might change */ this.__length) { if (callbackfn(load<T>(this.__memory + <usize>i * sizeof<T>()), 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(<usize>newCapacity * sizeof<T>()); if (memory) { move_memory( newMemory + sizeof<T>(), memory, <usize>capacity * sizeof<T>() ); free_memory(memory); } this.__memory = newMemory; this.__capacity = newCapacity; memory = newMemory; } else { move_memory( memory + sizeof<T>(), memory, <usize>capacity * sizeof<T>() ); } store<T>(memory, element); this.__length = ++length; return length; } slice(begin: i32 = 0, end: i32 = i32.MAX_VALUE): Array<T> { 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<T>(capacity); if (capacity) { move_memory( sliced.__memory, this.__memory + <usize>begin * sizeof<T>(), <usize>capacity * sizeof<T>() ); } 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 + <usize>start * sizeof<T>(), memory + <usize>(start + deleteCount) * sizeof<T>(), <usize>deleteCount * sizeof<T>() ); this.__length = length - deleteCount; } reverse(): Array<T> { var memory = this.__memory; for (let front: usize = 0, back: usize = <usize>this.__length - 1; front < back; ++front, --back) { let temp = load<T>(memory + front * sizeof<T>()); store<T>(memory + front * sizeof<T>(), load<T>(memory + back * sizeof<T>())); store<T>(memory + back * sizeof<T>(), temp); } return this; } sort(comparator: (a: T, b: T) => i32 = defaultComparator<T>()): Array<T> { var len = this.length; if (len <= 1) return this; if (len == 2) { let memory = this.__memory; let a = load<T>(memory, sizeof<T>()); // var a = <T>arr[1]; let b = load<T>(memory, 0); // var b = <T>arr[0]; if (comparator(a, b) < 0) { store<T>(memory, b, sizeof<T>()); // arr[1] = b; store<T>(memory, a, 0); // arr[0] = a; } return this; } return len <= 256 ? insertionSort<T>(this, comparator) : weakHeapSort<T>(this, comparator); } }