index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. 'use strict';
  2. /**
  3. * Custom implementation of a double ended queue.
  4. */
  5. function Denque(array, options) {
  6. var options = options || {};
  7. this._capacity = options.capacity;
  8. this._head = 0;
  9. this._tail = 0;
  10. if (Array.isArray(array)) {
  11. this._fromArray(array);
  12. } else {
  13. this._capacityMask = 0x3;
  14. this._list = new Array(4);
  15. }
  16. }
  17. /**
  18. * --------------
  19. * PUBLIC API
  20. * -------------
  21. */
  22. /**
  23. * Returns the item at the specified index from the list.
  24. * 0 is the first element, 1 is the second, and so on...
  25. * Elements at negative values are that many from the end: -1 is one before the end
  26. * (the last element), -2 is two before the end (one before last), etc.
  27. * @param index
  28. * @returns {*}
  29. */
  30. Denque.prototype.peekAt = function peekAt(index) {
  31. var i = index;
  32. // expect a number or return undefined
  33. if ((i !== (i | 0))) {
  34. return void 0;
  35. }
  36. var len = this.size();
  37. if (i >= len || i < -len) return undefined;
  38. if (i < 0) i += len;
  39. i = (this._head + i) & this._capacityMask;
  40. return this._list[i];
  41. };
  42. /**
  43. * Alias for peekAt()
  44. * @param i
  45. * @returns {*}
  46. */
  47. Denque.prototype.get = function get(i) {
  48. return this.peekAt(i);
  49. };
  50. /**
  51. * Returns the first item in the list without removing it.
  52. * @returns {*}
  53. */
  54. Denque.prototype.peek = function peek() {
  55. if (this._head === this._tail) return undefined;
  56. return this._list[this._head];
  57. };
  58. /**
  59. * Alias for peek()
  60. * @returns {*}
  61. */
  62. Denque.prototype.peekFront = function peekFront() {
  63. return this.peek();
  64. };
  65. /**
  66. * Returns the item that is at the back of the queue without removing it.
  67. * Uses peekAt(-1)
  68. */
  69. Denque.prototype.peekBack = function peekBack() {
  70. return this.peekAt(-1);
  71. };
  72. /**
  73. * Returns the current length of the queue
  74. * @return {Number}
  75. */
  76. Object.defineProperty(Denque.prototype, 'length', {
  77. get: function length() {
  78. return this.size();
  79. }
  80. });
  81. /**
  82. * Return the number of items on the list, or 0 if empty.
  83. * @returns {number}
  84. */
  85. Denque.prototype.size = function size() {
  86. if (this._head === this._tail) return 0;
  87. if (this._head < this._tail) return this._tail - this._head;
  88. else return this._capacityMask + 1 - (this._head - this._tail);
  89. };
  90. /**
  91. * Add an item at the beginning of the list.
  92. * @param item
  93. */
  94. Denque.prototype.unshift = function unshift(item) {
  95. if (arguments.length === 0) return this.size();
  96. var len = this._list.length;
  97. this._head = (this._head - 1 + len) & this._capacityMask;
  98. this._list[this._head] = item;
  99. if (this._tail === this._head) this._growArray();
  100. if (this._capacity && this.size() > this._capacity) this.pop();
  101. if (this._head < this._tail) return this._tail - this._head;
  102. else return this._capacityMask + 1 - (this._head - this._tail);
  103. };
  104. /**
  105. * Remove and return the first item on the list,
  106. * Returns undefined if the list is empty.
  107. * @returns {*}
  108. */
  109. Denque.prototype.shift = function shift() {
  110. var head = this._head;
  111. if (head === this._tail) return undefined;
  112. var item = this._list[head];
  113. this._list[head] = undefined;
  114. this._head = (head + 1) & this._capacityMask;
  115. if (head < 2 && this._tail > 10000 && this._tail <= this._list.length >>> 2) this._shrinkArray();
  116. return item;
  117. };
  118. /**
  119. * Add an item to the bottom of the list.
  120. * @param item
  121. */
  122. Denque.prototype.push = function push(item) {
  123. if (arguments.length === 0) return this.size();
  124. var tail = this._tail;
  125. this._list[tail] = item;
  126. this._tail = (tail + 1) & this._capacityMask;
  127. if (this._tail === this._head) {
  128. this._growArray();
  129. }
  130. if (this._capacity && this.size() > this._capacity) {
  131. this.shift();
  132. }
  133. if (this._head < this._tail) return this._tail - this._head;
  134. else return this._capacityMask + 1 - (this._head - this._tail);
  135. };
  136. /**
  137. * Remove and return the last item on the list.
  138. * Returns undefined if the list is empty.
  139. * @returns {*}
  140. */
  141. Denque.prototype.pop = function pop() {
  142. var tail = this._tail;
  143. if (tail === this._head) return undefined;
  144. var len = this._list.length;
  145. this._tail = (tail - 1 + len) & this._capacityMask;
  146. var item = this._list[this._tail];
  147. this._list[this._tail] = undefined;
  148. if (this._head < 2 && tail > 10000 && tail <= len >>> 2) this._shrinkArray();
  149. return item;
  150. };
  151. /**
  152. * Remove and return the item at the specified index from the list.
  153. * Returns undefined if the list is empty.
  154. * @param index
  155. * @returns {*}
  156. */
  157. Denque.prototype.removeOne = function removeOne(index) {
  158. var i = index;
  159. // expect a number or return undefined
  160. if ((i !== (i | 0))) {
  161. return void 0;
  162. }
  163. if (this._head === this._tail) return void 0;
  164. var size = this.size();
  165. var len = this._list.length;
  166. if (i >= size || i < -size) return void 0;
  167. if (i < 0) i += size;
  168. i = (this._head + i) & this._capacityMask;
  169. var item = this._list[i];
  170. var k;
  171. if (index < size / 2) {
  172. for (k = index; k > 0; k--) {
  173. this._list[i] = this._list[i = (i - 1 + len) & this._capacityMask];
  174. }
  175. this._list[i] = void 0;
  176. this._head = (this._head + 1 + len) & this._capacityMask;
  177. } else {
  178. for (k = size - 1 - index; k > 0; k--) {
  179. this._list[i] = this._list[i = (i + 1 + len) & this._capacityMask];
  180. }
  181. this._list[i] = void 0;
  182. this._tail = (this._tail - 1 + len) & this._capacityMask;
  183. }
  184. return item;
  185. };
  186. /**
  187. * Remove number of items from the specified index from the list.
  188. * Returns array of removed items.
  189. * Returns undefined if the list is empty.
  190. * @param index
  191. * @param count
  192. * @returns {array}
  193. */
  194. Denque.prototype.remove = function remove(index, count) {
  195. var i = index;
  196. var removed;
  197. var del_count = count;
  198. // expect a number or return undefined
  199. if ((i !== (i | 0))) {
  200. return void 0;
  201. }
  202. if (this._head === this._tail) return void 0;
  203. var size = this.size();
  204. var len = this._list.length;
  205. if (i >= size || i < -size || count < 1) return void 0;
  206. if (i < 0) i += size;
  207. if (count === 1 || !count) {
  208. removed = new Array(1);
  209. removed[0] = this.removeOne(i);
  210. return removed;
  211. }
  212. if (i === 0 && i + count >= size) {
  213. removed = this.toArray();
  214. this.clear();
  215. return removed;
  216. }
  217. if (i + count > size) count = size - i;
  218. var k;
  219. removed = new Array(count);
  220. for (k = 0; k < count; k++) {
  221. removed[k] = this._list[(this._head + i + k) & this._capacityMask];
  222. }
  223. i = (this._head + i) & this._capacityMask;
  224. if (index + count === size) {
  225. this._tail = (this._tail - count + len) & this._capacityMask;
  226. for (k = count; k > 0; k--) {
  227. this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
  228. }
  229. return removed;
  230. }
  231. if (index === 0) {
  232. this._head = (this._head + count + len) & this._capacityMask;
  233. for (k = count - 1; k > 0; k--) {
  234. this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
  235. }
  236. return removed;
  237. }
  238. if (i < size / 2) {
  239. this._head = (this._head + index + count + len) & this._capacityMask;
  240. for (k = index; k > 0; k--) {
  241. this.unshift(this._list[i = (i - 1 + len) & this._capacityMask]);
  242. }
  243. i = (this._head - 1 + len) & this._capacityMask;
  244. while (del_count > 0) {
  245. this._list[i = (i - 1 + len) & this._capacityMask] = void 0;
  246. del_count--;
  247. }
  248. if (index < 0) this._tail = i;
  249. } else {
  250. this._tail = i;
  251. i = (i + count + len) & this._capacityMask;
  252. for (k = size - (count + index); k > 0; k--) {
  253. this.push(this._list[i++]);
  254. }
  255. i = this._tail;
  256. while (del_count > 0) {
  257. this._list[i = (i + 1 + len) & this._capacityMask] = void 0;
  258. del_count--;
  259. }
  260. }
  261. if (this._head < 2 && this._tail > 10000 && this._tail <= len >>> 2) this._shrinkArray();
  262. return removed;
  263. };
  264. /**
  265. * Native splice implementation.
  266. * Remove number of items from the specified index from the list and/or add new elements.
  267. * Returns array of removed items or empty array if count == 0.
  268. * Returns undefined if the list is empty.
  269. *
  270. * @param index
  271. * @param count
  272. * @param {...*} [elements]
  273. * @returns {array}
  274. */
  275. Denque.prototype.splice = function splice(index, count) {
  276. var i = index;
  277. // expect a number or return undefined
  278. if ((i !== (i | 0))) {
  279. return void 0;
  280. }
  281. var size = this.size();
  282. if (i < 0) i += size;
  283. if (i > size) return void 0;
  284. if (arguments.length > 2) {
  285. var k;
  286. var temp;
  287. var removed;
  288. var arg_len = arguments.length;
  289. var len = this._list.length;
  290. var arguments_index = 2;
  291. if (!size || i < size / 2) {
  292. temp = new Array(i);
  293. for (k = 0; k < i; k++) {
  294. temp[k] = this._list[(this._head + k) & this._capacityMask];
  295. }
  296. if (count === 0) {
  297. removed = [];
  298. if (i > 0) {
  299. this._head = (this._head + i + len) & this._capacityMask;
  300. }
  301. } else {
  302. removed = this.remove(i, count);
  303. this._head = (this._head + i + len) & this._capacityMask;
  304. }
  305. while (arg_len > arguments_index) {
  306. this.unshift(arguments[--arg_len]);
  307. }
  308. for (k = i; k > 0; k--) {
  309. this.unshift(temp[k - 1]);
  310. }
  311. } else {
  312. temp = new Array(size - (i + count));
  313. var leng = temp.length;
  314. for (k = 0; k < leng; k++) {
  315. temp[k] = this._list[(this._head + i + count + k) & this._capacityMask];
  316. }
  317. if (count === 0) {
  318. removed = [];
  319. if (i != size) {
  320. this._tail = (this._head + i + len) & this._capacityMask;
  321. }
  322. } else {
  323. removed = this.remove(i, count);
  324. this._tail = (this._tail - leng + len) & this._capacityMask;
  325. }
  326. while (arguments_index < arg_len) {
  327. this.push(arguments[arguments_index++]);
  328. }
  329. for (k = 0; k < leng; k++) {
  330. this.push(temp[k]);
  331. }
  332. }
  333. return removed;
  334. } else {
  335. return this.remove(i, count);
  336. }
  337. };
  338. /**
  339. * Soft clear - does not reset capacity.
  340. */
  341. Denque.prototype.clear = function clear() {
  342. this._list = new Array(this._list.length);
  343. this._head = 0;
  344. this._tail = 0;
  345. };
  346. /**
  347. * Returns true or false whether the list is empty.
  348. * @returns {boolean}
  349. */
  350. Denque.prototype.isEmpty = function isEmpty() {
  351. return this._head === this._tail;
  352. };
  353. /**
  354. * Returns an array of all queue items.
  355. * @returns {Array}
  356. */
  357. Denque.prototype.toArray = function toArray() {
  358. return this._copyArray(false);
  359. };
  360. /**
  361. * -------------
  362. * INTERNALS
  363. * -------------
  364. */
  365. /**
  366. * Fills the queue with items from an array
  367. * For use in the constructor
  368. * @param array
  369. * @private
  370. */
  371. Denque.prototype._fromArray = function _fromArray(array) {
  372. var length = array.length;
  373. var capacity = this._nextPowerOf2(length);
  374. this._list = new Array(capacity);
  375. this._capacityMask = capacity - 1;
  376. this._tail = length;
  377. for (var i = 0; i < length; i++) this._list[i] = array[i];
  378. };
  379. /**
  380. *
  381. * @param fullCopy
  382. * @param size Initialize the array with a specific size. Will default to the current list size
  383. * @returns {Array}
  384. * @private
  385. */
  386. Denque.prototype._copyArray = function _copyArray(fullCopy, size) {
  387. var src = this._list;
  388. var capacity = src.length;
  389. var length = this.length;
  390. size = size | length;
  391. // No prealloc requested and the buffer is contiguous
  392. if (size == length && this._head < this._tail) {
  393. // Simply do a fast slice copy
  394. return this._list.slice(this._head, this._tail);
  395. }
  396. var dest = new Array(size);
  397. var k = 0;
  398. var i;
  399. if (fullCopy || this._head > this._tail) {
  400. for (i = this._head; i < capacity; i++) dest[k++] = src[i];
  401. for (i = 0; i < this._tail; i++) dest[k++] = src[i];
  402. } else {
  403. for (i = this._head; i < this._tail; i++) dest[k++] = src[i];
  404. }
  405. return dest;
  406. }
  407. /**
  408. * Grows the internal list array.
  409. * @private
  410. */
  411. Denque.prototype._growArray = function _growArray() {
  412. if (this._head != 0) {
  413. // double array size and copy existing data, head to end, then beginning to tail.
  414. var newList = this._copyArray(true, this._list.length << 1);
  415. this._tail = this._list.length;
  416. this._head = 0;
  417. this._list = newList;
  418. } else {
  419. this._tail = this._list.length;
  420. this._list.length <<= 1;
  421. }
  422. this._capacityMask = (this._capacityMask << 1) | 1;
  423. };
  424. /**
  425. * Shrinks the internal list array.
  426. * @private
  427. */
  428. Denque.prototype._shrinkArray = function _shrinkArray() {
  429. this._list.length >>>= 1;
  430. this._capacityMask >>>= 1;
  431. };
  432. /**
  433. * Find the next power of 2, at least 4
  434. * @private
  435. * @param {number} num
  436. * @returns {number}
  437. */
  438. Denque.prototype._nextPowerOf2 = function _nextPowerOf2(num) {
  439. var log2 = Math.log(num) / Math.log(2);
  440. var nextPow2 = 1 << (log2 + 1);
  441. return Math.max(nextPow2, 4);
  442. }
  443. module.exports = Denque;