Skip to content

Commit e450b98

Browse files
authored
Add circular-buffer (#103)
* Add circular-buffer * implement overwrite with an argument to write
1 parent d9ca0e9 commit e450b98

File tree

9 files changed

+385
-0
lines changed

9 files changed

+385
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,14 @@
538538
"prerequisites": [],
539539
"difficulty": 5
540540
},
541+
{
542+
"slug": "circular-buffer",
543+
"name": "Circular Buffer",
544+
"uuid": "1ef26e0a-c4de-4250-999e-0ab72c57f3cf",
545+
"practices": [],
546+
"prerequisites": [],
547+
"difficulty": 5
548+
},
541549
{
542550
"slug": "flower-field",
543551
"name": "Flower Field",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Instructions
2+
3+
A circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end.
4+
5+
A circular buffer first starts empty and of some predefined length.
6+
For example, this is a 7-element buffer:
7+
8+
```text
9+
[ ][ ][ ][ ][ ][ ][ ]
10+
```
11+
12+
Assume that a 1 is written into the middle of the buffer (exact starting location does not matter in a circular buffer):
13+
14+
```text
15+
[ ][ ][ ][1][ ][ ][ ]
16+
```
17+
18+
Then assume that two more elements are added — 2 & 3 — which get appended after the 1:
19+
20+
```text
21+
[ ][ ][ ][1][2][3][ ]
22+
```
23+
24+
If two elements are then removed from the buffer, the oldest values inside the buffer are removed.
25+
The two elements removed, in this case, are 1 & 2, leaving the buffer with just a 3:
26+
27+
```text
28+
[ ][ ][ ][ ][ ][3][ ]
29+
```
30+
31+
If the buffer has 7 elements then it is completely full:
32+
33+
```text
34+
[5][6][7][8][9][3][4]
35+
```
36+
37+
When the buffer is full an error will be raised, alerting the client that further writes are blocked until a slot becomes free.
38+
39+
When the buffer is full, the client can opt to overwrite the oldest data with a forced write.
40+
In this case, two more elements — A & B — are added and they overwrite the 3 & 4:
41+
42+
```text
43+
[5][6][7][8][9][A][B]
44+
```
45+
46+
3 & 4 have been replaced by A & B making 5 now the oldest data in the buffer.
47+
Finally, if two elements are removed then what would be returned is 5 & 6 yielding the buffer:
48+
49+
```text
50+
[ ][ ][7][8][9][A][B]
51+
```
52+
53+
Because there is space available, if the client again uses overwrite to store C & D then the space where 5 & 6 were stored previously will be used not the location of 7 & 8.
54+
7 is still the oldest element and the buffer is once again full.
55+
56+
```text
57+
[C][D][7][8][9][A][B]
58+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"glennj"
4+
],
5+
"files": {
6+
"solution": [
7+
"circular_buffer.moon"
8+
],
9+
"test": [
10+
"circular_buffer_spec.moon"
11+
],
12+
"example": [
13+
".meta/example.moon"
14+
]
15+
},
16+
"blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Circular_buffer"
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class CircularBuffer
2+
new: (@size) =>
3+
@buff = [0 for _ = 1, @size]
4+
@idx = r: 1, w: 1
5+
@count = 0
6+
7+
read: =>
8+
return nil, false if @count == 0
9+
item = @buff[@idx.r]
10+
@idx.r = @idx.r == @size and 1 or @idx.r + 1
11+
@count -= 1
12+
item, true
13+
14+
write: (item, options={}) =>
15+
if @count == @size
16+
if options.overwrite
17+
@read!
18+
else
19+
return false
20+
21+
@buff[@idx.w] = item
22+
@idx.w = @idx.w == @size and 1 or @idx.w + 1
23+
@count += 1
24+
true
25+
26+
clear: =>
27+
@count = 0
28+
@idx.r = @idx.w
29+
30+
31+
CircularBuffer
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
module_name: 'CircularBuffer',
3+
4+
generate_test: (case, level) ->
5+
lines = { "buffer = CircularBuffer #{case.input.capacity}" }
6+
for op in *case.input.operations
7+
switch op.operation
8+
when 'read'
9+
table.insert lines, 'value, ok = buffer\\read!'
10+
table.insert lines, "assert.is_#{op.should_succeed} ok"
11+
if op.should_succeed
12+
table.insert lines, "assert.are.equal #{op.expected}, value"
13+
14+
when 'write'
15+
table.insert lines, "ok = buffer\\write #{op.item}"
16+
table.insert lines, "assert.is_#{op.should_succeed} ok"
17+
18+
when 'clear'
19+
table.insert lines, 'buffer\\clear!'
20+
21+
when 'overwrite'
22+
table.insert lines, "buffer\\write #{op.item}, overwrite: true"
23+
24+
table.concat [indent line, level for line in *lines], '\n'
25+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[28268ed4-4ff3-45f3-820e-895b44d53dfa]
13+
description = "reading empty buffer should fail"
14+
15+
[2e6db04a-58a1-425d-ade8-ac30b5f318f3]
16+
description = "can read an item just written"
17+
18+
[90741fe8-a448-45ce-be2b-de009a24c144]
19+
description = "each item may only be read once"
20+
21+
[be0e62d5-da9c-47a8-b037-5db21827baa7]
22+
description = "items are read in the order they are written"
23+
24+
[2af22046-3e44-4235-bfe6-05ba60439d38]
25+
description = "full buffer can't be written to"
26+
27+
[547d192c-bbf0-4369-b8fa-fc37e71f2393]
28+
description = "a read frees up capacity for another write"
29+
30+
[04a56659-3a81-4113-816b-6ecb659b4471]
31+
description = "read position is maintained even across multiple writes"
32+
33+
[60c3a19a-81a7-43d7-bb0a-f07242b1111f]
34+
description = "items cleared out of buffer can't be read"
35+
36+
[45f3ae89-3470-49f3-b50e-362e4b330a59]
37+
description = "clear frees up capacity for another write"
38+
39+
[e1ac5170-a026-4725-bfbe-0cf332eddecd]
40+
description = "clear does nothing on empty buffer"
41+
42+
[9c2d4f26-3ec7-453f-a895-7e7ff8ae7b5b]
43+
description = "overwrite acts like write on non-full buffer"
44+
45+
[880f916b-5039-475c-bd5c-83463c36a147]
46+
description = "overwrite replaces the oldest item on full buffer"
47+
48+
[bfecab5b-aca1-4fab-a2b0-cd4af2b053c3]
49+
description = "overwrite replaces the oldest item remaining in buffer following a read"
50+
51+
[9cebe63a-c405-437b-8b62-e3fdc1ecec5a]
52+
description = "initial clear does not affect wrapping around"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class CircularBuffer
2+
new: (size) =>
3+
error 'Implement the constructor'
4+
5+
read: =>
6+
error 'Implement the read method'
7+
8+
write: (item) =>
9+
error 'Implement the write method'
10+
11+
clear: =>
12+
error 'Implement the clear method'
13+
14+
15+
CircularBuffer
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
CircularBuffer = require 'circular_buffer'
2+
3+
describe 'circular-buffer', ->
4+
it 'reading empty buffer should fail', ->
5+
buffer = CircularBuffer 1
6+
value, ok = buffer\read!
7+
assert.is_false ok
8+
9+
pending 'can read an item just written', ->
10+
buffer = CircularBuffer 1
11+
ok = buffer\write 1
12+
assert.is_true ok
13+
value, ok = buffer\read!
14+
assert.is_true ok
15+
assert.are.equal 1, value
16+
17+
pending 'each item may only be read once', ->
18+
buffer = CircularBuffer 1
19+
ok = buffer\write 1
20+
assert.is_true ok
21+
value, ok = buffer\read!
22+
assert.is_true ok
23+
assert.are.equal 1, value
24+
value, ok = buffer\read!
25+
assert.is_false ok
26+
27+
pending 'items are read in the order they are written', ->
28+
buffer = CircularBuffer 2
29+
ok = buffer\write 1
30+
assert.is_true ok
31+
ok = buffer\write 2
32+
assert.is_true ok
33+
value, ok = buffer\read!
34+
assert.is_true ok
35+
assert.are.equal 1, value
36+
value, ok = buffer\read!
37+
assert.is_true ok
38+
assert.are.equal 2, value
39+
40+
pending "full buffer can't be written to", ->
41+
buffer = CircularBuffer 1
42+
ok = buffer\write 1
43+
assert.is_true ok
44+
ok = buffer\write 2
45+
assert.is_false ok
46+
47+
pending 'a read frees up capacity for another write', ->
48+
buffer = CircularBuffer 1
49+
ok = buffer\write 1
50+
assert.is_true ok
51+
value, ok = buffer\read!
52+
assert.is_true ok
53+
assert.are.equal 1, value
54+
ok = buffer\write 2
55+
assert.is_true ok
56+
value, ok = buffer\read!
57+
assert.is_true ok
58+
assert.are.equal 2, value
59+
60+
pending 'read position is maintained even across multiple writes', ->
61+
buffer = CircularBuffer 3
62+
ok = buffer\write 1
63+
assert.is_true ok
64+
ok = buffer\write 2
65+
assert.is_true ok
66+
value, ok = buffer\read!
67+
assert.is_true ok
68+
assert.are.equal 1, value
69+
ok = buffer\write 3
70+
assert.is_true ok
71+
value, ok = buffer\read!
72+
assert.is_true ok
73+
assert.are.equal 2, value
74+
value, ok = buffer\read!
75+
assert.is_true ok
76+
assert.are.equal 3, value
77+
78+
pending "items cleared out of buffer can't be read", ->
79+
buffer = CircularBuffer 1
80+
ok = buffer\write 1
81+
assert.is_true ok
82+
buffer\clear!
83+
value, ok = buffer\read!
84+
assert.is_false ok
85+
86+
pending 'clear frees up capacity for another write', ->
87+
buffer = CircularBuffer 1
88+
ok = buffer\write 1
89+
assert.is_true ok
90+
buffer\clear!
91+
ok = buffer\write 2
92+
assert.is_true ok
93+
value, ok = buffer\read!
94+
assert.is_true ok
95+
assert.are.equal 2, value
96+
97+
pending 'clear does nothing on empty buffer', ->
98+
buffer = CircularBuffer 1
99+
buffer\clear!
100+
ok = buffer\write 1
101+
assert.is_true ok
102+
value, ok = buffer\read!
103+
assert.is_true ok
104+
assert.are.equal 1, value
105+
106+
pending 'overwrite acts like write on non-full buffer', ->
107+
buffer = CircularBuffer 2
108+
ok = buffer\write 1
109+
assert.is_true ok
110+
buffer\write 2, overwrite: true
111+
value, ok = buffer\read!
112+
assert.is_true ok
113+
assert.are.equal 1, value
114+
value, ok = buffer\read!
115+
assert.is_true ok
116+
assert.are.equal 2, value
117+
118+
pending 'overwrite replaces the oldest item on full buffer', ->
119+
buffer = CircularBuffer 2
120+
ok = buffer\write 1
121+
assert.is_true ok
122+
ok = buffer\write 2
123+
assert.is_true ok
124+
buffer\write 3, overwrite: true
125+
value, ok = buffer\read!
126+
assert.is_true ok
127+
assert.are.equal 2, value
128+
value, ok = buffer\read!
129+
assert.is_true ok
130+
assert.are.equal 3, value
131+
132+
pending 'overwrite replaces the oldest item remaining in buffer following a read', ->
133+
buffer = CircularBuffer 3
134+
ok = buffer\write 1
135+
assert.is_true ok
136+
ok = buffer\write 2
137+
assert.is_true ok
138+
ok = buffer\write 3
139+
assert.is_true ok
140+
value, ok = buffer\read!
141+
assert.is_true ok
142+
assert.are.equal 1, value
143+
ok = buffer\write 4
144+
assert.is_true ok
145+
buffer\write 5, overwrite: true
146+
value, ok = buffer\read!
147+
assert.is_true ok
148+
assert.are.equal 3, value
149+
value, ok = buffer\read!
150+
assert.is_true ok
151+
assert.are.equal 4, value
152+
value, ok = buffer\read!
153+
assert.is_true ok
154+
assert.are.equal 5, value
155+
156+
pending 'initial clear does not affect wrapping around', ->
157+
buffer = CircularBuffer 2
158+
buffer\clear!
159+
ok = buffer\write 1
160+
assert.is_true ok
161+
ok = buffer\write 2
162+
assert.is_true ok
163+
buffer\write 3, overwrite: true
164+
buffer\write 4, overwrite: true
165+
value, ok = buffer\read!
166+
assert.is_true ok
167+
assert.are.equal 3, value
168+
value, ok = buffer\read!
169+
assert.is_true ok
170+
assert.are.equal 4, value
171+
value, ok = buffer\read!
172+
assert.is_false ok

0 commit comments

Comments
 (0)