From c87f9627efc8b612eb9b000acfcc6731cad15765 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Tue, 7 Oct 2025 09:12:22 +0200 Subject: Initial commit --- src/buffer.cc | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 src/buffer.cc (limited to 'src/buffer.cc') diff --git a/src/buffer.cc b/src/buffer.cc new file mode 100644 index 0000000..18913a5 --- /dev/null +++ b/src/buffer.cc @@ -0,0 +1,213 @@ +#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); +} -- cgit v1.2.3-70-g09d2