// // MessagePack for C++ zero-copy buffer implementation // // Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef MSGPACK_VREFBUFFER_HPP #define MSGPACK_VREFBUFFER_HPP #include "msgpack/versioning.hpp" #include #ifndef MSGPACK_VREFBUFFER_REF_SIZE #define MSGPACK_VREFBUFFER_REF_SIZE 32 #endif #ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE #define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192 #endif #ifndef _WIN32 #include #else struct iovec { void *iov_base; size_t iov_len; }; #endif namespace msgpack { /// @cond MSGPACK_API_VERSION_NAMESPACE(v1) { /// @endcond namespace detail { // int64, uint64, double std::size_t const packer_max_buffer_size = 9; } // detail class vrefbuffer { private: struct chunk { chunk* next; }; struct inner_buffer { size_t free; char* ptr; chunk* head; }; public: vrefbuffer(size_t ref_size = MSGPACK_VREFBUFFER_REF_SIZE, size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE) :m_ref_size(std::max(ref_size, detail::packer_max_buffer_size + 1)), m_chunk_size(chunk_size) { size_t nfirst = (sizeof(iovec) < 72/2) ? 72 / sizeof(iovec) : 8; iovec* array = static_cast(::malloc( sizeof(iovec) * nfirst)); if(!array) { throw std::bad_alloc(); } m_tail = array; m_end = array + nfirst; m_array = array; chunk* c = static_cast(::malloc(sizeof(chunk) + chunk_size)); if(!c) { ::free(array); throw std::bad_alloc(); } inner_buffer* const ib = &m_inner_buffer; ib->free = chunk_size; ib->ptr = reinterpret_cast(c) + sizeof(chunk); ib->head = c; c->next = nullptr; } ~vrefbuffer() { chunk* c = m_inner_buffer.head; while(true) { chunk* n = c->next; ::free(c); if(n != NULL) { c = n; } else { break; } } ::free(m_array); } public: void write(const char* buf, size_t len) { if(len < m_ref_size) { append_copy(buf, len); } else { append_ref(buf, len); } } void append_ref(const char* buf, size_t len) { if(m_tail == m_end) { const size_t nused = m_tail - m_array; const size_t nnext = nused * 2; iovec* nvec = static_cast(::realloc( m_array, sizeof(iovec)*nnext)); if(!nvec) { throw std::bad_alloc(); } m_array = nvec; m_end = nvec + nnext; m_tail = nvec + nused; } m_tail->iov_base = const_cast(buf); m_tail->iov_len = len; ++m_tail; } void append_copy(const char* buf, size_t len) { inner_buffer* const ib = &m_inner_buffer; if(ib->free < len) { size_t sz = m_chunk_size; if(sz < len) { sz = len; } chunk* c = static_cast(::malloc(sizeof(chunk) + sz)); if(!c) { throw std::bad_alloc(); } c->next = ib->head; ib->head = c; ib->free = sz; ib->ptr = reinterpret_cast(c) + sizeof(chunk); } char* m = ib->ptr; std::memcpy(m, buf, len); ib->free -= len; ib->ptr += len; if(m_tail != m_array && m == static_cast( const_cast((m_tail - 1)->iov_base) ) + (m_tail - 1)->iov_len) { (m_tail - 1)->iov_len += len; return; } else { append_ref( m, len); } } const struct iovec* vector() const { return m_array; } size_t vector_size() const { return m_tail - m_array; } void migrate(vrefbuffer* to) { size_t sz = m_chunk_size; chunk* empty = static_cast(::malloc(sizeof(chunk) + sz)); if(!empty) { throw std::bad_alloc(); } empty->next = nullptr; const size_t nused = m_tail - m_array; if(to->m_tail + nused < m_end) { const size_t tosize = to->m_tail - to->m_array; const size_t reqsize = nused + tosize; size_t nnext = (to->m_end - to->m_array) * 2; while(nnext < reqsize) { size_t tmp_nnext = nnext * 2; if (tmp_nnext <= nnext) { nnext = reqsize; break; } nnext = tmp_nnext; } iovec* nvec = static_cast(::realloc( to->m_array, sizeof(iovec)*nnext)); if(!nvec) { ::free(empty); throw std::bad_alloc(); } to->m_array = nvec; to->m_end = nvec + nnext; to->m_tail = nvec + tosize; } std::memcpy(to->m_tail, m_array, sizeof(iovec)*nused); to->m_tail += nused; m_tail = m_array; inner_buffer* const ib = &m_inner_buffer; inner_buffer* const toib = &to->m_inner_buffer; chunk* last = ib->head; while(last->next) { last = last->next; } last->next = toib->head; toib->head = ib->head; if(toib->free < ib->free) { toib->free = ib->free; toib->ptr = ib->ptr; } ib->head = empty; ib->free = sz; ib->ptr = reinterpret_cast(empty) + sizeof(chunk); } void clear() { chunk* c = m_inner_buffer.head->next; chunk* n; while(c) { n = c->next; ::free(c); c = n; } inner_buffer* const ib = &m_inner_buffer; c = ib->head; c->next = nullptr; ib->free = m_chunk_size; ib->ptr = reinterpret_cast(c) + sizeof(chunk); m_tail = m_array; } #if defined(MSGPACK_USE_CPP03) private: vrefbuffer(const vrefbuffer&); vrefbuffer& operator=(const vrefbuffer&); #else // defined(MSGPACK_USE_CPP03) vrefbuffer(const vrefbuffer&) = delete; vrefbuffer& operator=(const vrefbuffer&) = delete; #endif // defined(MSGPACK_USE_CPP03) private: iovec* m_tail; iovec* m_end; iovec* m_array; size_t m_ref_size; size_t m_chunk_size; inner_buffer m_inner_buffer; }; /// @cond } // MSGPACK_API_VERSION_NAMESPACE(v1) /// @endcond } // namespace msgpack #endif /* msgpack/vrefbuffer.hpp */