Browse Source

* Implemented stream buffer to read from/write to std::vector

master
bergmann 4 years ago
parent
commit
637e33a7c6
6 changed files with 266 additions and 3 deletions
  1. +2
    -2
      include/cppcore/conversion/enum.inl
  2. +1
    -0
      include/cppcore/misc.h
  3. +3
    -1
      include/cppcore/misc/hexdump.inl
  4. +74
    -0
      include/cppcore/misc/vector_streambuf.h
  5. +97
    -0
      include/cppcore/misc/vector_streambuf.inl
  6. +89
    -0
      test/cppcore/misc/vector_streambuf_tests.cpp

+ 2
- 2
include/cppcore/conversion/enum.inl View File

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



+ 1
- 0
include/cppcore/misc.h View File

@@ -7,3 +7,4 @@
#include "misc/stream.h"
#include "misc/type_helper.h"
#include "misc/utils.h"
#include "misc/vector_streambuf.h"

+ 3
- 1
include/cppcore/misc/hexdump.inl View File

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


+ 74
- 0
include/cppcore/misc/vector_streambuf.h View File

@@ -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"

+ 97
- 0
include/cppcore/misc/vector_streambuf.inl View File

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

}

+ 89
- 0
test/cppcore/misc/vector_streambuf_tests.cpp View File

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

Loading…
Cancel
Save