293 lines
6.9 KiB
C++
293 lines
6.9 KiB
C++
|
//
|
||
|
// MessagePack for C++ zero-copy buffer implementation
|
||
|
//
|
||
|
// Copyright (C) 2008-2017 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_V1_VREFBUFFER_HPP
|
||
|
#define MSGPACK_V1_VREFBUFFER_HPP
|
||
|
|
||
|
#include "rpc/msgpack/v1/vrefbuffer_decl.hpp"
|
||
|
|
||
|
#include <stdexcept>
|
||
|
#include <algorithm>
|
||
|
|
||
|
#if defined(_MSC_VER)
|
||
|
// avoiding confliction std::max, std::min, and macro in windows.h
|
||
|
#ifndef NOMINMAX
|
||
|
#define NOMINMAX
|
||
|
#endif
|
||
|
#endif // defined(_MSC_VER)
|
||
|
|
||
|
#if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
|
||
|
#include <sys/uio.h>
|
||
|
#else
|
||
|
struct iovec {
|
||
|
void *iov_base;
|
||
|
size_t iov_len;
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
namespace clmdep_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 = MSGPACK_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 = MSGPACK_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 = MSGPACK_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 clmdep_msgpack
|
||
|
|
||
|
#endif // MSGPACK_V1_VREFBUFFER_HPP
|