blob: 5ab2a3f379004dd8d7eb74fcf9fa40b1e9f6e7ad [file] [log] [blame]
Keir Mierle32829d32020-11-25 15:38:40 -08001// Copyright 2020 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// This tests the system installed C standard library version of memset.
16//
17// Note: We have caught real production bugs with these tests. Do not assume
18// your vendor's C library is correct! For standard C functions like memset and
19// memcpy, there are compiler intrisics which assume that the C standard is
20// followed. If the implemention of memset or memcpy does not exactly follow
21// the standard, subtle and hard to track down bugs can be the result.
22
23#include <array>
24#include <cstring>
25#include <numeric>
26
27#include "gtest/gtest.h"
28
29namespace pw {
30namespace {
31
32// From the ISO C standard:
33// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
34//
35// Section 7.21.6.1: memset(void *s, int c, size_t n)
36//
37// void* memset(void* buffer,
38// int character,
39// size_t num_bytes);
40//
41// Copy c into the first n bytes of s.
42// Returns buffer, a copy of the destination pointer.
43//
44
45TEST(Memset, EmptyCase) {
46 std::array<char, 5> arr{'h', 'e', 'l', 'l', 'o'};
47 void* ret = memset(arr.data(), 0, 0);
48
49 // Destination buffer returned.
50 EXPECT_EQ(ret, arr.data());
51
52 // Destination buffer untouched.
53 constexpr std::array<char, 5> kExpected{'h', 'e', 'l', 'l', 'o'};
54 EXPECT_TRUE(
55 std::equal(arr.begin(), arr.end(), kExpected.begin(), kExpected.end()));
56}
57
58TEST(Memset, OneCharacter) {
59 std::array<char, 5> arr{'h', 'e', 'l', 'l', 'o'};
60 void* ret = memset(arr.data(), 0, 1);
61
62 // Ensure the destination buffer is returned.
63 EXPECT_EQ(ret, arr.data());
64
65 // Ensure the destination buffer is untouched.
66 constexpr std::array<char, 5> kExpected{0, 'e', 'l', 'l', 'o'};
67 EXPECT_TRUE(
68 std::equal(arr.begin(), arr.end(), kExpected.begin(), kExpected.end()));
69}
70
71// Now do a detailed case with more values. Span both word sizes and alignments
72// to ensure we hit some edge cases.
73TEST(Memset, MultipleSizesMultipleAlignments) {
74 constexpr int kMaxBytes = 64;
75 std::array<char, kMaxBytes> arr;
76
77 constexpr int kMaxAlignment = 16;
78
79 // Avoid 0 sentinel to prevent interaction with uninitialized memory.
80 constexpr char kSentinel = 3;
81 constexpr char kIotaStart = kSentinel + 7;
82
83 // Try different alignments.
84 for (int alignment = 0; alignment < kMaxAlignment; ++alignment) {
85 // Try different memset sizes.
86 for (int write_size = 0; write_size < (kMaxBytes - kMaxAlignment);
87 ++write_size) {
88 // Fill entire array with incrementing integers; starting above sentinel.
89 std::iota(arr.begin(), arr.end(), kIotaStart);
90
91 // Memset the first write_size bytes, with our sentinel
92 void* write_head = &arr[alignment];
93 const void* ret = memset(write_head, kSentinel, write_size);
94
95 // Check destination buffer returned.
96 EXPECT_EQ(ret, write_head);
97
98 for (int j = 0; j < kMaxBytes; ++j) {
99 if (j < alignment) {
100 // First part of destination buffer untouched; should match iota.
101 EXPECT_EQ(arr[j], kIotaStart + j);
102 } else if (j < alignment + write_size) {
103 // Second part is set to the sentinel value.
104 EXPECT_EQ(arr[j], kSentinel);
105 } else {
106 // Third part is back to the iota content.
107 EXPECT_EQ(arr[j], kIotaStart + j);
108 }
109 }
110 }
111 }
112}
113
114} // namespace
115} // namespace pw