Skip to content

Commit 9e7be10

Browse files
Add circular-buffer exercise
1 parent e47e472 commit 9e7be10

9 files changed

Lines changed: 687 additions & 8 deletions

File tree

config.json

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,6 @@
6565
"prerequisites": [],
6666
"difficulty": 1
6767
},
68-
{
69-
"slug": "isbn-verifier",
70-
"name": "ISBN Verifier",
71-
"uuid": "c4e3ea2f-8648-49ba-a9a5-54ba11b5c27e",
72-
"practices": [],
73-
"prerequisites": [],
74-
"difficulty": 3
75-
},
7668
{
7769
"slug": "leap",
7870
"name": "Leap",
@@ -129,6 +121,14 @@
129121
"prerequisites": [],
130122
"difficulty": 3
131123
},
124+
{
125+
"slug": "isbn-verifier",
126+
"name": "ISBN Verifier",
127+
"uuid": "c4e3ea2f-8648-49ba-a9a5-54ba11b5c27e",
128+
"practices": [],
129+
"prerequisites": [],
130+
"difficulty": 3
131+
},
132132
{
133133
"slug": "line-up",
134134
"name": "Line Up",
@@ -240,6 +240,14 @@
240240
"practices": [],
241241
"prerequisites": [],
242242
"difficulty": 5
243+
},
244+
{
245+
"slug": "circular-buffer",
246+
"name": "Circular Buffer",
247+
"uuid": "ed824c1e-cc70-43f7-86d9-0d767cf2845e",
248+
"practices": [],
249+
"prerequisites": [],
250+
"difficulty": 6
243251
}
244252
]
245253
},
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+
"erikschierboom"
4+
],
5+
"files": {
6+
"solution": [
7+
"solution.jai"
8+
],
9+
"test": [
10+
"tests.jai"
11+
],
12+
"example": [
13+
".meta/example.jai"
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: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Unfortunately, there is a limitation right now, with the interaction of polymorph variables
2+
// in the type slot, and parameter auto-bakes, which is that they need to all
3+
// be solved in one step, thus they can't depend on each other in the same
4+
// procedure declaration. We thus can't parameterize the Circular_Buffer type by the element type
5+
// and its capacity, and instead have to hardcode them.
6+
7+
Circular_Buffer :: struct {
8+
data: [] int;
9+
read_index: int;
10+
write_index: int;
11+
count: int;
12+
}
13+
14+
new_circular_buffer :: (capacity: int) -> Circular_Buffer {
15+
return Circular_Buffer.{ data = NewArray(capacity, int, initialized=true) };
16+
}
17+
18+
read :: (circular_buffer: *Circular_Buffer) -> bool, int {
19+
if circular_buffer.count == 0 return false, 0;
20+
value := circular_buffer.data[circular_buffer.read_index];
21+
circular_buffer.read_index = (circular_buffer.read_index + 1) % circular_buffer.data.count;
22+
circular_buffer.count -= 1;
23+
return true, value;
24+
}
25+
26+
write :: (circular_buffer: *Circular_Buffer, value: int) -> bool {
27+
if circular_buffer.count == circular_buffer.data.count return false;
28+
circular_buffer.data[circular_buffer.write_index] = value;
29+
circular_buffer.write_index = (circular_buffer.write_index + 1) % circular_buffer.data.count;
30+
circular_buffer.count += 1;
31+
return true;
32+
}
33+
34+
clear :: (circular_buffer: *Circular_Buffer) {
35+
circular_buffer.read_index = 0;
36+
circular_buffer.write_index = 0;
37+
circular_buffer.count = 0;
38+
}
39+
40+
overwrite :: (circular_buffer: *Circular_Buffer, value: int) {
41+
circular_buffer.data[circular_buffer.write_index] = value;
42+
circular_buffer.write_index = (circular_buffer.write_index + 1) % circular_buffer.data.count;
43+
if circular_buffer.count < circular_buffer.data.count {
44+
circular_buffer.count += 1;
45+
} else {
46+
circular_buffer.read_index = (circular_buffer.read_index + 1) % circular_buffer.data.count;
47+
}
48+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
to_test_case :: (canonical_case: CanonicalCase) -> Test {
2+
using test: Test;
3+
name = to_test_name(canonical_case.description);
4+
skip = canonical_case.index > 0;
5+
_, capacity := get_property(canonical_case.input, "capacity");
6+
_, operations := get_array_property(canonical_case.input, "operations");
7+
8+
lines: [..]string;
9+
array_add(*lines, tprint("CAPACITY :: %;", to_code(capacity)));
10+
array_add(*lines, "circular_buffer := new_circular_buffer(CAPACITY);");
11+
12+
write_idx, read_idx: int = 1;
13+
14+
for operation: operations {
15+
_, operation_name := get_string_property(*operation, "operation");
16+
_, should_succeed := get_boolean_property(*operation, "should_succeed");
17+
18+
if operation_name == {
19+
case "write";
20+
_, item := get_number_property(*operation, "item");
21+
array_add(*lines, tprint("write_successful_% := write(*circular_buffer, %);", write_idx, to_code(item)));
22+
array_add(*lines, tprint("assert_equal(%, write_successful_%);", to_code(should_succeed), write_idx));
23+
write_idx += 1;
24+
case "read";
25+
array_add(*lines, tprint("read_successful_%, read_value_% := read(*circular_buffer);", read_idx, read_idx));
26+
array_add(*lines, tprint("assert_equal(%, read_successful_%);", to_code(should_succeed), read_idx));
27+
if should_succeed {
28+
_, expected := get_number_property(*operation, "expected");
29+
array_add(*lines, tprint("assert_equal(%, read_value_%);", to_code(expected), read_idx));
30+
}
31+
read_idx += 1;
32+
case "clear";
33+
array_add(*lines, "clear(*circular_buffer);");
34+
case "overwrite";
35+
_, item := get_number_property(*operation, "item");
36+
array_add(*lines, tprint("overwrite(*circular_buffer, %);", to_code(item)));
37+
case;
38+
assert(false, tprint("Unexpected operation: %", operation_name));
39+
}
40+
}
41+
42+
test.multiline_body = lines;
43+
44+
return test;
45+
}
46+
47+
#run run_generator("circular-buffer", to_test_case);
48+
49+
#import "Basic";
50+
#import,dir "../../../shared/generator";
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"

0 commit comments

Comments
 (0)