| // |
| // 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 <stdexcept> |
| |
| #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 <sys/uio.h> |
| #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<iovec*>(::malloc( |
| sizeof(iovec) * nfirst)); |
| if(!array) { |
| throw std::bad_alloc(); |
| } |
| |
| m_tail = array; |
| m_end = array + nfirst; |
| m_array = array; |
| |
| chunk* c = static_cast<chunk*>(::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<char*>(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<iovec*>(::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<char*>(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<chunk*>(::malloc(sizeof(chunk) + sz)); |
| if(!c) { |
| throw std::bad_alloc(); |
| } |
| |
| c->next = ib->head; |
| ib->head = c; |
| ib->free = sz; |
| ib->ptr = reinterpret_cast<char*>(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 char*>( |
| const_cast<const void *>((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<chunk*>(::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<iovec*>(::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<char*>(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<char*>(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 */ |