/** | |
* Like `Array#splice`, but smarter for giant arrays. | |
* | |
* `Array#splice` takes all items to be inserted as individual argument which | |
* causes a stack overflow in V8 when trying to insert 100k items for instance. | |
* | |
* Otherwise, this does not return the removed items, and takes `items` as an | |
* array instead of rest parameters. | |
* | |
* @template {unknown} T | |
* Item type. | |
* @param {Array<T>} list | |
* List to operate on. | |
* @param {number} start | |
* Index to remove/insert at (can be negative). | |
* @param {number} remove | |
* Number of items to remove. | |
* @param {Array<T>} items | |
* Items to inject into `list`. | |
* @returns {void} | |
* Nothing. | |
*/ | |
export function splice(list, start, remove, items) { | |
const end = list.length | |
let chunkStart = 0 | |
/** @type {Array<unknown>} */ | |
let parameters | |
// Make start between zero and `end` (included). | |
if (start < 0) { | |
start = -start > end ? 0 : end + start | |
} else { | |
start = start > end ? end : start | |
} | |
remove = remove > 0 ? remove : 0 | |
// No need to chunk the items if there’s only a couple (10k) items. | |
if (items.length < 10000) { | |
parameters = Array.from(items) | |
parameters.unshift(start, remove) | |
// @ts-expect-error Hush, it’s fine. | |
list.splice(...parameters) | |
} else { | |
// Delete `remove` items starting from `start` | |
if (remove) list.splice(start, remove) | |
// Insert the items in chunks to not cause stack overflows. | |
while (chunkStart < items.length) { | |
parameters = items.slice(chunkStart, chunkStart + 10000) | |
parameters.unshift(start, 0) | |
// @ts-expect-error Hush, it’s fine. | |
list.splice(...parameters) | |
chunkStart += 10000 | |
start += 10000 | |
} | |
} | |
} | |
/** | |
* Append `items` (an array) at the end of `list` (another array). | |
* When `list` was empty, returns `items` instead. | |
* | |
* This prevents a potentially expensive operation when `list` is empty, | |
* and adds items in batches to prevent V8 from hanging. | |
* | |
* @template {unknown} T | |
* Item type. | |
* @param {Array<T>} list | |
* List to operate on. | |
* @param {Array<T>} items | |
* Items to add to `list`. | |
* @returns {Array<T>} | |
* Either `list` or `items`. | |
*/ | |
export function push(list, items) { | |
if (list.length > 0) { | |
splice(list, list.length, 0, items) | |
return list | |
} | |
return items | |
} | |