diff --git a/include/cppcore/conversion/enum.inl b/include/cppcore/conversion/enum.inl index 15fd2ac..301938c 100644 --- a/include/cppcore/conversion/enum.inl +++ b/include/cppcore/conversion/enum.inl @@ -76,10 +76,10 @@ namespace cppcore { ret = it->second; if (add_numeric_value) - ret += '(' + std::to_string(static_cast(value)) + ')'; // TODO + ret += '(' + std::to_string(static_cast(value)) + ')'; } else - ret = std::to_string(static_cast(value)); // TODO + ret = std::to_string(static_cast(value)); return ret; } diff --git a/include/cppcore/misc.h b/include/cppcore/misc.h index db5d054..1c567e7 100644 --- a/include/cppcore/misc.h +++ b/include/cppcore/misc.h @@ -7,3 +7,4 @@ #include "misc/stream.h" #include "misc/type_helper.h" #include "misc/utils.h" +#include "misc/vector_streambuf.h" diff --git a/include/cppcore/misc/hexdump.inl b/include/cppcore/misc/hexdump.inl index 23afc77..74a873f 100644 --- a/include/cppcore/misc/hexdump.inl +++ b/include/cppcore/misc/hexdump.inl @@ -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; } diff --git a/include/cppcore/misc/vector_streambuf.h b/include/cppcore/misc/vector_streambuf.h new file mode 100644 index 0000000..1650306 --- /dev/null +++ b/include/cppcore/misc/vector_streambuf.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +namespace cppcore +{ + + template + struct basic_vector_streambuf + : public std::basic_streambuf + { + 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; + 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; + + 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 + 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; + +} + +#include "vector_streambuf.inl" diff --git a/include/cppcore/misc/vector_streambuf.inl b/include/cppcore/misc/vector_streambuf.inl new file mode 100644 index 0000000..19890cf --- /dev/null +++ b/include/cppcore/misc/vector_streambuf.inl @@ -0,0 +1,97 @@ +#pragma once + +#include "vector_streambuf.h" + +namespace cppcore +{ + + template + basic_vector_streambuf + ::basic_vector_streambuf(size_t p_grow) + : _buffer () + , _grow (p_grow) + { } + + template + typename basic_vector_streambuf::vector_type + basic_vector_streambuf + ::get() const + { + auto vec = _buffer; + vec.resize(static_cast(this->pptr() - this->pbase())); + return vec; + } + + + template + template + void basic_vector_streambuf + ::set(T_vector&& v) + { + _buffer = std::forward(v); + auto beg = reinterpret_cast(&_buffer.front()); + auto end = beg + _buffer.size(); + this->setp(beg, end); + this->setg(beg, beg, end); + } + + template + typename basic_vector_streambuf::vector_type + basic_vector_streambuf + ::extract() + { + _buffer.resize(static_cast(this->pptr() - this->pbase())); + auto ret = std::move(_buffer); + this->setp(nullptr, nullptr); + this->setg(nullptr, nullptr, nullptr); + return ret; + } + + template + typename basic_vector_streambuf::int_type + basic_vector_streambuf + ::underflow() + { + auto gpos = static_cast(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(*this->gptr()); + return ret; + } + + template + typename basic_vector_streambuf::int_type + basic_vector_streambuf + ::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(&_buffer.front()); + auto end = beg + _buffer.size(); + auto ppos = static_cast(this->pptr() - this->pbase()); + auto gpos = static_cast(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(ch); + this->pbump(1); + } + return ch; + } + +} diff --git a/test/cppcore/misc/vector_streambuf_tests.cpp b/test/cppcore/misc/vector_streambuf_tests.cpp new file mode 100644 index 0000000..13fb85d --- /dev/null +++ b/test/cppcore/misc/vector_streambuf_tests.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace ::cppcore; +using namespace ::testing; + +TEST(vector_streambuf_tests, simple_write) +{ + vector_streambuf buf; + std::ostream os(&buf); + + os << "012345678901234567890123456789" + << "012345678901234567890123456789"; + + std::vector 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({ 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 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"); +}