| helics 3.6.1

SmallBuffer.hpp
1 /*
2 Copyright (c) 2017-2025,
3 Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable
4 Energy, LLC. See the top-level NOTICE for additional details. All rights reserved.
5 SPDX-License-Identifier: BSD-3-Clause
6 */
7 #pragma once
8
9 #include <array>
10 #include <cstddef>
11 #include <cstdint>
12 #include <cstring>
13 #include <new>
14 #include <stdexcept>
15 #include <string>
16 #include <string_view>
17 #include <utility>
18
19 #if defined(__clang__)
20 # pragma clang diagnostic push
21 # pragma clang diagnostic ignored "-Wuninitialized"
22 #endif
23
24 namespace helics {
25 class SmallBuffer {
26 public:
27 SmallBuffer() noexcept: heap(buffer.data()) {}
28
29 SmallBuffer(const SmallBuffer& sb): heap(buffer.data())
30 {
31 resize(sb.size());
32 std::memcpy(heap, sb.heap, sb.size());
33 }
34
35 SmallBuffer(SmallBuffer&& sb) noexcept
36 {
37 if (sb.usingAllocatedBuffer) {
38 heap = sb.heap;
39 bufferCapacity = sb.bufferCapacity;
40 usingAllocatedBuffer = sb.usingAllocatedBuffer;
41 nonOwning = sb.nonOwning;
42 sb.usingAllocatedBuffer = false;
43 } else {
44 std::memcpy(buffer.data(), sb.heap, sb.bufferSize);
45 heap = buffer.data();
46 }
47 bufferSize = sb.bufferSize;
48 sb.heap = sb.buffer.data();
49 sb.bufferCapacity = 64;
50 sb.bufferSize = 0;
51 }
52
53 template<typename U,
54 typename T = std::enable_if_t<std::is_constructible_v<std::string_view, U>>>
55 /*implicit*/ SmallBuffer(U&& u): heap(buffer.data())
56 {
57 std::string_view val(std::forward<U>(u));
58 resize(val.size());
59 std::memcpy(heap, val.data(), val.size());
60 }
61
62 SmallBuffer(const void* data, size_t size): heap(buffer.data())
63 {
64 resize(size);
65 std::memcpy(heap, data, size);
66 }
68 explicit SmallBuffer(std::size_t size): heap(buffer.data()) { resize(size); }
69
71 SmallBuffer(std::size_t size, std::byte val): heap(buffer.data()) { resize(size, val); }
73 SmallBuffer(std::size_t size, unsigned char val): heap(buffer.data())
74 {
75 resize(size, std::byte{val});
76 }
78 ~SmallBuffer()
79 {
80 if (usingAllocatedBuffer && !nonOwning) {
81 delete[] heap;
82 }
83 }
84 SmallBuffer& operator=(const SmallBuffer& sb)
85 {
86 if (this == &sb) {
87 return *this;
88 }
89 resize(sb.size());
90 std::memcpy(heap, sb.heap, sb.size());
91 return *this;
92 }
93 SmallBuffer& operator=(SmallBuffer&& sb) noexcept
94 {
95 if (locked) {
96 // if locked then use the copy operation not move
97 const SmallBuffer& buf = sb;
98 try {
99 return operator=(buf);
100 }
101 catch (std::bad_alloc&) {
102 errorCondition = 2;
103 return *this;
104 }
105 }
106 if (usingAllocatedBuffer) {
107 if (nonOwning) {
108 if (sb.heap == heap) {
109 bufferSize = sb.bufferSize;
110 bufferCapacity = sb.bufferCapacity;
111 return *this;
112 }
113 } else {
114 if (sb.heap == heap) {
115 bufferSize = sb.bufferSize;
116 return *this;
117 }
118 delete[] heap;
119 }
120 }
121
122 if (sb.usingAllocatedBuffer) {
123 heap = sb.heap;
124 bufferCapacity = sb.bufferCapacity;
125 usingAllocatedBuffer = true;
126 nonOwning = sb.nonOwning;
127 } else {
128 std::memcpy(buffer.data(), sb.heap, sb.bufferSize);
129 usingAllocatedBuffer = false;
130 nonOwning = false;
131 heap = buffer.data();
132 bufferCapacity = 64;
133 }
134 locked = sb.locked;
135 bufferSize = sb.bufferSize;
136 sb.heap = sb.buffer.data();
137 sb.bufferCapacity = 64;
138 sb.bufferSize = 0;
139 sb.usingAllocatedBuffer = false;
140 sb.locked = false;
141
142 return *this;
143 }
144 template<typename U,
145 typename T = std::enable_if_t<std::is_constructible_v<std::string_view, U>>>
146 SmallBuffer& operator=(U&& u)
147 {
148 std::string_view val(std::forward<U>(u));
149 if (reinterpret_cast<const std::byte*>(val.data()) == heap) {
150 bufferSize = val.size();
151 return *this;
152 }
153 resize(val.size());
154 if (val.size() > 0) {
155 std::memcpy(heap, val.data(), val.size());
156 }
157 return *this;
158 }
160 std::byte* data() const { return heap; }
162 std::byte* begin() { return heap; }
164 std::byte* end() { return heap + bufferSize; }
166 const std::byte* begin() const { return heap; }
168 const std::byte* end() const { return heap + bufferSize; }
170 std::byte operator[](size_t index) const { return heap[index]; }
172 std::byte& operator[](size_t index) { return heap[index]; }
174 std::byte at(size_t index) const
175 {
176 if (index >= bufferSize) {
177 throw(std::out_of_range("specified index is not valid"));
178 }
179 return heap[index];
180 }
182 std::byte& at(size_t index)
183 {
184 if (index >= bufferSize) {
185 throw(std::out_of_range("specified index is not valid"));
186 }
187 return heap[index];
188 }
190 void assign(const void* start, const void* end)
191 {
192 if (start > end) {
193 throw(
194 std::invalid_argument("invalid range specified, end pointer before start pointer"));
195 }
196 const auto* st1 = reinterpret_cast<const std::byte*>(start);
197 const auto* end1 = reinterpret_cast<const std::byte*>(end);
198 resize(end1 - st1);
199 std::memcpy(heap, st1, end1 - st1);
200 }
201 void assign(const void* start, std::size_t size)
202 {
203 const auto* st1 = reinterpret_cast<const std::byte*>(start);
204 resize(size);
205 std::memcpy(heap, st1, size);
206 }
207 void append(const void* start, const void* end)
208 {
209 if (start > end) {
210 throw(
211 std::invalid_argument("invalid range specified, end pointer before start pointer"));
212 }
213 const auto* st1 = reinterpret_cast<const std::byte*>(start);
214 const auto* end1 = reinterpret_cast<const std::byte*>(end);
215 auto csize = bufferSize;
216 resize(bufferSize + (end1 - st1));
217 std::memcpy(heap + csize, st1, end1 - st1);
218 }
219 void append(const void* start, std::size_t size)
220 {
221 const auto* st1 = reinterpret_cast<const std::byte*>(start);
222 auto csize = bufferSize;
223 resize(bufferSize + size);
224 std::memcpy(heap + csize, st1, size);
225 }
226 void append(std::string_view data)
227 {
228 const auto* st1 = reinterpret_cast<const std::byte*>(data.data());
229 auto csize = bufferSize;
230 resize(bufferSize + data.size());
231 std::memcpy(heap + csize, st1, data.size());
232 }
233
234 void push_back(char c) { append(&c, 1); }
235
236 void pop_back() { bufferSize > 0 ? --bufferSize : 0; }
238 std::string_view to_string() const
239 {
240 return std::string_view{reinterpret_cast<const char*>(heap), bufferSize};
241 }
242
244 void null_terminate()
245 {
246 if (bufferCapacity > bufferSize) {
247 heap[bufferSize] = std::byte(0);
248 } else {
249 push_back('\0');
250 pop_back();
251 }
252 }
254 const char* char_data() const { return reinterpret_cast<const char*>(heap); }
256 void moveAssign(void* data, std::size_t size, std::size_t capacity)
257 {
258 auto* newHeap = reinterpret_cast<std::byte*>(data);
259 if (usingAllocatedBuffer && !nonOwning) {
260 if (newHeap != heap) {
261 delete[] heap;
262 }
263 }
264 heap = newHeap;
265 bufferCapacity = capacity;
266 bufferSize = size;
267 nonOwning = false;
268 usingAllocatedBuffer = true;
269 locked = false;
270 }
272 void spanAssign(void* data, std::size_t size, std::size_t capacity)
273 {
274 auto* newHeap = reinterpret_cast<std::byte*>(data);
275 if (usingAllocatedBuffer && !nonOwning) {
276 if (newHeap == heap) {
277 // if the heaps are the same the only thing to change is the size and capacity
278 // don't change the other characteristics
279 bufferSize = size;
280 bufferCapacity = capacity;
281 return;
282 }
283 delete[] heap;
284 }
285 locked = false;
286 heap = newHeap;
287 bufferCapacity = capacity;
288 bufferSize = size;
289 nonOwning = true;
290 usingAllocatedBuffer = true;
291 }
292 void resize(size_t size)
293 {
294 reserve(size);
295 bufferSize = size;
296 }
297 void resize(size_t size, std::byte val)
298 {
299 reserve(size);
300 if (size > bufferSize) {
301 std::memset(heap + bufferSize, std::to_integer<int>(val), size - bufferSize);
302 }
303 bufferSize = size;
304 }
305 void reserve(size_t size)
306 {
307 static constexpr size_t bigSize{sizeof(size_t) == 8 ? 0x010'0000'0000U : 0xFFFF'0000U};
308 if (size > bufferCapacity) {
309 if (size > bigSize || locked) {
310 throw(std::bad_alloc());
311 }
312 auto* ndata = new std::byte[size + 8];
313 std::memcpy(ndata, heap, bufferSize);
314 if (usingAllocatedBuffer && !nonOwning) {
315 delete[] heap;
316 }
317 heap = ndata;
318 nonOwning = false;
319 usingAllocatedBuffer = true;
320 bufferCapacity = size + 8;
321 }
322 }
323 void lock(bool lockStatus = true) { locked = lockStatus; }
324
325 bool isLocked() const { return locked; }
327 std::int8_t errorState() const { return errorCondition; }
329 bool empty() const { return (bufferSize == 0); }
331 std::size_t size() const { return bufferSize; }
333 std::size_t capacity() const { return bufferCapacity; }
335 void clear() { bufferSize = 0; }
336
338 void swap(SmallBuffer& sb2) noexcept
339 {
340 if (sb2.usingAllocatedBuffer && usingAllocatedBuffer) {
341 std::swap(heap, sb2.heap);
342 std::swap(nonOwning, sb2.nonOwning);
343 std::swap(bufferCapacity, sb2.bufferCapacity);
344 std::swap(bufferSize, sb2.bufferSize);
345 } else if (usingAllocatedBuffer) {
346 sb2.heap = heap;
347 sb2.bufferCapacity = bufferCapacity;
348 sb2.usingAllocatedBuffer = true;
349 sb2.nonOwning = nonOwning;
350 usingAllocatedBuffer = false;
351 nonOwning = false;
352 heap = buffer.data();
353 bufferCapacity = 64;
354
355 std::memcpy(heap, sb2.buffer.data(), sb2.size());
356 std::swap(sb2.bufferSize, bufferSize);
357 } else if (sb2.usingAllocatedBuffer) {
358 heap = sb2.heap;
359 bufferCapacity = sb2.bufferCapacity;
360 usingAllocatedBuffer = true;
361 nonOwning = sb2.nonOwning;
362 sb2.usingAllocatedBuffer = false;
363 sb2.nonOwning = false;
364 sb2.heap = buffer.data();
365 sb2.bufferCapacity = 64;
366
367 std::memcpy(sb2.heap, buffer.data(), bufferSize);
368 std::swap(sb2.bufferSize, bufferSize);
369 } else {
370 std::swap(sb2.buffer, buffer);
371 std::swap(sb2.bufferSize, bufferSize);
372 }
373 }
375 std::byte* release()
376 {
377 if (!usingAllocatedBuffer) {
378 return nullptr;
379 }
380 auto* released = heap;
381 heap = buffer.data();
382 usingAllocatedBuffer = false;
383 nonOwning = false;
384 locked = false;
385 bufferCapacity = 64;
386 bufferSize = 0;
387 return released;
388 }
389
390 private:
391 std::array<std::byte, 64> buffer{{std::byte{0}}};
392 std::size_t bufferSize{0};
393 std::size_t bufferCapacity{64};
394 std::byte* heap;
395 bool nonOwning{false};
396 bool locked{false};
397 bool usingAllocatedBuffer{false};
398 std::int8_t errorCondition{0};
399
400 public:
401 std::uint32_t userKey{0}; // 32 bits of user data for whatever purpose is desired has no impact
402 // on state or operations
403 };
404
406 inline bool operator==(const SmallBuffer& sb1, const SmallBuffer& sb2)
407 {
408 return (sb1.to_string() == sb2.to_string());
409 }
410
412 inline bool operator!=(const SmallBuffer& sb1, const SmallBuffer& sb2)
413 {
414 return (sb1.to_string() != sb2.to_string());
415 }
416
417 #if defined(__clang__)
418 # pragma clang diagnostic pop
419 #endif
420
421 } // namespace helics
helics::SmallBuffer
Definition: SmallBuffer.hpp:25
helics::SmallBuffer::swap
void swap(SmallBuffer &sb2) noexcept
Definition: SmallBuffer.hpp:338
helics::SmallBuffer::release
std::byte * release()
Definition: SmallBuffer.hpp:375
helics::SmallBuffer::~SmallBuffer
~SmallBuffer()
Definition: SmallBuffer.hpp:78
helics::SmallBuffer::end
const std::byte * end() const
Definition: SmallBuffer.hpp:168
helics::SmallBuffer::begin
const std::byte * begin() const
Definition: SmallBuffer.hpp:166
helics::SmallBuffer::data
std::byte * data() const
Definition: SmallBuffer.hpp:160
helics::SmallBuffer::operator[]
std::byte & operator[](size_t index)
Definition: SmallBuffer.hpp:172
helics::SmallBuffer::clear
void clear()
Definition: SmallBuffer.hpp:335
helics::SmallBuffer::operator[]
std::byte operator[](size_t index) const
Definition: SmallBuffer.hpp:170
helics::SmallBuffer::to_string
std::string_view to_string() const
Definition: SmallBuffer.hpp:238
helics::SmallBuffer::spanAssign
void spanAssign(void *data, std::size_t size, std::size_t capacity)
Definition: SmallBuffer.hpp:272
helics::SmallBuffer::moveAssign
void moveAssign(void *data, std::size_t size, std::size_t capacity)
Definition: SmallBuffer.hpp:256
helics::SmallBuffer::char_data
const char * char_data() const
Definition: SmallBuffer.hpp:254
helics::SmallBuffer::empty
bool empty() const
Definition: SmallBuffer.hpp:329
helics::SmallBuffer::end
std::byte * end()
Definition: SmallBuffer.hpp:164
helics::SmallBuffer::errorState
std::int8_t errorState() const
Definition: SmallBuffer.hpp:327
helics::SmallBuffer::SmallBuffer
SmallBuffer(std::size_t size, std::byte val)
Definition: SmallBuffer.hpp:71
helics::SmallBuffer::capacity
std::size_t capacity() const
Definition: SmallBuffer.hpp:333
helics::SmallBuffer::begin
std::byte * begin()
Definition: SmallBuffer.hpp:162
helics::SmallBuffer::SmallBuffer
SmallBuffer(std::size_t size, unsigned char val)
Definition: SmallBuffer.hpp:73
helics::SmallBuffer::assign
void assign(const void *start, const void *end)
Definition: SmallBuffer.hpp:190
helics::SmallBuffer::SmallBuffer
SmallBuffer(std::size_t size)
Definition: SmallBuffer.hpp:68
helics::SmallBuffer::at
std::byte at(size_t index) const
Definition: SmallBuffer.hpp:174
helics::SmallBuffer::size
std::size_t size() const
Definition: SmallBuffer.hpp:331
helics::SmallBuffer::null_terminate
void null_terminate()
Definition: SmallBuffer.hpp:244
helics::SmallBuffer::at
std::byte & at(size_t index)
Definition: SmallBuffer.hpp:182
helics
the main namespace for the helics co-simulation library User functions will be in the helics namespac...
Definition: AsyncFedCallInfo.hpp:14
helics::operator!=
bool operator!=(const SmallBuffer &sb1, const SmallBuffer &sb2)
Definition: SmallBuffer.hpp:412
helics::operator==
bool operator==(const SmallBuffer &sb1, const SmallBuffer &sb2)
Definition: SmallBuffer.hpp:406