Skip to content

Commit b1c6b51

Browse files
committed
Circular Buffer for racket
1 parent 6018b00 commit b1c6b51

File tree

7 files changed

+288
-0
lines changed

7 files changed

+288
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,14 @@
630630
"practices": [],
631631
"prerequisites": [],
632632
"difficulty": 2
633+
},
634+
{
635+
"slug": "circular-buffer",
636+
"name": "Circular Buffer",
637+
"uuid": "fbf9d4b0-5943-4691-9bb9-f51a69c4c060",
638+
"practices": [],
639+
"prerequisites": [],
640+
"difficulty": 4
633641
}
634642
]
635643
},
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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"authors": ["blakelewis"],
3+
"files": {
4+
"solution": [
5+
"circular-buffer.rkt"
6+
],
7+
"test": [
8+
"circular-buffer-test.rkt"
9+
],
10+
"example": [
11+
".meta/example.rkt"
12+
]
13+
},
14+
"blurb": "A data structure that uses a single, fixed-size buffer as if it were connected end-to-end.",
15+
"source": "Wikipedia",
16+
"source_url": "https://en.wikipedia.org/wiki/Circular_buffer"
17+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#lang racket
2+
3+
(provide circular-buffer%)
4+
5+
(define circular-buffer%
6+
(class object%
7+
(super-new)
8+
(init capacity)
9+
(define buf-size capacity)
10+
(define buf-modulus (* 2 capacity))
11+
(define data (make-vector capacity))
12+
(define front 0)
13+
(define back 0)
14+
(define (buf-empty?) (= front back))
15+
(define (buf-full?) (= (modulo (- back front) buf-modulus) buf-size))
16+
(define (next-spot s) (modulo (add1 s) buf-modulus))
17+
(define (advance-front) (set! front (next-spot front)))
18+
(define (advance-back) (set! back (next-spot back)))
19+
(define/public (clear) (set! front back))
20+
(define/public (read)
21+
(if (buf-empty?)
22+
(error "buffer is empty")
23+
(let ([v (vector-ref data (modulo front buf-size))])
24+
(begin (advance-front) v))))
25+
(define/public (write v)
26+
(if (buf-full?)
27+
(error "buffer is full")
28+
(begin (vector-set! data (modulo back buf-size) v)
29+
(advance-back))))
30+
(define/public (overwrite v)
31+
(if (buf-full?) (advance-front) null)
32+
(write v))))
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: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#lang racket/base
2+
3+
(require racket/class)
4+
(require "circular-buffer.rkt")
5+
6+
(module+ test
7+
(require rackunit rackunit/text-ui))
8+
9+
(module+ test
10+
(define suite
11+
(test-suite
12+
"circular buffer tests"
13+
14+
(test-case "reading empty buffer should fail"
15+
(define buffer (new circular-buffer% [capacity 1]))
16+
(check-exn exn:fail? (λ () (send buffer read))))
17+
18+
(test-case "can read an item just written"
19+
(define buffer (new circular-buffer% [capacity 1]))
20+
(send buffer write 1)
21+
(check-equal? (send buffer read) 1))
22+
23+
(test-case "each item may only be read once"
24+
(define buffer (new circular-buffer% [capacity 1]))
25+
(send buffer write 1)
26+
(check-equal? (send buffer read) 1)
27+
(check-exn exn:fail? (λ () (send buffer read))))
28+
29+
(test-case "items are read in the order they are written"
30+
(define buffer (new circular-buffer% [capacity 2]))
31+
(send buffer write 1)
32+
(send buffer write 2)
33+
(check-equal? (send buffer read) 1)
34+
(check-equal? (send buffer read) 2))
35+
36+
(test-case "full buffer can't be written to"
37+
(define buffer (new circular-buffer% [capacity 1]))
38+
(send buffer write 1)
39+
(check-exn exn:fail? (λ () (send buffer write 2))))
40+
41+
(test-case "a read frees up capacity for another write"
42+
(define buffer (new circular-buffer% [capacity 1]))
43+
(send buffer write 1)
44+
(check-equal? (send buffer read) 1)
45+
(send buffer write 2)
46+
(check-equal? (send buffer read) 2))
47+
48+
(test-case "read position is maintained even across multiple writes"
49+
(define buffer (new circular-buffer% [capacity 3]))
50+
(send buffer write 1)
51+
(send buffer write 2)
52+
(check-equal? (send buffer read) 1)
53+
(send buffer write 3)
54+
(check-equal? (send buffer read) 2)
55+
(check-equal? (send buffer read) 3))
56+
57+
(test-case "items cleared out of buffer can't be read"
58+
(define buffer (new circular-buffer% [capacity 1]))
59+
(send buffer write 1)
60+
(send buffer clear)
61+
(check-exn exn:fail? (λ () (send buffer read))))
62+
63+
(test-case "clear frees up capacity for another write"
64+
(define buffer (new circular-buffer% [capacity 1]))
65+
(send buffer write 1)
66+
(send buffer clear)
67+
(send buffer write 2)
68+
(check-equal? (send buffer read) 2))
69+
70+
(test-case "clear does nothing on empty buffer"
71+
(define buffer (new circular-buffer% [capacity 1]))
72+
(send buffer clear)
73+
(send buffer write 1)
74+
(check-equal? (send buffer read) 1))
75+
76+
(test-case "overwrite acts like write on non-full buffer"
77+
(define buffer (new circular-buffer% [capacity 2]))
78+
(send buffer write 1)
79+
(send buffer overwrite 2)
80+
(check-equal? (send buffer read) 1)
81+
(check-equal? (send buffer read) 2))
82+
83+
(test-case "overwrite replaces the oldest item on full buffer"
84+
(define buffer (new circular-buffer% [capacity 2]))
85+
(send buffer write 1)
86+
(send buffer write 2)
87+
(send buffer overwrite 3)
88+
(check-equal? (send buffer read) 2)
89+
(check-equal? (send buffer read) 3))
90+
91+
(test-case "overwrite replaces the oldest item remaining in buffer following a read"
92+
(define buffer (new circular-buffer% [capacity 3]))
93+
(send buffer write 1)
94+
(send buffer write 2)
95+
(send buffer write 3)
96+
(check-equal? (send buffer read) 1)
97+
(send buffer write 4)
98+
(send buffer overwrite 5)
99+
(check-equal? (send buffer read) 3)
100+
(check-equal? (send buffer read) 4)
101+
(check-equal? (send buffer read) 5))
102+
103+
(test-case "initial clear does not affect wrapping around"
104+
(define buffer (new circular-buffer% [capacity 2]))
105+
(send buffer clear)
106+
(send buffer write 1)
107+
(send buffer write 2)
108+
(send buffer overwrite 3)
109+
(send buffer overwrite 4)
110+
(check-equal? (send buffer read) 3)
111+
(check-equal? (send buffer read) 4)
112+
(check-exn exn:fail? (λ () (send buffer read))))))
113+
114+
(run-tests suite))
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#lang racket
2+
3+
(provide circular-buffer%)
4+
5+
(define circular-buffer%
6+
(class object%
7+
(error "Not implemented yet")))

0 commit comments

Comments
 (0)