UNPKG

assemblyscript

Version:

A TypeScript-like language for WebAssembly.

314 lines (293 loc) 8.95 kB
import { compareImpl } from "./string"; type Comparator<T> = (a: T, b: T) => i32; // @ts-ignore: decorator @lazy @inline const EMPTY = u32.MAX_VALUE; // @ts-ignore: decorator @inline const INSERTION_SORT_THRESHOLD = 48; // @ts-ignore: decorator @inline const MIN_RUN_LENGTH = 32; // @ts-ignore: decorator @inline function log2u(n: u32): u32 { return 31 - clz(n); } // @ts-ignore: decorator @inline export function COMPARATOR<T>(): Comparator<T> { if (isInteger<T>()) { if (isSigned<T>() && sizeof<T>() <= 4) { return (a, b) => i32(a) - i32(b); } else { return (a, b) => i32(a > b) - i32(a < b); } } else if (isFloat<T>()) { if (sizeof<T>() == 4) { return (a, b) => { let ia = reinterpret<i32>(f32(a)); let ib = reinterpret<i32>(f32(b)); ia ^= ia >> 31 >>> 1; ib ^= ib >> 31 >>> 1; return i32(ia > ib) - i32(ia < ib); }; } else { return (a, b) => { let ia = reinterpret<i64>(f64(a)); let ib = reinterpret<i64>(f64(b)); ia ^= ia >> 63 >>> 1; ib ^= ib >> 63 >>> 1; return i32(ia > ib) - i32(ia < ib); }; } } else if (isString<T>()) { return (a, b) => { if ( changetype<usize>(a) == changetype<usize>(b) || changetype<usize>(a) == 0 || changetype<usize>(b) == 0 ) return 0; let alen = changetype<string>(a).length; let blen = changetype<string>(b).length; if (!(alen | blen)) return 0; if (!alen) return -1; if (!blen) return 1; let res = compareImpl( changetype<string>(a), 0, changetype<string>(b), 0, <usize>min(alen, blen) ); return res ? res : alen - blen; }; } else { return (a, b) => i32(a > b) - i32(a < b); } } // Power Sort implementation (stable) from paper "Nearly-Optimal Mergesorts" // https://arxiv.org/pdf/1805.04154.pdf // This method usually outperform TimSort. // TODO: refactor c >>> 31 to c < 0 when binaryen will support this opt export function SORT<T>( ptr: usize, len: i32, comparator: Comparator<T> ): void { if (len <= INSERTION_SORT_THRESHOLD) { if (len <= 1) return; if (ASC_SHRINK_LEVEL < 1) { switch (len) { case 3: { let a = load<T>(ptr, 0); let b = load<T>(ptr, 1 << alignof<T>()); let c = comparator(a, b) > 0; store<T>(ptr, select<T>(b, a, c), 0); a = select<T>(a, b, c); b = load<T>(ptr, 2 << alignof<T>()); c = comparator(a, b) > 0; store<T>(ptr, select<T>(b, a, c), 1 << alignof<T>()); store<T>(ptr, select<T>(a, b, c), 2 << alignof<T>()); } case 2: { let a = load<T>(ptr, 0); let b = load<T>(ptr, 1 << alignof<T>()); let c = comparator(a, b) > 0; store<T>(ptr, select<T>(b, a, c), 0); store<T>(ptr, select<T>(a, b, c), 1 << alignof<T>()); return; } } } insertionSort<T>(ptr, 0, len - 1, 0, comparator); return; } let lgPlus2 = log2u(len) + 2; let lgPlus2Size = lgPlus2 << alignof<u32>(); let leftRunStartBuf = __alloc(lgPlus2Size << 1); let leftRunEndBuf = leftRunStartBuf + lgPlus2Size; for (let i: u32 = 0; i < lgPlus2; ++i) { store<u32>(leftRunStartBuf + (<usize>i << alignof<u32>()), EMPTY); } let buffer = __alloc(len << alignof<T>()); let hi = len - 1; let endA = extendRunRight<T>(ptr, 0, hi, comparator); let lenA = endA + 1; if (lenA < MIN_RUN_LENGTH) { endA = min(hi, MIN_RUN_LENGTH - 1); insertionSort<T>(ptr, 0, endA, lenA, comparator); } let top: u32 = 0, startA = 0; while (endA < hi) { let startB = endA + 1; let endB = extendRunRight<T>(ptr, startB, hi, comparator); let lenB = endB - startB + 1; if (lenB < MIN_RUN_LENGTH) { endB = min(hi, startB + MIN_RUN_LENGTH - 1); insertionSort<T>(ptr, startB, endB, lenB, comparator); } let k = nodePower(0, hi, startA, startB, endB); for (let i = top; i > k; --i) { let start = load<u32>(leftRunStartBuf + (<usize>i << alignof<u32>())); if (start != EMPTY) { mergeRuns<T>( ptr, start, load<u32>(leftRunEndBuf + (<usize>i << alignof<u32>())) + 1, endA, buffer, comparator ); startA = start; store<u32>(leftRunStartBuf + (<usize>i << alignof<u32>()), EMPTY); } } store<u32>(leftRunStartBuf + (<usize>k << alignof<u32>()), startA); store<u32>(leftRunEndBuf + (<usize>k << alignof<u32>()), endA); startA = startB; endA = endB; top = k; } for (let i = top; i != 0; --i) { let start = load<u32>(leftRunStartBuf + (<usize>i << alignof<u32>())); if (start != EMPTY) { mergeRuns<T>( ptr, start, load<u32>(leftRunEndBuf + (<usize>i << alignof<u32>())) + 1, hi, buffer, comparator ); } } // dealloc aux buffers __free(buffer); __free(leftRunStartBuf); } function insertionSort<T>( ptr: usize, left: i32, right: i32, presorted: i32, comparator: Comparator<T> ): void { if (ASC_SHRINK_LEVEL >= 1) { // slightly improved original insertion sort for (let i = left + presorted; i <= right; ++i) { let j = i - 1; let a = load<T>(ptr + (<usize>i << alignof<T>())); while (j >= left) { let b = load<T>(ptr + (<usize>j << alignof<T>())); if (comparator(a, b) < 0) { store<T>(ptr + (<usize>j << alignof<T>()), b, 1 << alignof<T>()); --j; } else break; } store<T>(ptr + (<usize>j << alignof<T>()), a, 1 << alignof<T>()); } } else { // even-odd two-way insertion sort which allow increase minRunLen let range = right - left + 1; let i = left + select(range & 1, presorted - ((range - presorted) & 1), presorted == 0); for (; i <= right; i += 2) { let a = load<T>(ptr + (<usize>i << alignof<T>()), 0); let b = load<T>(ptr + (<usize>i << alignof<T>()), 1 << alignof<T>()); let min = b, max = a; if (comparator(a, b) <= 0) { min = a, max = b; } let j = i - 1; while (j >= left) { a = load<T>(ptr + (<usize>j << alignof<T>())); if (comparator(a, max) > 0) { store<T>(ptr + (<usize>j << alignof<T>()), a, 2 << alignof<T>()); --j; } else break; } store<T>(ptr + (<usize>j << alignof<T>()), max, 2 << alignof<T>()); while (j >= left) { a = load<T>(ptr + (<usize>j << alignof<T>())); if (comparator(a, min) > 0) { store<T>(ptr + (<usize>j << alignof<T>()), a, 1 << alignof<T>()); --j; } else break; } store<T>(ptr + (<usize>j << alignof<T>()), min, 1 << alignof<T>()); } } } function nodePower(left: u32, right: u32, startA: u32, startB: u32, endB: u32): u32 { let n: u64 = right - left + 1; let s = startB - (left << 1); let l = startA + s; let r = endB + s + 1; let a = (<u64>l << 30) / n; let b = (<u64>r << 30) / n; return clz(<u32>(a ^ b)); } function extendRunRight<T>( ptr: usize, i: i32, right: i32, comparator: Comparator<T> ): i32 { if (i == right) return i; let j = i; if (comparator( load<T>(ptr + (<usize> j << alignof<T>())), load<T>(ptr + (<usize>++j << alignof<T>())) ) > 0) { while ( j < right && (comparator( load<T>(ptr + (<usize>j << alignof<T>()), 1 << alignof<T>()), load<T>(ptr + (<usize>j << alignof<T>())) ) >>> 31) // < 0 ) ++j; // reverse let k = j; while (i < k) { let tmp = load<T>(ptr + (<usize>i << alignof<T>())); store<T>(ptr + (<usize>i << alignof<T>()), load<T>(ptr + (<usize>k << alignof<T>()))); ++i; store<T>(ptr + (<usize>k << alignof<T>()), tmp); --k; } } else { while ( j < right && comparator( load<T>(ptr + (<usize>j << alignof<T>()), 1 << alignof<T>()), load<T>(ptr + (<usize>j << alignof<T>())) ) >= 0 ) ++j; } return j; } // Merges arr[l..m - 1] and arr[m..r] function mergeRuns<T>( ptr: usize, l: i32, m: i32, r: i32, buffer: usize, comparator: Comparator<T> ): void { --m; let i: i32, j: i32, t = r + m; for (i = m + 1; i > l; --i) { store<T>( buffer + (<usize>(i - 1) << alignof<T>()), load<T>(ptr + (<usize>(i - 1) << alignof<T>())) ); } for (j = m; j < r; ++j) { store<T>( buffer + (<usize>(t - j) << alignof<T>()), load<T>(ptr + (<usize>j << alignof<T>()), 1 << alignof<T>()) ); } for (let k = l; k <= r; ++k) { let a = load<T>(buffer + (<usize>j << alignof<T>())); let b = load<T>(buffer + (<usize>i << alignof<T>())); if (comparator(a, b) < 0) { store<T>(ptr + (<usize>k << alignof<T>()), a); --j; } else { store<T>(ptr + (<usize>k << alignof<T>()), b); ++i; } } }