1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
16
17 #include <gmock/gmock.h>
18
19 #include <cstddef>
20 #include <type_traits>
21
22 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
23 #include "pw_unit_test/framework.h"
24
25 #pragma clang diagnostic ignored "-Wc99-extensions"
26
27 namespace bt {
28 namespace {
29
TEST(ByteBufferTest,StaticByteBuffer)30 TEST(ByteBufferTest, StaticByteBuffer) {
31 constexpr size_t kBufferSize = 5;
32 StaticByteBuffer<kBufferSize> buffer;
33
34 // The buffer is initialized to 0.
35 constexpr std::array<uint8_t, kBufferSize> kExpectedDefault{
36 {0x00, 0x00, 0x00, 0x00, 0x00}};
37 EXPECT_TRUE(ContainersEqual(kExpectedDefault, buffer));
38
39 EXPECT_EQ(kBufferSize, buffer.size());
40 buffer.SetToZeros();
41 buffer[3] = 3;
42
43 constexpr std::array<uint8_t, kBufferSize> kExpected{
44 {0x00, 0x00, 0x00, 0x03, 0x00}};
45 EXPECT_TRUE(ContainersEqual(kExpected, buffer));
46
47 // Moving will result in a copy.
48 StaticByteBuffer<kBufferSize> buffer_copy =
49 std::move(buffer); // NOLINT(performance-move-const-arg)
50 EXPECT_EQ(kBufferSize, buffer.size()); // NOLINT(bugprone-use-after-move)
51 EXPECT_EQ(kBufferSize, buffer_copy.size());
52 EXPECT_TRUE(
53 ContainersEqual(kExpected, buffer)); // NOLINT(bugprone-use-after-move)
54 EXPECT_TRUE(ContainersEqual(kExpected, buffer_copy));
55
56 // Copying should also copy.
57 StaticByteBuffer<kBufferSize> buffer_copy1 = buffer;
58 EXPECT_EQ(kBufferSize, buffer.size());
59 EXPECT_EQ(kBufferSize, buffer_copy1.size());
60 EXPECT_TRUE(ContainersEqual(kExpected, buffer));
61 EXPECT_TRUE(ContainersEqual(kExpected, buffer_copy1));
62
63 // Const ByteBuffer should still permit operator[] access.
64 const StaticByteBuffer const_buff(0x10);
65 EXPECT_EQ(0x10, const_buff[0]);
66 }
67
TEST(ByteBufferTest,StaticByteBufferPackConstructor)68 TEST(ByteBufferTest, StaticByteBufferPackConstructor) {
69 constexpr size_t kBufferSize = 3;
70 StaticByteBuffer<kBufferSize> buffer0;
71 buffer0[0] = 0x01;
72 buffer0[1] = 0x02;
73 buffer0[2] = 0x03;
74
75 StaticByteBuffer<kBufferSize> buffer1{0x01, 0x02, 0x03};
76 StaticByteBuffer buffer2(0x01, 0x02, 0x03);
77 StaticByteBuffer buffer3{0x01, 0x02, 0x03};
78
79 EXPECT_TRUE(ContainersEqual(buffer0, buffer1));
80 EXPECT_TRUE(ContainersEqual(buffer0, buffer2));
81 EXPECT_TRUE(ContainersEqual(buffer1, buffer2));
82 EXPECT_TRUE(ContainersEqual(buffer1, buffer3));
83
84 // The corresponding check is a BT_DEBUG_ASSERT runtime check
85 EXPECT_DEBUG_DEATH(StaticByteBuffer(-257), "ASSERT");
86 }
87
TEST(ByteBufferTest,DynamicByteBuffer)88 TEST(ByteBufferTest, DynamicByteBuffer) {
89 constexpr size_t kBufferSize = 5;
90 DynamicByteBuffer buffer(kBufferSize);
91 EXPECT_EQ(kBufferSize, buffer.size());
92
93 // The buffer is initialized to 0.
94 constexpr std::array<uint8_t, kBufferSize> kExpectedDefault{
95 {0x00, 0x00, 0x00, 0x00, 0x00}};
96 EXPECT_TRUE(ContainersEqual(kExpectedDefault, buffer));
97
98 buffer[3] = 3;
99 constexpr std::array<uint8_t, kBufferSize> kExpected{
100 {0x00, 0x00, 0x00, 0x03, 0x00}};
101 EXPECT_TRUE(ContainersEqual(kExpected, buffer));
102
103 // Moving will invalidate the source buffer.
104 DynamicByteBuffer buffer_moved = std::move(buffer);
105 EXPECT_EQ(0u, buffer.size()); // NOLINT(bugprone-use-after-move)
106 EXPECT_EQ(kBufferSize, buffer_moved.size());
107 EXPECT_EQ(nullptr, buffer.data()); // NOLINT(bugprone-use-after-move)
108 EXPECT_TRUE(ContainersEqual(kExpected, buffer_moved));
109
110 // Const ByteBuffer copied from buffer_moved should permit operator[] access.
111 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
112 const DynamicByteBuffer const_buff(buffer_moved);
113 EXPECT_EQ(0x03, const_buff[3]);
114 }
115
TEST(ByteBufferTest,DynamicByteBufferZeroSize)116 TEST(ByteBufferTest, DynamicByteBufferZeroSize) {
117 DynamicByteBuffer buffer;
118
119 EXPECT_EQ(0u, buffer.size());
120 EXPECT_EQ(nullptr, buffer.data());
121
122 DynamicByteBuffer zerosize(0);
123
124 EXPECT_EQ(0u, zerosize.size());
125 EXPECT_EQ(nullptr, buffer.data());
126 }
127
TEST(ByteBufferTest,DynamicByteBufferConstructFromBuffer)128 TEST(ByteBufferTest, DynamicByteBufferConstructFromBuffer) {
129 constexpr size_t kBufferSize = 3;
130 StaticByteBuffer<kBufferSize> buffer(1, 2, 3);
131
132 DynamicByteBuffer dyn_buffer(buffer);
133 EXPECT_EQ(kBufferSize, dyn_buffer.size());
134 EXPECT_TRUE(ContainersEqual(buffer, dyn_buffer));
135 }
136
TEST(ByteBufferTest,DynamicByteBufferExplicitCopy)137 TEST(ByteBufferTest, DynamicByteBufferExplicitCopy) {
138 DynamicByteBuffer src(1);
139 src[0] = 'a';
140
141 DynamicByteBuffer dst(src);
142 EXPECT_EQ(1u, dst.size());
143 EXPECT_TRUE(ContainersEqual(src, dst));
144 }
145
TEST(ByteBufferTest,DynamicByteBufferConstructFromBytes)146 TEST(ByteBufferTest, DynamicByteBufferConstructFromBytes) {
147 constexpr size_t kBufferSize = 3;
148 std::array<uint8_t, kBufferSize> kExpected{{0, 1, 2}};
149
150 auto bytes = std::make_unique<uint8_t[]>(kBufferSize);
151 std::memcpy(bytes.get(), kExpected.data(), kBufferSize);
152
153 DynamicByteBuffer buffer(kBufferSize, std::move(bytes));
154 EXPECT_EQ(nullptr, bytes.get());
155 EXPECT_TRUE(ContainersEqual(kExpected, buffer));
156 }
157
TEST(ByteBufferTest,DynamicByteBufferConstructFromString)158 TEST(ByteBufferTest, DynamicByteBufferConstructFromString) {
159 constexpr size_t kBufferSize = 3;
160 std::array<uint8_t, kBufferSize> kExpected{{0x41, 0x42, 0x43}};
161
162 auto str = std::string("ABC");
163 DynamicByteBuffer buffer(str);
164
165 EXPECT_EQ(std::string("ABC"), str);
166 EXPECT_TRUE(ContainersEqual(kExpected, buffer));
167 }
168
TEST(ByteBufferTest,BufferViewTest)169 TEST(ByteBufferTest, BufferViewTest) {
170 constexpr size_t kBufferSize = 5;
171 DynamicByteBuffer buffer(kBufferSize);
172
173 EXPECT_EQ(kBufferSize, buffer.size());
174 buffer.SetToZeros();
175
176 BufferView view(buffer);
177 EXPECT_EQ(0x00, buffer[0]);
178 EXPECT_EQ(0x00, view[0]);
179 EXPECT_EQ(kBufferSize, buffer.size());
180 EXPECT_EQ(kBufferSize, view.size());
181 }
182
TEST(ByteBufferTest,BufferViewFromVector)183 TEST(ByteBufferTest, BufferViewFromVector) {
184 const std::vector<uint8_t> kData{{1, 2, 3}};
185 BufferView view(kData);
186 EXPECT_TRUE(ContainersEqual(kData, view));
187 }
188
TEST(ByteBufferTest,MutableBufferViewTest)189 TEST(ByteBufferTest, MutableBufferViewTest) {
190 constexpr size_t kBufferSize = 5;
191 constexpr size_t kViewSize = 3;
192 DynamicByteBuffer buffer(kBufferSize);
193
194 EXPECT_EQ(kBufferSize, buffer.size());
195 buffer.SetToZeros();
196
197 MutableBufferView view(buffer.mutable_data(), kViewSize);
198 view[0] = 0x01;
199 EXPECT_EQ(0x01, buffer[0]);
200 EXPECT_EQ(0x01, view[0]);
201 EXPECT_EQ(kBufferSize, buffer.size());
202 EXPECT_EQ(kViewSize, view.size());
203
204 MutableBufferView view2(view);
205 view2[0] = 0x00;
206 EXPECT_EQ(0x00, buffer[0]);
207 EXPECT_EQ(0x00, view[0]);
208 EXPECT_EQ(0x00, view2[0]);
209 EXPECT_EQ(kBufferSize, buffer.size());
210 EXPECT_EQ(kViewSize, view.size());
211 }
212
TEST(ByteBufferDeathTest,Copy)213 TEST(ByteBufferDeathTest, Copy) {
214 StaticByteBuffer buffer('T', 'e', 's', 't');
215 BufferView empty_buffer;
216
217 // Create a large enough buffer.
218 StaticByteBuffer<10> target_buffer;
219
220 // Copying an empty buffer should copy 0 bytes.
221 empty_buffer.Copy(&target_buffer);
222
223 // Copy all of |buffer|. The first |buffer.size()| octets of |target_buffer|
224 // should match the contents of |buffer|.
225 buffer.Copy(&target_buffer);
226 EXPECT_TRUE(
227 ContainersEqual(buffer, BufferView(target_buffer, buffer.size())));
228
229 // Copy one octet of |buffer| starting at index 2
230 target_buffer.SetToZeros();
231 buffer.Copy(&target_buffer, 1, 1);
232 EXPECT_EQ(buffer[1], target_buffer[0]);
233
234 // Zero the buffer and copy its contents for later comparison.
235 target_buffer.SetToZeros();
236 auto target_buffer_copy = target_buffer;
237
238 // Copy all remaining octets starting just past the end of |buffer|. This
239 // should copy zero bytes and |target_buffer| should remain unchanged.
240 buffer.Copy(&target_buffer, buffer.size(), 0);
241 EXPECT_TRUE(ContainersEqual(target_buffer_copy, target_buffer));
242
243 // Copied range must remain within buffer limits
244 EXPECT_DEATH_IF_SUPPORTED(buffer.Copy(&target_buffer, buffer.size() + 1, 0),
245 "offset exceeds source range");
246 EXPECT_DEATH_IF_SUPPORTED(buffer.Copy(&target_buffer, 0, buffer.size() + 1),
247 "end exceeds source range");
248 EXPECT_DEATH_IF_SUPPORTED(
249 buffer.Copy(&target_buffer, buffer.size() / 2 + 1, buffer.size() / 2 + 1),
250 "end exceeds source range");
251
252 StaticByteBuffer<1> insufficient_target_buffer;
253 EXPECT_DEATH_IF_SUPPORTED(buffer.Copy(&insufficient_target_buffer),
254 "destination not large enough");
255
256 // Range calculation overflow is fatal rather than silently erroneous
257 MutableBufferView bogus_target_buffer(target_buffer.mutable_data(),
258 std::numeric_limits<size_t>::max());
259 EXPECT_DEATH_IF_SUPPORTED(
260 buffer.Copy(&bogus_target_buffer, 1, std::numeric_limits<size_t>::max()),
261 "end of source range overflows size_t");
262 }
263
TEST(ByteBufferTest,View)264 TEST(ByteBufferTest, View) {
265 StaticByteBuffer buffer('T', 'e', 's', 't');
266 BufferView empty_buffer;
267
268 BufferView view = empty_buffer.view();
269 EXPECT_EQ(0u, view.size());
270
271 view = empty_buffer.view(0, 200);
272 EXPECT_EQ(0u, view.size());
273
274 view = buffer.view();
275 EXPECT_EQ("Test", view.AsString());
276
277 view = buffer.view(4);
278 EXPECT_EQ(0u, view.size());
279
280 view = buffer.view(1);
281 EXPECT_EQ("est", view.AsString());
282
283 view = buffer.view(1, 1);
284 EXPECT_EQ("e", view.AsString());
285
286 view = buffer.view(1, 2);
287 EXPECT_EQ("es", view.AsString());
288 }
289
TEST(ByteBufferTest,Span)290 TEST(ByteBufferTest, Span) {
291 StaticByteBuffer buffer('T', 'e', 's', 't');
292 BufferView empty_buffer;
293
294 pw::span span = empty_buffer.subspan();
295 EXPECT_EQ(0u, span.size());
296
297 span = empty_buffer.subspan(0, 200);
298 EXPECT_EQ(0u, span.size());
299
300 span = buffer.subspan();
301 EXPECT_THAT(
302 span,
303 ::testing::ElementsAreArray(
304 {std::byte{'T'}, std::byte{'e'}, std::byte{'s'}, std::byte{'t'}}));
305
306 span = buffer.subspan(4);
307 EXPECT_EQ(0u, span.size());
308
309 span = buffer.subspan(1);
310 EXPECT_THAT(span,
311 ::testing::ElementsAreArray(
312 {std::byte{'e'}, std::byte{'s'}, std::byte{'t'}}));
313
314 span = buffer.subspan(1, 1);
315 EXPECT_THAT(span, ::testing::ElementsAreArray({std::byte{'e'}}));
316
317 span = buffer.subspan(1, 2);
318 EXPECT_THAT(span,
319 ::testing::ElementsAreArray({std::byte{'e'}, std::byte{'s'}}));
320 }
321
TEST(ByteBufferTest,MutableView)322 TEST(ByteBufferTest, MutableView) {
323 StaticByteBuffer buffer('T', 'e', 's', 't');
324 MutableBufferView empty_buffer;
325
326 MutableBufferView view;
327 view = empty_buffer.mutable_view();
328 EXPECT_EQ(0u, view.size());
329
330 view = empty_buffer.mutable_view(0, 200);
331 EXPECT_EQ(0u, view.size());
332
333 view = buffer.mutable_view();
334 EXPECT_EQ("Test", view.AsString());
335
336 view = buffer.mutable_view(4);
337 EXPECT_EQ(0u, view.size());
338
339 view = buffer.mutable_view(1);
340 EXPECT_EQ("est", view.AsString());
341
342 view = buffer.mutable_view(1, 1);
343 EXPECT_EQ("e", view.AsString());
344
345 view = buffer.mutable_view(1, 2);
346 EXPECT_EQ("es", view.AsString());
347
348 // MutableView can modify the underlying buffer.
349 view[0] = 'E';
350 EXPECT_EQ("Es", view.AsString());
351 EXPECT_EQ("TEst", buffer.AsString());
352 }
353
TEST(ByteBufferTest,ByteBufferEqualityFail)354 TEST(ByteBufferTest, ByteBufferEqualityFail) {
355 const StaticByteBuffer kData0('T', 'e', 's', 't');
356 const StaticByteBuffer kData1('F', 'o', 'o');
357 EXPECT_FALSE(kData0 == kData1);
358 }
359
TEST(ByteBufferTest,ByteBufferEqualitySuccess)360 TEST(ByteBufferTest, ByteBufferEqualitySuccess) {
361 const StaticByteBuffer kData0('T', 'e', 's', 't');
362 const StaticByteBuffer kData1('T', 'e', 's', 't');
363 EXPECT_TRUE(kData0 == kData1);
364 }
365
TEST(ByteBufferDeathTest,ByteBufferWithInsufficientBytesAssertsOnTo)366 TEST(ByteBufferDeathTest, ByteBufferWithInsufficientBytesAssertsOnTo) {
367 StaticByteBuffer buffer(0, 0, 0);
368 ASSERT_GT(sizeof(float), buffer.size());
369 EXPECT_DEATH_IF_SUPPORTED(
370 [=] { [[maybe_unused]] auto _ = buffer.To<float>(); }(),
371 "end exceeds source range");
372 }
373
374 template <typename T>
TestByteBufferRoundtrip(std::initializer_list<T> values)375 void TestByteBufferRoundtrip(std::initializer_list<T> values) {
376 for (auto value : values) {
377 BufferView value_view(&value, sizeof(value));
378 auto to_value = value_view.To<T>();
379 SCOPED_TRACE(value);
380 EXPECT_EQ(0, std::memcmp(&value, &to_value, sizeof(T)));
381 }
382 }
383
TEST(ByteBufferTest,ByteBufferToRoundtripOnPrimitives)384 TEST(ByteBufferTest, ByteBufferToRoundtripOnPrimitives) {
385 // Test values from absl::bit_cast tests
386 {
387 SCOPED_TRACE("bool");
388 TestByteBufferRoundtrip<bool>({true, false});
389 }
390 {
391 SCOPED_TRACE("array 1 of const bool");
392 TestByteBufferRoundtrip<const bool[1]>({{true}, {false}});
393 }
394 {
395 SCOPED_TRACE("int32_t");
396 TestByteBufferRoundtrip<int32_t>(
397 {0, 1, 100, 2147483647, -1, -100, -2147483647, -2147483647 - 1});
398 }
399 {
400 SCOPED_TRACE("int64_t");
401 TestByteBufferRoundtrip<int64_t>({0, 1, 1LL << 40, -1, -(1LL << 40)});
402 }
403 {
404 SCOPED_TRACE("uint64_t");
405 TestByteBufferRoundtrip<uint64_t>({0, 1, 1LLU << 40, 1LLU << 63});
406 }
407 {
408 SCOPED_TRACE("float");
409 TestByteBufferRoundtrip<float>({0.0f,
410 1.0f,
411 -1.0f,
412 10.0f,
413 -10.0f,
414 1e10f,
415 1e20f,
416 1e-10f,
417 1e-20f,
418 2.71828f,
419 3.14159f});
420 }
421 {
422 SCOPED_TRACE("double");
423 TestByteBufferRoundtrip<double>(
424 {0.0,
425 1.0,
426 -1.0,
427 10.0,
428 -10.0,
429 1e10,
430 1e100,
431 1e-10,
432 1e-100,
433 2.718281828459045,
434 3.141592653589793238462643383279502884197169399375105820974944});
435 }
436 }
437
TEST(ByteBufferTest,ByteBufferToStripsCvQualifiers)438 TEST(ByteBufferTest, ByteBufferToStripsCvQualifiers) {
439 auto to_value = StaticByteBuffer(2).To<const volatile char>();
440 static_assert(std::is_same_v<char, decltype(to_value)>);
441 }
442
TEST(ByteBufferTest,ByteBufferWithAdditionalBytesToBasicType)443 TEST(ByteBufferTest, ByteBufferWithAdditionalBytesToBasicType) {
444 EXPECT_EQ(191u, StaticByteBuffer(191, 25).To<uint8_t>());
445 }
446
TEST(ByteBufferTest,ByteBufferToStruct)447 TEST(ByteBufferTest, ByteBufferToStruct) {
448 const StaticByteBuffer data(10, 12);
449 struct [[gnu::packed]] point {
450 uint8_t x;
451 uint8_t y;
452 };
453 EXPECT_EQ(10, data.To<point>().x);
454 EXPECT_EQ(12, data.To<point>().y);
455 }
456
TEST(ByteBufferTest,ByteBufferToArray)457 TEST(ByteBufferTest, ByteBufferToArray) {
458 const StaticByteBuffer buf(191, 25);
459 const auto array = buf.To<uint8_t[2]>();
460 EXPECT_EQ(buf.size(), array.size());
461 EXPECT_EQ(191, array[0]);
462 EXPECT_EQ(25, array[1]);
463 }
464
TEST(ByteBufferTest,ByteBufferToDoesNotReadUnaligned)465 TEST(ByteBufferTest, ByteBufferToDoesNotReadUnaligned) {
466 const DynamicByteBuffer buf(2 * sizeof(float) - 1);
467
468 // Advance past the float alignment boundary
469 const size_t offset = std::distance(
470 buf.begin(), std::find_if(buf.begin(), buf.end(), [](auto& b) {
471 return reinterpret_cast<uintptr_t>(&b) % alignof(float) != 0;
472 }));
473 BufferView view = buf.view(offset, sizeof(float));
474
475 // Prove that the data in the view isn't aligned for a float
476 ASSERT_NE(0U, reinterpret_cast<uintptr_t>(view.data()) % alignof(float));
477
478 // This cref binds to a new object that ByteBuffer::To creates, so the
479 // alignment is correct
480 const float& to_object = view.To<float>();
481 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(&to_object) % alignof(float));
482 }
483
TEST(ByteBufferTest,ByteBufferReadMember)484 TEST(ByteBufferTest, ByteBufferReadMember) {
485 struct [[gnu::packed]] Point {
486 uint8_t x;
487 const uint8_t y;
488 const uint8_t array[2];
489 char multi[2][1];
490 uint8_t flex[];
491 };
492
493 StaticByteBuffer data(0x01, 0x02, 0x03, 0x37, 0x7f, 0x02, 0x45);
494 auto x = data.ReadMember<&Point::x>();
495 static_assert(std::is_same_v<uint8_t, decltype(x)>);
496 EXPECT_EQ(data[offsetof(Point, x)], x);
497
498 // Top-level const qualifier is removed
499 auto y = data.ReadMember<&Point::y>();
500 static_assert(std::is_same_v<uint8_t, decltype(y)>);
501 EXPECT_EQ(data[offsetof(Point, y)], y);
502
503 // Returned array elements are const just like in the original struct
504 auto array = data.ReadMember<&Point::array>();
505 static_assert(std::is_same_v<std::array<const uint8_t, 2>, decltype(array)>);
506 EXPECT_THAT(array, ::testing::ElementsAre(uint8_t{0x03}, uint8_t{0x37}));
507
508 auto multi = data.ReadMember<&Point::multi>();
509 static_assert(
510 std::is_same_v<std::array<std::array<char, 1>, 2>, decltype(multi)>);
511 EXPECT_THAT(
512 multi,
513 ::testing::ElementsAre(std::array{char{0x7f}}, std::array{char{0x02}}));
514 }
515
TEST(ByteBufferDeathTest,ByteBufferReadMemberOfFixedArrayType)516 TEST(ByteBufferDeathTest, ByteBufferReadMemberOfFixedArrayType) {
517 struct [[gnu::packed]] Point {
518 float f;
519 int8_t coordinates[3];
520 char multi[2][1];
521 };
522
523 StaticByteBuffer data(
524 // f
525 0,
526 0,
527 0,
528 0,
529
530 // coordinates[3]
531 0x01,
532 0x02,
533 0x03,
534
535 // multi[2][1]
536 0x37,
537 0x45);
538 ASSERT_LE(sizeof(Point), data.size());
539 EXPECT_DEATH_IF_SUPPORTED(data.ReadMember<&Point::coordinates>(3),
540 "index past array bounds");
541
542 auto view = data.view(0, 6);
543 ASSERT_GT(sizeof(Point), view.size());
544 EXPECT_DEATH_IF_SUPPORTED(view.ReadMember<&Point::coordinates>(0),
545 "insufficient buffer");
546
547 EXPECT_EQ(data[offsetof(Point, coordinates)],
548 data.ReadMember<&Point::coordinates>(0));
549 EXPECT_EQ(data[offsetof(Point, coordinates) + 1],
550 data.ReadMember<&Point::coordinates>(1));
551
552 // Elements of a multi-dimensional C array are returned as std::arrays
553 auto inner = data.ReadMember<&Point::multi>(1);
554 EXPECT_EQ(data[offsetof(Point, multi) + 1], inner.at(0));
555 }
556
TEST(ByteBufferDeathTest,ByteBufferReadMemberOfStdArrayType)557 TEST(ByteBufferDeathTest, ByteBufferReadMemberOfStdArrayType) {
558 struct [[gnu::packed]] Point {
559 float f;
560 std::array<int8_t, 3> coordinates;
561 };
562
563 StaticByteBuffer data(0, 0, 0, 0, 0x01, 0x02, 0x03, 0x37);
564 ASSERT_LE(sizeof(Point), data.size());
565 EXPECT_DEATH_IF_SUPPORTED(data.ReadMember<&Point::coordinates>(3),
566 "index past array bounds");
567
568 EXPECT_EQ(data[offsetof(Point, coordinates)],
569 data.ReadMember<&Point::coordinates>(0));
570 EXPECT_EQ(data[offsetof(Point, coordinates) + 1],
571 data.ReadMember<&Point::coordinates>(1));
572 }
573
TEST(ByteBufferDeathTest,ByteBufferReadMemberOfFlexibleArrayType)574 TEST(ByteBufferDeathTest, ByteBufferReadMemberOfFlexibleArrayType) {
575 struct [[gnu::packed]] Point {
576 uint16_t dimensions;
577 int8_t coordinates[];
578 };
579
580 StaticByteBuffer data(0, 0, 0x01, 0x02);
581 ASSERT_LE(sizeof(Point), data.size());
582 EXPECT_DEATH_IF_SUPPORTED(data.ReadMember<&Point::coordinates>(2),
583 "end exceeds source range");
584
585 EXPECT_EQ(data[offsetof(Point, coordinates)],
586 data.ReadMember<&Point::coordinates>(0));
587 EXPECT_EQ(data[offsetof(Point, coordinates) + 1],
588 data.ReadMember<&Point::coordinates>(1));
589 }
590
TEST(ByteBufferTest,ByteBufferReadMemberOfUnalignedArrayType)591 TEST(ByteBufferTest, ByteBufferReadMemberOfUnalignedArrayType) {
592 struct [[gnu::packed]] Point {
593 int8_t byte;
594 float f[1];
595 } point;
596
597 BufferView view(&point, sizeof(point));
598 static_assert(alignof(decltype(view.ReadMember<&Point::f>(0))) ==
599 alignof(float));
600
601 // This branch (that the second field of |point| is unaligned) does get taken
602 // in manual testing but there's no way to guarantee it, so make it run-time
603 // conditional.
604 if (reinterpret_cast<uintptr_t>(&point.f) % alignof(float) != 0) {
605 // Casting (which is UB here) a pointer into the buffer contents yields a
606 // pointer that is not aligned to type requirements
607 const float* unaligned_pointer =
608 reinterpret_cast<const Point*>(view.data())->f;
609 ASSERT_NE(0U,
610 reinterpret_cast<uintptr_t>(unaligned_pointer) % alignof(float));
611 }
612
613 // The same cref binds to a temporary new object that ByteBuffer::ReadMember
614 // creates, so the alignment is correct.
615 const float& through_read_member = view.ReadMember<&Point::f>(0);
616 EXPECT_EQ(0U,
617 reinterpret_cast<uintptr_t>(&through_read_member) % alignof(float));
618 }
619
TEST(ByteBufferTest,MutableByteBufferAsMutableFundamental)620 TEST(ByteBufferTest, MutableByteBufferAsMutableFundamental) {
621 StaticByteBuffer data(10, 12);
622 ++*data.AsMutable<uint8_t>();
623 EXPECT_EQ(11, data[0]);
624 }
625
TEST(ByteBufferTest,MutableByteBufferAsMutableStruct)626 TEST(ByteBufferTest, MutableByteBufferAsMutableStruct) {
627 StaticByteBuffer data(10, 12);
628 struct point {
629 uint8_t x;
630 uint8_t y;
631 };
632 ++data.AsMutable<point>()->x;
633 ++data.AsMutable<point>()->y;
634
635 const StaticByteBuffer expected_data(11, 13);
636 EXPECT_EQ(expected_data, data);
637 }
638
TEST(ByteBufferTest,MutableByteBufferAsMutableArray)639 TEST(ByteBufferTest, MutableByteBufferAsMutableArray) {
640 StaticByteBuffer buf(10, 12);
641 uint8_t(&array)[2] = *buf.AsMutable<uint8_t[2]>();
642 ++array[0];
643 ++array[1];
644
645 EXPECT_EQ(11, buf[0]);
646 EXPECT_EQ(13, buf[1]);
647 }
648
TEST(ByteBufferDeathTest,MutableByteBufferWrite)649 TEST(ByteBufferDeathTest, MutableByteBufferWrite) {
650 const StaticByteBuffer kData0('T', 'e', 's', 't');
651 const StaticByteBuffer kData1('F', 'o', 'o');
652
653 StaticByteBuffer buffer('X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
654 EXPECT_EQ("XXXXXXXX", buffer.AsString());
655
656 buffer.Write(kData0);
657 EXPECT_EQ("TestXXXX", buffer.AsString());
658
659 // Write from raw pointer.
660 buffer = StaticByteBuffer('X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
661 buffer.Write(kData0.data(), kData0.size());
662 EXPECT_EQ("TestXXXX", buffer.AsString());
663
664 // Write at offset.
665 buffer.Write(kData1, 1);
666 EXPECT_EQ("TFooXXXX", buffer.AsString());
667
668 // Write at offset from raw pointer
669 buffer.Write(kData1.data(), kData1.size(), 3);
670 EXPECT_EQ("TFoFooXX", buffer.AsString());
671
672 // Writing zero bytes should have no effect.
673 buffer = StaticByteBuffer('X', 'X', 'X', 'X', 'X', 'X', 'X', 'X');
674 buffer.Write(kData1.data(), 0u);
675 buffer.Write(/*data=*/nullptr, 0u); // Passing nullptr is OK when size is 0
676 EXPECT_EQ("XXXXXXXX", buffer.AsString());
677
678 // Writing zero bytes just past the buffer should be accepted (i.e. no
679 // assertion) and have no effect.
680 buffer.Write(kData1.data(), 0u, buffer.size());
681 EXPECT_EQ("XXXXXXXX", buffer.AsString());
682
683 // Buffer limits are strictly enforced
684 EXPECT_DEATH_IF_SUPPORTED(buffer.Write(kData0.data(), buffer.size() + 1, 0),
685 "destination not large enough");
686 EXPECT_DEATH_IF_SUPPORTED(buffer.Write(kData0.data(), 0, buffer.size() + 1),
687 "offset past buffer");
688 }
689
TEST(ByteBufferTest,AsString)690 TEST(ByteBufferTest, AsString) {
691 StaticByteBuffer buffer('T', 'e', 's', 't');
692 EXPECT_EQ("Test", buffer.AsString());
693 }
694
TEST(ByteBufferTest,Fill)695 TEST(ByteBufferTest, Fill) {
696 StaticByteBuffer<5> buffer;
697 buffer.Fill('A');
698 EXPECT_EQ("AAAAA", buffer.AsString());
699 }
700
TEST(ByteBufferTest,ToVectorEmpty)701 TEST(ByteBufferTest, ToVectorEmpty) {
702 BufferView buffer;
703 std::vector<uint8_t> vec = buffer.ToVector();
704 EXPECT_TRUE(vec.empty());
705 }
706
TEST(ByteBufferTest,ToVector)707 TEST(ByteBufferTest, ToVector) {
708 StaticByteBuffer buffer('h', 'e', 'l', 'l', 'o');
709 std::vector<uint8_t> vec = buffer.ToVector();
710 EXPECT_TRUE(ContainersEqual(vec, buffer));
711 }
712
TEST(ByteBufferTest,PrintableAscii)713 TEST(ByteBufferTest, PrintableAscii) {
714 StaticByteBuffer buffer('f', 'o', 'o', 'b', 'a', 'r', 'b', 'a', 'z');
715 std::string result = buffer.Printable(3, 3);
716 EXPECT_EQ("bar", result);
717 }
718
TEST(ByteBufferTest,PrintableUtf8)719 TEST(ByteBufferTest, PrintableUtf8) {
720 StaticByteBuffer buffer(
721 0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xB5);
722 std::string result = buffer.Printable(0, buffer.size());
723 EXPECT_EQ("κόσμε", result);
724 }
725
TEST(ByteBufferTest,PrintableInvalidUtf8)726 TEST(ByteBufferTest, PrintableInvalidUtf8) {
727 StaticByteBuffer buffer(0xce, 0xba, 0x80);
728 std::string result = buffer.Printable(0, buffer.size());
729 EXPECT_EQ("...", result);
730 }
731
TEST(ByteBufferDeathTest,PrintableSizeTooLarge)732 TEST(ByteBufferDeathTest, PrintableSizeTooLarge) {
733 StaticByteBuffer buffer(0xce, 0xba, 0x80);
734 ASSERT_DEATH({ buffer.Printable(0, buffer.size() + 1); }, "");
735 ASSERT_DEATH({ buffer.Printable(1, buffer.size()); }, "");
736 ASSERT_DEATH({ buffer.Printable(1, buffer.size() + 1); }, "");
737 }
738
739 } // namespace
740 } // namespace bt
741