#include "buffer.hh" #include #include #include #include #include namespace { class FixedBuffer : public Buffer { public: explicit FixedBuffer(size_t size) : size_(size) {} void const* rptr(size_t& avail, size_t need) override { if (rptr_ < wptr_) { avail = wptr_ - rptr_; } else if (rptr_ == wptr_ && !full_) { avail = 0; } else { avail = (data_.get() + size_) - rptr_; if (avail < need && rptr_ > data_.get()) { rotate(); return rptr(avail, need); } } return rptr_; } void consume(size_t size) override { if (size == 0) return; if (rptr_ < wptr_) { assert(std::cmp_greater_equal(wptr_ - rptr_, size)); rptr_ += size; if (rptr_ == wptr_) reset(); } else { assert(rptr_ != wptr_ || full_); assert(std::cmp_greater_equal((data_.get() + size_) - rptr_, size)); rptr_ += size; if (rptr_ == data_.get() + size_) { rptr_ = data_.get(); if (rptr_ == wptr_) reset(); } } } void* wptr(size_t& avail, size_t need) override { if (wptr_ == nullptr) { data_ = std::make_unique_for_overwrite(size_); rptr_ = wptr_ = data_.get(); } if (wptr_ < rptr_) { avail = rptr_ - wptr_; } else if (rptr_ == wptr_ && full_) { avail = 0; } else { avail = (data_.get() + size_) - wptr_; if (avail < need && rptr_ > data_.get()) { rotate(); return wptr(avail, need); } } return wptr_; } void commit(size_t size) override { if (size == 0) return; if (wptr_ < rptr_) { assert(std::cmp_greater_equal(rptr_ - wptr_, size)); wptr_ += size; if (wptr_ == rptr_) { full_ = true; } } else { assert(rptr_ != wptr_ || !full_); assert(std::cmp_greater_equal((data_.get() + size_) - wptr_, size)); wptr_ += size; if (wptr_ == data_.get() + size_) { wptr_ = data_.get(); if (wptr_ == rptr_) full_ = true; } } } [[nodiscard]] bool full() const override { return rptr_ == wptr_ && full_; } [[nodiscard]] bool empty() const override { return rptr_ == wptr_ && !full_; } private: void reset() { rptr_ = wptr_ = data_.get(); full_ = false; } void rotate() { if (rptr_ < wptr_) { size_t size = wptr_ - rptr_; memmove(data_.get(), rptr_, size); wptr_ = data_.get() + size; } else { size_t to_move = (data_.get() + size_) - rptr_; if (wptr_ + to_move > rptr_) { auto tmp = std::make_unique_for_overwrite(to_move); memcpy(tmp.get(), rptr_, to_move); memmove(data_.get() + to_move, data_.get(), wptr_ - data_.get()); memcpy(data_.get(), tmp.get(), to_move); } else { memmove(data_.get() + to_move, data_.get(), wptr_ - data_.get()); memcpy(data_.get(), rptr_, to_move); } wptr_ += to_move; } rptr_ = data_.get(); } size_t const size_; std::unique_ptr data_; char* rptr_{nullptr}; char* wptr_{nullptr}; bool full_{false}; }; class DynamicBuffer : public Buffer { public: DynamicBuffer(size_t start_size, size_t max_size) : start_size_(start_size), max_size_(max_size) {} void const* rptr(size_t& avail, size_t /* need */) override { avail = wptr_ - rptr_; return rptr_; } void consume(size_t size) override { assert(std::cmp_greater_equal(wptr_ - rptr_, size)); rptr_ += size; if (rptr_ == wptr_) { reset(); } } void* wptr(size_t& avail, size_t need) override { avail = end_ - wptr_; if (avail < need) { if (end_ == nullptr) { size_t size = std::min(max_size_, std::max(need, start_size_)); data_ = std::make_unique_for_overwrite(size); end_ = data_.get() + size; rptr_ = wptr_ = data_.get(); avail = end_ - wptr_; } else if (std::cmp_greater_equal(rptr_ - data_.get(), need - avail)) { memmove(data_.get(), rptr_, wptr_ - rptr_); wptr_ = data_.get() + (wptr_ - rptr_); rptr_ = data_.get(); avail = end_ - wptr_; } else if (std::cmp_less(end_ - data_.get(), max_size_)) { size_t current_size = end_ - data_.get(); size_t new_size = std::min( max_size_, current_size + std::max(need - avail, current_size)); auto tmp = std::make_unique_for_overwrite(new_size); memcpy(tmp.get(), rptr_, wptr_ - rptr_); end_ = tmp.get() + new_size; wptr_ = tmp.get() + (wptr_ - rptr_); rptr_ = tmp.get(); data_ = std::move(tmp); avail = end_ - wptr_; } } return wptr_; } void commit(size_t size) override { assert(std::cmp_greater_equal(end_ - wptr_, size)); wptr_ += size; } [[nodiscard]] bool full() const override { return rptr_ == data_.get() && wptr_ == end_ && std::cmp_equal(end_ - data_.get(), max_size_); } [[nodiscard]] bool empty() const override { return rptr_ == wptr_; } private: void reset() { if (std::cmp_greater(end_ - data_.get(), start_size_)) { data_ = std::make_unique_for_overwrite(start_size_); } rptr_ = wptr_ = data_.get(); } size_t const start_size_; size_t const max_size_; std::unique_ptr data_; char* end_{nullptr}; char* rptr_{nullptr}; char* wptr_{nullptr}; }; } // namespace std::unique_ptr Buffer::fixed(size_t size) { return std::make_unique(size); } std::unique_ptr Buffer::dynamic(size_t start_size, size_t max_size) { return std::make_unique(start_size, max_size); }