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