#include "buffer.hh" #include #include #include namespace { class Round : public Buffer { public: explicit Round(size_t size) : data_(std::make_unique(size)), size_(size) {} bool empty() const override { return rptr_ == wptr_ && !full_; } bool full() const override { return rptr_ == wptr_ && full_; } void clear() override { rptr_ = wptr_ = 0; full_ = false; } char const* rbuf(size_t want, size_t& avail) override { if (rptr_ < wptr_) { avail = wptr_ - rptr_; } else if (rptr_ == wptr_ && !full_) { avail = 0; } else { avail = size_ - rptr_; if (want > avail) { auto* target = data_.get(); for (; rptr_ < size_; ++rptr_) { std::swap(*target, data_[rptr_]); ++target; } rptr_ = 0; wptr_ += avail; if (wptr_ == size_) { assert(full_); wptr_ = 0; avail = size_ - rptr_; } else { avail = wptr_ - rptr_; } } } return data_.get() + rptr_; } void rcommit(size_t bytes) override { if (bytes == 0) return; full_ = false; assert(rptr_ < wptr_ ? rptr_ + bytes <= wptr_ : rptr_ + bytes <= size_); rptr_ += bytes; if (rptr_ == size_) rptr_ = 0; if (rptr_ == wptr_) rptr_ = wptr_ = 0; } char* wbuf(size_t request, size_t& avail) override { if (wptr_ < rptr_) { avail = rptr_ - wptr_; } else if (rptr_ == wptr_ && full_) { avail = 0; } else { avail = size_ - wptr_; if (avail < request && wptr_ != 0 && rptr_ != 0) { std::copy(data_.get() + rptr_, data_.get() + wptr_, data_.get()); auto size = wptr_ - rptr_; wptr_ = size; rptr_ = 0; avail = size_ - wptr_; } } return data_.get() + wptr_; } void wcommit(size_t bytes) override { if (bytes == 0) return; assert(wptr_ < rptr_ ? wptr_ + bytes <= rptr_ : wptr_ + bytes <= size_); wptr_ += bytes; if (wptr_ == size_) wptr_ = 0; if (wptr_ == rptr_) full_ = true; } private: std::unique_ptr data_; size_t const size_; size_t rptr_{0}; size_t wptr_{0}; bool full_{false}; }; class Growing : public Buffer { public: Growing(size_t base_size, size_t max_size) : base_size_(base_size), max_size_(max_size), data_(base_size) { } bool empty() const override { return rptr_ == wptr_; } bool full() const override { return rptr_ == 0 && wptr_ == max_size_; } void clear() override { data_.resize(base_size_); rptr_ = wptr_ = 0; } char const* rbuf(size_t, size_t& avail) override { avail = wptr_ - rptr_; return data_.data() + rptr_; } void rcommit(size_t bytes) override { assert(rptr_ + bytes <= wptr_); rptr_ += bytes; if (rptr_ == wptr_) rptr_ = wptr_ = 0; } char* wbuf(size_t request, size_t& avail) override { avail = data_.size() - wptr_; if (request > avail && rptr_ > 0) { std::copy(data_.begin() + rptr_, data_.begin() + wptr_, data_.begin()); wptr_ -= rptr_; rptr_ = 0; avail = data_.size() - wptr_; } if (request > avail && data_.size() < max_size_) { data_.resize( std::min(max_size_, data_.size() + std::max(request - avail, (max_size_ - base_size_) / 8))); avail = data_.size() - wptr_; } return data_.data() + wptr_; } void wcommit(size_t bytes) override { assert(wptr_ + bytes <= data_.size()); wptr_ += bytes; } private: size_t const base_size_; size_t const max_size_; std::vector data_; size_t rptr_{0}; size_t wptr_{0}; }; class Null : public Buffer { public: bool empty() const override { return true; } char const* rbuf(size_t, size_t& avail) override { avail = 0; return buf_; } void rcommit(size_t bytes) override { assert(bytes == 0); } bool full() const override { return false; } void clear() override {} char* wbuf(size_t, size_t& avail) override { avail = sizeof(buf_); return buf_; } void wcommit(size_t) override { } private: char buf_[4096]; }; } // namespace std::unique_ptr Buffer::fixed(size_t size) { return std::make_unique(size); } std::unique_ptr Buffer::growing(size_t base_size, size_t max_size) { return std::make_unique(base_size, max_size); } std::unique_ptr Buffer::null() { return std::make_unique(); } size_t Buffer::write(void const* data, size_t len) { assert(data); if (len == 0) return 0; auto* d = reinterpret_cast(data); size_t wrote = 0; while (true) { size_t avail; auto want = len - wrote; auto* ptr = wbuf(want, avail); if (avail == 0) return wrote; if (avail >= want) { std::copy_n(d + wrote, want, ptr); wcommit(want); return len; } std::copy_n(d + wrote, avail, ptr); wcommit(avail); wrote += avail; } }