export class Array<T> { private __memory: usize; private __capacity: i32; length: i32; constructor(capacity: i32 = 0) { if (capacity < 0) throw new RangeError("invalid array length"); this.__capacity = this.length = capacity; this.__memory = capacity > 0 ? allocate_memory(<usize>capacity * sizeof<T>()) : 0; } @operator("[]") private __get(index: i32): T { if (<u32>index >= this.__capacity) throw new RangeError("index out of range"); return load<T>(this.__memory + <usize>index * sizeof<T>()); } @operator("[]=") private __set(index: i32, value: T): void { if (<u32>index >= this.__capacity) throw new RangeError("index out of range"); store<T>(this.__memory + <usize>index * sizeof<T>(), value); } indexOf(searchElement: T, fromIndex: i32 = 0): i32 { if (<u32>fromIndex >= this.__capacity) throw new RangeError("fromIndex out of range"); for (var index: usize = <usize>fromIndex, length: usize = min<u32>(this.length, this.__capacity); index < length; ++index) if (load<T>(this.__memory + index * sizeof<T>()) == searchElement) return index; return -1; } 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; } push(element: T): i32 { if (<u32>this.length >= this.__capacity) this.__grow(max(this.length + 1, this.__capacity << 1)); store<T>(this.__memory + <usize>this.length * sizeof<T>(), element); return ++this.length; } pop(): T { if (this.length < 1 || <u32>this.length > this.__capacity) throw new RangeError("index out of range"); --this.length; return load<T>(this.__memory + <usize>this.length * sizeof<T>()); } shift(): T { if (this.length < 1 || <u32>this.length > this.__capacity) throw new RangeError("index out of range"); 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 (<u32>this.length >= oldCapacity) { // inlined `this.__grow(max(this.length + 1, oldCapacity * 2))` (avoids moving twice) var newCapacity = max(this.length + 1, oldCapacity * 2); 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; } } @explicit 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); } }