diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2025-02-20 22:54:56 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2025-02-20 22:54:56 +0100 |
| commit | b4d6df902253637f24647d3db2bc3781d69eec1c (patch) | |
| tree | d8bf9ac04a270fabdfee1c15628c702471ef8bf5 /src/buffer.cc | |
| parent | 441cafc7124f633e5abc684e85a11ce3c991f6ae (diff) | |
Diffstat (limited to 'src/buffer.cc')
| -rw-r--r-- | src/buffer.cc | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/buffer.cc b/src/buffer.cc new file mode 100644 index 0000000..d39ec86 --- /dev/null +++ b/src/buffer.cc @@ -0,0 +1,233 @@ +#include "buffer.hh" + +#include <algorithm> +#include <cassert> +#include <vector> + +namespace { + +class Round : public Buffer { +public: + explicit Round(size_t size) + : data_(std::make_unique<char[]>(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<char[]> 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<char> 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> Buffer::fixed(size_t size) { + return std::make_unique<Round>(size); +} + +std::unique_ptr<Buffer> Buffer::growing(size_t base_size, size_t max_size) { + return std::make_unique<Growing>(base_size, max_size); +} + +std::unique_ptr<Buffer> Buffer::null() { + return std::make_unique<Null>(); +} + +size_t Buffer::write(void const* data, size_t len) { + assert(data); + if (len == 0) + return 0; + auto* d = reinterpret_cast<char const*>(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; + } +} |
