summaryrefslogtreecommitdiff
path: root/src/buffer.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2025-02-20 22:54:56 +0100
committerJoel Klinghed <the_jk@spawned.biz>2025-02-20 22:54:56 +0100
commitb4d6df902253637f24647d3db2bc3781d69eec1c (patch)
treed8bf9ac04a270fabdfee1c15628c702471ef8bf5 /src/buffer.cc
parent441cafc7124f633e5abc684e85a11ce3c991f6ae (diff)
Initial commitHEADmain
Diffstat (limited to 'src/buffer.cc')
-rw-r--r--src/buffer.cc233
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;
+ }
+}