1 /** @file
2 
3     MemArena unit tests.
4 
5     @section license License
6 
7     Licensed to the Apache Software Foundation (ASF) under one
8     or more contributor license agreements.  See the NOTICE file
9     distributed with this work for additional information
10     regarding copyright ownership.  The ASF licenses this file
11     to you under the Apache License, Version 2.0 (the
12     "License"); you may not use this file except in compliance
13     with the License.  You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17     Unless required by applicable law or agreed to in writing, software
18     distributed under the License is distributed on an "AS IS" BASIS,
19     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20     See the License for the specific language governing permissions and
21     limitations under the License.
22 */
23 
24 #include <catch.hpp>
25 
26 #include <string_view>
27 #include "tscore/MemArena.h"
28 using ts::MemSpan;
29 using ts::MemArena;
30 using namespace std::literals;
31 
32 TEST_CASE("MemArena generic", "[libts][MemArena]")
33 {
34   ts::MemArena arena{64};
35   REQUIRE(arena.size() == 0);
36   REQUIRE(arena.reserved_size() == 0);
37   arena.alloc(0);
38   REQUIRE(arena.size() == 0);
39   REQUIRE(arena.reserved_size() >= 64);
40 
41   auto span1 = arena.alloc(32);
42   REQUIRE(span1.size() == 32);
43 
44   auto span2 = arena.alloc(32);
45   REQUIRE(span2.size() == 32);
46 
47   REQUIRE(span1.data() != span2.data());
48   REQUIRE(arena.size() == 64);
49 
50   auto extent{arena.reserved_size()};
51   span1 = arena.alloc(128);
52   REQUIRE(extent < arena.reserved_size());
53 }
54 
55 TEST_CASE("MemArena freeze and thaw", "[libts][MemArena]")
56 {
57   MemArena arena;
58   auto span1{arena.alloc(1024)};
59   REQUIRE(span1.size() == 1024);
60   REQUIRE(arena.size() == 1024);
61   REQUIRE(arena.reserved_size() >= 1024);
62 
63   arena.freeze();
64 
65   REQUIRE(arena.size() == 0);
66   REQUIRE(arena.allocated_size() == 1024);
67   REQUIRE(arena.reserved_size() >= 1024);
68 
69   arena.thaw();
70   REQUIRE(arena.size() == 0);
71   REQUIRE(arena.allocated_size() == 0);
72   REQUIRE(arena.reserved_size() == 0);
73 
74   span1 = arena.alloc(1024);
75   arena.freeze();
76   auto extent{arena.reserved_size()};
77   arena.alloc(512);
78   REQUIRE(arena.reserved_size() > extent); // new extent should be bigger.
79   arena.thaw();
80   REQUIRE(arena.size() == 512);
81   REQUIRE(arena.reserved_size() >= 1024);
82 
83   arena.clear();
84   REQUIRE(arena.size() == 0);
85   REQUIRE(arena.reserved_size() == 0);
86 
87   span1 = arena.alloc(262144);
88   arena.freeze();
89   extent = arena.reserved_size();
90   arena.alloc(512);
91   REQUIRE(arena.reserved_size() > extent); // new extent should be bigger.
92   arena.thaw();
93   REQUIRE(arena.size() == 512);
94   REQUIRE(arena.reserved_size() >= 262144);
95 
96   arena.clear();
97 
98   span1  = arena.alloc(262144);
99   extent = arena.reserved_size();
100   arena.freeze();
101   for (int i = 0; i < 262144 / 512; ++i)
102     arena.alloc(512);
103   REQUIRE(arena.reserved_size() > extent); // Bigger while frozen memory is still around.
104   arena.thaw();
105   REQUIRE(arena.size() == 262144);
106   REQUIRE(arena.reserved_size() == extent); // should be identical to before freeze.
107 
108   arena.alloc(512);
109   arena.alloc(768);
110   arena.freeze(32000);
111   arena.thaw();
112   arena.alloc(0);
113   REQUIRE(arena.reserved_size() >= 32000);
114   REQUIRE(arena.reserved_size() < 2 * 32000);
115 }
116 
117 TEST_CASE("MemArena helper", "[libts][MemArena]")
118 {
119   struct Thing {
120     int ten{10};
121     std::string_view name{"name"};
122 
ThingThing123     Thing() {}
ThingThing124     Thing(int const x) : ten(x) {}
ThingThing125     Thing(std::string_view const sv) : name(sv) {}
ThingThing126     Thing(int const x, std::string_view const sv) : ten(x), name(sv) {}
ThingThing127     Thing(std::string_view const sv, int const x) : ten(x), name(sv) {}
128   };
129 
130   ts::MemArena arena{256};
131   REQUIRE(arena.size() == 0);
132   ts::MemSpan<char> s = arena.alloc(56).rebind<char>();
133   REQUIRE(arena.size() == 56);
134   void *ptr = s.begin();
135 
136   REQUIRE(arena.contains((char *)ptr));
137   REQUIRE(arena.contains((char *)ptr + 100)); // even though span isn't this large, this pointer should still be in arena
138   REQUIRE(!arena.contains((char *)ptr + 300));
139   REQUIRE(!arena.contains((char *)ptr - 1));
140 
141   arena.freeze(128);
142   REQUIRE(arena.contains((char *)ptr));
143   REQUIRE(arena.contains((char *)ptr + 100));
144   ts::MemSpan<char> s2 = arena.alloc(10).rebind<char>();
145   void *ptr2           = s2.begin();
146   REQUIRE(arena.contains((char *)ptr));
147   REQUIRE(arena.contains((char *)ptr2));
148   REQUIRE(arena.allocated_size() == 56 + 10);
149 
150   arena.thaw();
151   REQUIRE(!arena.contains((char *)ptr));
152   REQUIRE(arena.contains((char *)ptr2));
153 
154   Thing *thing_one{arena.make<Thing>()};
155 
156   REQUIRE(thing_one->ten == 10);
157   REQUIRE(thing_one->name == "name");
158 
159   std::string_view const bob{"bob"};
160   thing_one = arena.make<Thing>(17, bob);
161 
162   REQUIRE(thing_one->name == "bob");
163   REQUIRE(thing_one->ten == 17);
164 
165   std::string const dave{"Dave"};
166   thing_one = arena.make<Thing>(dave, 137);
167 
168   REQUIRE(thing_one->name == "Dave");
169   REQUIRE(thing_one->ten == 137);
170 
171   thing_one = arena.make<Thing>(9999);
172 
173   REQUIRE(thing_one->ten == 9999);
174   REQUIRE(thing_one->name == "name");
175 
176   std::string const persia{"Persia"};
177   thing_one = arena.make<Thing>(persia);
178 
179   REQUIRE(thing_one->ten == 10);
180   REQUIRE(thing_one->name == "Persia");
181 }
182 
183 TEST_CASE("MemArena large alloc", "[libts][MemArena]")
184 {
185   ts::MemArena arena;
186   ts::MemSpan s = arena.alloc(4000);
187   REQUIRE(s.size() == 4000);
188 
189   ts::MemSpan<void> s_a[10];
190   s_a[0] = arena.alloc(100);
191   s_a[1] = arena.alloc(200);
192   s_a[2] = arena.alloc(300);
193   s_a[3] = arena.alloc(400);
194   s_a[4] = arena.alloc(500);
195   s_a[5] = arena.alloc(600);
196   s_a[6] = arena.alloc(700);
197   s_a[7] = arena.alloc(800);
198   s_a[8] = arena.alloc(900);
199   s_a[9] = arena.alloc(1000);
200 
201   // ensure none of the spans have any overlap in memory.
202   for (int i = 0; i < 10; ++i) {
203     s = s_a[i];
204     for (int j = i + 1; j < 10; ++j) {
205       REQUIRE(s_a[i].data() != s_a[j].data());
206     }
207   }
208 }
209 
210 TEST_CASE("MemArena block allocation", "[libts][MemArena]")
211 {
212   ts::MemArena arena{64};
213   ts::MemSpan<char> s  = arena.alloc(32).rebind<char>();
214   ts::MemSpan<char> s2 = arena.alloc(16).rebind<char>();
215   ts::MemSpan<char> s3 = arena.alloc(16).rebind<char>();
216 
217   REQUIRE(s.size() == 32);
218   REQUIRE(arena.allocated_size() == 64);
219 
220   REQUIRE(arena.contains((char *)s.begin()));
221   REQUIRE(arena.contains((char *)s2.begin()));
222   REQUIRE(arena.contains((char *)s3.begin()));
223 
224   REQUIRE((char *)s.begin() + 32 == (char *)s2.begin());
225   REQUIRE((char *)s.begin() + 48 == (char *)s3.begin());
226   REQUIRE((char *)s2.begin() + 16 == (char *)s3.begin());
227 
228   REQUIRE(s.end() == s2.begin());
229   REQUIRE(s2.end() == s3.begin());
230   REQUIRE((char *)s.begin() + 64 == s3.end());
231 }
232 
233 TEST_CASE("MemArena full blocks", "[libts][MemArena]")
234 {
235   // couple of large allocations - should be exactly sized in the generation.
236   size_t init_size = 32000;
237   ts::MemArena arena(init_size);
238 
239   MemSpan<char> m1{arena.alloc(init_size - 64).rebind<char>()};
240   MemSpan<char> m2{arena.alloc(32000).rebind<char>()};
241   MemSpan<char> m3{arena.alloc(64000).rebind<char>()};
242 
243   REQUIRE(arena.remaining() >= 64);
244   REQUIRE(arena.reserved_size() > 32000 + 64000 + init_size);
245   REQUIRE(arena.reserved_size() < 2 * (32000 + 64000 + init_size));
246 
247   // Let's see if that memory is really there.
248   memset(m1.data(), 0xa5, m1.size());
249   memset(m2.data(), 0xc2, m2.size());
250   memset(m3.data(), 0x56, m3.size());
251 
__anonb92dfb9c0102(uint8_t c) 252   REQUIRE(std::all_of(m1.begin(), m1.end(), [](uint8_t c) { return 0xa5 == c; }));
__anonb92dfb9c0202(uint8_t c) 253   REQUIRE(std::all_of(m2.begin(), m2.end(), [](uint8_t c) { return 0xc2 == c; }));
__anonb92dfb9c0302(uint8_t c) 254   REQUIRE(std::all_of(m3.begin(), m3.end(), [](uint8_t c) { return 0x56 == c; }));
255 }
256