1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "libevent_util.h"
26
27 #include <cstring>
28 #include <algorithm>
29
30 namespace nghttp2 {
31
32 namespace util {
33
EvbufferBuffer()34 EvbufferBuffer::EvbufferBuffer()
35 : evbuffer_(nullptr),
36 bucket_(nullptr),
37 buf_(nullptr),
38 bufmax_(0),
39 buflen_(0),
40 limit_(0),
41 writelen_(0) {}
42
EvbufferBuffer(evbuffer * evbuffer,uint8_t * buf,size_t bufmax,ssize_t limit)43 EvbufferBuffer::EvbufferBuffer(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
44 ssize_t limit)
45 : evbuffer_(evbuffer),
46 bucket_(limit == -1 ? nullptr : evbuffer_new()),
47 buf_(buf),
48 bufmax_(bufmax),
49 buflen_(0),
50 limit_(limit),
51 writelen_(0) {}
52
reset(evbuffer * evbuffer,uint8_t * buf,size_t bufmax,ssize_t limit)53 void EvbufferBuffer::reset(evbuffer *evbuffer, uint8_t *buf, size_t bufmax,
54 ssize_t limit) {
55 evbuffer_ = evbuffer;
56 buf_ = buf;
57 if (limit != -1 && !bucket_) {
58 bucket_ = evbuffer_new();
59 }
60 bufmax_ = bufmax;
61 buflen_ = 0;
62 limit_ = limit;
63 writelen_ = 0;
64 }
65
~EvbufferBuffer()66 EvbufferBuffer::~EvbufferBuffer() {
67 if (bucket_) {
68 evbuffer_free(bucket_);
69 }
70 }
71
write_buffer()72 int EvbufferBuffer::write_buffer() {
73 for (auto pos = buf_, end = buf_ + buflen_; pos < end;) {
74 // To avoid merging chunks in evbuffer, we first add to temporal
75 // buffer bucket_ and then move its chain to evbuffer_.
76 auto nwrite = std::min(end - pos, limit_);
77 auto rv = evbuffer_add(bucket_, pos, nwrite);
78 if (rv == -1) {
79 return -1;
80 }
81 rv = evbuffer_add_buffer(evbuffer_, bucket_);
82 if (rv == -1) {
83 return -1;
84 }
85 pos += nwrite;
86 }
87 return 0;
88 }
89
flush()90 int EvbufferBuffer::flush() {
91 int rv;
92 if (buflen_ > 0) {
93 if (limit_ == -1) {
94 rv = evbuffer_add(evbuffer_, buf_, buflen_);
95 } else {
96 rv = write_buffer();
97 }
98 if (rv == -1) {
99 return -1;
100 }
101 writelen_ += buflen_;
102 buflen_ = 0;
103 }
104 return 0;
105 }
106
add(const uint8_t * data,size_t datalen)107 int EvbufferBuffer::add(const uint8_t *data, size_t datalen) {
108 int rv;
109 if (buflen_ + datalen > bufmax_) {
110 if (buflen_ > 0) {
111 if (limit_ == -1) {
112 rv = evbuffer_add(evbuffer_, buf_, buflen_);
113 } else {
114 rv = write_buffer();
115 }
116 if (rv == -1) {
117 return -1;
118 }
119 writelen_ += buflen_;
120 buflen_ = 0;
121 }
122 if (datalen > bufmax_) {
123 if (limit_ == -1) {
124 rv = evbuffer_add(evbuffer_, data, datalen);
125 } else {
126 rv = write_buffer();
127 }
128 if (rv == -1) {
129 return -1;
130 }
131 writelen_ += buflen_;
132 return 0;
133 }
134 }
135 memcpy(buf_ + buflen_, data, datalen);
136 buflen_ += datalen;
137 return 0;
138 }
139
get_buflen() const140 size_t EvbufferBuffer::get_buflen() const { return buflen_; }
141
get_writelen() const142 size_t EvbufferBuffer::get_writelen() const { return writelen_; }
143
bev_enable_unless(bufferevent * bev,int events)144 void bev_enable_unless(bufferevent *bev, int events) {
145 if ((bufferevent_get_enabled(bev) & events) == events) {
146 return;
147 }
148
149 bufferevent_enable(bev, events);
150 }
151
bev_disable_unless(bufferevent * bev,int events)152 void bev_disable_unless(bufferevent *bev, int events) {
153 if ((bufferevent_get_enabled(bev) & events) == 0) {
154 return;
155 }
156
157 bufferevent_disable(bev, events);
158 }
159
160 } // namespace util
161
162 } // namespace nghttp2
163