1 // Copyright 2024 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_allocator/pmr_allocator.h"
16
17 #include <deque>
18 #include <forward_list>
19 #include <list>
20 #include <map>
21 #include <set>
22 #include <unordered_map>
23 #include <unordered_set>
24 #include <vector>
25
26 #include "pw_allocator/testing.h"
27 #include "pw_unit_test/framework.h"
28
29 namespace {
30
31 // Test fixtures.
32
33 using ::pw::allocator::PmrAllocator;
34 using ::pw::allocator::test::AllocatorForTest;
35
36 struct Foo {
37 uintptr_t value;
38
Foo__anonc77615730111::Foo39 Foo(uintptr_t value_) : value(value_) {}
40 };
41
operator ==(const Foo & lhs,const Foo & rhs)42 bool operator==(const Foo& lhs, const Foo& rhs) {
43 return lhs.value == rhs.value;
44 }
45
operator <(const Foo & lhs,const Foo & rhs)46 bool operator<(const Foo& lhs, const Foo& rhs) { return lhs.value < rhs.value; }
47
48 } // namespace
49
50 template <>
51 struct std::hash<Foo> {
operator ()std::hash52 size_t operator()(const Foo& foo) const {
53 return std::hash<uintptr_t>()(foo.value);
54 }
55 };
56
57 namespace {
58
59 struct Bar {
60 std::array<std::byte, 16> buffer;
61
Bar__anonc77615730211::Bar62 Bar(int value) { std::memset(buffer.data(), value, buffer.size()); }
63 };
64
65 template <typename T, typename = void>
66 struct has_emplace_front : std::false_type {};
67
68 template <typename T>
69 struct has_emplace_front<
70 T,
71 std::void_t<decltype(std::declval<T>().emplace_front())> >
72 : std::true_type {};
73
74 template <typename T, typename = void>
75 struct has_emplace_back : std::false_type {};
76
77 template <typename T>
78 struct has_emplace_back<
79 T,
80 std::void_t<decltype(std::declval<T>().emplace_back())> > : std::true_type {
81 };
82
83 template <typename T, typename = void>
84 struct has_key_type : std::false_type {};
85
86 template <typename T>
87 struct has_key_type<T, std::void_t<typename T::key_type> > : std::true_type {};
88
89 template <typename T, typename = void>
90 struct has_mapped_type : std::false_type {};
91
92 template <typename T>
93 struct has_mapped_type<T, std::void_t<typename T::mapped_type> >
94 : std::true_type {};
95
96 template <typename Container, size_t kCapacity = 256>
TestPmrAllocator()97 void TestPmrAllocator() {
98 AllocatorForTest<kCapacity> underlying;
99 static_assert(sizeof(Foo) >= AllocatorForTest<kCapacity>::kMinSize);
100 auto& requested_bytes = underlying.metrics().requested_bytes;
101 EXPECT_EQ(requested_bytes.value(), 0U);
102
103 PmrAllocator allocator(underlying);
104 {
105 Container container(allocator);
106 size_t size = 0;
107 // Sequence containers.
108 if constexpr (has_emplace_front<Container>::value) {
109 container.emplace_front(1);
110 container.emplace_front(2);
111 size += sizeof(typename Container::value_type) * 2;
112 }
113 if constexpr (has_emplace_back<Container>::value) {
114 container.emplace_back(3);
115 container.emplace_back(4);
116 size += sizeof(typename Container::value_type) * 2;
117 }
118 // Associative containers.
119 if constexpr (has_mapped_type<Container>::value) {
120 container.insert({1, 10});
121 container.insert({2, 20});
122 size += sizeof(typename Container::key_type) * 2;
123 size += sizeof(typename Container::mapped_type) * 2;
124 } else if constexpr (has_key_type<Container>::value) {
125 container.insert(3);
126 container.insert(4);
127 size += sizeof(typename Container::key_type) * 2;
128 }
129 EXPECT_GE(requested_bytes.value(), size);
130 }
131 EXPECT_EQ(requested_bytes.value(), 0U);
132 }
133
134 // Unit tests.
135
TEST(PmrAllocatorTest,Vector)136 TEST(PmrAllocatorTest, Vector) { TestPmrAllocator<pw::pmr::vector<Foo> >(); }
137
TEST(PmrAllocatorTest,Deque)138 TEST(PmrAllocatorTest, Deque) {
139 // Some implementations preallocate a lot of memory.
140 TestPmrAllocator<pw::pmr::deque<Foo>, 8192>();
141 }
142
TEST(PmrAllocatorTest,ForwardList)143 TEST(PmrAllocatorTest, ForwardList) {
144 TestPmrAllocator<pw::pmr::forward_list<Foo> >();
145 }
146
TEST(PmrAllocatorTest,List)147 TEST(PmrAllocatorTest, List) { TestPmrAllocator<pw::pmr::list<Foo> >(); }
148
TEST(PmrAllocatorTest,Set)149 TEST(PmrAllocatorTest, Set) { TestPmrAllocator<pw::pmr::set<Foo> >(); }
150
TEST(PmrAllocatorTest,Map)151 TEST(PmrAllocatorTest, Map) { TestPmrAllocator<pw::pmr::map<Foo, Bar> >(); }
152
TEST(PmrAllocatorTest,MultiSet)153 TEST(PmrAllocatorTest, MultiSet) {
154 TestPmrAllocator<pw::pmr::multiset<Foo> >();
155 }
156
TEST(PmrAllocatorTest,MultiMap)157 TEST(PmrAllocatorTest, MultiMap) {
158 TestPmrAllocator<pw::pmr::multimap<Foo, Bar> >();
159 }
160
TEST(PmrAllocatorTest,UnorderedSet)161 TEST(PmrAllocatorTest, UnorderedSet) {
162 // Some implementations preallocate a lot of memory.
163 TestPmrAllocator<pw::pmr::unordered_set<Foo>, 1024>();
164 }
165
TEST(PmrAllocatorTest,UnorderedMap)166 TEST(PmrAllocatorTest, UnorderedMap) {
167 // Some implementations preallocate a lot of memory.
168 TestPmrAllocator<pw::pmr::unordered_map<Foo, Bar>, 1024>();
169 }
170
TEST(PmrAllocatorTest,UnorderedMultiSet)171 TEST(PmrAllocatorTest, UnorderedMultiSet) {
172 // Some implementations preallocate a lot of memory.
173 TestPmrAllocator<pw::pmr::unordered_multiset<Foo>, 1024>();
174 }
175
TEST(PmrAllocatorTest,UnorderedMultiMap)176 TEST(PmrAllocatorTest, UnorderedMultiMap) {
177 // Some implementations preallocate a lot of memory.
178 TestPmrAllocator<pw::pmr::unordered_multimap<Foo, Bar>, 1024>();
179 }
180
181 } // namespace
182