• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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