Software APIs
memory_unittest.cc
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
7 #include <algorithm>
8 #include <stdint.h>
9 #include <vector>
10 
11 #include "gmock/gmock.h"
12 #include "gtest/gtest-message.h"
13 #include "gtest/gtest.h"
14 
15 namespace memory_unittest {
16 namespace {
17 
18 // Wrappers for builtins because they must be called directly when building with
19 // clang.
20 
21 void *builtin_memcpy_wrapper(void *dest, const void *src, size_t count) {
22  return __builtin_memcpy(dest, src, count);
23 }
24 
25 int builtin_memcmp_wrapper(const void *lhs, const void *rhs, size_t count) {
26  return __builtin_memcmp(lhs, rhs, count);
27 }
28 
29 void *builtin_memset_wrapper(void *dest, int ch, size_t count) {
30  return __builtin_memset(dest, ch, count);
31 }
32 
33 // Reference implementations of memory functions.
34 
35 enum {
36  kMemCmpEq = 0,
37  kMemCmpLt = -42,
38  kMemCmpGt = 42,
39 };
40 
41 int ref_memrcmp(const void *lhs, const void *rhs, size_t len) {
42  const uint8_t *lhs8 = (uint8_t *)lhs;
43  const uint8_t *rhs8 = (uint8_t *)rhs;
44  size_t j;
45  for (size_t i = 0; i < len; ++i) {
46  j = len - 1 - i;
47  if (lhs8[j] < rhs8[j]) {
48  return kMemCmpLt;
49  } else if (lhs8[j] > rhs8[j]) {
50  return kMemCmpGt;
51  }
52  }
53  return kMemCmpEq;
54 }
55 
56 void *ref_memchr(const void *ptr, int value, size_t len) {
57  uint8_t *ptr8 = (uint8_t *)ptr;
58  uint8_t value8 = (uint8_t)value;
59  for (size_t i = 0; i < len; ++i) {
60  if (ptr8[i] == value8) {
61  return ptr8 + i;
62  }
63  }
64  return nullptr;
65 }
66 
67 void *ref_memrchr(const void *ptr, int value, size_t len) {
68  uint8_t *ptr8 = (uint8_t *)ptr;
69  uint8_t value8 = (uint8_t)value;
70  for (size_t i = 0; i < len; ++i) {
71  size_t idx = len - i - 1;
72  if (ptr8[idx] == value8) {
73  return ptr8 + idx;
74  }
75  }
76  return nullptr;
77 }
78 
79 // Parameterized test suites enable us to run the same tests against each memory
80 // function and its builtin equivalent (or reference implementation when there
81 // is no builtin). We can also run the same tests on any "r" variants that
82 // operate from right to left.
83 class MemCpyTest : public ::testing::TestWithParam<decltype(&ot_memcpy)> {};
84 class MemCmpTest : public ::testing::TestWithParam<decltype(&ot_memcmp)> {};
85 class MemSetTest : public ::testing::TestWithParam<decltype(&ot_memset)> {};
86 class MemChrTest : public ::testing::TestWithParam<decltype(&ot_memchr)> {};
87 
88 INSTANTIATE_TEST_SUITE_P(MemCpy, MemCpyTest,
89  ::testing::Values(ot_memcpy, builtin_memcpy_wrapper));
90 INSTANTIATE_TEST_SUITE_P(MemCmp, MemCmpTest,
91  ::testing::Values(ot_memcmp, builtin_memcmp_wrapper,
92  memrcmp, ref_memrcmp));
93 INSTANTIATE_TEST_SUITE_P(MemSet, MemSetTest,
94  ::testing::Values(ot_memset, builtin_memset_wrapper));
95 INSTANTIATE_TEST_SUITE_P(MemChr, MemChrTest,
96  ::testing::Values(ot_memchr, ref_memchr, ot_memrchr,
97  ref_memrchr));
98 
99 using ::testing::Each;
100 using ::testing::ElementsAre;
101 
102 TEST_P(MemCpyTest, Simple) {
103  auto memcpy_func = GetParam();
104 
105  static constexpr size_t kLen = 8;
106  std::vector<uint32_t> xs = {1, 2, 3, 4, 5, 6, 7, 8};
107  std::vector<uint32_t> ys(kLen);
108  ASSERT_EQ(xs.size(), kLen);
109 
110  // Copy `xs[:-1]` to `ys[1:]`.
111  memcpy_func(ys.data() + 1, xs.data(), (kLen - 1) * sizeof(uint32_t));
112  EXPECT_THAT(ys, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
113 
114  // Copy `xs[:-1]` to `ys[:-1]`.
115  memcpy_func(ys.data(), xs.data(), (kLen - 1) * sizeof(uint32_t));
116  EXPECT_THAT(ys, ElementsAre(1, 2, 3, 4, 5, 6, 7, 7));
117 
118  // Copy `xs[:-2]` to `ys[2:]`.
119  memcpy_func(&ys[2], xs.data(), (kLen - 2) * sizeof(uint32_t));
120  EXPECT_THAT(ys, ElementsAre(1, 2, 1, 2, 3, 4, 5, 6));
121 }
122 
123 TEST_P(MemCpyTest, VaryingSize) {
124  auto memcpy_func = GetParam();
125 
126  static constexpr size_t kLen = 8;
127  static constexpr uint32_t kZeroes[kLen] = {0};
128  std::vector<uint32_t> xs = {1, 2, 3, 4, 5, 6, 7, 8};
129  ASSERT_EQ(xs.size(), kLen);
130 
131  for (size_t i = 0; i < kLen; ++i) {
132  // Zero out `xs[:i]`.
133  memcpy_func(xs.data(), kZeroes, i * sizeof(uint32_t));
134 
135  std::vector<uint32_t> expected = {1, 2, 3, 4, 5, 6, 7, 8};
136  std::fill_n(/*first=*/expected.begin(), /*count=*/i, /*value=*/0);
137  EXPECT_EQ(xs, expected) << "i=" << i;
138  }
139 }
140 
141 TEST_P(MemCpyTest, VarySrcAlignment) {
142  auto memcpy_func = GetParam();
143 
144  static constexpr size_t kLen = 8;
145  const std::vector<uint32_t> xs_original = {1, 2, 3, 4, 5, 6, 7, 8};
146  ASSERT_EQ(xs_original.size(), kLen);
147 
148  const std::vector<uint32_t> ys = {11, 12, 13, 14, 15, 16, 17, 18};
149  ASSERT_EQ(ys.size(), kLen);
150 
151  for (size_t i = 0; i < kLen; ++i) {
152  SCOPED_TRACE(testing::Message() << "i=" << i);
153 
154  std::vector<uint32_t> xs(xs_original);
155 
156  // Copy `ys[i:]` to `xs[:-i]`.
157  const size_t num_u32_to_copy = kLen - i;
158  memcpy_func(xs.data(), &ys[i], num_u32_to_copy * sizeof(uint32_t));
159 
160  std::vector<uint32_t> xs_expected(xs_original);
161  for (size_t j = 0; j < num_u32_to_copy; ++j) {
162  xs_expected[j] = 11 + i + j;
163  }
164  EXPECT_EQ(xs, xs_expected);
165  }
166 }
167 
168 TEST_P(MemCpyTest, VaryDestAlignment) {
169  auto memcpy_func = GetParam();
170 
171  static constexpr size_t kLen = 8;
172  const std::vector<uint32_t> xs_original = {1, 2, 3, 4, 5, 6, 7, 8};
173  ASSERT_EQ(xs_original.size(), kLen);
174 
175  const std::vector<uint32_t> ys = {11, 12, 13, 14, 15, 16, 17, 18};
176  ASSERT_EQ(ys.size(), kLen);
177 
178  for (size_t i = 0; i < kLen; ++i) {
179  SCOPED_TRACE(testing::Message() << "i=" << i);
180 
181  std::vector<uint32_t> xs(xs_original);
182 
183  // Copy `ys[:-i]` to `xs[i:]`.
184  const size_t num_u32_to_copy = kLen - i;
185  memcpy_func(&xs[i], ys.data(), num_u32_to_copy * sizeof(uint32_t));
186 
187  std::vector<uint32_t> xs_expected(xs_original);
188  for (size_t j = 0; j < num_u32_to_copy; ++j) {
189  xs_expected[j + i] = 11 + j;
190  }
191 
192  EXPECT_EQ(xs, xs_expected);
193  }
194 }
195 
196 TEST_P(MemCmpTest, NullParam) {
197  auto memcmp_func = GetParam();
198 
199  const uint8_t data[16] = {0};
200  EXPECT_EQ(memcmp_func(nullptr, nullptr, 0), 0);
201  EXPECT_EQ(memcmp_func(data, nullptr, 0), 0);
202  EXPECT_EQ(memcmp_func(nullptr, data, 0), 0);
203  EXPECT_EQ(memcmp_func(data, data, 0), 0);
204 }
205 
206 TEST_P(MemCmpTest, ZeroesVaryingOffsetsAndLengths) {
207  auto memcmp_func = GetParam();
208 
209  const uint8_t data[16] = {0};
210  for (size_t offset = 0; offset <= 8; offset++) {
211  for (size_t len = 0; len < 8; len++) {
212  EXPECT_EQ(memcmp_func(data, data, len), 0);
213  EXPECT_EQ(memcmp_func(data, data + offset, len), 0);
214  EXPECT_EQ(memcmp_func(data + offset, data, len), 0);
215  EXPECT_EQ(memcmp_func(data + offset, data + offset, len), 0);
216  }
217  }
218 }
219 
220 TEST_P(MemSetTest, Null) {
221  auto memset_func = GetParam();
222 
223  EXPECT_EQ(memset_func(nullptr, 0, 0), nullptr);
224 }
225 
226 TEST_P(MemCmpTest, Properties) {
227  auto memcmp_func = GetParam();
228 
229  constexpr size_t kLen = 5;
230  constexpr uint8_t xs[kLen] = {0, 0, 0, 0, 0};
231  constexpr uint8_t ys[kLen] = {0, 0, 0, 1, 3};
232  constexpr uint8_t zs[kLen] = {0, 0, 0, 1, 4};
233 
234  // Reflexive property.
235  EXPECT_EQ(memcmp_func(xs, xs, kLen), 0);
236  EXPECT_EQ(memcmp_func(ys, ys, kLen), 0);
237  EXPECT_EQ(memcmp_func(zs, zs, kLen), 0);
238  // Transitive property for less-than result.
239  EXPECT_LT(memcmp_func(xs, ys, kLen), 0);
240  EXPECT_LT(memcmp_func(ys, zs, kLen), 0);
241  EXPECT_LT(memcmp_func(xs, zs, kLen), 0);
242  // Transitive property for greater-than result.
243  EXPECT_GT(memcmp_func(ys, xs, kLen), 0);
244  EXPECT_GT(memcmp_func(zs, ys, kLen), 0);
245  EXPECT_GT(memcmp_func(zs, xs, kLen), 0);
246 }
247 
248 TEST_P(MemCmpTest, DoesNotUseSystemEndianness) {
249  auto memcmp_func = GetParam();
250 
251  const bool reverse = memcmp_func == &memrcmp || memcmp_func == &ref_memrcmp;
252 
253  constexpr uint8_t kBuf1[] = {0, 0, 0, 1};
254  constexpr uint8_t kBuf2[] = {0, 0, 1, 0};
255 
256  if (!reverse) {
257  // A lexicographic, byte-wise comparison will see that `kBuf1 < kBuf2`, but
258  // a naive word-wise comparison might think that `kBuf1 > kBuf2` because it
259  // interprets `kBuf1` as 0x01000000 and `kBuf2` as 0x00010000 and compares
260  // `uint32_t` values.
261  EXPECT_LT(memcmp_func(kBuf1, kBuf2, sizeof(kBuf1)), 0);
262  } else {
263  EXPECT_GT(memcmp_func(kBuf1, kBuf2, sizeof(kBuf1)), 0);
264  }
265 }
266 
267 TEST_P(MemSetTest, Simple) {
268  auto memset_func = GetParam();
269 
270  for (uint32_t len = 0; len < 32; ++len) {
271  SCOPED_TRACE(testing::Message() << "len = " << len);
272 
273  std::vector<uint32_t> xs(len, 123);
274  EXPECT_THAT(xs, Each(123));
275  EXPECT_EQ(memset_func(xs.data(), 0, xs.size() * sizeof(uint32_t)),
276  xs.data());
277  EXPECT_THAT(xs, Each(0));
278  }
279 }
280 
281 TEST_P(MemChrTest, Null) {
282  auto memchr_func = GetParam();
283 
284  EXPECT_EQ(memchr_func(nullptr, 0, 0), nullptr);
285  EXPECT_EQ(memchr_func(nullptr, 42, 0), nullptr);
286 }
287 
288 TEST_P(MemChrTest, NonNullButEmpty) {
289  auto memchr_func = GetParam();
290 
291  const uint8_t kEmpty[] = {};
292  static_assert(sizeof(kEmpty) == 0, "kEmpty should contain zero bytes");
293 
294  EXPECT_EQ(memchr_func(kEmpty, 0, sizeof(kEmpty)), nullptr);
295  EXPECT_EQ(memchr_func(kEmpty, 42, sizeof(kEmpty)), nullptr);
296 }
297 
298 TEST_P(MemChrTest, Simple) {
299  auto memchr_func = GetParam();
300 
301  std::vector<uint8_t> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
302  EXPECT_EQ(memchr_func(vec.data(), 0, vec.size()), vec.data());
303  EXPECT_EQ(memchr_func(vec.data(), 5, vec.size()), vec.data() + 5);
304 
305  constexpr size_t kLen = 11;
306  constexpr uint8_t data[kLen] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
307  EXPECT_EQ(memchr_func(data, 0, kLen), data);
308  EXPECT_EQ(memchr_func(data, 1, kLen), data + 1);
309  EXPECT_EQ(memchr_func(data, 4, kLen), data + 4);
310  EXPECT_EQ(memchr_func(data, 8, kLen), data + 8);
311  EXPECT_EQ(memchr_func(data, 42, kLen), nullptr);
312 }
313 
314 TEST_P(MemChrTest, VaryingLengths) {
315  auto memchr_func = GetParam();
316 
317  for (size_t len = 0; len < 16; ++len) {
318  std::vector<uint8_t> vec;
319  vec.reserve(len);
320  for (size_t i = 0; i < len; ++i) {
321  vec.push_back(static_cast<uint8_t>(i));
322  }
323  // Search for each value.
324  for (size_t i = 0; i < len; ++i) {
325  EXPECT_EQ(memchr_func(vec.data(), i, len), vec.data() + i);
326  }
327  // Search for a value that is not in the vector.
328  EXPECT_EQ(memchr_func(vec.data(), len + 1, len), nullptr);
329  }
330 }
331 
332 TEST_P(MemChrTest, RepeatedBytes) {
333  auto memchr_func = GetParam();
334 
335  const bool reverse =
336  memchr_func == &ot_memrchr || memchr_func == &ref_memrchr;
337 
338  // Define a buffer that contains repeated bytes at a variety of offsets.
339  constexpr uint8_t kBuf[] = {// No repeated bytes yet.
340  0, 1, 2, 3,
341  // First byte is repeated once.
342  4, 4, 5, 6,
343  // Second byte is repeated once
344  7, 8, 8, 9,
345  // Third byte is repeated once.
346  10, 11, 12, 12,
347  // First byte is repeated three times.
348  13, 13, 13, 14,
349  // Second byte is repeated three times.
350  15, 16, 16, 16,
351  // First byte is repeated four times.
352  17, 17, 17, 17};
353 
354  EXPECT_EQ(memchr_func(kBuf, 0, sizeof(kBuf)), kBuf);
355  EXPECT_EQ(memchr_func(kBuf, 1, sizeof(kBuf)), kBuf + 1);
356  EXPECT_EQ(memchr_func(kBuf, 2, sizeof(kBuf)), kBuf + 2);
357  EXPECT_EQ(memchr_func(kBuf, 3, sizeof(kBuf)), kBuf + 3);
358  EXPECT_EQ(memchr_func(kBuf, 4, sizeof(kBuf)), reverse ? kBuf + 5 : kBuf + 4);
359  EXPECT_EQ(memchr_func(kBuf, 5, sizeof(kBuf)), kBuf + 6);
360  EXPECT_EQ(memchr_func(kBuf, 6, sizeof(kBuf)), kBuf + 7);
361  EXPECT_EQ(memchr_func(kBuf, 7, sizeof(kBuf)), kBuf + 8);
362  EXPECT_EQ(memchr_func(kBuf, 8, sizeof(kBuf)), reverse ? kBuf + 10 : kBuf + 9);
363  EXPECT_EQ(memchr_func(kBuf, 9, sizeof(kBuf)), kBuf + 11);
364  EXPECT_EQ(memchr_func(kBuf, 10, sizeof(kBuf)), kBuf + 12);
365  EXPECT_EQ(memchr_func(kBuf, 11, sizeof(kBuf)), kBuf + 13);
366  EXPECT_EQ(memchr_func(kBuf, 12, sizeof(kBuf)),
367  reverse ? kBuf + 15 : kBuf + 14);
368  EXPECT_EQ(memchr_func(kBuf, 13, sizeof(kBuf)),
369  reverse ? kBuf + 18 : kBuf + 16);
370  EXPECT_EQ(memchr_func(kBuf, 14, sizeof(kBuf)), kBuf + 19);
371  EXPECT_EQ(memchr_func(kBuf, 15, sizeof(kBuf)), kBuf + 20);
372  EXPECT_EQ(memchr_func(kBuf, 16, sizeof(kBuf)),
373  reverse ? kBuf + 23 : kBuf + 21);
374  EXPECT_EQ(memchr_func(kBuf, 17, sizeof(kBuf)),
375  reverse ? kBuf + 27 : kBuf + 24);
376 }
377 
378 } // namespace
379 } // namespace memory_unittest