VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
RingBuffer.h
1// -----------------------------------------------------------------------------
2// This file is part of VirtualC64
3//
4// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de
5// This FILE is dual-licensed. You are free to choose between:
6//
7// - The GNU General Public License v3 (or any later version)
8// - The Mozilla Public License v2
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0
11// -----------------------------------------------------------------------------
12
13#pragma once
14
15#include "Types.h"
16#include <utility>
17
18namespace vc64::util {
19
20/* The emulator uses buffers at various places. Most of them are derived from
21 * one of the following two classes:
22 *
23 * Array : A fixed size array
24 * SortedArray : A fixed size array with sorted insert
25 * RingBuffer : A standard ringbuffer
26 * SortedRingBuffer : A standard ringbuffer with sorted insert
27 */
28
29//
30// Array
31//
32
33template <class T, isize capacity> struct Array
34{
35 // Element storage
36 T *elements = new T[capacity];
37
38 // Write pointer
39 isize w;
40
41
42 //
43 // Initializing
44 //
45
46 Array() { clear(); }
47 ~Array() { delete[] elements; }
48
49 void clear() { w = 0; }
50 void clear(T t) { for (isize i = 0; i < capacity; i++) elements[i] = t; clear(); }
51 void align(isize offset) { w = offset; }
52
53
54 //
55 // Querying the fill status
56 //
57
58 isize cap() const { return capacity; }
59 isize count() const { return w; }
60 isize free() const { return capacity - w; }
61 double fillLevel() const { return (double)count() / capacity; }
62 bool isEmpty() const { return w == 0; }
63 bool isFull() const { return count() == capacity; }
64
65
66 //
67 // Reading and writing elements
68 //
69
70 T operator [] (isize i) const
71 {
72 return elements[i];
73 }
74
75 T& operator [] (isize i)
76 {
77 return elements[i];
78 }
79
80 void write(T element)
81 {
82 assert(!isFull());
83 elements[w++] = element;
84 }
85};
86
87template <class T, isize capacity>
88struct SortedArray : public Array<T, capacity>
89{
90 // Key storage
91 i64 *keys = new i64[capacity];
92
93
94 //
95 // Initializing
96 //
97
98 ~SortedArray() { delete[] keys; }
99
100
101 //
102 // Inserting
103 //
104
105 // Inserts an element at the end
106 void write(i64 key, T element)
107 {
108 assert(!this->isFull());
109
110 this->elements[this->w] = element;
111 this->keys[this->w] = key;
112 this->w++;
113 }
114
115 // Inserts an element at the proper position
116 void insert(i64 key, T element)
117 {
118 assert(!this->isFull());
119
120 // Search the proper position
121 auto pos = this->w;
122 while (pos && keys[pos - 1] > key) pos--;
123
124 // Create a free spot
125 for (isize i = this->w; i > pos; i--) {
126 this->elements[i] = this->elements[i - 1];
127 keys[i] = keys[i - 1];
128 }
129
130 // Add the new element
131 this->elements[pos] = element;
132 this->keys[pos] = key;
133 this->w++;
134 }
135};
136
137//
138// Ringbuffer
139//
140
141template <class T, isize capacity> struct RingBuffer
142{
143 // Element storage
144 T *elements = new T[capacity]();
145
146 // Read and write pointers
147 isize r, w;
148
149
150 //
151 // Initializing
152 //
153
154 RingBuffer() { clear(); }
155 ~RingBuffer() { delete[] elements; }
156
157 void clear() { r = w = 0; }
158 void clear(T t) { for (isize i = 0; i < capacity; i++) elements[i] = t; clear(); }
159 void align(isize offset) { w = (r + offset) % capacity; }
160
161
162 //
163 // Querying the fill status
164 //
165
166 isize cap() const { return capacity; }
167 isize count() const { return (capacity + w - r) % capacity; }
168 isize free() const { return capacity - count() - 1; }
169 double fillLevel() const { return (double)count() / capacity; }
170 bool isEmpty() const { return r == w; }
171 bool isFull() const { return count() == capacity - 1; }
172
173
174 //
175 // Working with indices
176 //
177
178 isize begin() const { return r; }
179 isize end() const { return w; }
180 static isize next(isize i) { return i < capacity - 1 ? i + 1 : 0; }
181 static isize prev(isize i) { return i > 0 ? i - 1 : capacity - 1; }
182
183
184 //
185 // Reading and writing elements
186 //
187
188 T& read()
189 {
190 assert(!isEmpty());
191
192 auto oldr = r;
193 r = next(r);
194 return elements[oldr];
195 }
196
197 const T& read(T fallback)
198 {
199 if (isEmpty()) write(fallback);
200 return read();
201 }
202
203 void write(T element)
204 {
205 assert(!isFull());
206
207 elements[w] = element;
208 w = next(w);
209 }
210
211 void skip()
212 {
213 r = next(r);
214 }
215
216 void skip(isize n)
217 {
218 r = (r + n) % capacity;
219 }
220
221
222 //
223 // Examining the element storage
224 //
225
226 const T& current() const
227 {
228 return elements[r];
229 }
230
231 T *currentAddr()
232 {
233 return &elements[r];
234 }
235
236 const T& current(isize offset) const
237 {
238 return elements[(r + offset) % capacity];
239 }
240};
241
242template <class T, isize capacity>
243struct SortedRingBuffer : public RingBuffer<T, capacity>
244{
245 // Key storage
246 i64 *keys = new i64[capacity];
247
248
249 //
250 // Initializing
251 //
252
253 ~SortedRingBuffer() { delete[] keys; }
254
255 // Inserts an element at the proper position
256 void insert(i64 key, T element)
257 {
258 assert(!this->isFull());
259
260 // Add the new element
261 auto oldw = this->w;
262 this->write(element);
263 keys[oldw] = key;
264
265 // Keep the elements sorted
266 while (oldw != this->r) {
267
268 // Get the index of the preceeding element
269 auto p = this->prev(oldw);
270
271 // Exit the loop once we've found the correct position
272 if (key > keys[p]) break;
273
274 // Otherwise, swap elements
275 std::swap(this->elements[oldw], this->elements[p]);
276 std::swap(keys[oldw], keys[p]);
277 oldw = p;
278 }
279 }
280
281 // Inserts an element for which we know that sorting is not necessary
282 void append(i64 key, T element)
283 {
284 assert(!this->isFull());
285 assert(this->isEmpty() || this->keys[this->prev(this->w)] <= key);
286
287 this->elements[this->w] = element;
288 this->keys[this->w] = key;
289 this->w = this->next(this->w);
290 }
291};
292
293}