index.js 11 KB

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