helics  3.5.2
SmallBuffer.hpp
1 /*
2 Copyright (c) 2017-2024,
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  }
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 
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
Definition: SmallBuffer.hpp:25
void swap(SmallBuffer &sb2) noexcept
Definition: SmallBuffer.hpp:338
std::byte * release()
Definition: SmallBuffer.hpp:375
~SmallBuffer()
Definition: SmallBuffer.hpp:78
const std::byte * end() const
Definition: SmallBuffer.hpp:168
const std::byte * begin() const
Definition: SmallBuffer.hpp:166
std::byte * data() const
Definition: SmallBuffer.hpp:160
std::byte & operator[](size_t index)
Definition: SmallBuffer.hpp:172
void clear()
Definition: SmallBuffer.hpp:335
std::byte operator[](size_t index) const
Definition: SmallBuffer.hpp:170
std::string_view to_string() const
Definition: SmallBuffer.hpp:238
void spanAssign(void *data, std::size_t size, std::size_t capacity)
Definition: SmallBuffer.hpp:272
void moveAssign(void *data, std::size_t size, std::size_t capacity)
Definition: SmallBuffer.hpp:256
const char * char_data() const
Definition: SmallBuffer.hpp:254
bool empty() const
Definition: SmallBuffer.hpp:329
std::byte * end()
Definition: SmallBuffer.hpp:164
std::int8_t errorState() const
Definition: SmallBuffer.hpp:327
SmallBuffer(std::size_t size, std::byte val)
Definition: SmallBuffer.hpp:71
std::size_t capacity() const
Definition: SmallBuffer.hpp:333
std::byte * begin()
Definition: SmallBuffer.hpp:162
SmallBuffer(std::size_t size, unsigned char val)
Definition: SmallBuffer.hpp:73
void assign(const void *start, const void *end)
Definition: SmallBuffer.hpp:190
SmallBuffer(std::size_t size)
Definition: SmallBuffer.hpp:68
std::byte at(size_t index) const
Definition: SmallBuffer.hpp:174
std::size_t size() const
Definition: SmallBuffer.hpp:331
void null_terminate()
Definition: SmallBuffer.hpp:244
std::byte & at(size_t index)
Definition: SmallBuffer.hpp:182
the main namespace for the helics co-simulation library User functions will be in the helics namespac...
Definition: AsyncFedCallInfo.hpp:14
bool operator!=(const SmallBuffer &sb1, const SmallBuffer &sb2)
Definition: SmallBuffer.hpp:412
bool operator==(const SmallBuffer &sb1, const SmallBuffer &sb2)
Definition: SmallBuffer.hpp:406