Skip to content

Instantly share code, notes, and snippets.

@oliverlee
Created December 5, 2020 19:12
Show Gist options
  • Save oliverlee/651c49840f7301a4148d0ee40a867ebb to your computer and use it in GitHub Desktop.
Save oliverlee/651c49840f7301a4148d0ee40a867ebb to your computer and use it in GitHub Desktop.
array backed streambuf
#include <array>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <new>
#include <sstream>
#include <vector>
namespace {
template <std::size_t N>
struct output_arraybuf : std::streambuf {
using base_type = std::streambuf;
using char_type = typename base_type::char_type;
using int_type = typename base_type::int_type;
static constexpr auto capacity = N;
output_arraybuf(std::ostream& os = std::cout) : output_sink_{os} {
// Reserve the last character for null-termination, which is necessary in case of overflow.
setp(buffer_.begin(), buffer_.end() - 1);
}
~output_arraybuf() {
std::cout << "[output_arraybuf::~output_arraybuf()]" << std::endl;
sync();
}
auto sink() -> std::ostream& { return output_sink_; }
protected:
auto overflow(int_type ch) -> int_type override {
static constexpr auto not_eof = char_type{};
commit();
sputc(ch);
return not_eof;
}
auto sync() -> int override {
sputc(0);
commit();
output_sink_.flush();
return 0;
}
private:
auto commit() -> void {
const auto n = pptr() - pbase();
output_sink_.write(pbase(), n);
pbump(-n);
}
std::array<base_type::char_type, N> buffer_ = {};
std::ostream& output_sink_;
};
struct output_stringbuf : std::stringbuf {
using base_type = std::stringbuf;
using char_type = typename base_type::char_type;
using int_type = typename base_type::int_type;
output_stringbuf(std::ostream& os = std::cout) : output_sink_{os} {}
~output_stringbuf() {
std::cout << "[output_stringbuf::~output_stringbuf()]" << std::endl;
sync();
}
auto sink() -> std::ostream& { return output_sink_; }
protected:
auto sync() -> int override {
const auto n = pptr() - pbase();
output_sink_.write(pbase(), n);
output_sink_.flush();
str("");
return 0;
}
private:
std::ostream& output_sink_;
};
template <class Prefix, class OutputStreambuf>
struct prefixed_output_buf : OutputStreambuf {
using base_type = OutputStreambuf;
using char_type = typename base_type::char_type;
using int_type = typename base_type::int_type;
static constexpr auto prefix_value = Prefix::value;
protected:
auto overflow(int_type ch) -> int_type override {
if (send_prefix_) {
this->sink().write(prefix_value, prefix_length);
send_prefix_ = false;
}
return base_type::overflow(ch);
}
auto sync() -> int override {
if (send_prefix_) {
this->sink().write(prefix_value, prefix_length);
}
send_prefix_ = true;
return base_type::sync();
}
private:
static constexpr auto length(const char* s) -> std::size_t {
return (*s == 0) ? 0 : length(s + 1) + 1;
}
static constexpr auto prefix_length = length(prefix_value);
bool send_prefix_ = true;
};
struct dummy_prefix {
static constexpr auto value = "[DUMMY]: ";
};
} // namespace
auto operator new(std::size_t size) -> void* {
if (auto ptr = std::malloc(size)) {
std::cout << "[operator::new(" << size << ") -> " << ptr << "]\n";
return ptr;
}
throw std::bad_alloc{};
}
auto operator delete(void* ptr) noexcept -> void {
std::cout << "[operator::delete(" << ptr << ")]\n";
std::free(ptr);
}
int main(int, char**) {
std::cout << "[info] creating stream" << std::endl;
// auto buf = output_stringbuf{};
// auto buf = prefixed_output_buf<dummy_prefix, output_stringbuf>{};
// auto buf = output_arraybuf<8>{};
auto buf = prefixed_output_buf<dummy_prefix, output_arraybuf<8>>{};
auto os = std::ostream{&buf};
std::cout << "[info] writing to stream" << std::endl;
os << "Here is a very long string that would normally result in allocation!" << std::endl;
std::cout << "[info] done!" << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment