Skip to content

Commit 2e57b98

Browse files
committed
Add implementations
1 parent 823ed52 commit 2e57b98

File tree

4 files changed

+351
-0
lines changed

4 files changed

+351
-0
lines changed

JavaScript/1-queue.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
class Queue {
4+
#buffer = [];
5+
6+
get length() {
7+
return this.#buffer.length;
8+
}
9+
10+
isEmpty() {
11+
return this.#buffer.length === 0;
12+
}
13+
14+
enqueue(item) {
15+
this.#buffer.push(item);
16+
}
17+
18+
dequeue() {
19+
return this.#buffer.shift();
20+
}
21+
}
22+
23+
// Usage
24+
25+
const mq = new Queue();
26+
27+
for (let id = 0; id < 5; id++) {
28+
mq.enqueue({ id });
29+
}
30+
31+
while (!mq.isEmpty()) {
32+
const task = mq.dequeue();
33+
console.log(`Processing ${task.id}`);
34+
}
35+
36+
for (let id = 100; id < 105; id++) {
37+
mq.enqueue({ id });
38+
}
39+
40+
while (!mq.isEmpty()) {
41+
const task = mq.dequeue();
42+
console.log(`Processing ${task.id}`);
43+
}

JavaScript/2-list.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
class ListNode {
4+
constructor(value) {
5+
this.value = value;
6+
this.next = null;
7+
}
8+
}
9+
10+
class Queue {
11+
#length = 0;
12+
13+
constructor() {
14+
this.head = null;
15+
this.tail = null;
16+
}
17+
18+
get length() {
19+
return this.#length;
20+
}
21+
22+
isEmpty() {
23+
return this.#length === 0;
24+
}
25+
26+
enqueue(item) {
27+
const node = new ListNode(item);
28+
if (this.tail) {
29+
this.tail.next = node;
30+
} else {
31+
this.head = node;
32+
}
33+
this.tail = node;
34+
this.#length++;
35+
}
36+
37+
dequeue() {
38+
if (this.isEmpty()) return null;
39+
const item = this.head.value;
40+
this.head = this.head.next;
41+
if (!this.head) this.tail = null;
42+
this.#length--;
43+
return item;
44+
}
45+
}
46+
47+
// Usage
48+
49+
const mq = new Queue();
50+
51+
for (let id = 0; id < 5; id++) {
52+
mq.enqueue({ id });
53+
}
54+
55+
while (!mq.isEmpty()) {
56+
const task = mq.dequeue();
57+
console.log(`Processing ${task.id}`);
58+
}
59+
60+
for (let id = 100; id < 105; id++) {
61+
mq.enqueue({ id });
62+
}
63+
64+
while (!mq.isEmpty()) {
65+
const task = mq.dequeue();
66+
console.log(`Processing ${task.id}`);
67+
}

JavaScript/3-fixed.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
'use strict';
2+
3+
// Copyright Node.js contributors. All rights reserved.
4+
// Original sources: [lib/internal/fixed_queue.js](https://github.com/nodejs/node/blob/86bfdb552863f09d36cba7f1145134346eb2e640/lib/internal/fixed_queue.js)
5+
6+
// Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two.
7+
8+
const kSize = 2048;
9+
const kMask = kSize - 1;
10+
11+
// The FixedQueue is implemented as a singly-linked list of fixed-size
12+
// circular buffers. It looks something like this:
13+
//
14+
// head tail
15+
// | |
16+
// v v
17+
// +-----------+ <-----\ +-----------+ <------\ +-----------+
18+
// | [null] | \----- | next | \------- | next |
19+
// +-----------+ +-----------+ +-----------+
20+
// | item | <-- bottom | item | <-- bottom | undefined |
21+
// | item | | item | | undefined |
22+
// | item | | item | | undefined |
23+
// | item | | item | | undefined |
24+
// | item | | item | bottom --> | item |
25+
// | item | | item | | item |
26+
// | ... | | ... | | ... |
27+
// | item | | item | | item |
28+
// | item | | item | | item |
29+
// | undefined | <-- top | item | | item |
30+
// | undefined | | item | | item |
31+
// | undefined | | undefined | <-- top top --> | undefined |
32+
// +-----------+ +-----------+ +-----------+
33+
//
34+
// Or, if there is only one circular buffer, it looks something
35+
// like either of these:
36+
//
37+
// head tail head tail
38+
// | | | |
39+
// v v v v
40+
// +-----------+ +-----------+
41+
// | [null] | | [null] |
42+
// +-----------+ +-----------+
43+
// | undefined | | item |
44+
// | undefined | | item |
45+
// | item | <-- bottom top --> | undefined |
46+
// | item | | undefined |
47+
// | undefined | <-- top bottom --> | item |
48+
// | undefined | | item |
49+
// +-----------+ +-----------+
50+
//
51+
// Adding a value means moving `top` forward by one, removing means
52+
// moving `bottom` forward by one. After reaching the end, the queue
53+
// wraps around.
54+
//
55+
// When `top === bottom` the current queue is empty and when
56+
// `top + 1 === bottom` it's full. This wastes a single space of storage
57+
// but allows much quicker checks.
58+
59+
class FixedCircularBuffer {
60+
constructor() {
61+
this.bottom = 0;
62+
this.top = 0;
63+
this.list = new Array(kSize).fill(undefined);
64+
this.next = null;
65+
}
66+
67+
isEmpty() {
68+
return this.top === this.bottom;
69+
}
70+
71+
isFull() {
72+
return ((this.top + 1) & kMask) === this.bottom;
73+
}
74+
75+
enqueue(data) {
76+
this.list[this.top] = data;
77+
this.top = (this.top + 1) & kMask;
78+
}
79+
80+
deque() {
81+
const nextItem = this.list[this.bottom];
82+
if (nextItem === undefined) return null;
83+
this.list[this.bottom] = undefined;
84+
this.bottom = (this.bottom + 1) & kMask;
85+
return nextItem;
86+
}
87+
}
88+
89+
class FixedQueue {
90+
constructor() {
91+
this.head = this.tail = new FixedCircularBuffer();
92+
}
93+
94+
isEmpty() {
95+
return this.head.isEmpty();
96+
}
97+
98+
enqueue(data) {
99+
if (this.head.isFull()) {
100+
// Head is full: Creates a new queue, sets the old queue's `.next` to it,
101+
// and sets it as the new main queue.
102+
this.head = this.head.next = new FixedCircularBuffer();
103+
}
104+
this.head.enqueue(data);
105+
}
106+
107+
deque() {
108+
const tail = this.tail;
109+
const next = tail.deque();
110+
if (tail.isEmpty() && tail.next !== null) {
111+
// If there is another queue, it forms the new tail.
112+
this.tail = tail.next;
113+
tail.next = null;
114+
}
115+
return next;
116+
}
117+
}
118+
119+
// Usage
120+
121+
const mq = new FixedQueue();
122+
123+
for (let id = 0; id < 5; id++) {
124+
mq.enqueue({ id });
125+
}
126+
127+
while (!mq.isEmpty()) {
128+
const task = mq.deque();
129+
console.log(`Processing ${task.id}`);
130+
}
131+
132+
for (let id = 100; id < 105; id++) {
133+
mq.enqueue({ id });
134+
}
135+
136+
while (!mq.isEmpty()) {
137+
const task = mq.deque();
138+
console.log(`Processing ${task.id}`);
139+
}

JavaScript/4-unrolled.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
3+
class QueueNode {
4+
#length = 0;
5+
6+
constructor({ size }) {
7+
this.size = size;
8+
this.buffer = new Array(size);
9+
this.readIndex = 0;
10+
this.writeIndex = 0;
11+
this.next = null;
12+
}
13+
14+
get length() {
15+
return this.#length;
16+
}
17+
18+
isEmpty() {
19+
return this.#length === 0;
20+
}
21+
22+
isFull() {
23+
return this.#length === this.size;
24+
}
25+
26+
enqueue(item) {
27+
if (this.isFull()) return false;
28+
this.buffer[this.writeIndex++] = item;
29+
this.#length++;
30+
return true;
31+
}
32+
33+
dequeue() {
34+
if (this.isEmpty()) return null;
35+
const item = this.buffer[this.readIndex++];
36+
this.#length--;
37+
return item;
38+
}
39+
}
40+
41+
class UnrolledQueue {
42+
#length = 0;
43+
44+
constructor(options = {}) {
45+
const { nodeSize = 2048 } = options;
46+
const node = new QueueNode({ size: nodeSize });
47+
this.head = node;
48+
this.tail = node;
49+
this.nodeSize = nodeSize;
50+
}
51+
52+
get length() {
53+
return this.#length;
54+
}
55+
56+
isEmpty() {
57+
return this.#length === 0;
58+
}
59+
60+
enqueue(item) {
61+
if (!this.head.enqueue(item)) {
62+
const node = new QueueNode({ size: this.nodeSize });
63+
this.head.next = node;
64+
this.head = node;
65+
this.head.enqueue(item);
66+
}
67+
this.#length++;
68+
}
69+
70+
dequeue() {
71+
const item = this.tail.dequeue();
72+
if (item !== null) {
73+
this.#length--;
74+
if (this.tail.isEmpty() && this.tail.next) {
75+
this.tail = this.tail.next;
76+
}
77+
}
78+
return item;
79+
}
80+
}
81+
82+
// Usage
83+
84+
const mq = new UnrolledQueue({ nodeSize: 8 });
85+
86+
for (let id = 0; id < 5; id++) {
87+
mq.enqueue({ id });
88+
}
89+
90+
while (!mq.isEmpty()) {
91+
const task = mq.dequeue();
92+
console.log(`Processing ${task.id}`);
93+
}
94+
95+
for (let id = 100; id < 105; id++) {
96+
mq.enqueue({ id });
97+
}
98+
99+
while (!mq.isEmpty()) {
100+
const task = mq.dequeue();
101+
console.log(`Processing ${task.id}`);
102+
}

0 commit comments

Comments
 (0)