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 = TupleElementSize<TUPLE>::value;
68  auto* newEnd = end + len;
69  if (unlikely(newEnd > finish)) grow(len);
70 
71  InsertTupleHelper<TUPLE, 0, std::tuple_size<TUPLE>::value> helper;
72  helper(tuple, end);
73 
74  end = newEnd;
75  }
76  template<typename T> ALWAYS_INLINE void insert_tuple_ptr(const std::tuple<T*>& tuple)
77  {
78  // single-element tuple -> use insert() because it's better tuned
79  insert(std::get<0>(tuple), sizeof(T));
80  }
81 
86  void insertAt(size_t pos, const void* __restrict data, size_t len)
87  {
88  assert(buf.data() + pos + len <= finish);
89  memcpy(buf.data() + pos, data, len);
90  }
91 
100  uint8_t* allocate(size_t len)
101  {
102  auto* newEnd = end + len;
103  // Make sure the next OutputBuffer will start with an initial size
104  // that can hold this much space plus some slack.
105  size_t newSize = newEnd - buf.data();
106  lastSize = std::max(lastSize, newSize + 1000);
107  if (newEnd <= finish) {
108  uint8_t* result = end;
109  end = newEnd;
110  return result;
111  } else {
112  return allocateGrow(len);
113  }
114  }
115 
127  void deallocate(uint8_t* pos)
128  {
129  assert(buf.data() <= pos);
130  assert(pos <= end);
131  end = pos;
132  }
133 
136  size_t getPosition() const
137  {
138  return end - buf.data();
139  }
140 
145 
146 private:
147  void insertGrow(const void* __restrict data, size_t len);
148  uint8_t* allocateGrow(size_t len);
149  void grow(size_t len);
150 
151  // TupleElementSize
152  template<size_t N, typename TUPLE> struct TupleElementSizeImpl {
153  using ElemPtr = typename std::tuple_element<N - 1, TUPLE>::type;
154  using Elem = typename std::remove_pointer<ElemPtr>::type;
155  static const size_t value
156  = sizeof(Elem) + TupleElementSizeImpl<N - 1, TUPLE>::value;
157  };
158  template<typename TUPLE> struct TupleElementSizeImpl<0, TUPLE> {
159  static const size_t value = 0;
160  };
161  template<typename TUPLE> struct TupleElementSize
162  : TupleElementSizeImpl<std::tuple_size<TUPLE>::value, TUPLE> {};
163 
164  // InsertTupleHelper
165  template<typename TUPLE, size_t I, size_t N> struct InsertTupleHelper {
166  ALWAYS_INLINE void operator()(const TUPLE& tuple, uint8_t* p) {
167  using ElemPtr = typename std::tuple_element<I, TUPLE>::type;
168  using Elem = typename std::remove_pointer<ElemPtr>::type;
169  memcpy(p, std::get<I>(tuple), sizeof(Elem));
170  InsertTupleHelper<TUPLE, I + 1, N> helper;
171  helper(tuple, p + sizeof(Elem));
172  }
173  };
174  template<typename TUPLE, size_t N> struct InsertTupleHelper<TUPLE, N, N> {
175  ALWAYS_INLINE void operator()(const TUPLE& /*tuple*/, uint8_t* /*p*/) {
176  // nothing
177  }
178  };
179 
180  MemBuffer<uint8_t> buf; // begin of allocated memory
181  uint8_t* end; // points right after the last used byte
182  // so end - buf == size
183  uint8_t* finish; // points right after the last allocated byte
184  // so finish - buf == capacity
185 
186  static size_t lastSize;
187 };
188 
189 
195 {
196 public:
200  InputBuffer(const uint8_t* data, size_t size);
201 
206  void read(void* __restrict result, size_t len) __restrict
207  {
208  memcpy(result, buf, len);
209  buf += len;
210  assert(buf <= finish);
211  }
212 
217  void skip(size_t len)
218  {
219  buf += len;
220  assert(buf <= finish);
221  }
222 
228  const uint8_t* getCurrentPos() const { return buf; }
229 
230 private:
231  const uint8_t* buf;
232 #ifndef NDEBUG
233  const uint8_t* finish; // only used to check asserts
234 #endif
235 };
236 
237 } // namespace openmsx
238 
239 #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:287
void insert(const void *data, size_t len)
Insert data at the end of this buffer.
OutputBuffer()
Create an empty output buffer.
void deallocate(uint8_t *pos)
Free part of a previously allocated buffer.
Memory output buffer.
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
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)
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
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.