Commit dbc17e5e authored by Dominik Charousset's avatar Dominik Charousset

enhanced interface of `util::buffer`

this patch added previously missing member functions to the buffer class
and improves the overall documentation of the class; furthermore, the class
exposes only `void*` pointers to make clear the buffer's content should not
be accessed as string
parent c5e7363f
......@@ -45,10 +45,10 @@ class binary_deserializer : public deserializer {
public:
binary_deserializer(const char* buf, size_t buf_size,
binary_deserializer(const void* buf, size_t buf_size,
actor_addressing* addressing = nullptr);
binary_deserializer(const char* begin, const char* end,
binary_deserializer(const void* begin, const void* m_end,
actor_addressing* addressing = nullptr);
std::string seek_object();
......@@ -65,8 +65,8 @@ class binary_deserializer : public deserializer {
private:
const char* pos;
const char* end;
const void* m_pos;
const void* m_end;
};
......
......@@ -51,12 +51,21 @@ enum buffer_write_policy {
*/
class buffer {
friend class deserializer;
friend class cppa::deserializer;
public:
/**
* @brief Creates an empty buffer.
*/
buffer();
/**
* @brief Creates a buffer holding at most @p max_buffer_size bytes
* that allocates memory in steps of @p chunk_size bytes.
* @note The default chunk_size used by other constuctors is 512 bytes.
* @note The default maximum buffer size is 16MB.
*/
buffer(size_t chunk_size, size_t max_buffer_size);
buffer(buffer&& other);
......@@ -70,87 +79,146 @@ class buffer {
~buffer();
/**
* @brief Clears the buffer's content.
* @brief Makes sure the buffer can at least write @p num_bytes additional
* bytes, increasing the final size if needed.
*/
inline void clear() {
m_written = 0;
}
void acquire(size_t num_bytes);
/**
* @brief Clears the buffer's content and sets the new final size to
* @p new_final_size.
* @brief Erases the first @p num_bytes bytes from the buffer.
*/
void reset(size_t new_final_size = 0);
void erase_leading(size_t num_bytes);
/**
* @brief Makes sure the buffer can at least write @p num_bytes additional
* bytes and increases the final size if needed.
* @brief Erases the last @p num_bytes bytes from the buffer.
*/
void acquire(size_t num_bytes);
void erase_trailing(size_t num_bytes);
void erase_leading(size_t num_bytes);
/**
* @brief Clears the buffer's content.
*/
inline void clear();
void erase_trailing(size_t num_bytes);
/**
* @brief Returns the size of the buffer's content in bytes.
*/
inline size_t size() const;
inline size_t size() const {
return m_written;
}
/**
* @brief Returns the configured final size of this buffer. This value
* can be changed by using {@link final_size()} and controls how much
* bytes are consumed when using {@link append_from()}.
*/
inline size_t final_size() const;
inline size_t final_size() const {
return m_final_size;
}
/**
* @brief Sets the buffer's final size to @p new_value.
* @throws std::invalid_argument if <tt>new_value > maximum_size()</tt>.
*/
void final_size(size_t new_value);
inline size_t remaining() const {
return m_final_size - m_written;
}
/**
* @brief Returns the difference between {@link final_size()}
* and {@link size()}.
*/
inline size_t remaining() const;
inline const char* data() const {
return m_data;
}
/**
* @brief Returns the buffer's content.
*/
inline const void* data() const;
inline char* data() {
return m_data;
}
/**
* @brief Returns the buffer's content.
*/
inline void* data();
inline bool full() const {
return remaining() == 0;
}
/**
* @brief Returns the buffer's content offset by @p offset.
*/
inline void* offset_data(size_t offset);
inline bool empty() const {
return size() == 0;
}
/**
* @brief Returns the buffer's content offset by @p offset.
*/
inline const void* offset_data(size_t offset) const;
inline size_t maximum_size() const {
return m_max_buffer_size;
}
/**
* @brief Checks whether <tt>size() == remaining()</tt>.
*/
inline bool full() const;
void write(size_t num_bytes, const void* data, buffer_write_policy wp);
/**
* @brief Checks whether <tt>size() == 0</tt>.
*/
inline bool empty() const;
void write(const buffer& other, buffer_write_policy wp);
/**
* @brief Returns the maximum size of this buffer.
*/
inline size_t maximum_size() const;
const char* begin() const { return m_data; }
/**
* @brief Sets the maximum size of this buffer.
*/
inline void maximum_size(size_t new_value);
const char* end() const { return m_data + m_written; }
/**
* @brief Writes @p num_bytes from @p data to this buffer.
* @note The configured final size is ignored
* if <tt>wp == grow_if_needed</tt>.
*/
void write(size_t num_bytes, const void* data, buffer_write_policy wp = grow_if_needed);
/**
* @brief Writes the content of @p other to this buffer.
* @note The configured final size is ignored
* if <tt>wp == grow_if_needed</tt>.
*/
void write(const buffer& other, buffer_write_policy wp = grow_if_needed);
/**
* @brief Writes the content of @p other to this buffer.
* @note The configured final size is ignored
* if <tt>wp == grow_if_needed</tt>.
*/
void write(buffer&& other, buffer_write_policy wp = grow_if_needed);
/**
* @brief Appends up to @p remaining() bytes from @p istream to the buffer.
*/
void append_from(network::input_stream* istream);
/**
* @brief Returns the number of bytes used as minimal allocation unit.
* @note The default chunk size is 512 bytes.
*/
inline size_t chunk_size() const;
/**
* @brief Sets the number of bytes used as minimal allocation unit.
* @note The default chunk size is 512 bytes.
*/
inline void chunk_size(size_t new_value);
/**
* @brief Returns the number of currently allocated bytes.
*/
inline size_t allocated() const;
private:
// pointer to the current write position
inline char* wr_ptr() {
return m_data + m_written;
}
inline char* wr_ptr();
// called whenever bytes are written to m_data
inline void inc_size(size_t value);
inline void inc_size(size_t value) {
m_written += value;
}
// called from erase_* functions to adjust buffer size
inline void dec_size(size_t value);
inline void dec_size(size_t value) {
m_written -= value;
}
// adjusts alloc_size according to m_chunk_size
inline size_t adjust(size_t alloc_size) const;
char* m_data;
size_t m_written;
......@@ -161,6 +229,89 @@ class buffer {
};
/******************************************************************************
* inline and template member function implementations *
******************************************************************************/
inline void buffer::clear() {
m_written = 0;
}
inline size_t buffer::size() const {
return m_written;
}
inline size_t buffer::final_size() const {
return m_final_size;
}
inline size_t buffer::remaining() const {
return m_final_size - m_written;
}
inline const void* buffer::data() const {
return m_data;
}
inline void* buffer::data() {
return m_data;
}
inline void* buffer::offset_data(size_t offset) {
return m_data + offset;
}
inline const void* buffer::offset_data(size_t offset) const {
return m_data + offset;
}
inline bool buffer::full() const {
return remaining() == 0;
}
inline bool buffer::empty() const {
return size() == 0;
}
inline size_t buffer::maximum_size() const {
return m_max_buffer_size;
}
inline void buffer::maximum_size(size_t new_value) {
m_max_buffer_size = new_value;
}
inline char* buffer::wr_ptr() {
return m_data + m_written;
}
inline void buffer::inc_size(size_t value) {
m_written += value;
}
inline void buffer::dec_size(size_t value) {
m_written -= value;
}
inline size_t buffer::chunk_size() const {
return m_chunk_size;
}
inline void buffer::chunk_size(size_t new_value) {
m_chunk_size = new_value;
}
inline size_t buffer::allocated() const {
return m_allocated;
}
inline size_t buffer::adjust(size_t alloc_size) const {
auto remainder = (alloc_size % m_chunk_size);
return (remainder == 0) ? alloc_size
: (alloc_size - remainder) + m_chunk_size;
}
} } // namespace cppa::detail
#endif // CPPA_BUFFER_HPP
......@@ -48,36 +48,44 @@ namespace cppa {
namespace {
typedef const char* iterator;
typedef const void* pointer;
inline void range_check(iterator begin, iterator end, size_t read_size) {
if ((begin + read_size) > end) {
const char* as_char_pointer(pointer ptr) {
return reinterpret_cast<const char*>(ptr);
}
pointer advanced(pointer ptr, size_t num_bytes) {
return reinterpret_cast<const char*>(ptr) + num_bytes;
}
inline void range_check(pointer begin, pointer end, size_t read_size) {
if (advanced(begin, read_size) > end) {
CPPA_LOGF(CPPA_ERROR, self, "range_check failed");
throw out_of_range("binary_deserializer::read_range()");
}
}
template<typename T>
iterator read_range(iterator begin, iterator end, T& storage,
typename enable_if<is_integral<T>::value>::type* = 0) {
pointer read_range(pointer begin, pointer end, T& storage,
typename enable_if<is_integral<T>::value>::type* = 0) {
range_check(begin, end, sizeof(T));
memcpy(&storage, begin, sizeof(T));
return begin + sizeof(T);
return advanced(begin, sizeof(T));
}
iterator read_range(iterator begin, iterator end, string& storage) {
pointer read_range(pointer begin, pointer end, string& storage) {
uint32_t str_size;
begin = read_range(begin, end, str_size);
range_check(begin, end, str_size);
storage.clear();
storage.reserve(str_size);
iterator cpy_end = begin + str_size;
copy(begin, cpy_end, back_inserter(storage));
return begin + str_size;
pointer cpy_end = advanced(begin, str_size);
copy(as_char_pointer(begin), as_char_pointer(cpy_end), back_inserter(storage));
return advanced(begin, str_size);
}
template<typename CharType, typename StringType>
iterator read_unicode_string(iterator begin, iterator end, StringType& str) {
pointer read_unicode_string(pointer begin, pointer end, StringType& str) {
uint32_t str_size;
begin = read_range(begin, end, str_size);
str.reserve(str_size);
......@@ -91,7 +99,7 @@ iterator read_unicode_string(iterator begin, iterator end, StringType& str) {
// @returns the next iterator position
template<typename T>
iterator read_range(iterator begin, iterator end, T& value,
pointer read_range(pointer begin, pointer end, T& value,
typename enable_if<is_floating_point<T>::value>::type* = 0) {
// floating points are written as strings
string str;
......@@ -101,13 +109,13 @@ iterator read_range(iterator begin, iterator end, T& value,
return result;
}
iterator read_range(iterator begin, iterator end, u16string& storage) {
pointer read_range(pointer begin, pointer end, u16string& storage) {
// char16_t is guaranteed to has *at least* 16 bytes,
// but not to have *exactly* 16 bytes; thus use uint16_t
return read_unicode_string<uint16_t>(begin, end, storage);
}
iterator read_range(iterator begin, iterator end, u32string& storage) {
pointer read_range(pointer begin, pointer end, u32string& storage) {
// char32_t is guaranteed to has *at least* 32 bytes,
// but not to have *exactly* 32 bytes; thus use uint32_t
return read_unicode_string<uint32_t>(begin, end, storage);
......@@ -115,10 +123,10 @@ iterator read_range(iterator begin, iterator end, u32string& storage) {
struct pt_reader {
iterator begin;
iterator end;
pointer begin;
pointer end;
pt_reader(iterator bbegin, iterator bend) : begin(bbegin), end(bend) { }
pt_reader(pointer bbegin, pointer bend) : begin(bbegin), end(bend) { }
template<typename T>
inline void operator()(T& value) {
......@@ -129,23 +137,23 @@ struct pt_reader {
} // namespace <anonmyous>
binary_deserializer::binary_deserializer(const char* buf, size_t buf_size,
binary_deserializer::binary_deserializer(const void* buf, size_t buf_size,
actor_addressing* addressing)
: super(addressing), pos(buf), end(buf + buf_size) { }
: super(addressing), m_pos(buf), m_end(advanced(buf, buf_size)) { }
binary_deserializer::binary_deserializer(const char* bbegin, const char* bend,
binary_deserializer::binary_deserializer(const void* bbegin, const void* bend,
actor_addressing* addressing)
: super(addressing), pos(bbegin), end(bend) { }
: super(addressing), m_pos(bbegin), m_end(bend) { }
string binary_deserializer::seek_object() {
string result;
pos = read_range(pos, end, result);
m_pos = read_range(m_pos, m_end, result);
return result;
}
string binary_deserializer::peek_object() {
string result;
read_range(pos, end, result);
read_range(m_pos, m_end, result);
return result;
}
......@@ -158,7 +166,7 @@ size_t binary_deserializer::begin_sequence() {
static_assert(sizeof(size_t) >= sizeof(uint32_t),
"sizeof(size_t) < sizeof(uint32_t)");
uint32_t result;
pos = read_range(pos, end, result);
m_pos = read_range(m_pos, m_end, result);
return static_cast<size_t>(result);
}
......@@ -166,9 +174,9 @@ void binary_deserializer::end_sequence() { }
primitive_variant binary_deserializer::read_value(primitive_type ptype) {
primitive_variant val(ptype);
pt_reader ptr(pos, end);
pt_reader ptr(m_pos, m_end);
val.apply(ptr);
pos = ptr.begin;
m_pos = ptr.begin;
return val;
}
......@@ -182,9 +190,9 @@ void binary_deserializer::read_tuple(size_t size,
}
void binary_deserializer::read_raw(size_t num_bytes, void* storage) {
range_check(pos, end, num_bytes);
memcpy(storage, pos, num_bytes);
pos += num_bytes;
range_check(m_pos, m_end, num_bytes);
memcpy(storage, m_pos, num_bytes);
m_pos = advanced(m_pos, num_bytes);
}
} // namespace cppa
......@@ -87,56 +87,47 @@ buffer::~buffer() {
delete[] m_data;
}
void buffer::reset(size_t new_final_size) {
if (new_final_size > m_max_buffer_size) {
m_written = 0;
m_allocated = 0;
m_final_size = 0;
delete[] m_data;
m_data = nullptr;
throw std::ios_base::failure("maximum buffer size exceeded");
void buffer::final_size(size_t new_value) {
if (new_value > m_max_buffer_size) {
throw std::invalid_argument("new_value > maximum_size()");
}
m_written = 0;
m_final_size = new_final_size;
if (new_final_size > m_allocated) {
auto remainder = (new_final_size % m_chunk_size);
if (remainder == 0) {
m_allocated = new_final_size;
}
else {
m_allocated = (new_final_size - remainder) + m_chunk_size;
}
m_final_size = new_value;
if (new_value > m_allocated) {
auto remainder = (new_value % m_chunk_size);
if (remainder == 0) m_allocated = new_value;
else m_allocated = (new_value - remainder) + m_chunk_size;
if (remainder == 0) m_allocated = new_value;
else m_allocated = (new_value - remainder) + m_chunk_size;
delete[] m_data;
m_data = new char[m_allocated];
}
}
void buffer::acquire(size_t num_bytes) {
if (!m_data) {
reset(num_bytes);
if (m_data == nullptr) {
if (num_bytes > m_final_size) m_final_size = num_bytes;
m_allocated = adjust(m_final_size);
m_data = new char[m_allocated];
}
else if (remaining() < num_bytes) {
m_final_size += num_bytes - remaining();
if ((m_allocated - m_written) < num_bytes) {
auto remainder = (m_final_size % m_chunk_size);
if (remainder == 0) {
m_allocated = m_final_size;
}
else {
m_allocated = (m_final_size - remainder) + m_chunk_size;
}
auto old_data = m_data;
m_data = new char[m_allocated];
memcpy(m_data, old_data, m_written);
delete[] old_data;
}
if (remaining() >= num_bytes) return; // nothing to do
m_final_size += num_bytes - remaining();
if ((m_allocated - m_written) < num_bytes) {
auto old_data = m_data;
m_allocated = adjust(m_final_size);
m_data = new char[m_allocated];
memcpy(m_data, old_data, m_written);
delete[] old_data;
}
}
void buffer::erase_leading(size_t num_bytes) {
if (num_bytes >= size()) {
clear();
}
if (num_bytes >= size()) clear();
else {
memmove(m_data, m_data + num_bytes, size() - num_bytes);
dec_size(num_bytes);
......@@ -144,12 +135,8 @@ void buffer::erase_leading(size_t num_bytes) {
}
void buffer::erase_trailing(size_t num_bytes) {
if (num_bytes >= size()) {
clear();
}
else {
dec_size(num_bytes);
}
if (num_bytes >= size()) clear();
else dec_size(num_bytes);
}
void buffer::write(size_t num_bytes, const void* data, buffer_write_policy wp) {
......@@ -167,6 +154,14 @@ void buffer::write(const buffer& other, buffer_write_policy wp) {
write(other.size(), other.data(), wp);
}
void buffer::write(buffer&& other, buffer_write_policy wp) {
if (empty() && (wp == grow_if_needed || other.size() <= remaining())) {
*this = std::move(other);
}
else write(other.size(), other.data(), wp);
other.clear();
}
void buffer::append_from(network::input_stream* istream) {
CPPA_REQUIRE(remaining() > 0);
auto num_bytes = istream->read_some(wr_ptr(), remaining());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment