openMSX
SerializeBuffer.cc
Go to the documentation of this file.
1 #include "SerializeBuffer.hh"
2 #include "likely.hh"
3 #include <cstdlib>
4 #include <utility>
5 
6 namespace openmsx {
7 
8 // class OutputBuffer
9 
10 size_t OutputBuffer::lastSize = 50000; // initial estimate
11 
13  : buf(lastSize)
14  , end(buf.data())
15  , finish(buf.data() + lastSize)
16 {
17  // We've allocated a buffer with an estimated initial size. This
18  // estimate is based on the largest intermediate size of the previously
19  // required buffers.
20  // For correctness this initial size doesn't matter (we could even not
21  // allocate any initial buffer at all). But it can make a difference in
22  // performance. If later we discover the buffer is too small, we have
23  // to reallocate (and thus make a copy). In profiling this reallocation
24  // step was noticable.
25 
26  // Slowly drop the estimated required size. This makes sure that when
27  // we've overestimated the size once, we don't forever keep this too
28  // high value. For performance an overestimation is less bad than an
29  // underestimation.
30  lastSize -= lastSize >> 7;
31 }
32 
33 #ifdef __GNUC__
34 template<size_t LEN> void OutputBuffer::insertN(const void* __restrict data)
35 {
36  uint8_t* newEnd = end + LEN;
37  if (likely(newEnd <= finish)) {
38  memcpy(end, data, LEN);
39  end = newEnd;
40  } else {
41  insertGrow(data, LEN);
42  }
43 }
44 // Force template instantiation
45 template void OutputBuffer::insertN<1>(const void* __restrict data);
46 template void OutputBuffer::insertN<2>(const void* __restrict data);
47 template void OutputBuffer::insertN<4>(const void* __restrict data);
48 template void OutputBuffer::insertN<8>(const void* __restrict data);
49 #endif
50 
51 void OutputBuffer::insertN(const void* __restrict data, size_t len)
52 {
53  uint8_t* newEnd = end + len;
54  if (likely(newEnd <= finish)) {
55  memcpy(end, data, len);
56  end = newEnd;
57  } else {
58  insertGrow(data, len);
59  }
60 }
61 
63 {
64  size = end - buf.data();
65 
66  // Deallocate unused buffer space.
67  buf.resize(size);
68 
69  end = finish = nullptr;
70  return std::move(buf);
71 }
72 
73 void OutputBuffer::grow(size_t len)
74 {
75  size_t oldSize = end - buf.data();
76  size_t newSize = std::max(oldSize + len, oldSize + oldSize / 2);
77  buf.resize(newSize);
78  end = buf.data() + oldSize;
79  finish = buf.data() + newSize;
80 }
81 
82 uint8_t* OutputBuffer::allocateGrow(size_t len)
83 {
84  grow(len);
85  auto* result = end;
86  end += len;
87  return result;
88 }
89 
90 void OutputBuffer::insertGrow(const void* __restrict data, size_t len)
91 {
92  uint8_t* pos = allocateGrow(len);
93  memcpy(pos, data, len);
94 }
95 
96 
97 
98 // class InputBuffer
99 
100 InputBuffer::InputBuffer(const uint8_t* data, size_t size)
101  : buf(data)
102 #ifndef NDEBUG
103  , finish(buf + size)
104 #endif
105 {
106  (void)size;
107 }
108 
109 } // namespace openmsx
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
OutputBuffer()
Create an empty output buffer.
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:120
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.
#define likely(x)
Definition: likely.hh:14
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
void insertN(const void *data, size_t len)
InputBuffer(const uint8_t *data, size_t size)
Construct new InputBuffer, typically the data and size parameters will come from a MemBuffer object...
auto end(const string_view &x)
Definition: string_view.hh:152