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