| @@ -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"); | |||||
| } | |||||