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 { ...@@ -45,10 +45,10 @@ class binary_deserializer : public deserializer {
public: public:
binary_deserializer(const char* buf, size_t buf_size, binary_deserializer(const void* buf, size_t buf_size,
actor_addressing* addressing = nullptr); 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); actor_addressing* addressing = nullptr);
std::string seek_object(); std::string seek_object();
...@@ -65,8 +65,8 @@ class binary_deserializer : public deserializer { ...@@ -65,8 +65,8 @@ class binary_deserializer : public deserializer {
private: private:
const char* pos; const void* m_pos;
const char* end; const void* m_end;
}; };
......
...@@ -51,12 +51,21 @@ enum buffer_write_policy { ...@@ -51,12 +51,21 @@ enum buffer_write_policy {
*/ */
class buffer { class buffer {
friend class deserializer; friend class cppa::deserializer;
public: public:
/**
* @brief Creates an empty buffer.
*/
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(size_t chunk_size, size_t max_buffer_size);
buffer(buffer&& other); buffer(buffer&& other);
...@@ -70,87 +79,146 @@ class buffer { ...@@ -70,87 +79,146 @@ class buffer {
~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() { void acquire(size_t num_bytes);
m_written = 0;
}
/** /**
* @brief Clears the buffer's content and sets the new final size to * @brief Erases the first @p num_bytes bytes from the buffer.
* @p new_final_size.
*/ */
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 * @brief Erases the last @p num_bytes bytes from the buffer.
* bytes and increases the final size if needed.
*/ */
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. * @brief Appends up to @p remaining() bytes from @p istream to the buffer.
*/ */
void append_from(network::input_stream* istream); 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: private:
// pointer to the current write position // pointer to the current write position
inline char* wr_ptr() { inline char* wr_ptr();
return m_data + m_written;
} // called whenever bytes are written to m_data
inline void inc_size(size_t value);
inline void inc_size(size_t value) { // called from erase_* functions to adjust buffer size
m_written += value; inline void dec_size(size_t value);
}
inline void dec_size(size_t value) { // adjusts alloc_size according to m_chunk_size
m_written -= value; inline size_t adjust(size_t alloc_size) const;
}
char* m_data; char* m_data;
size_t m_written; size_t m_written;
...@@ -161,6 +229,89 @@ class buffer { ...@@ -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 } } // namespace cppa::detail
#endif // CPPA_BUFFER_HPP #endif // CPPA_BUFFER_HPP
...@@ -48,36 +48,44 @@ namespace cppa { ...@@ -48,36 +48,44 @@ namespace cppa {
namespace { namespace {
typedef const char* iterator; typedef const void* pointer;
inline void range_check(iterator begin, iterator end, size_t read_size) { const char* as_char_pointer(pointer ptr) {
if ((begin + read_size) > end) { 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"); CPPA_LOGF(CPPA_ERROR, self, "range_check failed");
throw out_of_range("binary_deserializer::read_range()"); throw out_of_range("binary_deserializer::read_range()");
} }
} }
template<typename T> template<typename T>
iterator read_range(iterator begin, iterator end, T& storage, pointer read_range(pointer begin, pointer end, T& storage,
typename enable_if<is_integral<T>::value>::type* = 0) { typename enable_if<is_integral<T>::value>::type* = 0) {
range_check(begin, end, sizeof(T)); range_check(begin, end, sizeof(T));
memcpy(&storage, begin, 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; uint32_t str_size;
begin = read_range(begin, end, str_size); begin = read_range(begin, end, str_size);
range_check(begin, end, str_size); range_check(begin, end, str_size);
storage.clear(); storage.clear();
storage.reserve(str_size); storage.reserve(str_size);
iterator cpy_end = begin + str_size; pointer cpy_end = advanced(begin, str_size);
copy(begin, cpy_end, back_inserter(storage)); copy(as_char_pointer(begin), as_char_pointer(cpy_end), back_inserter(storage));
return begin + str_size; return advanced(begin, str_size);
} }
template<typename CharType, typename StringType> 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; uint32_t str_size;
begin = read_range(begin, end, str_size); begin = read_range(begin, end, str_size);
str.reserve(str_size); str.reserve(str_size);
...@@ -91,7 +99,7 @@ iterator read_unicode_string(iterator begin, iterator end, StringType& str) { ...@@ -91,7 +99,7 @@ iterator read_unicode_string(iterator begin, iterator end, StringType& str) {
// @returns the next iterator position // @returns the next iterator position
template<typename T> 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) { typename enable_if<is_floating_point<T>::value>::type* = 0) {
// floating points are written as strings // floating points are written as strings
string str; string str;
...@@ -101,13 +109,13 @@ iterator read_range(iterator begin, iterator end, T& value, ...@@ -101,13 +109,13 @@ iterator read_range(iterator begin, iterator end, T& value,
return result; 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, // char16_t is guaranteed to has *at least* 16 bytes,
// but not to have *exactly* 16 bytes; thus use uint16_t // but not to have *exactly* 16 bytes; thus use uint16_t
return read_unicode_string<uint16_t>(begin, end, storage); 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, // char32_t is guaranteed to has *at least* 32 bytes,
// but not to have *exactly* 32 bytes; thus use uint32_t // but not to have *exactly* 32 bytes; thus use uint32_t
return read_unicode_string<uint32_t>(begin, end, storage); return read_unicode_string<uint32_t>(begin, end, storage);
...@@ -115,10 +123,10 @@ iterator read_range(iterator begin, iterator end, u32string& storage) { ...@@ -115,10 +123,10 @@ iterator read_range(iterator begin, iterator end, u32string& storage) {
struct pt_reader { struct pt_reader {
iterator begin; pointer begin;
iterator end; 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> template<typename T>
inline void operator()(T& value) { inline void operator()(T& value) {
...@@ -129,23 +137,23 @@ struct pt_reader { ...@@ -129,23 +137,23 @@ struct pt_reader {
} // namespace <anonmyous> } // 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) 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) actor_addressing* addressing)
: super(addressing), pos(bbegin), end(bend) { } : super(addressing), m_pos(bbegin), m_end(bend) { }
string binary_deserializer::seek_object() { string binary_deserializer::seek_object() {
string result; string result;
pos = read_range(pos, end, result); m_pos = read_range(m_pos, m_end, result);
return result; return result;
} }
string binary_deserializer::peek_object() { string binary_deserializer::peek_object() {
string result; string result;
read_range(pos, end, result); read_range(m_pos, m_end, result);
return result; return result;
} }
...@@ -158,7 +166,7 @@ size_t binary_deserializer::begin_sequence() { ...@@ -158,7 +166,7 @@ size_t binary_deserializer::begin_sequence() {
static_assert(sizeof(size_t) >= sizeof(uint32_t), static_assert(sizeof(size_t) >= sizeof(uint32_t),
"sizeof(size_t) < sizeof(uint32_t)"); "sizeof(size_t) < sizeof(uint32_t)");
uint32_t result; uint32_t result;
pos = read_range(pos, end, result); m_pos = read_range(m_pos, m_end, result);
return static_cast<size_t>(result); return static_cast<size_t>(result);
} }
...@@ -166,9 +174,9 @@ void binary_deserializer::end_sequence() { } ...@@ -166,9 +174,9 @@ void binary_deserializer::end_sequence() { }
primitive_variant binary_deserializer::read_value(primitive_type ptype) { primitive_variant binary_deserializer::read_value(primitive_type ptype) {
primitive_variant val(ptype); primitive_variant val(ptype);
pt_reader ptr(pos, end); pt_reader ptr(m_pos, m_end);
val.apply(ptr); val.apply(ptr);
pos = ptr.begin; m_pos = ptr.begin;
return val; return val;
} }
...@@ -182,9 +190,9 @@ void binary_deserializer::read_tuple(size_t size, ...@@ -182,9 +190,9 @@ void binary_deserializer::read_tuple(size_t size,
} }
void binary_deserializer::read_raw(size_t num_bytes, void* storage) { void binary_deserializer::read_raw(size_t num_bytes, void* storage) {
range_check(pos, end, num_bytes); range_check(m_pos, m_end, num_bytes);
memcpy(storage, pos, num_bytes); memcpy(storage, m_pos, num_bytes);
pos += num_bytes; m_pos = advanced(m_pos, num_bytes);
} }
} // namespace cppa } // namespace cppa
...@@ -87,56 +87,47 @@ buffer::~buffer() { ...@@ -87,56 +87,47 @@ buffer::~buffer() {
delete[] m_data; delete[] m_data;
} }
void buffer::reset(size_t new_final_size) { void buffer::final_size(size_t new_value) {
if (new_final_size > m_max_buffer_size) { if (new_value > m_max_buffer_size) {
m_written = 0; throw std::invalid_argument("new_value > maximum_size()");
m_allocated = 0;
m_final_size = 0;
delete[] m_data;
m_data = nullptr;
throw std::ios_base::failure("maximum buffer size exceeded");
} }
m_written = 0; m_final_size = new_value;
m_final_size = new_final_size;
if (new_final_size > m_allocated) {
auto remainder = (new_final_size % m_chunk_size); if (new_value > m_allocated) {
if (remainder == 0) { auto remainder = (new_value % m_chunk_size);
m_allocated = new_final_size; if (remainder == 0) m_allocated = new_value;
} else m_allocated = (new_value - remainder) + m_chunk_size;
else {
m_allocated = (new_final_size - remainder) + m_chunk_size; if (remainder == 0) m_allocated = new_value;
} else m_allocated = (new_value - remainder) + m_chunk_size;
delete[] m_data; delete[] m_data;
m_data = new char[m_allocated]; m_data = new char[m_allocated];
} }
} }
void buffer::acquire(size_t num_bytes) { void buffer::acquire(size_t num_bytes) {
if (!m_data) { if (m_data == nullptr) {
reset(num_bytes); 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) { if (remaining() >= num_bytes) return; // nothing to do
m_final_size += num_bytes - remaining(); m_final_size += num_bytes - remaining();
if ((m_allocated - m_written) < num_bytes) { if ((m_allocated - m_written) < num_bytes) {
auto remainder = (m_final_size % m_chunk_size); auto old_data = m_data;
if (remainder == 0) { m_allocated = adjust(m_final_size);
m_allocated = m_final_size; m_data = new char[m_allocated];
} memcpy(m_data, old_data, m_written);
else { delete[] old_data;
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;
}
} }
} }
void buffer::erase_leading(size_t num_bytes) { void buffer::erase_leading(size_t num_bytes) {
if (num_bytes >= size()) { if (num_bytes >= size()) clear();
clear();
}
else { else {
memmove(m_data, m_data + num_bytes, size() - num_bytes); memmove(m_data, m_data + num_bytes, size() - num_bytes);
dec_size(num_bytes); dec_size(num_bytes);
...@@ -144,12 +135,8 @@ void buffer::erase_leading(size_t num_bytes) { ...@@ -144,12 +135,8 @@ void buffer::erase_leading(size_t num_bytes) {
} }
void buffer::erase_trailing(size_t num_bytes) { void buffer::erase_trailing(size_t num_bytes) {
if (num_bytes >= size()) { if (num_bytes >= size()) clear();
clear(); else dec_size(num_bytes);
}
else {
dec_size(num_bytes);
}
} }
void buffer::write(size_t num_bytes, const void* data, buffer_write_policy wp) { 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) { ...@@ -167,6 +154,14 @@ void buffer::write(const buffer& other, buffer_write_policy wp) {
write(other.size(), other.data(), 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) { void buffer::append_from(network::input_stream* istream) {
CPPA_REQUIRE(remaining() > 0); CPPA_REQUIRE(remaining() > 0);
auto num_bytes = istream->read_some(wr_ptr(), remaining()); 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