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);
}
}
|