@@ -76,10 +76,10 @@ namespace cppcore | |||||
{ | { | ||||
ret = it->second; | ret = it->second; | ||||
if (add_numeric_value) | if (add_numeric_value) | ||||
ret += '(' + std::to_string(static_cast<base_type>(value)) + ')'; // TODO | |||||
ret += '(' + std::to_string(static_cast<base_type>(value)) + ')'; | |||||
} | } | ||||
else | else | ||||
ret = std::to_string(static_cast<base_type>(value)); // TODO | |||||
ret = std::to_string(static_cast<base_type>(value)); | |||||
return ret; | return ret; | ||||
} | } | ||||
@@ -7,3 +7,4 @@ | |||||
#include "misc/stream.h" | #include "misc/stream.h" | ||||
#include "misc/type_helper.h" | #include "misc/type_helper.h" | ||||
#include "misc/utils.h" | #include "misc/utils.h" | ||||
#include "misc/vector_streambuf.h" |
@@ -35,6 +35,8 @@ namespace cppcore | |||||
{ | { | ||||
if (i % newline == 0) | if (i % newline == 0) | ||||
{ | { | ||||
if (i > 0) | |||||
os << endl; | |||||
os << hex << setw(8) << setfill('0') << (offset + i); | os << hex << setw(8) << setfill('0') << (offset + i); | ||||
} | } | ||||
@@ -54,7 +56,7 @@ namespace cppcore | |||||
if (i % newline == 0) | if (i % newline == 0) | ||||
{ | { | ||||
os << " " << s << "|" << endl; | |||||
os << " " << s << "|"; | |||||
s.clear(); | s.clear(); | ||||
l = 0; | 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"); | |||||
} |