summaryrefslogtreecommitdiff
path: root/src/buffer.cc
blob: 4bde195394c96d4965a2e077c8f720ab7b623bfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// -*- 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_(
        std::max(min_avail, static_cast<size_t>(1))) {
    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;
    }
  }
}

void Buffer::clear() {
  while (true) {
    size_t avail;
    read_ptr(&avail);
    if (avail == 0) return;
    consume(avail);
  }
}