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; } 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 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); } includes(searchElement: T, fromIndex: i32 = 0): bool { if (this.__length == 0 || fromIndex >= this.__length) { return false; } if (fromIndex < 0) { fromIndex = this.__length + fromIndex; if (fromIndex < 0) { fromIndex = 0; } } while (fromIndex < this.__length) { if (load(this.__memory + fromIndex * sizeof()) == searchElement) { return true; } ++fromIndex; } return false; } indexOf(searchElement: T, fromIndex: i32 = 0): i32 { if (this.__length == 0 || fromIndex >= this.__length) { return -1; } if (fromIndex < 0) { fromIndex = this.__length + fromIndex; if (fromIndex < 0) { fromIndex = 0; } } while (fromIndex < this.__length) { if (load(this.__memory + fromIndex * sizeof()) == searchElement) { return fromIndex; } ++fromIndex; } return -1; } lastIndexOf(searchElement: T, fromIndex: i32 = this.__length): i32 { if (this.__length == 0) { return -1; } 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()); } 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 < this.__length) { accumulator = callbackfn(accumulator, load(this.__memory + i * sizeof()), i, this); i += 1; } return accumulator; } 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; } some(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 true; } i += 1; } return false; } unshift(element: T): i32 { var oldCapacity = this.__capacity; if (this.__length == oldCapacity) { // inlined __grow (avoids moving twice) let newCapacity: i32 = oldCapacity ? oldCapacity << 1 : 1; assert(newCapacity > this.__capacity); let 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 (let front: usize = 0, back: usize = this.__length - 1; front < back; ++front, --back) { let temp = load(this.__memory + front * sizeof()); store(this.__memory + front * sizeof(), load(this.__memory + back * sizeof())); store(this.__memory + back * sizeof(), temp); } return this; } sort(comparator: (a: T, b: T) => i32 = createDefaultComparator()): Array { return sort(this, comparator); } } @unmanaged @sealed 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); } } /* * Internal methods */ // TODO remove this wrapper when indirect table landed function createDefaultComparator(): (a: T, b: T) => i32 { return (a: T, b: T): i32 => ( (a > b) - (a < b) ); } function insertionSort(arr: Array, comparator: (a: T, b: T) => i32): Array { var a: T, b: T, j: i32; const typeShift = alignof(); for (let i: i32 = 0, len: i32 = arr.length; i < len; i++) { a = load(arr.__memory + (i << typeShift)); // a = arr[i]; j = i - 1; while (j >= 0) { b = load(arr.__memory + (j << typeShift)); // b = arr[j]; if (comparator(a, b) < 0) { store(arr.__memory + ((j + 1) << typeShift), b); // arr[j + 1] = b; j--; } else break; } store(arr.__memory + ((j + 1) << typeShift), a); // arr[j + 1] = a; } return arr; } /* Weak Heap Sort implementation based on paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.21.1863&rep=rep1&type=pdf */ function weakHeapSort(arr: Array, comparator: (a: T, b: T) => i32): Array { var len: i32 = arr.length; var i: i32, j: i32, y: i32, p: i32, a: T, b: T; const typeShift = alignof(); const intShift = alignof(); var blen = (len + 7) >> 3; var bitset = allocate_memory(blen << intShift); set_memory(bitset, 0, blen << intShift); for (i = len - 1; i > 0; i--) { j = i; while ((j & 1) == ((load(bitset + ((j >> 4) << intShift)) >> ((j >> 1) & 7)) & 1)) { j >>= 1; } p = j >> 1; a = load(arr.__memory + (p << typeShift)); // a = arr[p]; b = load(arr.__memory + (i << typeShift)); // b = arr[i]; if (comparator(a, b) < 0) { store( bitset + ((i >> 3) << intShift), load(bitset + ((i >> 3) << intShift)) ^ (1 << (i & 7)) ); store(arr.__memory + (i << typeShift), a); // arr[i] = a; store(arr.__memory + (p << typeShift), b); // arr[p] = b; } } for (i = len - 1; i >= 2; i--) { /* a = arr[0]; arr[0] = arr[i]; arr[i] = a; */ a = load(arr.__memory, 0); store(arr.__memory, load(arr.__memory + (i << typeShift)), 0); store(arr.__memory + (i << typeShift), a); let x = 1; while ((y = (x << 1) + ((load(bitset + ((x >> 3) << intShift)) >> (x & 7)) & 1)) < i) { x = y; } while (x > 0) { a = load(arr.__memory, 0); // a = arr[0]; b = load(arr.__memory + (x << typeShift)); // b = arr[x]; if (comparator(a, b) < 0) { store( bitset + ((x >> 3) << intShift), load(bitset + ((x >> 3) << intShift)) ^ (1 << (x & 7)) ); store(arr.__memory + (x << typeShift), a); // arr[x] = a; store(arr.__memory, b, 0); // arr[0] = b; } x >>= 1; } } free_memory(bitset); /* let t = arr[1]; arr[1] = arr[0]; arr[0] = t; */ var t = load(arr.__memory, sizeof()); store(arr.__memory, load(arr.__memory, 0), sizeof()); store(arr.__memory, t, 0); return arr; } function sort(arr: Array, comparator: (a: T, b: T) => i32): Array { var len = arr.length; if (len <= 1) return arr; if (len == 2) { let a = load(arr.__memory, sizeof()); // var a = arr[1]; let b = load(arr.__memory, 0); // var b = arr[0]; if (comparator(a, b) < 0) { store(arr.__memory, b, sizeof()); // arr[1] = b; store(arr.__memory, a, 0); // arr[0] = a; } return arr; } if (len <= 256) { return insertionSort(arr, comparator); } return weakHeapSort(arr, comparator); }