// -*- mode: c++; c-basic-offset: 2; -*- #include "common.hh" #include #include #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(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(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(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; } } }