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