1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 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 "memchunk_test.h"
26
27 #include <CUnit/CUnit.h>
28
29 #include <nghttp2/nghttp2.h>
30
31 #include "memchunk.h"
32 #include "util.h"
33
34 namespace nghttp2 {
35
test_pool_recycle(void)36 void test_pool_recycle(void) {
37 MemchunkPool pool;
38
39 CU_ASSERT(!pool.pool);
40 CU_ASSERT(0 == pool.poolsize);
41 CU_ASSERT(nullptr == pool.freelist);
42
43 auto m1 = pool.get();
44
45 CU_ASSERT(m1 == pool.pool);
46 CU_ASSERT(MemchunkPool::value_type::size == pool.poolsize);
47 CU_ASSERT(nullptr == pool.freelist);
48
49 auto m2 = pool.get();
50
51 CU_ASSERT(m2 == pool.pool);
52 CU_ASSERT(2 * MemchunkPool::value_type::size == pool.poolsize);
53 CU_ASSERT(nullptr == pool.freelist);
54 CU_ASSERT(m1 == m2->knext);
55 CU_ASSERT(nullptr == m1->knext);
56
57 auto m3 = pool.get();
58
59 CU_ASSERT(m3 == pool.pool);
60 CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
61 CU_ASSERT(nullptr == pool.freelist);
62
63 pool.recycle(m3);
64
65 CU_ASSERT(m3 == pool.pool);
66 CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
67 CU_ASSERT(m3 == pool.freelist);
68
69 auto m4 = pool.get();
70
71 CU_ASSERT(m3 == m4);
72 CU_ASSERT(m4 == pool.pool);
73 CU_ASSERT(3 * MemchunkPool::value_type::size == pool.poolsize);
74 CU_ASSERT(nullptr == pool.freelist);
75
76 pool.recycle(m2);
77 pool.recycle(m1);
78
79 CU_ASSERT(m1 == pool.freelist);
80 CU_ASSERT(m2 == m1->next);
81 CU_ASSERT(nullptr == m2->next);
82 }
83
84 using Memchunk16 = Memchunk<16>;
85 using MemchunkPool16 = Pool<Memchunk16>;
86 using Memchunks16 = Memchunks<Memchunk16>;
87 using PeekMemchunks16 = PeekMemchunks<Memchunk16>;
88
test_memchunks_append(void)89 void test_memchunks_append(void) {
90 MemchunkPool16 pool;
91 Memchunks16 chunks(&pool);
92
93 chunks.append("012");
94
95 auto m = chunks.tail;
96
97 CU_ASSERT(3 == m->len());
98 CU_ASSERT(13 == m->left());
99
100 chunks.append("3456789abcdef@");
101
102 CU_ASSERT(16 == m->len());
103 CU_ASSERT(0 == m->left());
104
105 m = chunks.tail;
106
107 CU_ASSERT(1 == m->len());
108 CU_ASSERT(15 == m->left());
109 CU_ASSERT(17 == chunks.rleft());
110
111 char buf[16];
112 size_t nread;
113
114 nread = chunks.remove(buf, 8);
115
116 CU_ASSERT(8 == nread);
117 CU_ASSERT(0 == memcmp("01234567", buf, nread));
118 CU_ASSERT(9 == chunks.rleft());
119
120 nread = chunks.remove(buf, sizeof(buf));
121
122 CU_ASSERT(9 == nread);
123 CU_ASSERT(0 == memcmp("89abcdef@", buf, nread));
124 CU_ASSERT(0 == chunks.rleft());
125 CU_ASSERT(nullptr == chunks.head);
126 CU_ASSERT(nullptr == chunks.tail);
127 CU_ASSERT(32 == pool.poolsize);
128 }
129
test_memchunks_drain(void)130 void test_memchunks_drain(void) {
131 MemchunkPool16 pool;
132 Memchunks16 chunks(&pool);
133
134 chunks.append("0123456789");
135
136 size_t nread;
137
138 nread = chunks.drain(3);
139
140 CU_ASSERT(3 == nread);
141
142 char buf[16];
143
144 nread = chunks.remove(buf, sizeof(buf));
145
146 CU_ASSERT(7 == nread);
147 CU_ASSERT(0 == memcmp("3456789", buf, nread));
148 }
149
test_memchunks_riovec(void)150 void test_memchunks_riovec(void) {
151 MemchunkPool16 pool;
152 Memchunks16 chunks(&pool);
153
154 std::array<char, 3 * 16> buf{};
155
156 chunks.append(buf.data(), buf.size());
157
158 std::array<struct iovec, 2> iov;
159 auto iovcnt = chunks.riovec(iov.data(), iov.size());
160
161 auto m = chunks.head;
162
163 CU_ASSERT(2 == iovcnt);
164 CU_ASSERT(m->buf.data() == iov[0].iov_base);
165 CU_ASSERT(m->len() == iov[0].iov_len);
166
167 m = m->next;
168
169 CU_ASSERT(m->buf.data() == iov[1].iov_base);
170 CU_ASSERT(m->len() == iov[1].iov_len);
171
172 chunks.drain(2 * 16);
173
174 iovcnt = chunks.riovec(iov.data(), iov.size());
175
176 CU_ASSERT(1 == iovcnt);
177
178 m = chunks.head;
179 CU_ASSERT(m->buf.data() == iov[0].iov_base);
180 CU_ASSERT(m->len() == iov[0].iov_len);
181 }
182
test_memchunks_recycle(void)183 void test_memchunks_recycle(void) {
184 MemchunkPool16 pool;
185 {
186 Memchunks16 chunks(&pool);
187 std::array<char, 32> buf{};
188 chunks.append(buf.data(), buf.size());
189 }
190 CU_ASSERT(32 == pool.poolsize);
191 CU_ASSERT(nullptr != pool.freelist);
192
193 auto m = pool.freelist;
194 m = m->next;
195
196 CU_ASSERT(nullptr != m);
197 CU_ASSERT(nullptr == m->next);
198 }
199
test_memchunks_reset(void)200 void test_memchunks_reset(void) {
201 MemchunkPool16 pool;
202 Memchunks16 chunks(&pool);
203
204 std::array<uint8_t, 32> b{};
205
206 chunks.append(b.data(), b.size());
207
208 CU_ASSERT(32 == chunks.rleft());
209
210 chunks.reset();
211
212 CU_ASSERT(0 == chunks.rleft());
213 CU_ASSERT(nullptr == chunks.head);
214 CU_ASSERT(nullptr == chunks.tail);
215
216 auto m = pool.freelist;
217
218 CU_ASSERT(nullptr != m);
219 CU_ASSERT(nullptr != m->next);
220 CU_ASSERT(nullptr == m->next->next);
221 }
222
test_peek_memchunks_append(void)223 void test_peek_memchunks_append(void) {
224 MemchunkPool16 pool;
225 PeekMemchunks16 pchunks(&pool);
226
227 std::array<uint8_t, 32> b{
228 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
229 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
230 },
231 d;
232
233 pchunks.append(b.data(), b.size());
234
235 CU_ASSERT(32 == pchunks.rleft());
236 CU_ASSERT(32 == pchunks.rleft_buffered());
237
238 CU_ASSERT(0 == pchunks.remove(nullptr, 0));
239
240 CU_ASSERT(32 == pchunks.rleft());
241 CU_ASSERT(32 == pchunks.rleft_buffered());
242
243 CU_ASSERT(12 == pchunks.remove(d.data(), 12));
244
245 CU_ASSERT(std::equal(std::begin(b), std::begin(b) + 12, std::begin(d)));
246
247 CU_ASSERT(20 == pchunks.rleft());
248 CU_ASSERT(32 == pchunks.rleft_buffered());
249
250 CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));
251
252 CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));
253
254 CU_ASSERT(0 == pchunks.rleft());
255 CU_ASSERT(32 == pchunks.rleft_buffered());
256 }
257
test_peek_memchunks_disable_peek_drain(void)258 void test_peek_memchunks_disable_peek_drain(void) {
259 MemchunkPool16 pool;
260 PeekMemchunks16 pchunks(&pool);
261
262 std::array<uint8_t, 32> b{
263 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
264 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
265 },
266 d;
267
268 pchunks.append(b.data(), b.size());
269
270 CU_ASSERT(12 == pchunks.remove(d.data(), 12));
271
272 pchunks.disable_peek(true);
273
274 CU_ASSERT(!pchunks.peeking);
275 CU_ASSERT(20 == pchunks.rleft());
276 CU_ASSERT(20 == pchunks.rleft_buffered());
277
278 CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));
279
280 CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));
281
282 CU_ASSERT(0 == pchunks.rleft());
283 CU_ASSERT(0 == pchunks.rleft_buffered());
284 }
285
test_peek_memchunks_disable_peek_no_drain(void)286 void test_peek_memchunks_disable_peek_no_drain(void) {
287 MemchunkPool16 pool;
288 PeekMemchunks16 pchunks(&pool);
289
290 std::array<uint8_t, 32> b{
291 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
292 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
293 },
294 d;
295
296 pchunks.append(b.data(), b.size());
297
298 CU_ASSERT(12 == pchunks.remove(d.data(), 12));
299
300 pchunks.disable_peek(false);
301
302 CU_ASSERT(!pchunks.peeking);
303 CU_ASSERT(32 == pchunks.rleft());
304 CU_ASSERT(32 == pchunks.rleft_buffered());
305
306 CU_ASSERT(32 == pchunks.remove(d.data(), d.size()));
307
308 CU_ASSERT(std::equal(std::begin(b), std::end(b), std::begin(d)));
309
310 CU_ASSERT(0 == pchunks.rleft());
311 CU_ASSERT(0 == pchunks.rleft_buffered());
312 }
313
test_peek_memchunks_reset(void)314 void test_peek_memchunks_reset(void) {
315 MemchunkPool16 pool;
316 PeekMemchunks16 pchunks(&pool);
317
318 std::array<uint8_t, 32> b{
319 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
320 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
321 },
322 d;
323
324 pchunks.append(b.data(), b.size());
325
326 CU_ASSERT(12 == pchunks.remove(d.data(), 12));
327
328 pchunks.disable_peek(true);
329 pchunks.reset();
330
331 CU_ASSERT(0 == pchunks.rleft());
332 CU_ASSERT(0 == pchunks.rleft_buffered());
333
334 CU_ASSERT(nullptr == pchunks.cur);
335 CU_ASSERT(nullptr == pchunks.cur_pos);
336 CU_ASSERT(nullptr == pchunks.cur_last);
337 CU_ASSERT(pchunks.peeking);
338 }
339
340 } // namespace nghttp2
341