OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_buffer.cpp
Go to the documentation of this file.
1#include "test_common.hpp"
2
4
5#include <cstdint>
6
7using namespace openvpn;
8
10{
11 int i = 42;
12};
13
14// Test align_as
15template <typename T>
16void realign_test(BufferAllocated &buf, std::size_t headroom)
17{
18 constexpr std::size_t at_align = alignof(T);
19 const std::size_t at_misalign = headroom;
20 const std::size_t at_align_ex = at_misalign & ~(at_align - 1);
21
22 buf.write_alloc(at_misalign);
23 buf.read_alloc(at_misalign);
24 EXPECT_EQ(buf.offset(), at_misalign);
25
26 align_test at;
27 std::memcpy(buf.write_alloc(sizeof(at)), &at, sizeof(at));
28 EXPECT_EQ(buf.offset(), at_misalign);
29
30 auto ptr = align_as<align_test>(buf); // Align the buffer contents
31
32 EXPECT_EQ(ptr->i, 42);
33 EXPECT_EQ(buf.offset(), at_align_ex); // Nearest aligned offset
34
35 std::cout << "Aligning buffer: " << at_misalign << " -> " << at_align_ex << std::endl;
36}
37
38TEST(buffer, buffer_alignas)
39{
40 constexpr std::size_t test_lim = std::numeric_limits<std::size_t>::digits;
41 for (auto i = std::size_t(0); i < test_lim; ++i)
42 {
43 BufferAllocated buf(test_lim * 2);
44 realign_test<align_test>(buf, i);
45 }
46}
47
48// test equality of Buffer and ConstBuffer
49TEST(buffer, const_buffer_ref_1)
50{
51 static unsigned char hello[] = "hello world";
52 Buffer buf(hello, sizeof(hello) - 1, true);
53 ConstBuffer &cbuf = const_buffer_ref(buf);
54 EXPECT_EQ(cbuf.size(), 11u);
55 EXPECT_EQ(buf_to_string(buf), buf_to_string(cbuf));
56}
57
58// test equality of BufferAllocatedRc and ConstBuffer
59TEST(buffer, const_buffer_ref_2)
60{
61 BufferAllocated buf(64);
62 buf_append_string(buf, "hello world");
63 ConstBuffer &cbuf = const_buffer_ref(buf);
64 EXPECT_EQ(cbuf.size(), 11u);
65 EXPECT_EQ(buf_to_string(buf), buf_to_string(cbuf));
66}
67
68// test ConstBufferType with an explicitly const type
69TEST(buffer, my_const_buffer_1)
70{
71 typedef ConstBufferType<const char> MyConstBuffer;
72 static const char hello[] = "hello world";
73 MyConstBuffer cbuf(hello, sizeof(hello) - 1, true);
74 EXPECT_EQ(cbuf.size(), 11u);
75 EXPECT_EQ(std::string(cbuf.c_data(), cbuf.size()), "hello world");
76}
77
78// Test read access and bounds check on ConstBufferType
79TEST(buffer, const_buffer_access1)
80{
81 constexpr char data[] = "hello world";
82 ConstBufferType<char> cbuf(data, sizeof(data) - 1, true);
83 EXPECT_EQ(cbuf[0], 'h');
84 EXPECT_EQ(cbuf[10], 'd');
85 EXPECT_THROW(cbuf[11], BufferException);
86}
87
88// Test read access and bounds check on ConstBufferType
89TEST(buffer, const_buffer_access2)
90{
91 constexpr char data[] = "hello world";
92 ConstBufferType<char> cbuf(data, sizeof(data) - 1, true);
93
94 while (cbuf.empty() == false)
95 {
96 auto back = cbuf[cbuf.size() - 1];
97 EXPECT_EQ(cbuf.pop_back(), back);
98 }
99
100 EXPECT_THROW(cbuf.pop_back(), BufferException);
101 EXPECT_THROW(cbuf[0], BufferException);
102 EXPECT_THROW(cbuf[1], BufferException);
103 EXPECT_THROW(cbuf[11], BufferException);
104 EXPECT_THROW(cbuf[12], BufferException);
105}
106
107// Test read access and bounds check on ConstBufferType
108TEST(buffer, const_buffer_access3)
109{
110 constexpr char data[] = "hello world";
111 ConstBufferType<char> cbuf(data, sizeof(data) - 1, true);
112
113 while (cbuf.empty() == false)
114 {
115 auto front = cbuf[0];
116 EXPECT_EQ(cbuf.pop_front(), front);
117 }
118
119 EXPECT_THROW(cbuf.pop_front(), BufferException);
120 EXPECT_THROW(cbuf[0], BufferException);
121 EXPECT_THROW(cbuf[1], BufferException);
122 EXPECT_THROW(cbuf[11], BufferException);
123 EXPECT_THROW(cbuf[12], BufferException);
124}
125
126// Test read access and bounds check
127TEST(buffer, buffer_access1)
128{
129 char data[] = "hello world";
130 BufferType<char> buf(data, sizeof(data) - 1, true);
131 EXPECT_EQ(buf[0], 'h');
132 EXPECT_EQ(buf[10], 'd');
133 EXPECT_THROW(buf[11], BufferException);
134}
135
136// Test read/write access and bounds check
137TEST(buffer, buffer_access2)
138{
139 char data[] = "hello world";
140 BufferType<char> buf(data, sizeof(data) - 1, true);
141 buf[0] = 'j';
142 EXPECT_EQ(buf[0], 'j');
143 EXPECT_EQ(buf[4], 'o');
144 EXPECT_THROW(buf[-1], BufferException);
145 EXPECT_THROW(buf[20], BufferException);
146}
147
148// Test push/pop and bounds check
149TEST(buffer, buffer_access3)
150{
151 char data1[] = "hello world";
152 char data2[sizeof(data1)];
153 BufferType<char> buf1(data1, sizeof(data1) - 1, true);
154 BufferType<char> buf2(data2, sizeof(data2) - 1, false);
155
156 auto items = buf1.size();
157 while (items--)
158 {
159 buf2.push_back(buf1.pop_front());
160 }
161
162 buf2[0] = 'j';
163 EXPECT_EQ(buf2[0], 'j');
164 EXPECT_EQ(buf2[4], 'o');
165 EXPECT_THROW(buf2[-1], BufferException);
166 EXPECT_THROW(buf2[20], BufferException);
167}
168
169// Test push/pop and bounds check
170TEST(buffer, buffer_access4)
171{
172 char data1[] = "hello world";
173 char data2[sizeof(data1)];
174 BufferType<char> buf1(data1, sizeof(data1) - 1, true);
175 BufferType<char> buf2(data2, sizeof(data2) - 1, false);
176
177 auto items = buf1.size();
178 while (items--)
179 {
180 buf2.push_back(buf1.pop_front());
181 }
182
183 items = buf2.size();
184 while (items--)
185 {
186 buf1.push_front(buf2.pop_back());
187 }
188
189 buf1[0] = 'j';
190 EXPECT_EQ(buf1[0], 'j');
191 EXPECT_EQ(buf1[4], 'o');
192 EXPECT_THROW(buf1[-1], BufferException);
193 EXPECT_THROW(buf1[20], BufferException);
194}
195
196// Test read access and bounds check
197TEST(buffer, alloc_buffer_access1)
198{
199 BufferAllocated buf(64);
200 buf_append_string(buf, "hello world");
201 EXPECT_EQ(buf[0], 'h');
202 EXPECT_EQ(buf[10], 'd');
203 EXPECT_THROW(buf[11], BufferException);
204}
205
206// Test read/write access and bounds check
207TEST(buffer, alloc_buffer_access2)
208{
210 buf_append_string(buf, "hello world");
211
212 buf[0] = 'j';
213 EXPECT_EQ(buf[0], 'j');
214 EXPECT_EQ(buf[4], 'o');
215 EXPECT_THROW(buf[-1], BufferException);
216 EXPECT_THROW(buf[20], BufferException);
217}
218
219// Test read/write access and bounds check
220TEST(buffer, alloc_buffer_access3)
221{
222 char data[] = "hello world";
223 BufferType<char> buf1(data, sizeof(data) - 1, true);
224 BufferAllocated buf(sizeof(data));
225
226 auto items = buf1.size();
227 while (items--)
228 {
229 buf.push_back(buf1.pop_front());
230 }
231
232 buf[0] = 'j';
233 EXPECT_EQ(buf[0], 'j');
234 EXPECT_EQ(buf[4], 'o');
235 EXPECT_THROW(buf[-20], BufferException);
236 EXPECT_THROW(buf[20], BufferException);
237}
238
239// Test pop_front
240TEST(buffer, alloc_buffer_pop_front)
241{
242 BufferAllocated buf(64);
243 buf_append_string(buf, "hello world");
244
245 while (buf.pop_front() != 'd')
246 ;
247 EXPECT_THROW(buf.pop_front(), BufferException);
248}
249
250// Test advance
251TEST(buffer, alloc_buffer_advance1)
252{
253 BufferAllocated buf(64);
254 buf_append_string(buf, "hello world");
255
256 do
257 {
258 buf.advance(1);
259 } while (buf.front() != 'd');
260
261 EXPECT_EQ(buf[0], 'd');
262 EXPECT_EQ(buf.back(), 'd');
263 EXPECT_EQ(buf.pop_front(), 'd');
264 EXPECT_THROW(buf.pop_front(), BufferException);
265}
266
267// Test advance
268TEST(buffer, alloc_buffer_advance2)
269{
270 constexpr char data[] = "hello world";
271 BufferAllocated buf(64);
272 buf_append_string(buf, data);
273
274 EXPECT_THROW(buf.advance(sizeof(data)), BufferException);
275}
276
277// Test advance
278TEST(buffer, alloc_buffer_advance3)
279{
280 constexpr char data[] = "hello world";
281 BufferAllocated buf(64);
282 buf_append_string(buf, data);
283
284 buf.advance(sizeof(data) - 2);
285
286 EXPECT_EQ(buf[0], 'd');
287 EXPECT_EQ(buf.back(), 'd');
288 EXPECT_EQ(buf.pop_front(), 'd');
289 EXPECT_THROW(buf.pop_front(), BufferException);
290}
291
292// Test remaining()
293TEST(buffer, alloc_buffer_remaining)
294{
295 BufferAllocated buf(64);
296
297 for (auto remaining = buf.remaining();
298 remaining > 0;
299 --remaining)
300 {
301 buf.push_back('X');
302 EXPECT_EQ(remaining - 1, buf.remaining());
303 EXPECT_EQ(buf.back(), 'X');
304 }
305 EXPECT_THROW(buf.push_back('X'), BufferException);
306}
307
308// Test init_headroom()
309TEST(buffer, alloc_buffer_init_headroom)
310{
311 BufferAllocated buf(64);
312
313 EXPECT_EQ(buf.remaining(), 64);
314 buf.init_headroom(32);
315 EXPECT_EQ(buf.remaining(), 32);
316
317 for (auto remaining = buf.remaining();
318 remaining > 0;
319 --remaining)
320 {
321 buf.push_back('X');
322 EXPECT_EQ(remaining - 1, buf.remaining());
323 EXPECT_EQ(buf.back(), 'X');
324 }
325 EXPECT_THROW(buf.push_back('X'), BufferException);
326}
327
328// Test reset_offset()
329TEST(buffer, alloc_buffer_reset_offset)
330{
331 BufferAllocated buf(64);
332
333 EXPECT_EQ(buf.remaining(), 64);
334
335 for (auto remaining = buf.remaining();
336 remaining > 0;
337 --remaining)
338 {
339 buf.push_back('X');
340 EXPECT_EQ(remaining - 1, buf.remaining());
341 EXPECT_EQ(buf.back(), 'X');
342 }
343 EXPECT_THROW(buf.push_back('X'), BufferException);
344
345 buf.reset_offset(32);
346 EXPECT_EQ(0, buf.remaining());
347
348 buf.reset_offset(16);
349 EXPECT_EQ(0, buf.remaining());
350}
351
352// Test reset_size()
353TEST(buffer, alloc_buffer_reset_size)
354{
355 BufferAllocated buf(64);
356
357 EXPECT_EQ(buf.remaining(), 64);
358
359 for (auto remaining = buf.remaining();
360 remaining > 0;
361 --remaining)
362 {
363 buf.push_back('X');
364 EXPECT_EQ(remaining - 1, buf.remaining());
365 EXPECT_EQ(buf.back(), 'X');
366 }
367 EXPECT_THROW(buf.push_back('X'), BufferException);
368 buf.reset_size();
369 EXPECT_THROW(buf.back(), BufferException);
370 buf.push_back('X');
371 EXPECT_EQ(buf.back(), 'X');
372}
373
374// Test read()
375TEST(buffer, alloc_buffer_read1)
376{
377 constexpr char data[] = "hello world";
378 BufferAllocated buf(64);
379 buf_append_string(buf, data);
380
381 char raw[sizeof(data) - 1];
382
383 buf.read(raw, sizeof(raw));
384
385 EXPECT_EQ(memcmp(raw, data, sizeof(raw)), 0);
386}
387
388TEST(buffer, prepend_alloc)
389{
390 BufferAllocated buf(64);
391 buf_append_string(buf, "hello world");
392 EXPECT_EQ(buf.offset(), 0u);
393
394 buf.prepend_alloc(5);
395 EXPECT_EQ(buf.size(), 16u);
396 EXPECT_EQ(buf.remaining(), 48u);
397}
398
399
400TEST(buffer, prepend_alloc_2)
401{
402 BufferAllocated buf(64);
403 EXPECT_EQ(buf.offset(), 0u);
404 buf.init_headroom(2);
405 EXPECT_EQ(buf.offset(), 2u);
406 buf_append_string(buf, "hello world");
407 EXPECT_EQ(buf.offset(), 2u);
408
409 buf.prepend_alloc(5);
410 EXPECT_EQ(buf.offset(), 0u);
411 EXPECT_EQ(buf.size(), 16u);
412 EXPECT_EQ(buf.remaining(), 48u);
413}
414
415
416TEST(buffer, prepend_alloc_fits)
417{
418 BufferAllocated buf(64);
419 EXPECT_EQ(buf.offset(), 0u);
420 buf.init_headroom(5);
421 EXPECT_EQ(buf.offset(), 5u);
422 buf_append_string(buf, "hello world");
423 EXPECT_EQ(buf.offset(), 5u);
424
425 buf.prepend_alloc(5);
426 EXPECT_EQ(buf.offset(), 0u);
427 EXPECT_EQ(buf.size(), 16u);
428 EXPECT_EQ(buf.remaining(), 48u);
429}
430
431TEST(buffer, prepend_alloc_fail)
432{
433 BufferAllocated buf(11);
434 buf_append_string(buf, "hello world");
435
436 EXPECT_THROW(buf.prepend_alloc(5), std::exception);
437 EXPECT_EQ(buf.size(), 11u);
438 EXPECT_EQ(buf.remaining(), 0u);
439}
440
441TEST(buffer, prepend_alloc_fail2)
442{
443 BufferAllocated buf(14);
444 buf_append_string(buf, "hello world");
445
446 EXPECT_THROW(buf.prepend_alloc(5), std::exception);
447 EXPECT_EQ(buf.size(), 11u);
448 EXPECT_EQ(buf.remaining(), 3u);
449}
450
451TEST(buffer, realign)
452{
453 BufferAllocated buf(64);
454 buf_append_string(buf, "hello world");
455
456 buf.advance(5);
457 EXPECT_EQ(buf.c_data_raw()[0], 'h');
458
459 buf.realign(0);
460
461 EXPECT_EQ(buf[0], ' ');
462 EXPECT_EQ(buf[5], 'd');
463 EXPECT_THROW(buf[6], BufferException);
464 EXPECT_EQ(buf.size(), 6u);
465 EXPECT_EQ(buf.c_data_raw()[0], ' ');
466}
467
468TEST(buffer, realign2)
469{
470 BufferAllocated buf(64);
471 buf_append_string(buf, "hello world");
472
473 EXPECT_EQ(buf.c_data_raw()[0], 'h');
474
475 buf.realign(5);
476
477 EXPECT_EQ(buf.c_data_raw()[5], 'h');
478 EXPECT_EQ(buf[0], 'h');
479 EXPECT_EQ(buf.size(), 11u);
480}
481
482TEST(buffer, realign3)
483{
484 BufferAllocated buf(11);
485 buf_append_string(buf, "hello world");
486
487 EXPECT_EQ(buf.c_data_raw()[0], 'h');
488
489 buf.realign(5);
490
491 EXPECT_EQ(buf.c_data_raw()[5], 'h');
492 EXPECT_EQ(buf[0], 'h');
493 EXPECT_EQ(buf.size(), 11u);
494 EXPECT_EQ(buf.offset(), 5u);
495}
496
497TEST(buffer, realign4)
498{
499 BufferAllocated buf(32);
500 buf.realign(7u);
501 buf_append_string(buf, "hello world");
502 EXPECT_EQ(buf.offset(), 7u);
503 buf.realign(0);
504
505 EXPECT_EQ(buf.c_data_raw()[0], 'h');
506 EXPECT_EQ(buf[0], 'h');
507 EXPECT_EQ(buf.offset(), 0);
508}
509
510/*
511 We need to be sure the object is in a useable state after a move operation. This
512 reflects implied expectations in our codebase and does not violate the standard. The
513 following tests are to ensure that the object is in a usable state after a move
514 operation. The invariants of the object will be checked in unit tests post-move and
515 the PR and relevant notes will be noted in Coverity and the codebase. The goal is
516 to ensure such use cases do not lead to undefined behavior. The preexisting move
517 implementation is correct and the object is in a valid state post-move. This set
518 of tests seeks to ensure that stays true.
519
520 The standard says:
521
522 C++11 Standard, Section 12.8/32: "If the parameter is a non-const lvalue reference
523 to a non-volatile object type or a non-const rvalue reference to a non-volatile
524 object type, the implicit move constructor ([class.copy.ctor], [class.copy.ctor]/2)
525 and the implicit move assignment operator ([class.copy.assign], [class.copy.assign]/2)
526 are invoked to initialize the parameter object or to assign to it, respectively. The
527 object referred to by the rvalue expression is guaranteed to be left in a valid but
528 unspecified state."
529*/
530
531TEST(buffer, invariants_after_move_safe)
532{
533 BufferAllocated buf(32);
534 buf_append_string(buf, "hello world");
535
536 BufferAllocated buf2(std::move(buf));
537
538 // coverity[USE_AFTER_MOVE]
539 EXPECT_EQ(buf.size(), 0u);
540 // coverity[USE_AFTER_MOVE]
541 EXPECT_EQ(buf.capacity(), 0u);
542 // coverity[USE_AFTER_MOVE]
543 EXPECT_THROW(buf[0], BufferException);
544 // coverity[USE_AFTER_MOVE]
545 EXPECT_EQ(buf.c_data(), nullptr);
546 // coverity[USE_AFTER_MOVE]
547 EXPECT_EQ(buf.c_data_raw(), nullptr);
548 // coverity[USE_AFTER_MOVE]
549 EXPECT_EQ(buf.data(), nullptr);
550 // coverity[USE_AFTER_MOVE]
551 EXPECT_EQ(buf.data_raw(), nullptr);
552 // coverity[USE_AFTER_MOVE]
553 EXPECT_EQ(buf.offset(), 0u);
554 // coverity[USE_AFTER_MOVE]
555 EXPECT_EQ(buf.remaining(), 0u);
556}
557
558TEST(buffer, push_back_after_move_safe)
559{
560 BufferAllocated buf(32);
561 buf_append_string(buf, "hello world");
562
563 BufferAllocated buf2(std::move(buf));
564 buf.realloc(11);
565 buf.push_back('X');
566
567 // coverity[USE_AFTER_MOVE]
568 EXPECT_EQ(buf2.size(), 11u);
569 // coverity[USE_AFTER_MOVE]
570 EXPECT_EQ(buf2[0], 'h');
571 // coverity[USE_AFTER_MOVE]
572 EXPECT_EQ(buf2[10], 'd');
573 // coverity[USE_AFTER_MOVE]
574 EXPECT_EQ(buf[0], 'X');
575}
576
577TEST(buffer, append_after_move_safe)
578{
579 BufferAllocated buf(32);
580 buf_append_string(buf, "hello world");
581
582 BufferAllocated buf2(std::move(buf));
583 auto buf3 = BufferAllocated(32);
584 buf_append_string(buf3, "hello again");
585 buf = buf3;
586
587 // coverity[USE_AFTER_MOVE]
588 EXPECT_EQ(buf2.size(), 11u);
589 // coverity[USE_AFTER_MOVE]
590 EXPECT_EQ(buf2[0], 'h');
591 // coverity[USE_AFTER_MOVE]
592 EXPECT_EQ(buf2[10], 'd');
593 // coverity[USE_AFTER_MOVE]
594 EXPECT_EQ(buf, buf3);
595}
void realloc(const size_t newcap)
Reallocates the buffer to the specified new capacity.
Definition buffer.hpp:1753
BufferAllocatedType & realign(const size_t headroom)
Realign the buffer with the specified headroom.
Definition buffer.hpp:1760
report various types of exceptions or errors that may occur when working with buffers
Definition buffer.hpp:115
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1194
T * prepend_alloc(const size_t size)
Allocate space for prepending data to the buffer.
Definition buffer.hpp:1597
void init_headroom(const size_t headroom)
Initializes the headroom (offset) of the buffer.
Definition buffer.hpp:1151
T front() const
Returns the first element of the buffer.
Definition buffer.hpp:1265
void push_back(const T &value)
Append a T object to the end of the array, resizing the array if necessary.
Definition buffer.hpp:1482
T back() const
Returns the last element of the buffer.
Definition buffer.hpp:1271
T * write_alloc(const size_t size)
Allocate space for writing data to the buffer.
Definition buffer.hpp:1587
size_t capacity() const
Returns the capacity (raw size) of the allocated buffer in T objects.
Definition buffer.hpp:1212
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1242
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1450
T pop_back()
Removes and returns the last element from the buffer.
Definition buffer.hpp:1248
void advance(const size_t delta)
Advances the buffer by the specified delta.
Definition buffer.hpp:1277
bool empty() const
Returns true if the buffer is empty.
Definition buffer.hpp:1236
const T * c_data_raw() const
Returns a const pointer to the start of the raw data in the buffer.
Definition buffer.hpp:1206
auto * read_alloc(const size_t size)
Allocate memory and read data from the buffer into the allocated memory.
Definition buffer.hpp:1343
T pop_front()
Removes and returns the first element from the buffer.
Definition buffer.hpp:1256
size_t remaining(const size_t tailroom=0) const
Return the number of additional T objects that can be added before capacity is reached (without consi...
Definition buffer.hpp:1468
T * data_raw()
Get a mutable pointer to the start of the raw data.
Definition buffer.hpp:1462
size_t offset() const
Returns the current offset (headroom) into the buffer.
Definition buffer.hpp:1218
void reset_offset(const size_t offset)
Resets the offset of the buffer.
Definition buffer.hpp:1160
void reset_size()
Resets the size of the buffer to zero.
Definition buffer.hpp:1170
void read(NCT *data, const size_t size)
Read data from the buffer into the specified memory location.
Definition buffer.hpp:1331
constexpr BufferFlags CONSTRUCT_ZERO(1u<< 0)
if enabled, constructors/init will zero allocated space
constexpr BufferFlags DESTRUCT_ZERO(1u<< 1)
if enabled, destructor will zero data before deletion
ConstBufferType< T > & const_buffer_ref(BufferType< T > &src)
Definition buffer.hpp:1920
BufferAllocatedType< unsigned char > BufferAllocated
Definition buffer.hpp:1897
void buf_append_string(Buffer &buf, const std::string &str)
Definition bufstr.hpp:82
std::string buf_to_string(const Buffer &buf)
Definition bufstr.hpp:22
TEST(buffer, buffer_alignas)
void realign_test(BufferAllocated &buf, std::size_t headroom)