@@ -76,10 +76,10 @@ namespace cppcore | |||
{ | |||
ret = it->second; | |||
if (add_numeric_value) | |||
ret += '(' + std::to_string(static_cast<base_type>(value)) + ')'; // TODO | |||
ret += '(' + std::to_string(static_cast<base_type>(value)) + ')'; | |||
} | |||
else | |||
ret = std::to_string(static_cast<base_type>(value)); // TODO | |||
ret = std::to_string(static_cast<base_type>(value)); | |||
return ret; | |||
} | |||
@@ -7,3 +7,4 @@ | |||
#include "misc/stream.h" | |||
#include "misc/type_helper.h" | |||
#include "misc/utils.h" | |||
#include "misc/vector_streambuf.h" |
@@ -35,6 +35,8 @@ namespace cppcore | |||
{ | |||
if (i % newline == 0) | |||
{ | |||
if (i > 0) | |||
os << endl; | |||
os << hex << setw(8) << setfill('0') << (offset + i); | |||
} | |||
@@ -54,7 +56,7 @@ namespace cppcore | |||
if (i % newline == 0) | |||
{ | |||
os << " " << s << "|" << endl; | |||
os << " " << s << "|"; | |||
s.clear(); | |||
l = 0; | |||
} | |||
@@ -0,0 +1,74 @@ | |||
#pragma once | |||
#include <vector> | |||
#include <streambuf> | |||
namespace cppcore | |||
{ | |||
template<typename T_data, typename T_char> | |||
struct basic_vector_streambuf | |||
: public std::basic_streambuf<T_char> | |||
{ | |||
private: | |||
static constexpr size_t min_grow = 32; // 32 Byte | |||
static constexpr size_t max_grow = 1024 * 1024; // 1 MByte | |||
public: | |||
using base_type = std::basic_streambuf<T_char>; | |||
using int_type = typename base_type::int_type; | |||
using char_type = typename base_type::char_type; | |||
using traits_type = typename base_type::traits_type; | |||
using data_type = T_data; | |||
using vector_type = std::vector<data_type>; | |||
static_assert( | |||
sizeof(data_type) >= sizeof(char_type), | |||
"Data type of the vector needs to be equal the char type!"); | |||
private: | |||
size_t _grow; //!< Number of bytes to grow on overflow (0 to grow dynamically) | |||
vector_type _buffer; //!< Buffer to store data | |||
public: | |||
/** | |||
* @brief Constructor. | |||
* | |||
* @param[in] p_grow Number of bytes to grow on overflow (0 to grow dynamically) | |||
*/ | |||
inline basic_vector_streambuf(size_t p_grow = 0); | |||
/** | |||
* @brief Get the data that was written to the vector. The data remains in the streambuf. | |||
*/ | |||
inline vector_type get() const; | |||
/** | |||
* @brief Set the vector to read data from. | |||
*/ | |||
template<typename T_vector> | |||
inline void set(T_vector&& v); | |||
/** | |||
* @brief Get the data that was written to the vector. The data is completely extracted from the steambuf. | |||
*/ | |||
inline vector_type extract(); | |||
protected: | |||
/** | |||
* @brief Buffer underflow. Try to get new data to read. | |||
*/ | |||
int_type underflow() override; | |||
/** | |||
* @brief Buffer overflow. Try to allocate more memory to write to. | |||
*/ | |||
int_type overflow(int_type ch) override; | |||
}; | |||
using vector_streambuf = basic_vector_streambuf<uint8_t, char>; | |||
} | |||
#include "vector_streambuf.inl" |
@@ -0,0 +1,97 @@ | |||
#pragma once | |||
#include "vector_streambuf.h" | |||
namespace cppcore | |||
{ | |||
template<typename T_data, typename T_char> | |||
basic_vector_streambuf<T_data, T_char> | |||
::basic_vector_streambuf(size_t p_grow) | |||
: _buffer () | |||
, _grow (p_grow) | |||
{ } | |||
template<typename T_data, typename T_char> | |||
typename basic_vector_streambuf<T_data, T_char>::vector_type | |||
basic_vector_streambuf<T_data, T_char> | |||
::get() const | |||
{ | |||
auto vec = _buffer; | |||
vec.resize(static_cast<size_t>(this->pptr() - this->pbase())); | |||
return vec; | |||
} | |||
template<typename T_data, typename T_char> | |||
template<typename T_vector> | |||
void basic_vector_streambuf<T_data, T_char> | |||
::set(T_vector&& v) | |||
{ | |||
_buffer = std::forward<T_vector>(v); | |||
auto beg = reinterpret_cast<char_type*>(&_buffer.front()); | |||
auto end = beg + _buffer.size(); | |||
this->setp(beg, end); | |||
this->setg(beg, beg, end); | |||
} | |||
template<typename T_data, typename T_char> | |||
typename basic_vector_streambuf<T_data, T_char>::vector_type | |||
basic_vector_streambuf<T_data, T_char> | |||
::extract() | |||
{ | |||
_buffer.resize(static_cast<size_t>(this->pptr() - this->pbase())); | |||
auto ret = std::move(_buffer); | |||
this->setp(nullptr, nullptr); | |||
this->setg(nullptr, nullptr, nullptr); | |||
return ret; | |||
} | |||
template<typename T_data, typename T_char> | |||
typename basic_vector_streambuf<T_data, T_char>::int_type | |||
basic_vector_streambuf<T_data, T_char> | |||
::underflow() | |||
{ | |||
auto gpos = static_cast<size_t>(this->gptr() - this->eback()); | |||
this->setg(this->pbase(), this->pbase() + gpos, this->pptr()); | |||
if (this->gptr() == this->pptr()) | |||
return traits_type::eof(); | |||
auto ret = static_cast<int_type>(*this->gptr()); | |||
return ret; | |||
} | |||
template<typename T_data, typename T_char> | |||
typename basic_vector_streambuf<T_data, T_char>::int_type | |||
basic_vector_streambuf<T_data, T_char> | |||
::overflow(int_type ch) | |||
{ | |||
if (ch != traits_type::eof()) | |||
{ | |||
size_t new_cap; | |||
if (_grow > 0) | |||
{ | |||
new_cap = _buffer.size() + _grow; | |||
} | |||
else | |||
{ | |||
new_cap = std::min(max_grow, std::max(2 * _buffer.size(), min_grow)); | |||
} | |||
_buffer.resize(new_cap); | |||
auto beg = reinterpret_cast<char_type*>(&_buffer.front()); | |||
auto end = beg + _buffer.size(); | |||
auto ppos = static_cast<int>(this->pptr() - this->pbase()); | |||
auto gpos = static_cast<size_t>(this->gptr() - this->eback()); | |||
this->setp(beg, end); | |||
this->setg(beg, beg + gpos, beg + ppos); | |||
this->pbump(ppos); | |||
auto p = this->pptr(); | |||
assert(this->pbase() <= p && p < this->epptr()); | |||
*p = static_cast<char_type>(ch); | |||
this->pbump(1); | |||
} | |||
return ch; | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
#include <gtest/gtest.h> | |||
#include <cppcore/misc/vector_streambuf.h> | |||
using namespace ::cppcore; | |||
using namespace ::testing; | |||
TEST(vector_streambuf_tests, simple_write) | |||
{ | |||
vector_streambuf buf; | |||
std::ostream os(&buf); | |||
os << "012345678901234567890123456789" | |||
<< "012345678901234567890123456789"; | |||
std::vector<uint8_t> expected({ | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
}); | |||
auto vec0 = buf.get(); | |||
auto vec1 = buf.extract(); | |||
EXPECT_EQ(vec0, expected); | |||
EXPECT_EQ(vec1, expected); | |||
EXPECT_NE(vec0.data(), vec1.data()); | |||
} | |||
TEST(vector_streambuf_tests, simple_read) | |||
{ | |||
vector_streambuf buf; | |||
std::istream is(&buf); | |||
buf.set(std::vector<uint8_t>({ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 })); | |||
std::string s; | |||
is >> s; | |||
EXPECT_EQ(s, "0123456789"); | |||
} | |||
TEST(vector_streambuf_tests, simple_write_read) | |||
{ | |||
vector_streambuf buf; | |||
std::iostream ios(&buf); | |||
std::string s; | |||
ios >> s; | |||
EXPECT_EQ(s, ""); | |||
ios.clear(); | |||
ios << "012345678901234567890123456789"; | |||
ios >> s; | |||
EXPECT_EQ(s, "012345678901234567890123456789"); | |||
ios.clear(); | |||
ios << "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||
ios >> s; | |||
EXPECT_EQ(s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); | |||
} | |||
TEST(vector_streambuf_tests, simple_write_extract_write_read) | |||
{ | |||
vector_streambuf buf; | |||
std::iostream ios(&buf); | |||
/* write */ | |||
std::string s; | |||
ios << "012345678901234567890123456789"; | |||
/* extract */ | |||
auto vec = buf.extract(); | |||
std::vector<uint8_t> expected({ | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, | |||
}); | |||
ASSERT_EQ(vec, expected); | |||
/* write */ | |||
ios << "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||
ASSERT_EQ(vec, expected); | |||
/* read */ | |||
ios >> s; | |||
ASSERT_EQ(s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); | |||
} |