Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah_arena_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
48#include <gtest/gtest.h>
49
50#include <ah-arena.H>
51
52#include <string>
53#include <type_traits>
54#include <vector>
55#include <cstdint>
56
57using namespace Aleph;
58
59// ============================================================================
60// Basic Tests with External Buffer
61// ============================================================================
62
63class ExternalBufferArena : public ::testing::Test
64{
65protected:
66 static constexpr size_t BUFFER_SIZE = 1024;
67 alignas(std::max_align_t) char buffer[BUFFER_SIZE];
69};
70
72{
73 EXPECT_TRUE(arena.is_valid());
74 EXPECT_FALSE(arena.owns_memory());
75 EXPECT_EQ(arena.capacity(), BUFFER_SIZE);
76 EXPECT_EQ(arena.allocated_size(), 0u);
77 EXPECT_EQ(arena.available_size(), BUFFER_SIZE);
78 EXPECT_TRUE(arena.empty());
79 EXPECT_FALSE(arena.full());
80}
81
83{
84 void* ptr = arena.alloc(100);
85
86 ASSERT_NE(ptr, nullptr);
87 EXPECT_EQ(ptr, buffer);
88 EXPECT_EQ(arena.allocated_size(), 100u);
89 EXPECT_EQ(arena.available_size(), BUFFER_SIZE - 100);
90 EXPECT_FALSE(arena.empty());
91}
92
94{
95 void* ptr1 = arena.alloc(100);
96 void* ptr2 = arena.alloc(200);
97 void* ptr3 = arena.alloc(50);
98
99 ASSERT_NE(ptr1, nullptr);
100 ASSERT_NE(ptr2, nullptr);
101 ASSERT_NE(ptr3, nullptr);
102
103 // Pointers should be sequential
104 EXPECT_EQ(static_cast<char*>(ptr2), static_cast<char*>(ptr1) + 100);
105 EXPECT_EQ(static_cast<char*>(ptr3), static_cast<char*>(ptr2) + 200);
106
107 EXPECT_EQ(arena.allocated_size(), 350u);
108}
109
111{
112 void* ptr1 = arena.alloc(BUFFER_SIZE - 10);
113 ASSERT_NE(ptr1, nullptr);
114
115 // This should fail (not enough space)
116 void* ptr2 = arena.alloc(20);
117 EXPECT_EQ(ptr2, nullptr);
118
119 // Original allocation should still be valid
120 EXPECT_EQ(arena.allocated_size(), BUFFER_SIZE - 10);
121}
122
124{
125 void* ptr = arena.alloc(BUFFER_SIZE);
126
127 ASSERT_NE(ptr, nullptr);
128 EXPECT_TRUE(arena.full());
129 EXPECT_EQ(arena.available_size(), 0u);
130
131 // No more allocations possible
132 void* ptr2 = arena.alloc(1);
133 EXPECT_EQ(ptr2, nullptr);
134}
135
137{
138 void* ptr = arena.alloc(0);
139 EXPECT_EQ(ptr, nullptr);
140 EXPECT_TRUE(arena.empty());
141}
142
144{
145 void* ptr1 = arena.alloc(100);
146 void* ptr2 = arena.alloc(200);
147
148 EXPECT_EQ(arena.allocated_size(), 300u);
149
150 // Dealloc in LIFO order works
151 arena.dealloc(ptr2, 200);
152 EXPECT_EQ(arena.allocated_size(), 100u);
153
154 arena.dealloc(ptr1, 100);
155 EXPECT_EQ(arena.allocated_size(), 0u);
156 EXPECT_TRUE(arena.empty());
157}
158
160{
161 void* ptr1 = arena.alloc(100);
162 (void)arena.alloc(200); // ptr2 not needed
163
164 EXPECT_EQ(arena.allocated_size(), 300u);
165
166 // Dealloc in non-LIFO order is a no-op
167 arena.dealloc(ptr1, 100);
168 EXPECT_EQ(arena.allocated_size(), 300u); // No change
169}
170
172{
173 (void)arena.alloc(100);
174 (void)arena.alloc(200);
175 (void)arena.alloc(50);
176
177 EXPECT_EQ(arena.allocated_size(), 350u);
178
179 arena.reset();
180
181 EXPECT_TRUE(arena.empty());
182 EXPECT_EQ(arena.allocated_size(), 0u);
183 EXPECT_EQ(arena.available_size(), BUFFER_SIZE);
184
185 // Can allocate again after reset
186 void* ptr = arena.alloc(500);
187 ASSERT_NE(ptr, nullptr);
188 EXPECT_EQ(ptr, buffer);
189}
190
192{
193 void* ptr = arena.alloc(100);
194
195 EXPECT_TRUE(arena.contains(ptr));
196 EXPECT_TRUE(arena.contains(buffer));
197 EXPECT_TRUE(arena.contains(buffer + BUFFER_SIZE - 1));
198 EXPECT_FALSE(arena.contains(buffer + BUFFER_SIZE)); // One past end
199 EXPECT_FALSE(arena.contains(nullptr));
200
201 int external_var = 42;
202 EXPECT_FALSE(arena.contains(&external_var));
203}
204
205// ============================================================================
206// Internal Allocation Tests
207// ============================================================================
208
217
219{
220 AhArenaAllocator arena(4096);
221
222 EXPECT_TRUE(arena.is_valid());
223 EXPECT_TRUE(arena.owns_memory());
224 EXPECT_EQ(arena.capacity(), 4096u);
225}
226
228{
229 AhArenaAllocator arena(1024);
230
231 void* ptr1 = arena.alloc(100);
232 void* ptr2 = arena.alloc(200);
233
234 ASSERT_NE(ptr1, nullptr);
235 ASSERT_NE(ptr2, nullptr);
236 EXPECT_EQ(arena.allocated_size(), 300u);
237}
238
239// ============================================================================
240// Move Semantics Tests
241// ============================================================================
242
244{
246 void* ptr = arena1.alloc(100);
247 ASSERT_NE(ptr, nullptr);
248
249 AhArenaAllocator arena2(std::move(arena1));
250
251 // arena2 should have the memory
252 EXPECT_TRUE(arena2.is_valid());
253 EXPECT_TRUE(arena2.owns_memory());
254 EXPECT_EQ(arena2.capacity(), 1024u);
255 EXPECT_EQ(arena2.allocated_size(), 100u);
256 EXPECT_TRUE(arena2.contains(ptr));
257
258 // arena1 should be invalid
259 EXPECT_FALSE(arena1.is_valid());
260 EXPECT_FALSE(arena1.owns_memory());
261 EXPECT_EQ(arena1.capacity(), 0u);
262}
263
265{
267 (void)arena1.alloc(100);
268
270 (void)arena2.alloc(50);
271
272 arena2 = std::move(arena1);
273
274 // arena2 should have arena1's memory
275 EXPECT_TRUE(arena2.is_valid());
276 EXPECT_EQ(arena2.capacity(), 1024u);
277 EXPECT_EQ(arena2.allocated_size(), 100u);
278
279 // arena1 should be invalid
280 EXPECT_FALSE(arena1.is_valid());
281}
282
284{
285 AhArenaAllocator arena(1024);
286 (void)arena.alloc(100);
287
288 // Self-move assignment - silence warning as this is intentional test
289#pragma GCC diagnostic push
290#pragma GCC diagnostic ignored "-Wself-move"
291 arena = std::move(arena);
292#pragma GCC diagnostic pop
293
294 // Should still be valid (self-move is a no-op)
295 EXPECT_TRUE(arena.is_valid());
296 EXPECT_EQ(arena.allocated_size(), 100u);
297}
298
299// ============================================================================
300// Typed Allocation Tests
301// ============================================================================
302
303namespace
304{
305struct TestObject
306{
307 int value;
308 std::string name;
309 static int construct_count;
310 static int destruct_count;
311
312 TestObject(int v, const std::string& n) : value(v), name(n)
313 {
314 ++construct_count;
315 }
316
318 {
319 ++destruct_count;
320 }
321};
322
323int TestObject::construct_count = 0;
324int TestObject::destruct_count = 0;
325
326class TypedAllocationTest : public ::testing::Test
327{
328protected:
329 void SetUp() override
330 {
331 TestObject::construct_count = 0;
332 TestObject::destruct_count = 0;
333 }
334};
335} // namespace
336
337TEST_F(TypedAllocationTest, AllocateAndDeallocate)
338{
339 AhArenaAllocator arena(4096);
340
341 TestObject* obj = allocate<TestObject>(arena, 42, "hello");
342
343 ASSERT_NE(obj, nullptr);
344 EXPECT_EQ(obj->value, 42);
345 EXPECT_EQ(obj->name, "hello");
346 EXPECT_EQ(TestObject::construct_count, 1);
347
348 deallocate(arena, obj);
349
350 EXPECT_EQ(TestObject::destruct_count, 1);
351}
352
353TEST_F(TypedAllocationTest, MultipleObjects)
354{
355 AhArenaAllocator arena(4096);
356
357 TestObject* obj1 = allocate<TestObject>(arena, 1, "one");
358 TestObject* obj2 = allocate<TestObject>(arena, 2, "two");
359 TestObject* obj3 = allocate<TestObject>(arena, 3, "three");
360
361 EXPECT_EQ(TestObject::construct_count, 3);
362
363 // Deallocate in LIFO order
364 deallocate(arena, obj3);
365 deallocate(arena, obj2);
366 deallocate(arena, obj1);
367
368 EXPECT_EQ(TestObject::destruct_count, 3);
369 EXPECT_TRUE(arena.empty());
370}
371
373{
374 AhArenaAllocator arena(4096);
375
376 (void)allocate<TestObject>(arena, 1, "one");
377 (void)allocate<TestObject>(arena, 2, "two");
378
379 EXPECT_EQ(TestObject::construct_count, 2);
380
381 // Reset does NOT call destructors
382 arena.reset();
383
384 EXPECT_EQ(TestObject::destruct_count, 0);
385 EXPECT_TRUE(arena.empty());
386}
387
389{
390 char buffer[sizeof(TestObject) - 1]; // Too small for TestObject
391 AhArenaAllocator arena(buffer, sizeof(buffer));
392
393 TestObject* obj = allocate<TestObject>(arena, 42, "test");
394
395 EXPECT_EQ(obj, nullptr);
396 EXPECT_EQ(TestObject::construct_count, 0); // Constructor not called
397}
398
399// ============================================================================
400// Alignment Tests
401// ============================================================================
402
404{
405 AhArenaAllocator arena(4096);
406
407 // Allocate something small first to misalign
408 (void)arena.alloc(3);
409
410 // Now allocate with alignment
411 void* ptr = arena.alloc_aligned(100, 16);
412
413 ASSERT_NE(ptr, nullptr);
414 EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr) % 16, 0u);
415}
416
418{
419 AhArenaAllocator arena(4096);
420
421 void* ptr1 = arena.alloc_aligned(10, 8);
422 void* ptr2 = arena.alloc_aligned(10, 16);
423 void* ptr3 = arena.alloc_aligned(10, 32);
424
425 ASSERT_NE(ptr1, nullptr);
426 ASSERT_NE(ptr2, nullptr);
427 ASSERT_NE(ptr3, nullptr);
428
429 EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr1) % 8, 0u);
430 EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr2) % 16, 0u);
431 EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr3) % 32, 0u);
432}
433
435{
436 struct alignas(32) AlignedStruct
437 {
438 char data[64];
439 };
440
441 AhArenaAllocator arena(4096);
442
443 // Misalign first
444 (void)arena.alloc(7);
445
447
448 ASSERT_NE(obj, nullptr);
449 EXPECT_EQ(reinterpret_cast<uintptr_t>(obj) % 32, 0u);
450}
451
452// ============================================================================
453// Type Traits Tests
454// ============================================================================
455
457{
458 static_assert(!std::is_copy_constructible_v<AhArenaAllocator>);
459 static_assert(!std::is_copy_assignable_v<AhArenaAllocator>);
460}
461
463{
464 static_assert(std::is_move_constructible_v<AhArenaAllocator>);
465 static_assert(std::is_move_assignable_v<AhArenaAllocator>);
466}
467
469{
470 static_assert(std::is_nothrow_move_constructible_v<AhArenaAllocator>);
471 static_assert(std::is_nothrow_move_assignable_v<AhArenaAllocator>);
472}
473
474// ============================================================================
475// Edge Cases
476// ============================================================================
477
479{
480 char buffer[1];
481 AhArenaAllocator arena(buffer, 1);
482
483 void* ptr = arena.alloc(1);
484 ASSERT_NE(ptr, nullptr);
485 EXPECT_TRUE(arena.full());
486
487 void* ptr2 = arena.alloc(1);
488 EXPECT_EQ(ptr2, nullptr);
489}
490
492{
493 AhArenaAllocator arena(1024);
494 void* ptr = arena.alloc(100);
495
496 arena.dealloc(ptr, 0); // Should be no-op
497
498 EXPECT_EQ(arena.allocated_size(), 100u);
499}
500
502{
503 AhArenaAllocator arena(1024);
504 (void)arena.alloc(100);
505
506 arena.dealloc(nullptr, 100); // Should be no-op
507
508 EXPECT_EQ(arena.allocated_size(), 100u);
509}
510
512{
513 AhArenaAllocator arena(1024);
514
515 void* ptr = arena.alloc_aligned(0, 16);
516 EXPECT_EQ(ptr, nullptr);
517}
518
520{
521 AhArenaAllocator arena(1024);
522
523 void* ptr = arena.alloc_aligned(100, 0);
524 EXPECT_EQ(ptr, nullptr);
525}
526
527// ============================================================================
528// Backward Compatibility
529// ============================================================================
530
532{
533 AhArenaAllocator arena(1024);
534
535 // These are deprecated but should still work
536 void* ptr = arena.allocate(100);
537 EXPECT_NE(ptr, nullptr);
538
539 arena.deallocate(ptr, 100);
540 EXPECT_TRUE(arena.empty());
541}
542
544{
545 AhArenaAllocator arena(4096);
546
547 TestObject* obj = allocate<TestObject>(arena, 1, "test");
548 ASSERT_NE(obj, nullptr);
549
550 // dealloc is an alias for deallocate
551 dealloc(arena, obj);
552 EXPECT_TRUE(arena.empty());
553}
554
555int main(int argc, char** argv)
556{
557 ::testing::InitGoogleTest(&argc, argv);
558 return RUN_ALL_TESTS();
559}
Memory arena for fast bulk allocations.
TEST_F(ExternalBufferArena, InitialState)
int main()
Arena allocator for fast bump-pointer allocation.
Definition ah-arena.H:122
void deallocate(const void *addr, size_t size) noexcept
Definition ah-arena.H:451
bool is_valid() const noexcept
Returns true if the arena is valid (has memory).
Definition ah-arena.H:383
void * alloc(const size_t size) noexcept
Allocate raw memory from the arena.
Definition ah-arena.H:271
void * alloc_aligned(const size_t size, const size_t alignment) noexcept
Allocate aligned memory from the arena.
Definition ah-arena.H:294
void reset() noexcept
Reset arena, making all memory available again.
Definition ah-arena.H:256
void dealloc(const void *addr, const size_t size) noexcept
Deallocate memory (only effective for LIFO pattern).
Definition ah-arena.H:326
bool owns_memory() const noexcept
Returns true if the arena owns its memory.
Definition ah-arena.H:389
size_t capacity() const noexcept
Get total arena capacity.
Definition ah-arena.H:407
size_t allocated_size() const noexcept
Get total bytes currently allocated.
Definition ah-arena.H:395
bool full() const noexcept
Returns true if the arena is full (no space left).
Definition ah-arena.H:419
bool empty() const noexcept
Returns true if the arena is empty (no allocations).
Definition ah-arena.H:413
static constexpr size_t DEFAULT_SIZE
Default arena size (1 MB).
Definition ah-arena.H:133
void * allocate(size_t size) noexcept
Definition ah-arena.H:448
char buffer[BUFFER_SIZE]
AhArenaAllocator arena
static constexpr size_t BUFFER_SIZE
#define TEST(name)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void dealloc(AhArenaAllocator &arena, T *ptr) noexcept
Alias for deallocate (backward compatibility).
Definition ah-arena.H:501
void deallocate(AhArenaAllocator &arena, T *ptr) noexcept
Destruct and deallocate an object from an arena.
Definition ah-arena.H:494
DynList< T > maps(const C &c, Op op)
Classic map operation.