openMSX
SerializeBuffer.hh
Go to the documentation of this file.
1 #ifndef SERIALIZEBUFFER_HH
2 #define SERIALIZEBUFFER_HH
3 
4 #include "MemBuffer.hh"
5 #include "inline.hh"
6 #include <algorithm>
7 #include <cstdint>
8 #include <cstring>
9 #include <cassert>
10 #include <tuple>
11 
12 namespace openmsx {
13 
28 {
29 public:
32  OutputBuffer();
33 
37  void insert(const void* __restrict data, size_t len)
38  {
39 #ifdef __GNUC__
40  if (__builtin_constant_p(len)) {
41  if (len == 1) {
42  insertN<1>(data); return;
43  } else if (len == 2) {
44  insertN<2>(data); return;
45  } else if (len == 4) {
46  insertN<4>(data); return;
47  } else if (len == 8) {
48  insertN<8>(data); return;
49  }
50  }
51 #endif
52  insertN(data, len);
53  }
54 #ifdef __GNUC__
55  template<size_t N> void insertN(const void* __restrict data);
56 #endif
57  void insertN(const void* __restrict data, size_t len);
58 
64  template<typename TUPLE> ALWAYS_INLINE void insert_tuple_ptr(const TUPLE& tuple)
65  {
66  size_t len = 0;
67  auto accum = [&](auto* p) { len += sizeof(*p); };
68  std::apply([&](auto&&... args) { (accum(args), ...); }, tuple);
69 
70  uint8_t* newEnd = end + len;
71  if (newEnd > finish) [[unlikely]] {
72  grow(len); // reallocates, thus newEnd is no longer valid.
73  }
74 
75  uint8_t* dst = end;
76  auto write = [&](auto* src) {
77  memcpy(dst, src, sizeof(*src));
78  dst += sizeof(*src);
79  };
80  std::apply([&](auto&&... args) { (write(args), ...); }, tuple);
81  assert(dst == (end + len));
82 
83  end = dst;
84  }
85 
86  template<typename T> ALWAYS_INLINE void insert_tuple_ptr(const std::tuple<T*>& tuple)
87  {
88  // single-element tuple -> use insert() because it's better tuned
89  insert(std::get<0>(tuple), sizeof(T));
90  }
91 
96  void insertAt(size_t pos, const void* __restrict data, size_t len)
97  {
98  assert(buf.data() + pos + len <= finish);
99  memcpy(buf.data() + pos, data, len);
100  }
101 
110  [[nodiscard]] uint8_t* allocate(size_t len)
111  {
112  auto* newEnd = end + len;
113  // Make sure the next OutputBuffer will start with an initial size
114  // that can hold this much space plus some slack.
115  size_t newSize = newEnd - buf.data();
116  lastSize = std::max(lastSize, newSize + 1000);
117  if (newEnd <= finish) {
118  uint8_t* result = end;
119  end = newEnd;
120  return result;
121  } else {
122  return allocateGrow(len);
123  }
124  }
125 
137  void deallocate(uint8_t* pos)
138  {
139  assert(buf.data() <= pos);
140  assert(pos <= end);
141  end = pos;
142  }
143 
146  [[nodiscard]] size_t getPosition() const
147  {
148  return end - buf.data();
149  }
150 
154  [[nodiscard]] MemBuffer<uint8_t> release(size_t& size);
155 
156 private:
157  void insertGrow(const void* __restrict data, size_t len);
158  [[nodiscard]] uint8_t* allocateGrow(size_t len);
159  void grow(size_t len);
160 
161  MemBuffer<uint8_t> buf; // begin of allocated memory
162  uint8_t* end; // points right after the last used byte
163  // so end - buf == size
164  uint8_t* finish; // points right after the last allocated byte
165  // so finish - buf == capacity
166 
167  static inline size_t lastSize = 50000; // initial estimate
168 };
169 
170 
176 {
177 public:
181  InputBuffer(const uint8_t* data, size_t size);
182 
187  void read(void* __restrict result, size_t len) __restrict
188  {
189  memcpy(result, buf, len);
190  buf += len;
191  assert(buf <= finish);
192  }
193 
198  void skip(size_t len)
199  {
200  buf += len;
201  assert(buf <= finish);
202  }
203 
209  [[nodiscard]] const uint8_t* getCurrentPos() const { return buf; }
210 
211 private:
212  const uint8_t* buf;
213 #ifndef NDEBUG
214  const uint8_t* finish; // only used to check asserts
215 #endif
216 };
217 
218 } // namespace openmsx
219 
220 #endif
This class is complementary to the OutputBuffer class.
void read(void *result, size_t len)
Read the given number of bytes.
const uint8_t * getCurrentPos() const
Return a pointer to the current position in the buffer.
InputBuffer(const uint8_t *data, size_t size)
Construct new InputBuffer, typically the data and size parameters will come from a MemBuffer object.
void skip(size_t len)
Skip the given number of bytes.
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:81
Memory output buffer.
void insertAt(size_t pos, const void *data, size_t len)
Insert data at a given position.
ALWAYS_INLINE void insert_tuple_ptr(const std::tuple< T * > &tuple)
void insertN(const void *data, size_t len)
OutputBuffer()
Create an empty output buffer.
uint8_t * allocate(size_t len)
Reserve space to insert the given number of bytes.
MemBuffer< uint8_t > release(size_t &size)
Release ownership of the buffer.
ALWAYS_INLINE void insert_tuple_ptr(const TUPLE &tuple)
Insert all the elements of the given tuple.
size_t getPosition() const
Get the current size of the buffer.
void deallocate(uint8_t *pos)
Free part of a previously allocated buffer.
void insert(const void *data, size_t len)
Insert data at the end of this buffer.
#define ALWAYS_INLINE
Definition: inline.hh:16
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:276
This file implemented 3 utility functions:
Definition: Autofire.cc:9
size_t size(std::string_view utf8)