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