1 /*******************************************************************************
2 
3     Contains a ring buffer implementation with fixed size
4 
5     Copyright:
6         Copyright (c) 2020 BOS Platform Foundation Korea
7         All rights reserved.
8 
9     License:
10         MIT License. See LICENSE for details.
11 
12 *******************************************************************************/
13 
14 module geod24.RingBuffer;
15 
16 import std.range;
17 
18 /*******************************************************************************
19 
20     A ring buffer with fixed size
21 
22     Params:
23         T = Type of elements
24 
25 *******************************************************************************/
26 
27 public class RingBuffer (T)
28 {
29     /*******************************************************************************
30 
31         Params:
32             size = Maximum number of elements, must be greater than 0
33 
34     *******************************************************************************/
35 
36     this (size_t size) @safe pure nothrow
37     {
38         assert(size, "Cannot create a RingBuffer with size of 0");
39         this.storage.length = size;
40         this.write_range = cycle(this.storage[]);
41         this.read_range = cycle(this.storage[]);
42     }
43 
44     /// Returns: true if full
45     public bool full () @safe @nogc pure nothrow
46     {
47         return this.len == this.storage.length;
48     }
49 
50     /// Returns: true if empty
51     public bool empty () @safe @nogc pure nothrow
52     {
53         return this.len == 0;
54     }
55 
56     /// Returns: Number of elements currently in the buffer
57     public size_t length () @safe @nogc pure nothrow
58     {
59         return this.len;
60     }
61 
62     /*******************************************************************************
63 
64         Insert an element to the buffer
65 
66         Params:
67             val = an element of type T
68 
69     *******************************************************************************/
70 
71     public void insert () (auto ref T val) @safe @nogc pure nothrow
72     {
73         assert(!this.full(), "Attempting to insert to a full RingBuffer");
74 
75         write_range.front() = val;
76         write_range.popFront();
77         this.len++;
78     }
79 
80     /// Returns: Element in the front of the buffer
81     public T front () @safe @nogc pure nothrow
82     {
83         assert(!this.empty(), "Attempting to read from a full RingBuffer");
84         return read_range.front();
85     }
86 
87     /// Removes the element in the front of the buffer
88     public void popFront () @safe @nogc pure nothrow
89     {
90         assert(!this.empty(), "Attempting to pop from a full RingBuffer");
91         this.len--;
92         read_range.popFront();
93     }
94 
95     /// Current number of elements
96     private size_t len;
97 
98     /// Underlying storage
99     private T[] storage;
100 
101     /// Cyclic range for writes
102     private Cycle!(T[]) write_range;
103 
104     /// Cyclic range for reads
105     private Cycle!(T[]) read_range;
106 }
107 
108 unittest
109 {
110     import std.stdio;
111     const buffer_size = 3;
112     auto buffer = new RingBuffer!int(buffer_size);
113 
114     foreach (_; 0..2)
115     {
116         assert(buffer.empty());
117         assert(!buffer.full());
118 
119         foreach (i; 0..buffer_size)
120         {
121             assert(!buffer.full());
122             buffer.insert(i);
123             assert(!buffer.empty());
124             assert(buffer.length == i + 1);
125         }
126         assert(buffer.full());
127 
128         foreach (i; 0..buffer_size)
129         {
130             assert(!buffer.empty());
131             assert(buffer.front() == i);
132             buffer.popFront();
133         }
134     }
135 }