summaryrefslogtreecommitdiff
path: root/src/buffer.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
committerJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
commitc029d90d1975e124d237605f1edb2be16bd05b5d (patch)
tree9df87ffb365354bdb74a969440b32c8304bdbcb7 /src/buffer.cc
Initial commit
Diffstat (limited to 'src/buffer.cc')
-rw-r--r--src/buffer.cc119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/buffer.cc b/src/buffer.cc
new file mode 100644
index 0000000..d0c5fbb
--- /dev/null
+++ b/src/buffer.cc
@@ -0,0 +1,119 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include <algorithm>
+#include <cstring>
+
+#include "buffer.hh"
+
+namespace {
+
+class BufferImpl : public Buffer {
+public:
+ BufferImpl(size_t capacity, size_t min_avail)
+ : capacity_(capacity), min_avail_(min_avail) {
+ data_ = capacity_ > 0 ?
+ reinterpret_cast<char*>(malloc(capacity_)) : nullptr;
+ end_ = data_;
+ rptr_ = data_;
+ wptr_ = data_;
+ }
+ ~BufferImpl() {
+ free(data_);
+ }
+
+ void const* read_ptr(size_t* avail) const override {
+ if (avail) *avail = wptr_ - rptr_;
+ return rptr_;
+ }
+
+ void consume(size_t bytes) override {
+ if (bytes == 0) return;
+ assert(rptr_ + bytes <= wptr_);
+ rptr_ += bytes;
+ if (rptr_ == wptr_) {
+ rptr_ = wptr_ = data_;
+ }
+ }
+
+ void* write_ptr(size_t* avail) override {
+ if (wptr_ + min_avail_ > end_) {
+ if (rptr_ > data_) {
+ memmove(data_, rptr_, wptr_ - rptr_);
+ wptr_ -= rptr_ - data_;
+ rptr_ = data_;
+ }
+ if (wptr_ + min_avail_ > end_) {
+ auto new_size = (end_ - data_) + std::max(capacity_, min_avail_);
+ auto tmp = reinterpret_cast<char*>(realloc(data_, new_size));
+ if (tmp) {
+ end_ = tmp + new_size;
+ rptr_ = tmp + (rptr_ - data_);
+ wptr_ = tmp + (wptr_ - data_);
+ data_ = tmp;
+ }
+ }
+ }
+ if (avail) *avail = end_ - wptr_;
+ return wptr_;
+ }
+
+ void commit(size_t bytes) override {
+ if (bytes == 0) return;
+ assert(wptr_ + bytes <= end_);
+ wptr_ += bytes;
+ }
+
+private:
+ size_t capacity_;
+ size_t min_avail_;
+ char* data_;
+ char* end_;
+ char* rptr_;
+ char* wptr_;
+};
+
+} // namespace
+
+// static
+Buffer* Buffer::create(size_t size, size_t min_avail) {
+ return new BufferImpl(std::max(size, min_avail), min_avail);
+}
+
+bool Buffer::empty() const {
+ size_t avail;
+ read_ptr(&avail);
+ return avail == 0;
+}
+
+size_t Buffer::read(void* data, size_t max) {
+ if (max == 0) return 0;
+ size_t avail;
+ auto ptr = read_ptr(&avail);
+ if (avail == 0) return 0;
+ avail = std::min(avail, max);
+ memcpy(data, ptr, avail);
+ commit(avail);
+ return avail;
+}
+
+void Buffer::write(void const* data, size_t size) {
+ if (size == 0) return;
+ auto d = reinterpret_cast<char const*>(data);
+ size_t pos = 0;
+ while (true) {
+ size_t avail;
+ auto ptr = write_ptr(&avail);
+ if (pos + avail < size) {
+ memcpy(ptr, d + pos, avail);
+ pos += avail;
+ commit(avail);
+ } else {
+ memcpy(ptr, d + pos, size - pos);
+ commit(size - pos);
+ return;
+ }
+ }
+}
+