index.js 11 KB

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