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