Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 5 | #include "base/containers/buffer_iterator.h" |
| 6 | |
| 7 | #include <string.h> |
| 8 | |
| 9 | #include <limits> |
Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 10 | #include <optional> |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 11 | #include <vector> |
| 12 | |
Tom Sepez | 7859fac5 | 2025-07-22 19:34:16 | [diff] [blame] | 13 | #include "base/compiler_specific.h" |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 14 | #include "base/containers/span.h" |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 15 | #include "testing/gtest/include/gtest/gtest.h" |
| 16 | |
| 17 | namespace base { |
| 18 | namespace { |
| 19 | |
| 20 | struct TestStruct { |
| 21 | uint32_t one; |
Peter Kasting | b6079b80 | 2024-12-03 23:05:53 | [diff] [blame] | 22 | uint8_t two, three, four, five; |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 23 | }; |
| 24 | |
| 25 | bool operator==(const TestStruct& lhs, const TestStruct& rhs) { |
| 26 | return lhs.one == rhs.one && lhs.two == rhs.two; |
| 27 | } |
| 28 | |
| 29 | TestStruct CreateTestStruct() { |
Peter Kasting | b6079b80 | 2024-12-03 23:05:53 | [diff] [blame] | 30 | return {0xabcdef12, 0x34, 0x56, 0x78, 0x90}; |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 31 | } |
| 32 | |
| 33 | TEST(BufferIteratorTest, Object) { |
| 34 | TestStruct expected = CreateTestStruct(); |
| 35 | |
| 36 | char buffer[sizeof(TestStruct)]; |
Tom Sepez | 7859fac5 | 2025-07-22 19:34:16 | [diff] [blame] | 37 | UNSAFE_TODO(memcpy(buffer, &expected, sizeof(buffer))); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 38 | |
| 39 | { |
| 40 | // Read the object. |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 41 | BufferIterator<char> iterator(buffer); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 42 | const TestStruct* actual = iterator.Object<TestStruct>(); |
| 43 | EXPECT_EQ(expected, *actual); |
| 44 | } |
| 45 | { |
| 46 | // Iterator's view of the data is not large enough to read the object. |
Jose Dapena Paz | 81fbbbf | 2024-03-05 21:13:16 | [diff] [blame] | 47 | BufferIterator<char> iterator( |
| 48 | span<char>(buffer).first<sizeof(buffer) - 1u>()); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 49 | const TestStruct* actual = iterator.Object<TestStruct>(); |
| 50 | EXPECT_FALSE(actual); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | TEST(BufferIteratorTest, MutableObject) { |
| 55 | TestStruct expected = CreateTestStruct(); |
| 56 | |
| 57 | char buffer[sizeof(TestStruct)]; |
| 58 | |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 59 | BufferIterator<char> iterator(buffer); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 60 | |
| 61 | { |
| 62 | // Write the object. |
Peter Kasting | b6079b80 | 2024-12-03 23:05:53 | [diff] [blame] | 63 | *iterator.MutableObject<TestStruct>() = expected; |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | // Rewind the iterator. |
| 67 | iterator.Seek(0); |
| 68 | |
| 69 | { |
| 70 | // Read the object back. |
| 71 | const TestStruct* actual = iterator.Object<TestStruct>(); |
| 72 | EXPECT_EQ(expected, *actual); |
| 73 | } |
| 74 | } |
| 75 | |
David Benjamin | fd7de0b | 2024-12-24 01:24:01 | [diff] [blame] | 76 | TEST(BufferIteratorTest, ObjectDoesNotFit) { |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 77 | char buffer[64]; |
David Benjamin | fd7de0b | 2024-12-24 01:24:01 | [diff] [blame] | 78 | BufferIterator<char> iterator(buffer); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 79 | |
| 80 | auto* pointer = iterator.Object<uint64_t>(); |
| 81 | EXPECT_TRUE(pointer); |
| 82 | |
| 83 | iterator.Seek(iterator.total_size() - 1); |
| 84 | |
| 85 | auto* invalid_pointer = iterator.Object<uint64_t>(); |
| 86 | EXPECT_FALSE(invalid_pointer); |
| 87 | } |
| 88 | |
| 89 | TEST(BufferIteratorTest, Span) { |
| 90 | TestStruct expected = CreateTestStruct(); |
| 91 | |
| 92 | std::vector<char> buffer(sizeof(TestStruct) * 3); |
| 93 | |
| 94 | { |
| 95 | // Load the span with data. |
| 96 | BufferIterator<char> iterator(buffer); |
| 97 | span<TestStruct> span = iterator.MutableSpan<TestStruct>(3); |
| 98 | for (auto& ts : span) { |
Tom Sepez | 7859fac5 | 2025-07-22 19:34:16 | [diff] [blame] | 99 | UNSAFE_TODO(memcpy(&ts, &expected, sizeof(expected))); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 100 | } |
| 101 | } |
| 102 | { |
| 103 | // Read the data back out. |
| 104 | BufferIterator<char> iterator(buffer); |
| 105 | |
| 106 | const TestStruct* actual = iterator.Object<TestStruct>(); |
| 107 | EXPECT_EQ(expected, *actual); |
| 108 | |
| 109 | actual = iterator.Object<TestStruct>(); |
| 110 | EXPECT_EQ(expected, *actual); |
| 111 | |
| 112 | actual = iterator.Object<TestStruct>(); |
| 113 | EXPECT_EQ(expected, *actual); |
| 114 | |
| 115 | EXPECT_EQ(iterator.total_size(), iterator.position()); |
| 116 | } |
| 117 | { |
| 118 | // Cannot create spans larger than there are data for. |
| 119 | BufferIterator<char> iterator(buffer); |
| 120 | span<const TestStruct> span = iterator.Span<TestStruct>(4); |
| 121 | EXPECT_TRUE(span.empty()); |
| 122 | } |
| 123 | } |
| 124 | |
danakj | 216d6edf | 2024-03-28 22:30:30 | [diff] [blame] | 125 | TEST(BufferIteratorTest, FixedSpan) { |
| 126 | TestStruct expected = CreateTestStruct(); |
| 127 | |
| 128 | std::vector<char> buffer(sizeof(TestStruct) * 3); |
| 129 | |
| 130 | { |
| 131 | // Load the span with data. |
| 132 | BufferIterator<char> iterator(buffer); |
| 133 | auto span = iterator.MutableSpan<TestStruct, 3u>(); |
| 134 | static_assert(std::same_as<std::optional<base::span<TestStruct, 3u>>, |
| 135 | decltype(span)>); |
| 136 | for (auto& ts : *span) { |
Tom Sepez | 7859fac5 | 2025-07-22 19:34:16 | [diff] [blame] | 137 | UNSAFE_TODO(memcpy(&ts, &expected, sizeof(expected))); |
danakj | 216d6edf | 2024-03-28 22:30:30 | [diff] [blame] | 138 | } |
| 139 | } |
| 140 | { |
| 141 | // Read the data back out. |
| 142 | BufferIterator<char> iterator(buffer); |
| 143 | |
| 144 | const TestStruct* actual = iterator.Object<TestStruct>(); |
| 145 | EXPECT_EQ(expected, *actual); |
| 146 | |
| 147 | actual = iterator.Object<TestStruct>(); |
| 148 | EXPECT_EQ(expected, *actual); |
| 149 | |
| 150 | actual = iterator.Object<TestStruct>(); |
| 151 | EXPECT_EQ(expected, *actual); |
| 152 | |
| 153 | EXPECT_EQ(iterator.total_size(), iterator.position()); |
| 154 | } |
| 155 | { |
| 156 | // Cannot create spans larger than there are data for. |
| 157 | BufferIterator<char> iterator(buffer); |
| 158 | auto maybe_span = iterator.Span<TestStruct, 4u>(); |
| 159 | EXPECT_FALSE(maybe_span.has_value()); |
| 160 | } |
| 161 | } |
| 162 | |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 163 | TEST(BufferIteratorTest, SpanOverflow) { |
| 164 | char buffer[64]; |
| 165 | |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 166 | BufferIterator<char> iterator( |
| 167 | // SAFETY: This intentionally makes an incorrectly-sized span. The span |
| 168 | // pointer, stored in the BufferIterator is never moved past the start in |
| 169 | // this test, as that would cause Undefined Behaviour. |
| 170 | UNSAFE_BUFFERS(span(buffer, std::numeric_limits<size_t>::max()))); |
danakj | 216d6edf | 2024-03-28 22:30:30 | [diff] [blame] | 171 | |
| 172 | constexpr size_t kInvalidU64Size = |
| 173 | (std::numeric_limits<size_t>::max() / sizeof(uint64_t)) + 1u; |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 174 | { |
danakj | 216d6edf | 2024-03-28 22:30:30 | [diff] [blame] | 175 | span<const uint64_t> empty_span = iterator.Span<uint64_t>(kInvalidU64Size); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 176 | EXPECT_TRUE(empty_span.empty()); |
| 177 | } |
| 178 | { |
| 179 | span<const uint64_t> empty_span = |
| 180 | iterator.Span<uint64_t>(std::numeric_limits<size_t>::max()); |
| 181 | EXPECT_TRUE(empty_span.empty()); |
| 182 | } |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | TEST(BufferIteratorTest, Position) { |
| 186 | char buffer[64]; |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 187 | BufferIterator<char> iterator(buffer); |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 188 | EXPECT_EQ(sizeof(buffer), iterator.total_size()); |
| 189 | |
| 190 | size_t position = iterator.position(); |
| 191 | EXPECT_EQ(0u, position); |
| 192 | |
| 193 | iterator.Object<uint8_t>(); |
| 194 | EXPECT_EQ(sizeof(uint8_t), iterator.position() - position); |
| 195 | position = iterator.position(); |
| 196 | |
| 197 | iterator.Object<uint32_t>(); |
| 198 | EXPECT_EQ(sizeof(uint32_t), iterator.position() - position); |
| 199 | position = iterator.position(); |
| 200 | |
| 201 | iterator.Seek(32); |
| 202 | EXPECT_EQ(32u, iterator.position()); |
| 203 | |
| 204 | EXPECT_EQ(sizeof(buffer), iterator.total_size()); |
| 205 | } |
| 206 | |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 207 | TEST(BufferIteratorTest, CopyObject) { |
| 208 | TestStruct expected = CreateTestStruct(); |
| 209 | |
| 210 | constexpr int kNumCopies = 3; |
| 211 | char buffer[sizeof(TestStruct) * kNumCopies]; |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 212 | for (int i = 0; i < kNumCopies; i++) { |
| 213 | as_writable_bytes(span(buffer)) |
| 214 | .subspan(i * sizeof(TestStruct)) |
Peter Kasting | 448444f | 2024-12-13 01:25:36 | [diff] [blame] | 215 | .copy_prefix_from(byte_span_from_ref(expected)); |
danakj | 6d325d7 | 2024-02-13 23:42:56 | [diff] [blame] | 216 | } |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 217 | |
| 218 | BufferIterator<char> iterator(buffer); |
Arthur Sonzogni | e5fff99c | 2024-02-21 15:58:24 | [diff] [blame] | 219 | std::optional<TestStruct> actual; |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 220 | for (int i = 0; i < kNumCopies; i++) { |
| 221 | actual = iterator.CopyObject<TestStruct>(); |
| 222 | ASSERT_TRUE(actual.has_value()); |
| 223 | EXPECT_EQ(expected, *actual); |
| 224 | } |
| 225 | actual = iterator.CopyObject<TestStruct>(); |
| 226 | EXPECT_FALSE(actual.has_value()); |
| 227 | } |
| 228 | |
| 229 | TEST(BufferIteratorTest, SeekWithSizeConfines) { |
| 230 | const char buffer[] = "vindicate"; |
danakj | fffdbb93 | 2024-06-07 17:11:41 | [diff] [blame] | 231 | BufferIterator<const char> iterator(base::span_from_cstring(buffer)); |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 232 | iterator.Seek(5); |
| 233 | iterator.TruncateTo(3); |
| 234 | EXPECT_TRUE(iterator.Span<char>(4).empty()); |
| 235 | |
| 236 | std::string result; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 237 | while (const char* c = iterator.Object<char>()) { |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 238 | result += *c; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 239 | } |
Jeremy Roman | e8965bd | 2022-10-18 00:40:18 | [diff] [blame] | 240 | EXPECT_EQ(result, "cat"); |
| 241 | } |
| 242 | |
Robert Sesek | e5844276 | 2019-03-01 13:25:40 | [diff] [blame] | 243 | } // namespace |
| 244 | } // namespace base |