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