index.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import {constants} from 'micromark-util-symbol'
  2. /**
  3. * Like `Array#splice`, but smarter for giant arrays.
  4. *
  5. * `Array#splice` takes all items to be inserted as individual argument which
  6. * causes a stack overflow in V8 when trying to insert 100k items for instance.
  7. *
  8. * Otherwise, this does not return the removed items, and takes `items` as an
  9. * array instead of rest parameters.
  10. *
  11. * @template {unknown} T
  12. * Item type.
  13. * @param {Array<T>} list
  14. * List to operate on.
  15. * @param {number} start
  16. * Index to remove/insert at (can be negative).
  17. * @param {number} remove
  18. * Number of items to remove.
  19. * @param {Array<T>} items
  20. * Items to inject into `list`.
  21. * @returns {undefined}
  22. * Nothing.
  23. */
  24. export function splice(list, start, remove, items) {
  25. const end = list.length
  26. let chunkStart = 0
  27. /** @type {Array<unknown>} */
  28. let parameters
  29. // Make start between zero and `end` (included).
  30. if (start < 0) {
  31. start = -start > end ? 0 : end + start
  32. } else {
  33. start = start > end ? end : start
  34. }
  35. remove = remove > 0 ? remove : 0
  36. // No need to chunk the items if there’s only a couple (10k) items.
  37. if (items.length < constants.v8MaxSafeChunkSize) {
  38. parameters = Array.from(items)
  39. parameters.unshift(start, remove)
  40. // @ts-expect-error Hush, it’s fine.
  41. list.splice(...parameters)
  42. } else {
  43. // Delete `remove` items starting from `start`
  44. if (remove) list.splice(start, remove)
  45. // Insert the items in chunks to not cause stack overflows.
  46. while (chunkStart < items.length) {
  47. parameters = items.slice(
  48. chunkStart,
  49. chunkStart + constants.v8MaxSafeChunkSize
  50. )
  51. parameters.unshift(start, 0)
  52. // @ts-expect-error Hush, it’s fine.
  53. list.splice(...parameters)
  54. chunkStart += constants.v8MaxSafeChunkSize
  55. start += constants.v8MaxSafeChunkSize
  56. }
  57. }
  58. }
  59. /**
  60. * Append `items` (an array) at the end of `list` (another array).
  61. * When `list` was empty, returns `items` instead.
  62. *
  63. * This prevents a potentially expensive operation when `list` is empty,
  64. * and adds items in batches to prevent V8 from hanging.
  65. *
  66. * @template {unknown} T
  67. * Item type.
  68. * @param {Array<T>} list
  69. * List to operate on.
  70. * @param {Array<T>} items
  71. * Items to add to `list`.
  72. * @returns {Array<T>}
  73. * Either `list` or `items`.
  74. */
  75. export function push(list, items) {
  76. if (list.length > 0) {
  77. splice(list, list.length, 0, items)
  78. return list
  79. }
  80. return items
  81. }