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

randombit / botan / 21972093278

13 Feb 2026 02:16AM UTC coverage: 90.067%. Remained the same
21972093278

Pull #5321

github

web-flow
Merge a72b55147 into e7443105f
Pull Request #5321: Avoid various unneeded include files

102230 of 113505 relevant lines covered (90.07%)

11457215.35 hits per line

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

99.57
/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/mem_ops.h>
11
#include <botan/internal/alignment_buffer.h>
12
#include <botan/internal/buffer_stuffer.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
               const std::vector<uint8_t> buffer(0);
1✔
33
               Botan::BufferSlicer s(buffer);
1✔
34
               result.confirm("empty slicer has no remaining bytes", s.remaining() == 0);
2✔
35
               result.confirm("empty slicer is empty()", s.empty());
2✔
36
               result.confirm("empty slicer can take() 0 bytes", s.take(0).empty());
2✔
37

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

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

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

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

59
               result.test_eq("remaining bytes", s.remaining(), 8);
2✔
60

61
               s.skip(1);
1✔
62
               result.test_eq("remaining bytes", s.remaining(), 7);
2✔
63

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

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

77
               s.skip(1);
1✔
78
               result.test_eq("remaining bytes", s.remaining(), 1);
1✔
79

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

85
               result.confirm("empty", s.empty());
2✔
86
               result.test_eq("nothing remaining", s.remaining(), 0);
1✔
87

88
               result.test_throws("empty slicer cannot emit bytes", [&]() { s.take(1); });
3✔
89
               result.test_throws("empty slicer cannot skip bytes", [&]() { s.skip(1); });
3✔
90
               result.test_throws("empty slicer cannot copy bytes", [&]() { s.copy_as_vector(1); });
4✔
91
            }),
3✔
92

93
      CHECK("Strong type support",
94
            [](auto& result) {
1✔
95
               const Botan::secure_vector<uint8_t> secure_buffer{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
1✔
96
               Botan::BufferSlicer s(secure_buffer);
1✔
97

98
               auto span1 = s.take(1);
1✔
99
               auto span2 = s.take<StrongBuffer>(2);
1✔
100
               auto vec1 = s.copy<StrongBuffer>(2);
1✔
101
               auto vec2 = s.copy_as_vector(2);
1✔
102
               auto vec3 = s.copy_as_secure_vector(2);
1✔
103
               StrongBuffer vec4(s.remaining());
1✔
104
               s.copy_into(vec4);
1✔
105

106
               const auto reproduce = Botan::concat<std::vector<uint8_t>>(span1, span2, vec1, vec2, vec3, vec4);
1✔
107
               result.test_eq("sliced into various types", reproduce, secure_buffer);
2✔
108
            }),
6✔
109
   };
4✔
110
}
1✔
111

112
std::vector<Test::Result> test_buffer_stuffer() {
1✔
113
   return {
1✔
114
      CHECK("Empty BufferStuffer",
115
            [](auto& result) {
1✔
116
               std::vector<uint8_t> empty_buffer;
1✔
117
               Botan::BufferStuffer s(empty_buffer);
1✔
118

119
               result.test_eq("has no capacity", s.remaining_capacity(), 0);
2✔
120
               result.confirm("is immediately full", s.full());
2✔
121
               result.confirm("can next() 0 bytes", s.next(0).empty());
2✔
122

123
               result.test_throws("cannot next() anything", [&]() { s.next(1); });
3✔
124
               result.test_throws("cannot append bytes", [&]() {
4✔
125
                  std::vector<uint8_t> some_bytes(42);
1✔
126
                  s.append(some_bytes);
1✔
127
               });
×
128
            }),
1✔
129

130
      CHECK("Fill BufferStuffer",
131
            [](auto& result) {
1✔
132
               std::vector<uint8_t> sink(13);
1✔
133
               Botan::BufferStuffer s(sink);
1✔
134

135
               result.test_eq("has some capacity", s.remaining_capacity(), sink.size());
2✔
136
               result.confirm("is not full", !s.full());
2✔
137

138
               auto n1 = s.next(5);
1✔
139
               result.require("got requested bytes", n1.size() == 5);
2✔
140
               n1[0] = 'h';
1✔
141
               n1[1] = 'e';
1✔
142
               n1[2] = 'l';
1✔
143
               n1[3] = 'l';
1✔
144
               n1[4] = 'o';
1✔
145

146
               auto n2 = s.next<StrongBuffer>(3);
1✔
147
               result.require("got requested bytes", n2.size() == 3);
1✔
148

149
               n2.get()[0] = ' ';
1✔
150
               n2.get()[1] = 'w';
1✔
151
               n2.get()[2] = 'o';
1✔
152

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

155
               std::vector<uint8_t> rld{'r', 'l', 'd'};
1✔
156
               s.append(rld);
1✔
157

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

160
               auto n3 = s.next<2>();
1✔
161
               result.require("got requested bytes", n3.size() == 2);
2✔
162
               result.require("is static extent", decltype(n3)::extent != std::dynamic_extent);
2✔
163
               result.require("static extent is 2", decltype(n3)::extent == 2);
2✔
164

165
               n3[0] = ' ';
1✔
166
               n3[1] = '!';
1✔
167

168
               result.confirm("is full", s.full());
2✔
169

170
               result.test_throws("cannot next() anything", [&]() { s.next(1); });
3✔
171
               result.test_throws("cannot append bytes", [&]() {
4✔
172
                  std::vector<uint8_t> some_bytes(42);
1✔
173
                  s.append(some_bytes);
1✔
174
               });
×
175

176
               const std::string final_string(Botan::cast_uint8_ptr_to_char(sink.data()), sink.size());
1✔
177
               result.test_eq("final buffer is as expected", final_string, "hello world !");
2✔
178
            }),
3✔
179
   };
3✔
180
}
1✔
181

182
std::vector<Test::Result> test_alignment_buffer() {
1✔
183
   const std::array<uint8_t, 32> data = {1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
1✔
184
                                         17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
185
   const 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✔
186
   const 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✔
187

188
   return {
1✔
189
      CHECK("Fresh Alignment Buffer",
190
            [](auto& result) {
1✔
191
               const Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
192
               result.test_eq("size()", b.size(), 32);
2✔
193
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
194
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
195
               result.confirm("in_alignment()", b.in_alignment());
2✔
196
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
197
            }),
1✔
198

199
      CHECK("Fill Alignment Buffer",
200
            [=](auto& result) {
1✔
201
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
202

203
               b.append(first_half_data);
2✔
204

205
               result.test_eq("size()", b.size(), 32);
2✔
206
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
207
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
208
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
209
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
210

211
               b.append(second_half_data);
1✔
212

213
               result.test_eq("size()", b.size(), 32);
2✔
214
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
215
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
216
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
217
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
218
            }),
1✔
219

220
      CHECK("Consume Alignment Buffer",
221
            [=](auto& result) {
1✔
222
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
223

224
               b.append(data);
1✔
225

226
               result.require("ready_to_consume()", b.ready_to_consume());
1✔
227
               const auto out = b.consume();
1✔
228

229
               result.test_eq("size()", b.size(), 32);
2✔
230
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
231
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
232
               result.confirm("in_alignment()", b.in_alignment());
2✔
233
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
234

235
               result.test_is_eq("in == out", v(data), v(out));
4✔
236
            }),
1✔
237

238
      CHECK("Consume partially filled Alignment Buffer",
239
            [=](auto& result) {
1✔
240
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
241

242
               result.require("!ready_to_consume()", !b.ready_to_consume());
2✔
243
               const auto out_empty = b.consume_partial();
1✔
244
               result.test_eq("consuming nothing resets buffer", b.elements_in_buffer(), 0);
2✔
245
               result.confirm("consumed empty buffer", out_empty.empty());
2✔
246

247
               b.append(first_half_data);
1✔
248
               result.require("!ready_to_consume()", !b.ready_to_consume());
2✔
249
               const auto out_half = b.consume_partial();
1✔
250
               result.test_eq("consumed half-data resets buffer", b.elements_in_buffer(), 0);
1✔
251
               result.test_is_eq("half-in == half-out", v(out_half), v(first_half_data));
3✔
252

253
               b.append(data);
1✔
254
               result.require("ready_to_consume()", b.ready_to_consume());
2✔
255
               const auto out_full = b.consume_partial();
1✔
256
               result.test_eq("consumed full-data resets buffer", b.elements_in_buffer(), 0);
1✔
257
               result.test_is_eq("full-in == full-out", v(out_full), v(data));
4✔
258
            }),
1✔
259

260
      CHECK("Clear Alignment Buffer",
261
            [=](auto& result) {
1✔
262
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
263

264
               b.append(first_half_data);
1✔
265

266
               result.require("elements_in_buffer()", b.elements_in_buffer() == 16);
2✔
267
               b.clear();
1✔
268

269
               result.test_eq("size()", b.size(), 32);
2✔
270
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
271
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
272
               result.confirm("in_alignment()", b.in_alignment());
2✔
273
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
274
            }),
1✔
275

276
      CHECK("Add Zero-Padding to Alignment Buffer",
277
            [=](auto& result) {
1✔
278
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
279

280
               b.append(first_half_data);
1✔
281

282
               result.require("elements_in_buffer()", b.elements_in_buffer() == 16);
2✔
283
               b.fill_up_with_zeros();
1✔
284

285
               result.test_eq("size()", b.size(), 32);
2✔
286
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
287
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
288
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
289
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
290

291
               const auto out = b.consume();
1✔
292

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

296
               // Regression test for GH #3734
297
               // fill_up_with_zeros() must work if called on a full (ready to consume) alignment buffer
298
               b.append(data);
1✔
299
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
300
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
301

302
               b.fill_up_with_zeros();
1✔
303
               const auto out_without_padding = b.consume();
1✔
304

305
               result.test_is_eq("no padding", v(out_without_padding), v(data));
4✔
306
            }),
1✔
307

308
      CHECK("Handle unaligned data in Alignment Buffer (no block-defer)",
309
            [=](auto& result) {
1✔
310
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
311

312
               Botan::BufferSlicer first_half(first_half_data);
1✔
313
               Botan::BufferSlicer second_half(second_half_data);
1✔
314

315
               const auto r1 = b.handle_unaligned_data(first_half);
1✔
316
               result.confirm("half a block is not returned", !r1.has_value());
2✔
317
               result.confirm("first input is consumed", first_half.empty());
2✔
318

319
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
320
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
321
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
322
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
323

324
               const auto r2 = b.handle_unaligned_data(second_half);
1✔
325
               result.require("second half completes block", r2.has_value());
2✔
326
               result.confirm("second input is consumed", second_half.empty());
2✔
327

328
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
329
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
330
               result.confirm("in_alignment()", b.in_alignment());
2✔
331
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
332

333
               result.test_is_eq("collected block is correct", v(r2.value()), v(data));
4✔
334
            }),
1✔
335

336
      CHECK("Aligned data is not buffered unnecessarily (no block-defer)",
337
            [=](auto& result) {
1✔
338
               Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
339

340
               Botan::BufferSlicer full_block_1(data);
1✔
341
               const auto r1 = b.handle_unaligned_data(full_block_1);
1✔
342
               result.confirm("aligned data is not buffered", !r1.has_value());
2✔
343
               result.confirm("in_alignment()", b.in_alignment());
2✔
344
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
345
               result.test_eq("aligned data is not consumed", full_block_1.remaining(), 32);
1✔
346

347
               Botan::BufferSlicer half_block(first_half_data);
1✔
348
               Botan::BufferSlicer full_block_2(data);
1✔
349
               const auto r2 = b.handle_unaligned_data(half_block);
1✔
350
               result.confirm("unaligned data is buffered", !r2.has_value());
2✔
351
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
352
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
353
               result.confirm("unaligned data is consumed", half_block.empty());
2✔
354

355
               const auto r3 = b.handle_unaligned_data(full_block_2);
1✔
356
               result.confirm("collected block is consumed", r3.has_value());
2✔
357
               result.confirm("in_alignment()", b.in_alignment());
2✔
358
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
359
               result.test_eq("input is consumed until alignment", full_block_2.remaining(), 16);
2✔
360
            }),
1✔
361

362
      CHECK("Handle unaligned data in Alignment Buffer (with block-defer)",
363
            [=](auto& result) {
1✔
364
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
365

366
               Botan::BufferSlicer first_half(first_half_data);
1✔
367
               Botan::BufferSlicer second_half(second_half_data);
1✔
368
               Botan::BufferSlicer third_half(first_half_data);
1✔
369

370
               const auto r1 = b.handle_unaligned_data(first_half);
1✔
371
               result.confirm("half a block is not returned", !r1.has_value());
2✔
372
               result.confirm("first input is consumed", first_half.empty());
2✔
373

374
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 16);
2✔
375
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 16);
2✔
376
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
377
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
378

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

383
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 32);
2✔
384
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 0);
2✔
385
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
386
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
387

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

392
               result.test_eq("elements_in_buffer()", b.elements_in_buffer(), 0);
2✔
393
               result.test_eq("elements_until_alignment()", b.elements_until_alignment(), 32);
2✔
394
               result.confirm("in_alignment()", b.in_alignment());
2✔
395
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
396

397
               result.test_is_eq("collected block is correct", v(r3.value()), v(data));
4✔
398
            }),
1✔
399

400
      CHECK("Aligned data is not buffered unnecessarily (with block-defer)",
401
            [=](auto& result) {
1✔
402
               Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
403

404
               Botan::BufferSlicer full_block_1(data);
1✔
405
               const auto r1 = b.handle_unaligned_data(full_block_1);
1✔
406
               result.confirm("exactly aligned data is buffered", !r1.has_value());
2✔
407
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
408
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
409
               result.confirm("exactly aligned block is consumed", full_block_1.empty());
2✔
410

411
               Botan::BufferSlicer empty_input({});
1✔
412
               const auto r2 = b.handle_unaligned_data(empty_input);
1✔
413
               result.require("empty input does not push out buffer", !r2.has_value());
2✔
414
               result.confirm("!in_alignment()", !b.in_alignment());
2✔
415
               result.confirm("ready_to_consume()", b.ready_to_consume());
2✔
416

417
               const uint8_t extra_byte = 1;
1✔
418
               Botan::BufferSlicer one_extra_byte({&extra_byte, 1});
1✔
419
               const auto r3 = b.handle_unaligned_data(one_extra_byte);
1✔
420
               result.require("more data pushes out buffer", r3.has_value());
2✔
421
               result.confirm("in_alignment()", b.in_alignment());
2✔
422
               result.confirm("!ready_to_consume()", !b.ready_to_consume());
2✔
423
               result.test_eq("no input data is consumed", one_extra_byte.remaining(), 1);
1✔
424

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

428
      CHECK("Aligned data passthrough (no block-defer)",
429
            [=](auto& result) {
1✔
430
               const Botan::AlignmentBuffer<uint8_t, 32> b;
1✔
431
               result.require("buffer is in alignment", b.in_alignment());
1✔
432

433
               Botan::BufferSlicer half_block(first_half_data);
1✔
434
               const auto [s1, r1] = b.aligned_data_to_process(half_block);
1✔
435
               result.confirm("not enough data for alignment processing", s1.empty());
2✔
436
               result.test_eq("not enough data for alignment processing", r1, 0);
2✔
437
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
438

439
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
440
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
441
               const auto [s2, r2] = b.aligned_data_to_process(one_and_a_half_block);
1✔
442
               result.test_eq("data of one block for processing", s2.size(), 32);
1✔
443
               result.test_eq("one block for processing", r2, 1);
1✔
444
               result.test_is_eq(v(s2), v(data));
3✔
445
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
446

447
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
448
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
449
               const auto [s3, r3] = b.aligned_data_to_process(two_blocks);
1✔
450
               result.test_eq("data of two block for processing", s3.size(), 64);
1✔
451
               result.test_eq("two blocks for processing", r3, 2);
2✔
452
               result.test_is_eq(v(s3), two_blocks_data);
2✔
453
               result.test_eq("aligned data is fully consumed", two_blocks.remaining(), 0);
2✔
454
            }),
2✔
455

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

461
               Botan::BufferSlicer half_block(first_half_data);
1✔
462
               const auto s1 = b.next_aligned_block_to_process(half_block);
1✔
463
               result.confirm("not enough data for alignment processing", !s1.has_value());
2✔
464
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
465

466
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
467
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
468
               const auto s2 = b.next_aligned_block_to_process(one_and_a_half_block);
1✔
469
               result.require("one block for processing", s2.has_value());
2✔
470
               result.test_eq("data of one block for processing", s2->size(), 32);
1✔
471
               result.test_is_eq(v(s2.value()), v(data));
3✔
472
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
473

474
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
475
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
476
               const auto s3_1 = b.next_aligned_block_to_process(two_blocks);
1✔
477
               result.require("first block for processing", s3_1.has_value());
2✔
478
               result.test_eq("data of first block for processing", s3_1->size(), 32);
1✔
479
               result.test_is_eq(v(s3_1.value()), v(data));
3✔
480
               result.test_eq("first block is consumed", two_blocks.remaining(), 32);
1✔
481

482
               const auto s3_2 = b.next_aligned_block_to_process(two_blocks);
1✔
483
               result.require("second block for processing", s3_2.has_value());
2✔
484
               result.test_eq("data of second block for processing", s3_2->size(), 32);
1✔
485
               result.test_is_eq(v(s3_2.value()), v(data));
3✔
486
               result.test_eq("second block is consumed", two_blocks.remaining(), 0);
2✔
487
            }),
2✔
488

489
      CHECK("Aligned data passthrough (with block-defer)",
490
            [=](auto& result) {
1✔
491
               const Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> 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, r1] = b.aligned_data_to_process(half_block);
1✔
496
               result.confirm("not enough data for alignment processing", s1.empty());
2✔
497
               result.test_eq("not enough data for alignment processing", r1, 0);
2✔
498
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
499

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

508
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
509
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
510
               const auto [s3, r3] = b.aligned_data_to_process(two_blocks);
1✔
511
               result.test_eq("data of first block for processing", s3.size(), 32);
1✔
512
               result.test_eq("one block for processing", r3, 1);
1✔
513
               result.test_is_eq(v(s3), v(data));
3✔
514
               result.test_eq("aligned data is partially consumed", two_blocks.remaining(), 32);
2✔
515
            }),
2✔
516

517
      CHECK("Aligned data blockwise (with block-defer)",
518
            [=](auto& result) {
1✔
519
               const Botan::AlignmentBuffer<uint8_t, 32, Botan::AlignmentBufferFinalBlock::must_be_deferred> b;
1✔
520
               result.require("buffer is in alignment", b.in_alignment());
1✔
521

522
               Botan::BufferSlicer half_block(first_half_data);
1✔
523
               const auto s1 = b.next_aligned_block_to_process(half_block);
1✔
524
               result.confirm("not enough data for alignment processing", !s1.has_value());
2✔
525
               result.test_eq("(short) unaligned data is not consumed", half_block.remaining(), 16);
1✔
526

527
               const auto more_than_one_block = Botan::concat<std::vector<uint8_t>>(data, first_half_data);
1✔
528
               Botan::BufferSlicer one_and_a_half_block(more_than_one_block);
1✔
529
               const auto s2 = b.next_aligned_block_to_process(one_and_a_half_block);
1✔
530
               result.require("one block for processing", s2.has_value());
2✔
531
               result.test_eq("data of one block for processing", s2->size(), 32);
1✔
532
               result.test_is_eq(v(s2.value()), v(data));
3✔
533
               result.test_eq("(middle) unaligned data is not consumed", one_and_a_half_block.remaining(), 16);
1✔
534

535
               const auto two_blocks_data = Botan::concat<std::vector<uint8_t>>(data, data);
1✔
536
               Botan::BufferSlicer two_blocks(two_blocks_data);
1✔
537
               const auto s3_1 = b.next_aligned_block_to_process(two_blocks);
1✔
538
               result.require("first block for processing", s3_1.has_value());
2✔
539
               result.test_eq("data of first block for processing", s3_1->size(), 32);
1✔
540
               result.test_is_eq(v(s3_1.value()), v(data));
3✔
541
               result.test_eq("first block is consumed", two_blocks.remaining(), 32);
1✔
542

543
               const auto s3_2 = b.next_aligned_block_to_process(two_blocks);
1✔
544
               result.confirm("second block is not passed through", !s3_2.has_value());
2✔
545
               result.test_eq("second block is not consumed", two_blocks.remaining(), 32);
2✔
546
            }),
2✔
547
   };
15✔
548
}
1✔
549

550
std::vector<Test::Result> test_concat() {
1✔
551
   return {
1✔
552
      CHECK("empty concat",
553
            [](Test::Result& result) {
1✔
554
               // only define a dynamic output type, but no input to be concat'ed
555
               const auto empty1 = Botan::concat<std::vector<uint8_t>>();
1✔
556
               result.confirm("empty concat 1", empty1.empty());
2✔
557

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

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

566
               // pass multiple empty input buffers to be concat'ed without auto-detection of the output buffer
567
               const auto empty4 = Botan::concat<std::array<uint8_t, 0>>(
1✔
568
                  std::vector<uint8_t>(), std::array<uint8_t, 0>(), Botan::secure_vector<uint8_t>());
1✔
569
               result.confirm("empty concat 4", empty4.empty());
2✔
570
            }),
1✔
571

572
      CHECK(
573
         "auto-detected output type",
574
         [](Test::Result& result) {
1✔
575
            // define a static output type without any input parameters
576
            const auto t0 = Botan::concat<std::array<uint8_t, 0>>();
1✔
577
            result.confirm("type 0", std::is_same_v<std::array<uint8_t, 0>, std::remove_cvref_t<decltype(t0)>>);
2✔
578

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

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

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

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

596
            // pass only static input buffers and auto-detect result type
597
            const std::array<uint8_t, 5> some_buffer{};
1✔
598
            const auto t5 = Botan::concat(std::array<uint8_t, 1>(), std::array<uint8_t, 3>(), std::span{some_buffer});
3✔
599
            result.confirm("type 5", std::is_same_v<std::array<uint8_t, 9>, std::remove_cvref_t<decltype(t5)>>);
2✔
600
         }),
1✔
601

602
      CHECK("concatenate",
603
            [](Test::Result& result) {
1✔
604
               constexpr std::array<uint8_t, 5> a1 = {1, 2, 3, 4, 5};
1✔
605
               const std::vector<uint8_t> v1{6, 7, 8, 9, 10};
1✔
606

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

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

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

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

625
               // concatenate into a statically sized output buffer, that is auto-detected, at compile time
626
               constexpr auto concat4 = Botan::concat(a1, a1);
1✔
627
               result.test_is_eq("concat 4", concat4, {1, 2, 3, 4, 5, 1, 2, 3, 4, 5});
1✔
628
               result.confirm("correct type 4",
2✔
629
                              std::is_same_v<std::array<uint8_t, 10>, std::remove_cvref_t<decltype(concat4)>>);
630
            }),
3✔
631

632
      CHECK("dynamic length-check",
633
            [](Test::Result& result) {
1✔
634
               std::vector<uint8_t> v1{1, 2, 3, 4, 5};
1✔
635
               std::vector<uint8_t> v2{6, 7, 8, 9, 10};
1✔
636

637
               result.test_throws("concatenate into a statically sized type with insufficient space",
2✔
638
                                  [&]() { Botan::concat<std::array<uint8_t, 4>>(v1, v2); });
2✔
639
               result.test_throws("concatenate into a statically sized type with too much space",
3✔
640
                                  [&]() { Botan::concat<std::array<uint8_t, 20>>(v1, v2); });
2✔
641
            }),
2✔
642

643
      CHECK("concatenate strong types",
644
            [](Test::Result& result) {
1✔
645
               using StrongV = Botan::Strong<std::vector<uint8_t>, struct StrongV_>;
1✔
646
               using StrongA = Botan::Strong<std::array<uint8_t, 4>, struct StrongA_>;
1✔
647

648
               StrongV v1(std::vector<uint8_t>{1, 2, 3, 4, 5});
1✔
649
               StrongA a2;
1✔
650
               a2[0] = 6;
1✔
651
               a2[1] = 7;
1✔
652
               a2[2] = 8;
1✔
653
               a2[3] = 9;
1✔
654

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

659
               // concat strong types into a dynamically allocated strong type
660
               auto concat2 = Botan::concat<StrongV>(a2, v1);
1✔
661
               result.test_is_eq("concat 2", concat2.get(), {6, 7, 8, 9, 1, 2, 3, 4, 5});
3✔
662
            }),
3✔
663
   };
6✔
664
}
1✔
665

666
BOTAN_REGISTER_TEST_FN(
667
   "utils", "buffer_utilities", test_buffer_slicer, test_buffer_stuffer, test_alignment_buffer, test_concat);
668

669
}  // namespace
670

671
}  // 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

© 2026 Coveralls, Inc