Line data Source code
1 : // Webthing-CPP
2 : // SPDX-FileCopyrightText: 2023-present Benno Waldhauer
3 : // SPDX-License-Identifier: MIT
4 :
5 : #pragma once
6 :
7 : #include <cstddef>
8 : #include <deque>
9 : #include <memory>
10 : #include <mutex>
11 : #include <vector>
12 :
13 : namespace bw::webthing {
14 :
15 : struct StorageConfig
16 : {
17 : size_t max_size = SIZE_MAX;
18 : bool write_protected = true;
19 : };
20 :
21 : // A simple ring buffer that overwrites oldest elements when max_size is reached.
22 : // This ring buffer can not remove a subset of its elements.
23 : template<class T>
24 : class SimpleRingBuffer
25 : {
26 : public:
27 43 : SimpleRingBuffer(size_t max_size = SIZE_MAX, bool write_protected = false)
28 43 : : max_size(max_size)
29 43 : , current_size(0)
30 43 : , start_pos(0)
31 : {
32 43 : if(max_size < SIZE_MAX)
33 40 : buffer.reserve(max_size);
34 :
35 43 : if(write_protected)
36 37 : mutex = std::make_unique<std::mutex>();
37 43 : }
38 :
39 36 : SimpleRingBuffer(const StorageConfig& config)
40 36 : : SimpleRingBuffer(config.max_size, config.write_protected)
41 36 : {}
42 :
43 2000049 : T& get(size_t index)
44 : {
45 2000049 : return buffer[resolve_index(index)];
46 : }
47 :
48 40 : const T& get(size_t index) const
49 : {
50 40 : return buffer[resolve_index(index)];
51 : }
52 :
53 2009762 : void add(T element)
54 : {
55 2009762 : auto lock = conditional_lock();
56 :
57 2010034 : if (current_size < max_size)
58 : {
59 2010026 : buffer.push_back(element);
60 2010026 : ++current_size;
61 : }
62 : else
63 : {
64 8 : buffer[start_pos] = element;
65 8 : start_pos = (start_pos + 1) % max_size;
66 : }
67 2010034 : }
68 :
69 14 : size_t size() const
70 : {
71 14 : return current_size;
72 : }
73 :
74 6 : auto begin() { return Iterator(this, 0); }
75 6 : auto end() { return Iterator(this, current_size); }
76 15 : auto begin() const { return ConstIterator(this, 0); }
77 15 : auto end() const { return ConstIterator(this, current_size); }
78 :
79 : private:
80 : std::vector<T> buffer;
81 : size_t max_size;
82 : size_t current_size;
83 : size_t start_pos;
84 : std::unique_ptr<std::mutex> mutex;
85 :
86 2000089 : const size_t resolve_index(size_t index) const
87 : {
88 2000089 : if (index >= current_size)
89 8 : throw std::out_of_range("Index out of range");
90 :
91 2000081 : return (start_pos + index) % max_size;
92 : }
93 :
94 2009759 : std::unique_ptr<std::scoped_lock<std::mutex>> conditional_lock()
95 : {
96 2009759 : std::unique_ptr<std::scoped_lock<std::mutex>> lock;
97 2009759 : if(mutex)
98 9685 : lock = std::make_unique<std::scoped_lock<std::mutex>>(*mutex);
99 2010034 : return lock;
100 0 : }
101 :
102 : struct Iterator
103 : {
104 12 : Iterator(SimpleRingBuffer* buffer, size_t position)
105 12 : : buffer(buffer)
106 12 : , position(position)
107 12 : {}
108 :
109 2000018 : bool operator!=(const Iterator& other) const
110 : {
111 2000018 : return position != other.position;
112 : }
113 :
114 2000012 : Iterator& operator++()
115 : {
116 2000012 : ++position;
117 2000012 : return *this;
118 : }
119 :
120 2000012 : T& operator*()
121 : {
122 2000012 : return buffer->get(position);
123 : }
124 :
125 : private:
126 : SimpleRingBuffer* buffer;
127 : size_t position;
128 : };
129 :
130 : struct ConstIterator
131 : {
132 30 : ConstIterator(const SimpleRingBuffer* buffer, size_t position)
133 30 : : buffer(buffer)
134 30 : , position(position)
135 30 : {}
136 :
137 55 : bool operator!=(const ConstIterator& other) const
138 : {
139 55 : return position != other.position;
140 : }
141 :
142 40 : ConstIterator& operator++()
143 : {
144 40 : ++position;
145 40 : return *this;
146 : }
147 :
148 40 : const T& operator*() const
149 : {
150 40 : return buffer->get(position);
151 : }
152 :
153 : private:
154 : const SimpleRingBuffer* buffer;
155 : size_t position;
156 : };
157 : };
158 :
159 : // A more feature rich ring buffer that overwrites oldest elements when max_size is reached.
160 : // This ring buffer is able to remove a subset of its elements while maintaining the insertion order.
161 : template<class T>
162 : class FlexibleRingBuffer
163 : {
164 : public:
165 34 : FlexibleRingBuffer(size_t max_size = SIZE_MAX, bool write_protected = false)
166 34 : : max_size(max_size)
167 : {
168 34 : if(write_protected)
169 16 : mutex = std::make_unique<std::mutex>();
170 34 : }
171 :
172 15 : FlexibleRingBuffer(const StorageConfig& config)
173 15 : : FlexibleRingBuffer(config.max_size, config.write_protected)
174 15 : {}
175 :
176 17 : T& get(size_t index)
177 : {
178 17 : return buffer.at(index);
179 : }
180 :
181 : const T& get(size_t index) const
182 : {
183 : return buffer.at(index);
184 : }
185 :
186 9801 : void add(T element)
187 : {
188 9801 : auto lock = conditional_lock();
189 10033 : buffer.push_back(element);
190 10033 : if (size() > max_size)
191 4 : buffer.pop_front();
192 10033 : }
193 :
194 3 : void remove_if(std::function<bool (const T& element)> predicate)
195 : {
196 3 : auto lock = conditional_lock();
197 3 : buffer.erase(std::remove_if(buffer.begin(), buffer.end(), predicate), buffer.end());
198 3 : }
199 :
200 10042 : size_t size() const
201 : {
202 10042 : return buffer.size();
203 : }
204 5 : auto begin() { return buffer.begin(); }
205 5 : auto end() { return buffer.end(); }
206 22 : auto begin() const { return buffer.begin(); }
207 22 : auto end() const { return buffer.end(); }
208 :
209 : private:
210 : std::deque<T> buffer;
211 : size_t max_size;
212 : std::unique_ptr<std::mutex> mutex;
213 :
214 9794 : std::unique_ptr<std::scoped_lock<std::mutex>> conditional_lock()
215 : {
216 9794 : std::unique_ptr<std::scoped_lock<std::mutex>> lock;
217 9794 : if(mutex)
218 9731 : lock = std::make_unique<std::scoped_lock<std::mutex>>(*mutex);
219 10036 : return lock;
220 0 : }
221 : };
222 :
223 : } // bw::webthing
|