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