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