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