• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 11844561993

14 Nov 2024 07:58PM UTC coverage: 91.178% (+0.1%) from 91.072%
11844561993

Pull #4435

github

web-flow
Merge 81dcb29da into e430f157a
Pull Request #4435: Test duration values ​​are now presented in seconds with six digits of precision. Tests without time measurements have been edited.

91856 of 100744 relevant lines covered (91.18%)

9311006.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.61
/src/tests/test_utils_buffer.cpp
1
/*
2
 * (C) 2023 Jack Lloyd
3
 * (C) 2023-2024 René Meusel, Rohde & Schwarz Cybersecurity
4
 *
5
 * Botan is released under the Simplified BSD License (see license.txt)
6
 */
7

8
#include "tests.h"
9

10
#include <botan/concepts.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/alignment_buffer.h>
13
#include <botan/internal/stl_util.h>
14

15
#include <array>
16

17
namespace Botan_Tests {
18

19
namespace {
20

21
template <typename T>
22
std::vector<uint8_t> v(const T& container) {
1✔
23
   return {container.begin(), container.end()};
26✔
24
}
25

26
using StrongBuffer = Botan::Strong<std::vector<uint8_t>, struct StrongBuffer_>;
27

28
std::vector<Test::Result> test_buffer_slicer() {
1✔
29
   return {
1✔
30
      CHECK("Empty BufferSlicer",
31
            [](auto& result) {
1✔
32
               result.start_timer();
1✔
33
               const std::vector<uint8_t> buffer(0);
1✔
34
               Botan::BufferSlicer s(buffer);
1✔
35
               result.confirm("empty slicer has no remaining bytes", s.remaining() == 0);
2✔
36
               result.confirm("empty slicer is empty()", s.empty());
2✔
37
               result.confirm("empty slicer can take() 0 bytes", s.take(0).empty());
2✔
38

39
               result.test_throws("empty slicer cannot emit bytes", [&]() { s.take(1); });
3✔
40
               result.test_throws("empty slicer cannot skip bytes", [&]() { s.skip(1); });
3✔
41
               result.test_throws("empty slicer cannot copy bytes", [&]() { s.copy_as_vector(1); });
3✔
42
               result.end_timer();
1✔
43
            }),
1✔
44

45
      CHECK("Read from BufferSlicer",
46
            [](auto& result) {
1✔
47
               result.start_timer();
1✔
48
               const std::vector<uint8_t> buffer{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', ' ', '!'};
1✔
49
               Botan::BufferSlicer s(buffer);
1✔
50

51
               result.test_eq("non-empty slicer has remaining bytes", s.remaining(), buffer.size());
2✔
52
               result.confirm("non-empty slicer is not empty()", !s.empty());
2✔
53

54
               const auto hello = s.take(5);
1✔
55
               result.require("has 5 bytes", hello.size() == 5);
1✔
56
               result.test_is_eq("took hello", hello[0], uint8_t('h'));
1✔
57
               result.test_is_eq("took hello", hello[1], uint8_t('e'));
1✔
58
               result.test_is_eq("took hello", hello[2], uint8_t('l'));
1✔
59
               result.test_is_eq("took hello", hello[3], uint8_t('l'));
1✔
60
               result.test_is_eq("took hello", hello[4], uint8_t('o'));
2✔
61

62
               result.test_eq("remaining bytes", s.remaining(), 8);
2✔
63

64
               s.skip(1);
1✔
65
               result.test_eq("remaining bytes", s.remaining(), 7);
2✔
66

67
               const auto wor = s.copy_as_vector(3);
1✔
68
               result.require("has 3 bytes", wor.size() == 3);
1✔
69
               result.test_is_eq("took wor...", wor[0], uint8_t('w'));
1✔
70
               result.test_is_eq("took wor...", wor[1], uint8_t('o'));
1✔
71
               result.test_is_eq("took wor...", wor[2], uint8_t('r'));
2✔
72
               result.test_eq("remaining bytes", s.remaining(), 4);
2✔
73

74
               std::vector<uint8_t> ld(2);
1✔
75
               s.copy_into(ld);
1✔
76
               result.test_is_eq("took ...ld", ld[0], uint8_t('l'));
1✔
77
               result.test_is_eq("took ...ld", ld[1], uint8_t('d'));
2✔
78
               result.test_eq("remaining bytes", s.remaining(), 2);
2✔
79

80
               s.skip(1);
1✔
81
               result.test_eq("remaining bytes", s.remaining(), 1);
1✔
82

83
               const auto exclaim = s.take<1>();
1✔
84
               result.test_is_eq("took ...!", exclaim[0], uint8_t('!'));
2✔
85
               result.confirm("has static extent", decltype(exclaim)::extent != std::dynamic_extent);
2✔
86
               result.confirm("static extent is 1", decltype(exclaim)::extent == 1);
2✔
87

88
               result.confirm("empty", s.empty());
2✔
89
               result.test_eq("nothing remaining", s.remaining(), 0);
1✔
90

91
               result.test_throws("empty slicer cannot emit bytes", [&]() { s.take(1); });
3✔
92
               result.test_throws("empty slicer cannot skip bytes", [&]() { s.skip(1); });
3✔
93
               result.test_throws("empty slicer cannot copy bytes", [&]() { s.copy_as_vector(1); });
3✔
94
               result.end_timer();
1✔
95
            }),
3✔
96

97
      CHECK("Strong type support",
98
            [](auto& result) {
1✔
99
               result.start_timer();
1✔
100
               const Botan::secure_vector<uint8_t> secure_buffer{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
1✔
101
               Botan::BufferSlicer s(secure_buffer);
1✔
102

103
               auto span1 = s.take(1);
1✔
104
               auto span2 = s.take<StrongBuffer>(2);
1✔
105
               auto vec1 = s.copy<StrongBuffer>(2);
1✔
106
               auto vec2 = s.copy_as_vector(2);
1✔
107
               auto vec3 = s.copy_as_secure_vector(2);
1✔
108
               StrongBuffer vec4(s.remaining());
1✔
109
               s.copy_into(vec4);
1✔
110

111
               const auto reproduce = Botan::concat<std::vector<uint8_t>>(span1, span2, vec1, vec2, vec3, vec4);
1✔
112
               result.test_eq("sliced into various types", reproduce, secure_buffer);
1✔
113
               result.end_timer();
1✔
114
            }),
6✔
115
   };
4✔
116
}
1✔
117

118
std::vector<Test::Result> test_buffer_stuffer() {
1✔
119
   return {
1✔
120
      CHECK("Empty BufferStuffer",
121
            [](auto& result) {
1✔
122
               result.start_timer();
1✔
123
               std::vector<uint8_t> empty_buffer;
1✔
124
               Botan::BufferStuffer s(empty_buffer);
1✔
125

126
               result.test_eq("has no capacity", s.remaining_capacity(), 0);
2✔
127
               result.confirm("is immediately full", s.full());
2✔
128
               result.confirm("can next() 0 bytes", s.next(0).empty());
2✔
129

130
               result.test_throws("cannot next() anything", [&]() { s.next(1); });
3✔
131
               result.test_throws("cannot append bytes", [&]() {
3✔
132
                  std::vector<uint8_t> some_bytes(42);
1✔
133
                  s.append(some_bytes);
1✔
134
               });
×
135
               result.end_timer();
1✔
136
            }),
1✔
137

138
      CHECK("Fill BufferStuffer",
139
            [](auto& result) {
1✔
140
               result.start_timer();
1✔
141
               std::vector<uint8_t> sink(13);
1✔
142
               Botan::BufferStuffer s(sink);
1✔
143

144
               result.test_eq("has some capacity", s.remaining_capacity(), sink.size());
2✔
145
               result.confirm("is not full", !s.full());
2✔
146

147
               auto n1 = s.next(5);
1✔
148
               result.require("got requested bytes", n1.size() == 5);
2✔
149
               n1[0] = 'h';
1✔
150
               n1[1] = 'e';
1✔
151
               n1[2] = 'l';
1✔
152
               n1[3] = 'l';
1✔
153
               n1[4] = 'o';
1✔
154

155
               auto n2 = s.next<StrongBuffer>(3);
1✔
156
               result.require("got requested bytes", n2.size() == 3);
1✔
157

158
               n2.get()[0] = ' ';
1✔
159
               n2.get()[1] = 'w';
1✔
160
               n2.get()[2] = 'o';
1✔
161

162
               result.test_eq("has 5 bytes remaining", s.remaining_capacity(), 5);
1✔
163

164
               std::vector<uint8_t> rld{'r', 'l', 'd'};
1✔
165
               s.append(rld);
1✔
166

167
               result.test_eq("has 2 bytes remaining", s.remaining_capacity(), 2);
1✔
168

169
               auto n3 = s.next<2>();
1✔
170
               result.require("got requested bytes", n3.size() == 2);
2✔
171
               result.require("is static extent", decltype(n3)::extent != std::dynamic_extent);
2✔
172
               result.require("static extent is 2", decltype(n3)::extent == 2);
2✔
173

174
               n3[0] = ' ';
1✔
175
               n3[1] = '!';
1✔
176

177
               result.confirm("is full", s.full());
2✔
178

179
               result.test_throws("cannot next() anything", [&]() { s.next(1); });
3✔
180
               result.test_throws("cannot append bytes", [&]() {
4✔
181
                  std::vector<uint8_t> some_bytes(42);
1✔
182
                  s.append(some_bytes);
1✔
183
               });
×
184

185
               std::string final_string(Botan::cast_uint8_ptr_to_char(sink.data()), sink.size());
1✔
186
               result.test_eq("final buffer is as expected", final_string, "hello world !");
2✔
187
               result.end_timer();
1✔
188
            }),
3✔
189
   };
3✔
190
}
1✔
191

192
std::vector<Test::Result> test_alignment_buffer() {
1✔
193
   std::array<uint8_t, 32> data = {1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
1✔
194
                                   17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
195
   std::array<uint8_t, 16> first_half_data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
1✔
196
   std::array<uint8_t, 16> second_half_data = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
1✔
197

198
   return {
1✔
199
      CHECK("Fresh Alignment Buffer",
200
            [](auto& result) {
1✔
201
               result.start_timer();
1✔
202
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
203
               result.test_eq("size()", b.size(), 32);
2✔
204
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
205
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
206
               result.confirm("in_alignment()", b.in_alignment());
2✔
207
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
208
               result.end_timer();
1✔
209
            }),
1✔
210

211
      CHECK("Fill Alignment Buffer",
212
            [=](auto& result) {
1✔
213
               result.start_timer();
1✔
214
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
215

216
               b.append(first_half_data);
1✔
217

218
               result.test_eq("size()", b.size(), 32);
2✔
219
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
220
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
221
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
222
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
223

224
               b.append(second_half_data);
1✔
225

226
               result.test_eq("size()", b.size(), 32);
2✔
227
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
228
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
229
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
230
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
231
               result.end_timer();
1✔
232
            }),
1✔
233

234
      CHECK("Consume Alignment Buffer",
235
            [=](auto& result) {
1✔
236
               result.start_timer();
1✔
237
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
238

239
               b.append(data);
1✔
240

241
               result.require("ready_to_consume()", b.ready_to_consume());
1✔
242
               const auto out = b.consume();
1✔
243

244
               result.test_eq("size()", b.size(), 32);
2✔
245
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
246
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
247
               result.confirm("in_alignment()", b.in_alignment());
2✔
248
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
249

250
               result.test_is_eq("in == out", v(data), v(out));
3✔
251
               result.end_timer();
1✔
252
            }),
1✔
253

254
      CHECK("Consume partially filled Alignment Buffer",
255
            [=](auto& result) {
1✔
256
               result.start_timer();
1✔
257
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
258

259
               result.require("!ready_to_consume()", !b.ready_to_consume());
2✔
260
               const auto out_empty = b.consume_partial();
1✔
261
               result.test_eq("consuming nothing resets buffer", b.elements_in_buffer(), 0);
2✔
262
               result.confirm("consumed empty buffer", out_empty.empty());
2✔
263

264
               b.append(first_half_data);
1✔
265
               result.require("!ready_to_consume()", !b.ready_to_consume());
2✔
266
               const auto out_half = b.consume_partial();
1✔
267
               result.test_eq("consumed half-data resets buffer", b.elements_in_buffer(), 0);
1✔
268
               result.test_is_eq("half-in == half-out", v(out_half), v(first_half_data));
3✔
269

270
               b.append(data);
1✔
271
               result.require("ready_to_consume()", b.ready_to_consume());
2✔
272
               const auto out_full = b.consume_partial();
1✔
273
               result.test_eq("consumed full-data resets buffer", b.elements_in_buffer(), 0);
1✔
274
               result.test_is_eq("full-in == full-out", v(out_full), v(data));
3✔
275
               result.end_timer();
1✔
276
            }),
1✔
277

278
      CHECK("Clear Alignment Buffer",
279
            [=](auto& result) {
1✔
280
               result.start_timer();
1✔
281
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
282

283
               b.append(first_half_data);
1✔
284

285
               result.require("elements_in_buffer()", b.elements_in_buffer() == 16);
2✔
286
               b.clear();
1✔
287

288
               result.test_eq("size()", b.size(), 32);
2✔
289
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
290
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
291
               result.confirm("in_alignment()", b.in_alignment());
2✔
292
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
293
               result.end_timer();
1✔
294
            }),
1✔
295

296
      CHECK("Add Zero-Padding to Alignment Buffer",
297
            [=](auto& result) {
1✔
298
               result.start_timer();
1✔
299
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
300

301
               b.append(first_half_data);
1✔
302

303
               result.require("elements_in_buffer()", b.elements_in_buffer() == 16);
2✔
304
               b.fill_up_with_zeros();
1✔
305

306
               result.test_eq("size()", b.size(), 32);
2✔
307
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
308
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
309
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
310
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
311

312
               const auto out = b.consume();
1✔
313

314
               result.test_is_eq("prefix", v(out.first(16)), v(first_half_data));
4✔
315
               result.test_is_eq("zero-padding", v(out.last(16)), std::vector<uint8_t>(16, 0));
3✔
316

317
               // Regression test for GH #3734
318
               // fill_up_with_zeros() must work if called on a full (ready to consume) alignment buffer
319
               b.append(data);
1✔
320
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
321
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
322

323
               b.fill_up_with_zeros();
1✔
324
               const auto out_without_padding = b.consume();
1✔
325

326
               result.test_is_eq("no padding", v(out_without_padding), v(data));
3✔
327
               result.end_timer();
1✔
328
            }),
1✔
329

330
      CHECK("Handle unaligned data in Alignment Buffer (no block-defer)",
331
            [=](auto& result) {
1✔
332
               result.start_timer();
1✔
333
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
334

335
               Botan::BufferSlicer first_half(first_half_data);
1✔
336
               Botan::BufferSlicer second_half(second_half_data);
1✔
337

338
               const auto r1 = b.handle_unaligned_data(first_half);
1✔
339
               result.confirm("half a block is not returned", !r1.has_value());
2✔
340
               result.confirm("first input is consumed", first_half.empty());
2✔
341

342
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
343
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
344
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
345
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
346

347
               const auto r2 = b.handle_unaligned_data(second_half);
1✔
348
               result.require("second half completes block", r2.has_value());
2✔
349
               result.confirm("second input is consumed", second_half.empty());
2✔
350

351
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
352
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
353
               result.confirm("in_alignment()", b.in_alignment());
2✔
354
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
355

356
               result.test_is_eq("collected block is correct", v(r2.value()), v(data));
3✔
357
               result.end_timer();
1✔
358
            }),
1✔
359

360
      CHECK("Aligned data is not buffered unneccesarily (no block-defer)",
361
            [=](auto& result) {
1✔
362
               result.start_timer();
1✔
363
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
364

365
               Botan::BufferSlicer full_block_1(data);
1✔
366
               const auto r1 = b.handle_unaligned_data(full_block_1);
1✔
367
               result.confirm("aligned data is not buffered", !r1.has_value());
2✔
368
               result.confirm("in_alignment()", b.in_alignment());
2✔
369
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
370
               result.test_eq("aligned data is not consumed", full_block_1.remaining(), 32);
1✔
371

372
               Botan::BufferSlicer half_block(first_half_data);
1✔
373
               Botan::BufferSlicer full_block_2(data);
1✔
374
               const auto r2 = b.handle_unaligned_data(half_block);
1✔
375
               result.confirm("unaligned data is buffered", !r2.has_value());
2✔
376
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
377
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
378
               result.confirm("unaligned data is consumed", half_block.empty());
2✔
379

380
               const auto r3 = b.handle_unaligned_data(full_block_2);
1✔
381
               result.confirm("collected block is consumed", r3.has_value());
2✔
382
               result.confirm("in_alignment()", b.in_alignment());
2✔
383
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
384
               result.test_eq("input is consumed until alignment", full_block_2.remaining(), 16);
1✔
385
               result.end_timer();
1✔
386
            }),
1✔
387

388
      CHECK("Handle unaligned data in Alignment Buffer (with block-defer)",
389
            [=](auto& result) {
1✔
390
               result.start_timer();
1✔
391
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
392

393
               Botan::BufferSlicer first_half(first_half_data);
1✔
394
               Botan::BufferSlicer second_half(second_half_data);
1✔
395
               Botan::BufferSlicer third_half(first_half_data);
1✔
396

397
               const auto r1 = b.handle_unaligned_data(first_half);
1✔
398
               result.confirm("half a block is not returned", !r1.has_value());
2✔
399
               result.confirm("first input is consumed", first_half.empty());
2✔
400

401
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
402
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
403
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
404
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
405

406
               const auto r2 = b.handle_unaligned_data(second_half);
1✔
407
               result.require("second half completes block but is not returned", !r2.has_value());
2✔
408
               result.confirm("second input is consumed", second_half.empty());
2✔
409

410
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
411
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
412
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
413
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
414

415
               const auto r3 = b.handle_unaligned_data(third_half);
1✔
416
               result.require("extra data pushes out block", r3.has_value());
2✔
417
               result.test_eq("third input is not consumed", third_half.remaining(), 16);
2✔
418

419
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
420
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
421
               result.confirm("in_alignment()", b.in_alignment());
2✔
422
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
423

424
               result.test_is_eq("collected block is correct", v(r3.value()), v(data));
3✔
425
               result.end_timer();
1✔
426
            }),
1✔
427

428
      CHECK("Aligned data is not buffered unneccesarily (with block-defer)",
429
            [=](auto& result) {
1✔
430
               result.start_timer();
1✔
431
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
432

433
               Botan::BufferSlicer full_block_1(data);
1✔
434
               const auto r1 = b.handle_unaligned_data(full_block_1);
1✔
435
               result.confirm("exactly aligned data is buffered", !r1.has_value());
2✔
436
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
437
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
438
               result.confirm("exactly aligned block is consumed", full_block_1.empty());
2✔
439

440
               Botan::BufferSlicer empty_input({});
1✔
441
               const auto r2 = b.handle_unaligned_data(empty_input);
1✔
442
               result.require("empty input does not push out buffer", !r2.has_value());
2✔
443
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
444
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
445

446
               const uint8_t extra_byte = 1;
1✔
447
               Botan::BufferSlicer one_extra_byte({&extra_byte, 1});
1✔
448
               const auto r3 = b.handle_unaligned_data(one_extra_byte);
1✔
449
               result.require("more data pushes out buffer", r3.has_value());
2✔
450
               result.confirm("in_alignment()", b.in_alignment());
2✔
451
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
452
               result.test_eq("no input data is consumed", one_extra_byte.remaining(), 1);
1✔
453

454
               result.test_is_eq("collected block is correct", v(r3.value()), v(data));
3✔
455
               result.end_timer();
1✔
456
            }),
1✔
457

458
      CHECK("Aligned data passthrough (no block-defer)",
459
            [=](auto& result) {
1✔
460
               result.start_timer();
1✔
461
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
462
               result.require("buffer is in alignment", b.in_alignment());
1✔
463

464
               Botan::BufferSlicer half_block(first_half_data);
1✔
465
               const auto [s1, r1] = b.aligned_data_to_process(half_block);
1✔
466
               result.confirm("not enough data for alignment processing", s1.empty());
2✔
467
               result.test_eq("not enough data for alignment processing", r1, 0);
2✔
468
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
469

470
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
471
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
472
               const auto [s2, r2] = b.aligned_data_to_process(one_and_a_half_block);
1✔
473
               result.test_eq("data of one block for processing", s2.size(), 32);
1✔
474
               result.test_eq("one block for processing", r2, 1);
1✔
475
               result.test_is_eq(v(s2), v(data));
3✔
476
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
477

478
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
479
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
480
               const auto [s3, r3] = b.aligned_data_to_process(two_blocks);
1✔
481
               result.test_eq("data of two block for processing", s3.size(), 64);
1✔
482
               result.test_eq("two blocks for processing", r3, 2);
2✔
483
               result.test_is_eq(v(s3), two_blocks_data);
2✔
484
               result.test_eq("aligned data is fully consumed", two_blocks.remaining(), 0);
1✔
485
               result.end_timer();
1✔
486
            }),
2✔
487

488
      CHECK("Aligned data blockwise (no block-defer)",
489
            [=](auto& result) {
1✔
490
               result.start_timer();
1✔
491
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
492
               result.require("buffer is in alignment", b.in_alignment());
1✔
493

494
               Botan::BufferSlicer half_block(first_half_data);
1✔
495
               const auto s1 = b.next_aligned_block_to_process(half_block);
1✔
496
               result.confirm("not enough data for alignment processing", !s1.has_value());
2✔
497
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
498

499
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
500
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
501
               const auto s2 = b.next_aligned_block_to_process(one_and_a_half_block);
1✔
502
               result.require("one block for processing", s2.has_value());
2✔
503
               result.test_eq("data of one block for processing", s2->size(), 32);
1✔
504
               result.test_is_eq(v(s2.value()), v(data));
3✔
505
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
506

507
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
508
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
509
               const auto s3_1 = b.next_aligned_block_to_process(two_blocks);
1✔
510
               result.require("first block for processing", s3_1.has_value());
2✔
511
               result.test_eq("data of first block for processing", s3_1->size(), 32);
1✔
512
               result.test_is_eq(v(s3_1.value()), v(data));
3✔
513
               result.test_eq("first block is consumed", two_blocks.remaining(), 32);
1✔
514

515
               const auto s3_2 = b.next_aligned_block_to_process(two_blocks);
1✔
516
               result.require("second block for processing", s3_2.has_value());
2✔
517
               result.test_eq("data of second block for processing", s3_2->size(), 32);
1✔
518
               result.test_is_eq(v(s3_2.value()), v(data));
3✔
519
               result.test_eq("second block is consumed", two_blocks.remaining(), 0);
1✔
520
               result.end_timer();
1✔
521
            }),
2✔
522

523
      CHECK("Aligned data passthrough (with block-defer)",
524
            [=](auto& result) {
1✔
525
               result.start_timer();
1✔
526
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
527
               result.require("buffer is in alignment", b.in_alignment());
1✔
528

529
               Botan::BufferSlicer half_block(first_half_data);
1✔
530
               const auto [s1, r1] = b.aligned_data_to_process(half_block);
1✔
531
               result.confirm("not enough data for alignment processing", s1.empty());
2✔
532
               result.test_eq("not enough data for alignment processing", r1, 0);
2✔
533
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
534

535
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
536
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
537
               const auto [s2, r2] = b.aligned_data_to_process(one_and_a_half_block);
1✔
538
               result.test_eq("data of one block for processing", s2.size(), 32);
1✔
539
               result.test_eq("one block for processing", r2, 1);
1✔
540
               result.test_is_eq(v(s2), v(data));
3✔
541
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
542

543
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
544
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
545
               const auto [s3, r3] = b.aligned_data_to_process(two_blocks);
1✔
546
               result.test_eq("data of first block for processing", s3.size(), 32);
1✔
547
               result.test_eq("one block for processing", r3, 1);
1✔
548
               result.test_is_eq(v(s3), v(data));
3✔
549
               result.test_eq("aligned data is partially consumed", two_blocks.remaining(), 32);
1✔
550
               result.end_timer();
1✔
551
            }),
2✔
552

553
      CHECK("Aligned data blockwise (with block-defer)",
554
            [=](auto& result) {
1✔
555
               result.start_timer();
1✔
556
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
557
               result.require("buffer is in alignment", b.in_alignment());
1✔
558

559
               Botan::BufferSlicer half_block(first_half_data);
1✔
560
               const auto s1 = b.next_aligned_block_to_process(half_block);
1✔
561
               result.confirm("not enough data for alignment processing", !s1.has_value());
2✔
562
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
563

564
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
565
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
566
               const auto s2 = b.next_aligned_block_to_process(one_and_a_half_block);
1✔
567
               result.require("one block for processing", s2.has_value());
2✔
568
               result.test_eq("data of one block for processing", s2->size(), 32);
1✔
569
               result.test_is_eq(v(s2.value()), v(data));
3✔
570
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
571

572
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
573
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
574
               const auto s3_1 = b.next_aligned_block_to_process(two_blocks);
1✔
575
               result.require("first block for processing", s3_1.has_value());
2✔
576
               result.test_eq("data of first block for processing", s3_1->size(), 32);
1✔
577
               result.test_is_eq(v(s3_1.value()), v(data));
3✔
578
               result.test_eq("first block is consumed", two_blocks.remaining(), 32);
1✔
579

580
               const auto s3_2 = b.next_aligned_block_to_process(two_blocks);
1✔
581
               result.confirm("second block is not passed through", !s3_2.has_value());
2✔
582
               result.test_eq("second block is not consumed", two_blocks.remaining(), 32);
1✔
583
               result.end_timer();
1✔
584
            }),
2✔
585
   };
15✔
586
}
1✔
587

588
std::vector<Test::Result> test_concat() {
1✔
589
   return {
1✔
590
      CHECK("empty concat",
591
            [](Test::Result& result) {
1✔
592
               result.start_timer();
1✔
593
               // only define a dynamic output type, but no input to be concat'ed
594
               const auto empty1 = Botan::concat<std::vector<uint8_t>>();
1✔
595
               result.confirm("empty concat 1", empty1.empty());
2✔
596

597
               // pass an empty input buffer to be concat'ed
598
               const auto empty2 = Botan::concat(std::vector<uint8_t>());
1✔
599
               result.confirm("empty concat 2", empty2.empty());
2✔
600

601
               // pass multiple empty input buffers to be concat'ed
602
               const auto empty3 = Botan::concat(std::vector<uint8_t>(), Botan::secure_vector<uint8_t>());
1✔
603
               result.confirm("empty concat 3", empty3.empty());
2✔
604

605
               // pass multiple empty input buffers to be concat'ed without auto-detection of the output buffer
606
               const auto empty4 = Botan::concat<std::array<uint8_t, 0>>(
1✔
607
                  std::vector<uint8_t>(), std::array<uint8_t, 0>(), Botan::secure_vector<uint8_t>());
1✔
608
               result.confirm("empty concat 4", empty4.empty());
2✔
609
               result.end_timer();
1✔
610
            }),
1✔
611

612
      CHECK(
613
         "auto-detected output type",
614
         [](Test::Result& result) {
1✔
615
            result.start_timer();
1✔
616
            // define a static output type without any input parameters
617
            const auto t0 = Botan::concat<std::array<uint8_t, 0>>();
1✔
618
            result.confirm("type 0", std::is_same_v<std::array<uint8_t, 0>, std::remove_cvref_t<decltype(t0)>>);
2✔
619

620
            // define a dynamic output type without any input parameters
621
            const auto t1 = Botan::concat<std::vector<uint8_t>>();
1✔
622
            result.confirm("type 1", std::is_same_v<std::vector<uint8_t>, std::remove_cvref_t<decltype(t1)>>);
2✔
623

624
            // pass a single dynamic input buffer and auto-detect result type
625
            const auto t2 = Botan::concat(std::vector<uint8_t>());
1✔
626
            result.confirm("type 2", std::is_same_v<std::vector<uint8_t>, std::remove_cvref_t<decltype(t2)>>);
2✔
627

628
            // pass multiple dynamic input buffers and auto-detect result type
629
            const auto t3 = Botan::concat(Botan::secure_vector<uint8_t>(), std::vector<uint8_t>());
1✔
630
            result.confirm("type 3", std::is_same_v<Botan::secure_vector<uint8_t>, std::remove_cvref_t<decltype(t3)>>);
2✔
631

632
            // pass a mixture of dynamic and static input buffers and auto-detect result type
633
            const auto t4 =
1✔
634
               Botan::concat(std::vector<uint8_t>(), std::array<uint8_t, 0>(), Botan::secure_vector<uint8_t>());
1✔
635
            result.confirm("type 4", std::is_same_v<std::vector<uint8_t>, std::remove_cvref_t<decltype(t4)>>);
2✔
636

637
            // pass only static input buffers and auto-detect result type
638
            const std::array<uint8_t, 5> some_buffer{};
1✔
639
            const auto t5 = Botan::concat(std::array<uint8_t, 1>(), std::array<uint8_t, 3>(), std::span{some_buffer});
3✔
640
            result.confirm("type 5", std::is_same_v<std::array<uint8_t, 9>, std::remove_cvref_t<decltype(t5)>>);
2✔
641
            result.end_timer();
1✔
642
         }),
1✔
643

644
      CHECK("concatenate",
645
            [](Test::Result& result) {
1✔
646
               result.start_timer();
1✔
647
               constexpr std::array<uint8_t, 5> a1 = {1, 2, 3, 4, 5};
1✔
648
               const std::vector<uint8_t> v1{6, 7, 8, 9, 10};
1✔
649

650
               // concatenate a single buffer
651
               const auto concat0 = Botan::concat<Botan::secure_vector<uint8_t>>(v1);
1✔
652
               result.test_is_eq("concat 0", concat0, {6, 7, 8, 9, 10});
2✔
653

654
               // concatenate into an dynamically allocated output buffer
655
               const auto concat1 = Botan::concat<std::vector<uint8_t>>(a1, v1);
1✔
656
               result.test_is_eq("concat 1", concat1, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
2✔
657

658
               // concatenate into a statically sized output buffer
659
               const auto concat2 = Botan::concat<std::array<uint8_t, 10>>(v1, a1);
1✔
660
               result.test_is_eq("concat 2", concat2, {6, 7, 8, 9, 10, 1, 2, 3, 4, 5});
1✔
661

662
               // concatenate into a statically sized output buffer, that is auto-detected
663
               const auto concat3 = Botan::concat(a1, std::span<const uint8_t, 5>(v1));
1✔
664
               result.test_is_eq("concat 3", concat3, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
1✔
665
               result.confirm("correct type 3",
2✔
666
                              std::is_same_v<std::array<uint8_t, 10>, std::remove_cvref_t<decltype(concat3)>>);
667

668
               // concatenate into a statically sized output buffer, that is auto-detected, at compile time
669
               constexpr auto concat4 = Botan::concat(a1, a1);
1✔
670
               result.test_is_eq("concat 4", concat4, {1, 2, 3, 4, 5, 1, 2, 3, 4, 5});
1✔
671
               result.confirm("correct type 4",
2✔
672
                              std::is_same_v<std::array<uint8_t, 10>, std::remove_cvref_t<decltype(concat4)>>);
673
               result.end_timer();
1✔
674
            }),
3✔
675

676
      CHECK("dynamic length-check",
677
            [](Test::Result& result) {
1✔
678
               result.start_timer();
1✔
679
               std::vector<uint8_t> v1{1, 2, 3, 4, 5};
1✔
680
               std::vector<uint8_t> v2{6, 7, 8, 9, 10};
1✔
681

682
               result.test_throws("concatenate into a statically sized type with insufficient space",
2✔
683
                                  [&]() { Botan::concat<std::array<uint8_t, 4>>(v1, v2); });
2✔
684
               result.test_throws("concatenate into a statically sized type with too much space",
2✔
685
                                  [&]() { Botan::concat<std::array<uint8_t, 20>>(v1, v2); });
2✔
686
               result.end_timer();
1✔
687
            }),
2✔
688

689
      CHECK("concatenate strong types",
690
            [](Test::Result& result) {
1✔
691
               result.start_timer();
1✔
692
               using StrongV = Botan::Strong<std::vector<uint8_t>, struct StrongV_>;
1✔
693
               using StrongA = Botan::Strong<std::array<uint8_t, 4>, struct StrongA_>;
1✔
694

695
               StrongV v1(std::vector<uint8_t>{1, 2, 3, 4, 5});
1✔
696
               StrongA a2;
1✔
697
               a2[0] = 6;
1✔
698
               a2[1] = 7;
1✔
699
               a2[2] = 8;
1✔
700
               a2[3] = 9;
1✔
701

702
               // concat strong types into a verbatim type
703
               auto concat1 = Botan::concat<std::vector<uint8_t>>(v1, a2);
1✔
704
               result.test_is_eq("concat 1", concat1, {1, 2, 3, 4, 5, 6, 7, 8, 9});
2✔
705

706
               // concat strong types into a dynamically allocated strong type
707
               auto concat2 = Botan::concat<StrongV>(a2, v1);
1✔
708
               result.test_is_eq("concat 2", concat2.get(), {6, 7, 8, 9, 1, 2, 3, 4, 5});
2✔
709
               result.end_timer();
1✔
710
            }),
3✔
711
   };
6✔
712
}
1✔
713

714
BOTAN_REGISTER_TEST_FN(
715
   "utils", "buffer_utilities", test_buffer_slicer, test_buffer_stuffer, test_alignment_buffer, test_concat);
716

717
}  // namespace
718

719
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc