MessagePack for C++
vrefbuffer.hpp
Go to the documentation of this file.
1 //
2 // MessagePack for C++ zero-copy buffer implementation
3 //
4 // Copyright (C) 2008-2017 FURUHASHI Sadayuki and KONDO Takatoshi
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 //
10 #ifndef MSGPACK_V1_VREFBUFFER_HPP
11 #define MSGPACK_V1_VREFBUFFER_HPP
12 
14 
15 #include <stdexcept>
16 #include <algorithm>
17 
18 #include <boost/assert.hpp>
19 
20 #if defined(_MSC_VER)
21 // avoiding confliction std::max, std::min, and macro in windows.h
22 #ifndef NOMINMAX
23 #define NOMINMAX
24 #endif
25 #endif // defined(_MSC_VER)
26 
27 #if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
28 #include <sys/uio.h>
29 namespace msgpack {
30 typedef ::iovec iovec;
31 } // namespace msgpack
32 #else
33 namespace msgpack {
34 struct iovec {
35  void *iov_base;
36  size_t iov_len;
37 };
38 } // namespace msgpack
39 #endif
40 
41 namespace msgpack {
42 
46 
47 namespace detail {
48  // int64, uint64, double
49  std::size_t const packer_max_buffer_size = 9;
50 } // detail
51 
52 class vrefbuffer {
53 private:
54  struct chunk {
55  chunk* next;
56  };
57  struct inner_buffer {
58  size_t free;
59  char* ptr;
60  chunk* head;
61  };
62 public:
64  size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE)
65  :m_ref_size(std::max(ref_size, detail::packer_max_buffer_size + 1)),
66  m_chunk_size(chunk_size)
67  {
68  if((sizeof(chunk) + chunk_size) < chunk_size) {
69  throw std::bad_alloc();
70  }
71 
72  size_t nfirst = (sizeof(iovec) < 72/2) ?
73  72 / sizeof(iovec) : 8;
74 
75  iovec* array = static_cast<iovec*>(::malloc(
76  sizeof(iovec) * nfirst));
77  if(!array) {
78  throw std::bad_alloc();
79  }
80 
81  m_tail = array;
82  m_end = array + nfirst;
83  m_array = array;
84 
85  chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size));
86  if(!c) {
87  ::free(array);
88  throw std::bad_alloc();
89  }
90  inner_buffer* const ib = &m_inner_buffer;
91 
92  ib->free = chunk_size;
93  ib->ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
94  ib->head = c;
95  c->next = MSGPACK_NULLPTR;
96 
97  }
98 
100  {
101  chunk* c = m_inner_buffer.head;
102  while(true) {
103  chunk* n = c->next;
104  ::free(c);
105  if(n != NULL) {
106  c = n;
107  } else {
108  break;
109  }
110  }
111  ::free(m_array);
112  }
113 
114 public:
115  void write(const char* buf, size_t len)
116  {
117  BOOST_ASSERT(buf || len == 0);
118 
119  if (!buf) return;
120 
121  if(len < m_ref_size) {
122  append_copy(buf, len);
123  } else {
124  append_ref(buf, len);
125  }
126  }
127 
128  void append_ref(const char* buf, size_t len)
129  {
130  if(m_tail == m_end) {
131  const size_t nused = static_cast<size_t>(m_tail - m_array);
132  const size_t nnext = nused * 2;
133 
134  iovec* nvec = static_cast<iovec*>(::realloc(
135  m_array, sizeof(iovec)*nnext));
136  if(!nvec) {
137  throw std::bad_alloc();
138  }
139 
140  m_array = nvec;
141  m_end = nvec + nnext;
142  m_tail = nvec + nused;
143  }
144 
145  m_tail->iov_base = const_cast<char*>(buf);
146  m_tail->iov_len = len;
147  ++m_tail;
148  }
149 
150  void append_copy(const char* buf, size_t len)
151  {
152  inner_buffer* const ib = &m_inner_buffer;
153 
154  if(ib->free < len) {
155  size_t sz = m_chunk_size;
156  if(sz < len) {
157  sz = len;
158  }
159 
160  if(sizeof(chunk) + sz < sz){
161  throw std::bad_alloc();
162  }
163 
164  chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
165  if(!c) {
166  throw std::bad_alloc();
167  }
168 
169  c->next = ib->head;
170  ib->head = c;
171  ib->free = sz;
172  ib->ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
173  }
174 
175  char* m = ib->ptr;
176  std::memcpy(m, buf, len);
177  ib->free -= len;
178  ib->ptr += len;
179 
180  if(m_tail != m_array && m ==
181  static_cast<const char*>(
182  const_cast<const void *>((m_tail - 1)->iov_base)
183  ) + (m_tail - 1)->iov_len) {
184  (m_tail - 1)->iov_len += len;
185  return;
186  } else {
187  append_ref( m, len);
188  }
189  }
190 
191  const iovec* vector() const
192  {
193  return m_array;
194  }
195 
196  size_t vector_size() const
197  {
198  return static_cast<size_t>(m_tail - m_array);
199  }
200 
201  void migrate(vrefbuffer* to)
202  {
203  size_t sz = m_chunk_size;
204 
205  if((sizeof(chunk) + sz) < sz){
206  throw std::bad_alloc();
207  }
208 
209  chunk* empty = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
210  if(!empty) {
211  throw std::bad_alloc();
212  }
213 
214  empty->next = MSGPACK_NULLPTR;
215 
216  const size_t nused = static_cast<size_t>(m_tail - m_array);
217  if(to->m_tail + nused < m_end) {
218  const size_t tosize = static_cast<size_t>(to->m_tail - to->m_array);
219  const size_t reqsize = nused + tosize;
220  size_t nnext = static_cast<size_t>(to->m_end - to->m_array) * 2;
221  while(nnext < reqsize) {
222  size_t tmp_nnext = nnext * 2;
223  if (tmp_nnext <= nnext) {
224  nnext = reqsize;
225  break;
226  }
227  nnext = tmp_nnext;
228  }
229 
230  iovec* nvec = static_cast<iovec*>(::realloc(
231  to->m_array, sizeof(iovec)*nnext));
232  if(!nvec) {
233  ::free(empty);
234  throw std::bad_alloc();
235  }
236 
237  to->m_array = nvec;
238  to->m_end = nvec + nnext;
239  to->m_tail = nvec + tosize;
240  }
241 
242  std::memcpy(to->m_tail, m_array, sizeof(iovec)*nused);
243 
244  to->m_tail += nused;
245  m_tail = m_array;
246 
247 
248  inner_buffer* const ib = &m_inner_buffer;
249  inner_buffer* const toib = &to->m_inner_buffer;
250 
251  chunk* last = ib->head;
252  while(last->next) {
253  last = last->next;
254  }
255  last->next = toib->head;
256  toib->head = ib->head;
257 
258  if(toib->free < ib->free) {
259  toib->free = ib->free;
260  toib->ptr = ib->ptr;
261  }
262 
263  ib->head = empty;
264  ib->free = sz;
265  ib->ptr = reinterpret_cast<char*>(empty) + sizeof(chunk);
266 
267  }
268 
269  void clear()
270  {
271  chunk* c = m_inner_buffer.head->next;
272  chunk* n;
273  while(c) {
274  n = c->next;
275  ::free(c);
276  c = n;
277  }
278 
279  inner_buffer* const ib = &m_inner_buffer;
280  c = ib->head;
281  c->next = MSGPACK_NULLPTR;
282  ib->free = m_chunk_size;
283  ib->ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
284 
285  m_tail = m_array;
286  }
287 
288 #if defined(MSGPACK_USE_CPP03)
289 private:
290  vrefbuffer(const vrefbuffer&);
292 #else // defined(MSGPACK_USE_CPP03)
293  vrefbuffer(const vrefbuffer&) = delete;
294  vrefbuffer& operator=(const vrefbuffer&) = delete;
295 #endif // defined(MSGPACK_USE_CPP03)
296 
297 private:
298  iovec* m_tail;
299  iovec* m_end;
300  iovec* m_array;
301 
302  size_t m_ref_size;
303  size_t m_chunk_size;
304 
305  inner_buffer m_inner_buffer;
306 
307 };
308 
310 } // MSGPACK_API_VERSION_NAMESPACE(v1)
312 
313 } // namespace msgpack
314 
315 #endif // MSGPACK_V1_VREFBUFFER_HPP
Definition: vrefbuffer.hpp:52
vrefbuffer(const vrefbuffer &)=delete
~vrefbuffer()
Definition: vrefbuffer.hpp:99
void clear()
Definition: vrefbuffer.hpp:269
void write(const char *buf, size_t len)
Definition: vrefbuffer.hpp:115
vrefbuffer & operator=(const vrefbuffer &)=delete
vrefbuffer(size_t ref_size=MSGPACK_VREFBUFFER_REF_SIZE, size_t chunk_size=MSGPACK_VREFBUFFER_CHUNK_SIZE)
Definition: vrefbuffer.hpp:63
void append_ref(const char *buf, size_t len)
Definition: vrefbuffer.hpp:128
void migrate(vrefbuffer *to)
Definition: vrefbuffer.hpp:201
const iovec * vector() const
Definition: vrefbuffer.hpp:191
size_t vector_size() const
Definition: vrefbuffer.hpp:196
void append_copy(const char *buf, size_t len)
Definition: vrefbuffer.hpp:150
std::size_t const packer_max_buffer_size
Definition: vrefbuffer.hpp:49
Definition: adaptor_base.hpp:15
Definition: vrefbuffer.hpp:34
size_t iov_len
Definition: vrefbuffer.hpp:36
void * iov_base
Definition: vrefbuffer.hpp:35
#define MSGPACK_NULLPTR
Definition: cpp_config_decl.hpp:85
#define MSGPACK_API_VERSION_NAMESPACE(ns)
Definition: versioning.hpp:66
#define MSGPACK_VREFBUFFER_CHUNK_SIZE
Definition: vrefbuffer_decl.hpp:22
#define MSGPACK_VREFBUFFER_REF_SIZE
Definition: vrefbuffer_decl.hpp:18