export class Array<T> { private __memory: usize; private __capacity: i32; // capped to [0, 0x7fffffff] private __length: i32; // capped to [0, __capacity] private __grow(newCapacity: i32): void { assert(newCapacity > this.__capacity); var newMemory = allocate_memory(<usize>newCapacity * sizeof<T>()); if (this.__memory) { move_memory(newMemory, this.__memory, this.__capacity * sizeof<T>()); free_memory(this.__memory); } 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 = this.__length = capacity; } 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 >= this.__capacity) throw new Error("Index out of bounds"); // return changetype<T>(0) ? 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"); if (index >= this.__capacity) this.__grow(max(index + 1, this.__capacity << 1)); store<T>(this.__memory + <usize>index * sizeof<T>(), value); } indexOf(searchElement: T, fromIndex: i32 = 0): i32 { if (fromIndex < 0) fromIndex = this.__length + fromIndex; while (<u32>fromIndex < this.__length) { if (load<T>(this.__memory + fromIndex * sizeof<T>()) == searchElement) return fromIndex; ++fromIndex; } return -1; } lastIndexOf(searchElement: T, fromIndex: i32 = 0): i32 { if (fromIndex < 0) fromIndex = this.__length + fromIndex; else if (fromIndex >= this.__length) fromIndex = this.__length - 1; while (fromIndex >= 0) { if (load<T>(this.__memory + fromIndex * sizeof<T>()) == searchElement) return fromIndex; --fromIndex; } return -1; } push(element: T): i32 { if (this.__length == this.__capacity) this.__grow(this.__capacity ? this.__capacity << 1 : 1); store<T>(this.__memory + this.__length * sizeof<T>(), element); return ++this.__length; } pop(): T { if (this.__length < 1) throw new RangeError("Array is empty"); // return changetype<T>(0) ? return load<T>(this.__memory + --this.__length * sizeof<T>()); } shift(): T { if (this.__length < 1) throw new RangeError("Array is empty"); // return changetype<T>(0) ? var element = load<T>(this.__memory); move_memory(this.__memory, this.__memory + sizeof<T>(), (this.__capacity - 1) * sizeof<T>()); set_memory(this.__memory + (this.__capacity - 1) * sizeof<T>(), 0, sizeof<T>()); --this.__length; return element; } unshift(element: T): i32 { var oldCapacity = this.__capacity; if (this.__length == oldCapacity) { // inlined __grow (avoids moving twice) var newCapacity: i32 = oldCapacity ? oldCapacity << 1 : 1; assert(newCapacity > this.__capacity); var newMemory = allocate_memory(<usize>newCapacity * sizeof<T>()); if (this.__memory) { move_memory(newMemory + sizeof<T>(), this.__memory, oldCapacity * sizeof<T>()); free_memory(this.__memory); } this.__memory = newMemory; this.__capacity = newCapacity; } else move_memory(this.__memory + sizeof<T>(), this.__memory, oldCapacity * sizeof<T>()); store<T>(this.__memory, element); return ++this.__length; } slice(begin: i32 = 0, end: i32 = i32.MAX_VALUE): Array<T> { if (begin < 0) { begin = this.__length + begin; if (begin < 0) begin = 0; } else if (begin > this.__length) begin = this.__length; if (end < 0) end = this.__length + end; else if (end > this.__length) end = this.__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; if (start < 0) { start = this.__length + start; if (start < 0) start = 0; else if (start >= this.__length) return; } else if (start >= this.__length) return; deleteCount = min(deleteCount, this.__length - start); move_memory(this.__memory + <usize>start * sizeof<T>(), this.__memory + <usize>(start + deleteCount) * sizeof<T>(), deleteCount * sizeof<T>()); this.__length -= deleteCount; } reverse(): Array<T> { for (var front: usize = 0, back: usize = <usize>this.__length - 1; front < back; ++front, --back) { var temp = load<T>(this.__memory + front * sizeof<T>()); store<T>(this.__memory + front * sizeof<T>(), load<T>(this.__memory + back * sizeof<T>())); store<T>(this.__memory + back * sizeof<T>(), temp); } return this; } } @unmanaged export class CArray<T> { private constructor() {} @operator("[]") private __get(index: i32): T { if (index < 0) throw new RangeError("Index out of range"); return load<T>(changetype<usize>(this) + <usize>index * sizeof<T>()); } @operator("[]=") private __set(index: i32, value: T): void { if (index < 0) throw new RangeError("Index out of range"); store<T>(changetype<usize>(this) + <usize>index * sizeof<T>(), value); } }