fixed-queue.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /* eslint-disable */
  2. 'use strict'
  3. // Extracted from node/lib/internal/fixed_queue.js
  4. // Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two.
  5. const kSize = 2048;
  6. const kMask = kSize - 1;
  7. // The FixedQueue is implemented as a singly-linked list of fixed-size
  8. // circular buffers. It looks something like this:
  9. //
  10. // head tail
  11. // | |
  12. // v v
  13. // +-----------+ <-----\ +-----------+ <------\ +-----------+
  14. // | [null] | \----- | next | \------- | next |
  15. // +-----------+ +-----------+ +-----------+
  16. // | item | <-- bottom | item | <-- bottom | [empty] |
  17. // | item | | item | | [empty] |
  18. // | item | | item | | [empty] |
  19. // | item | | item | | [empty] |
  20. // | item | | item | bottom --> | item |
  21. // | item | | item | | item |
  22. // | ... | | ... | | ... |
  23. // | item | | item | | item |
  24. // | item | | item | | item |
  25. // | [empty] | <-- top | item | | item |
  26. // | [empty] | | item | | item |
  27. // | [empty] | | [empty] | <-- top top --> | [empty] |
  28. // +-----------+ +-----------+ +-----------+
  29. //
  30. // Or, if there is only one circular buffer, it looks something
  31. // like either of these:
  32. //
  33. // head tail head tail
  34. // | | | |
  35. // v v v v
  36. // +-----------+ +-----------+
  37. // | [null] | | [null] |
  38. // +-----------+ +-----------+
  39. // | [empty] | | item |
  40. // | [empty] | | item |
  41. // | item | <-- bottom top --> | [empty] |
  42. // | item | | [empty] |
  43. // | [empty] | <-- top bottom --> | item |
  44. // | [empty] | | item |
  45. // +-----------+ +-----------+
  46. //
  47. // Adding a value means moving `top` forward by one, removing means
  48. // moving `bottom` forward by one. After reaching the end, the queue
  49. // wraps around.
  50. //
  51. // When `top === bottom` the current queue is empty and when
  52. // `top + 1 === bottom` it's full. This wastes a single space of storage
  53. // but allows much quicker checks.
  54. class FixedCircularBuffer {
  55. constructor() {
  56. this.bottom = 0;
  57. this.top = 0;
  58. this.list = new Array(kSize);
  59. this.next = null;
  60. }
  61. isEmpty() {
  62. return this.top === this.bottom;
  63. }
  64. isFull() {
  65. return ((this.top + 1) & kMask) === this.bottom;
  66. }
  67. push(data) {
  68. this.list[this.top] = data;
  69. this.top = (this.top + 1) & kMask;
  70. }
  71. shift() {
  72. const nextItem = this.list[this.bottom];
  73. if (nextItem === undefined)
  74. return null;
  75. this.list[this.bottom] = undefined;
  76. this.bottom = (this.bottom + 1) & kMask;
  77. return nextItem;
  78. }
  79. }
  80. module.exports = class FixedQueue {
  81. constructor() {
  82. this.head = this.tail = new FixedCircularBuffer();
  83. }
  84. isEmpty() {
  85. return this.head.isEmpty();
  86. }
  87. push(data) {
  88. if (this.head.isFull()) {
  89. // Head is full: Creates a new queue, sets the old queue's `.next` to it,
  90. // and sets it as the new main queue.
  91. this.head = this.head.next = new FixedCircularBuffer();
  92. }
  93. this.head.push(data);
  94. }
  95. shift() {
  96. const tail = this.tail;
  97. const next = tail.shift();
  98. if (tail.isEmpty() && tail.next !== null) {
  99. // If there is another queue, it forms the new tail.
  100. this.tail = tail.next;
  101. }
  102. return next;
  103. }
  104. };