Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-mapping_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
33
38#include <gtest/gtest.h>
39#include <cstring>
40#include <string>
41#include <vector>
42#include <filesystem>
43#include <ah-map-arena.H>
44
45using namespace Aleph;
46namespace fs = std::filesystem;
47
48// =============================================================================
49// Test Fixtures
50// =============================================================================
51
52class MapArenaTest : public ::testing::Test
53{
54protected:
55 std::string test_file;
56
57 void SetUp() override
58 {
59 // Create a unique test file name
60 test_file = "/tmp/map_arena_test_" + std::to_string(getpid()) + ".dat";
61 // Ensure file doesn't exist
62 fs::remove(test_file);
63 }
64
65 void TearDown() override
66 {
67 // Clean up test file
68 fs::remove(test_file);
69 }
70};
71
72// =============================================================================
73// Construction Tests
74// =============================================================================
75
77{
78 MapArena arena;
80 EXPECT_EQ(arena.mapped_addr(), nullptr);
81 EXPECT_EQ(arena.file_descriptor(), -1);
82}
83
85{
86 MapArena arena(test_file);
87
89 EXPECT_NE(arena.mapped_addr(), nullptr);
90 EXPECT_GE(arena.file_descriptor(), 0);
91 EXPECT_EQ(arena.size(), 0u);
93 EXPECT_TRUE(fs::exists(test_file));
94}
95
97{
98 MapArena arena;
99 arena.init(test_file);
100
102 EXPECT_NE(arena.mapped_addr(), nullptr);
103 EXPECT_EQ(arena.size(), 0u);
104}
105
107{
108 // First create an arena with some data
109 {
110 MapArena arena(test_file);
111 char * ptr = arena.reserve(100);
112 ASSERT_NE(ptr, nullptr);
113 std::memcpy(ptr, "test data", 10);
114 arena.commit(10);
115 arena.sync();
116 }
117
118 // Now init_and_erase should clear it
119 MapArena arena;
120 arena.init_and_erase(test_file);
121
123 EXPECT_EQ(arena.size(), 0u);
124 EXPECT_TRUE(arena.empty());
125}
126
127// =============================================================================
128// Move Semantics Tests
129// =============================================================================
130
132{
133 MapArena original(test_file);
134 char * ptr = original.reserve(50);
135 ASSERT_NE(ptr, nullptr);
136 std::memcpy(ptr, "hello", 6);
137 original.commit(6);
138
139 void * orig_addr = original.mapped_addr();
140
141 MapArena moved(std::move(original));
142
143 // Moved-to arena should have the resources
144 EXPECT_TRUE(moved.is_initialized());
145 EXPECT_EQ(moved.mapped_addr(), orig_addr);
146 EXPECT_EQ(moved.size(), 6u);
147
148 // Original should be empty
149 EXPECT_FALSE(original.is_initialized());
150 EXPECT_EQ(original.mapped_addr(), nullptr);
151 EXPECT_EQ(original.file_descriptor(), -1);
152}
153
155{
156 MapArena original(test_file);
157 char * ptr = original.reserve(50);
158 ASSERT_NE(ptr, nullptr);
159 std::memcpy(ptr, "data", 5);
160 original.commit(5);
161
163 target = std::move(original);
164
165 EXPECT_TRUE(target.is_initialized());
166 EXPECT_EQ(target.size(), 5u);
167 EXPECT_FALSE(original.is_initialized());
168}
169
171{
172 MapArena arena(test_file);
173 arena.reserve(10);
174 arena.commit(10);
175
177 tmp = std::move(arena);
178 arena = std::move(tmp);
179
180 // Should still be valid after roundtrip move
182 EXPECT_EQ(arena.size(), 10u);
183}
184
185// =============================================================================
186// Reserve and Commit Tests
187// =============================================================================
188
190{
191 MapArena arena(test_file);
192
193 char * ptr = arena.reserve(100);
194 ASSERT_NE(ptr, nullptr);
195 EXPECT_EQ(arena.size(), 0u); // Not committed yet
196
197 arena.commit(100);
198 EXPECT_EQ(arena.size(), 100u);
199}
200
202{
203 MapArena arena(test_file);
204
205 for (int i = 0; i < 10; ++i)
206 {
207 char * ptr = arena.reserve(50);
208 ASSERT_NE(ptr, nullptr);
209 arena.commit(50);
210 }
211
212 EXPECT_EQ(arena.size(), 500u);
213}
214
216{
217 MapArena arena(test_file);
218
219 char * ptr = arena.reserve(1000);
220 ASSERT_NE(ptr, nullptr);
221
222 // Only commit part of what was reserved
223 arena.commit(500);
224 EXPECT_EQ(arena.size(), 500u);
225}
226
228{
229 MapArena arena(test_file);
230
231 const char * message = "Hello, MapArena!";
232 size_t len = std::strlen(message) + 1;
233
234 char * ptr = arena.reserve(len);
235 ASSERT_NE(ptr, nullptr);
236
237 std::memcpy(ptr, message, len);
238 arena.commit(len);
239
240 // Verify data
241 EXPECT_STREQ(arena.base(), message);
242}
243
245{
246 MapArena arena(test_file);
247
248 size_t initial_capacity = arena.capacity();
249
250 // Reserve more than initial capacity
251 char * ptr = arena.reserve(initial_capacity + 1000);
252 ASSERT_NE(ptr, nullptr);
253
255 arena.commit(initial_capacity + 1000);
256 EXPECT_EQ(arena.size(), initial_capacity + 1000);
257}
258
260{
261 MapArena arena(test_file);
262
263 // Reserve 1 MB
264 size_t large_size = 1024 * 1024;
265 char * ptr = arena.reserve(large_size);
266 ASSERT_NE(ptr, nullptr);
267
268 // Write pattern
269 std::memset(ptr, 'X', large_size);
270 arena.commit(large_size);
271
272 EXPECT_EQ(arena.size(), large_size);
273 EXPECT_GE(arena.capacity(), large_size);
274}
275
276// =============================================================================
277// Iterator Tests
278// =============================================================================
279
281{
282 MapArena arena(test_file);
283
284 EXPECT_EQ(arena.begin(), arena.end());
285}
286
288{
289 MapArena arena(test_file);
290
291 const char * data = "ABCDEFGHIJ";
292 char * ptr = arena.reserve(10);
293 ASSERT_NE(ptr, nullptr);
294 std::memcpy(ptr, data, 10);
295 arena.commit(10);
296
297 // Traverse with iterators
298 int i = 0;
299 for (auto it = arena.begin(); it != arena.end(); ++it, ++i)
300 EXPECT_EQ(*it, data[i]);
301
302 EXPECT_EQ(i, 10);
303}
304
306{
307 MapArena arena(test_file);
308 char * ptr = arena.reserve(5);
309 std::memcpy(ptr, "test", 5);
310 arena.commit(5);
311
312 const MapArena & const_arena = arena;
314}
315
317{
318 MapArena arena(test_file);
319
320 char * ptr = arena.reserve(26);
321 for (int i = 0; i < 26; ++i)
322 ptr[i] = 'a' + i;
323 arena.commit(26);
324
325 std::string result;
326 for (char c : arena)
327 result += c;
328
329 EXPECT_EQ(result, "abcdefghijklmnopqrstuvwxyz");
330}
331
332// =============================================================================
333// Size and Capacity Tests
334// =============================================================================
335
341
343{
344 MapArena arena(test_file);
345
346 EXPECT_EQ(arena.avail(), arena.capacity());
347
348 arena.reserve(100);
349 arena.commit(100);
350
351 EXPECT_EQ(arena.avail(), arena.capacity() - 100);
352}
353
355{
356 MapArena arena(test_file);
357
358 EXPECT_EQ(arena.size(), 0u);
359 EXPECT_TRUE(arena.empty());
360
361 arena.reserve(50);
362 arena.commit(50);
363 EXPECT_EQ(arena.size(), 50u);
364 EXPECT_FALSE(arena.empty());
365
366 arena.reserve(30);
367 arena.commit(30);
368 EXPECT_EQ(arena.size(), 80u);
369}
370
371// =============================================================================
372// Persistence Tests
373// =============================================================================
374
376{
377 const char * message = "Persistent data!";
378 size_t len = std::strlen(message) + 1;
379
380 // Write data
381 {
382 MapArena arena(test_file);
383 char * ptr = arena.reserve(len);
384 ASSERT_NE(ptr, nullptr);
385 std::memcpy(ptr, message, len);
386 arena.commit(len);
387 arena.sync();
388 }
389
390 // Read data in new instance
391 {
392 MapArena arena(test_file);
393 // Note: Recovery reads end_ from file start, but our simple version
394 // doesn't store it. This test verifies basic file creation.
396 }
397}
398
400{
401 MapArena arena(test_file);
402
403 char * ptr = arena.reserve(100);
404 ASSERT_NE(ptr, nullptr);
405 std::memset(ptr, 'A', 100);
406 arena.commit(100);
407
408 // sync() should not throw
409 EXPECT_NO_THROW(arena.sync());
410}
411
412// =============================================================================
413// Query Method Tests
414// =============================================================================
415
417{
418 MapArena arena(test_file);
419
420 EXPECT_EQ(arena.base(), arena.begin());
421 EXPECT_EQ(arena.base(), arena.mapped_addr());
422}
423
425{
426 MapArena arena(test_file);
427
428 EXPECT_TRUE(arena.empty());
429
430 arena.reserve(1);
431 arena.commit(1);
432
433 EXPECT_FALSE(arena.empty());
434}
435
437{
438 MapArena arena;
440
441 arena.init(test_file);
443}
444
446{
447 MapArena arena(test_file);
448 int fd = arena.file_descriptor();
449
450 EXPECT_GE(fd, 0);
451
452 // Verify it's a valid fd by checking with fcntl
453 int flags = fcntl(fd, F_GETFD);
454 EXPECT_NE(flags, -1);
455}
456
457// =============================================================================
458// Edge Cases
459// =============================================================================
460
462{
463 MapArena arena(test_file);
464
465 char * ptr = arena.reserve(0);
466 EXPECT_NE(ptr, nullptr); // Should return current end pointer
467 arena.commit(0);
468 EXPECT_EQ(arena.size(), 0u);
469}
470
472{
473 MapArena arena(test_file);
474
475 size_t total = 0;
477
478 // Force multiple remaps
479 for (int i = 0; i < 10; ++i)
480 {
481 char * ptr = arena.reserve(allocation_size);
482 ASSERT_NE(ptr, nullptr) << "Failed at iteration " << i;
483 arena.commit(allocation_size);
485 }
486
487 EXPECT_EQ(arena.size(), total);
488 EXPECT_GE(arena.capacity(), total);
489}
490
491// =============================================================================
492// Output Operator Tests
493// =============================================================================
494
496{
497 MapArena arena(test_file);
498 arena.reserve(100);
499 arena.commit(100);
500
501 std::ostringstream oss;
502 oss << arena;
503
504 std::string output = oss.str();
505 EXPECT_NE(output.find("MapArena"), std::string::npos);
506 EXPECT_NE(output.find("size"), std::string::npos);
507 EXPECT_NE(output.find("capacity"), std::string::npos);
508}
509
510// =============================================================================
511// Destructor Safety Tests
512// =============================================================================
513
515{
516 // Should not crash when destroying uninitialized arena
517 MapArena arena;
518 // Destructor called automatically
519}
520
522{
523 MapArena arena(test_file);
524 MapArena other = std::move(arena);
525 // arena destructor should safely handle moved-from state
526}
527
528// =============================================================================
529// Stress Tests
530// =============================================================================
531
533{
534 MapArena arena(test_file);
535
536 constexpr int N = 10000;
537 for (int i = 0; i < N; ++i)
538 {
539 char * ptr = arena.reserve(8);
540 ASSERT_NE(ptr, nullptr) << "Failed at allocation " << i;
541 arena.commit(8);
542 }
543
544 EXPECT_EQ(arena.size(), N * 8u);
545}
546
548{
549 MapArena arena(test_file);
550
551 size_t total = 0;
552 for (int i = 0; i < 100; ++i)
553 {
554 size_t sz = (i % 2 == 0) ? 16 : 256;
555 char * ptr = arena.reserve(sz);
556 ASSERT_NE(ptr, nullptr);
557 arena.commit(sz);
558 total += sz;
559 }
560
561 EXPECT_EQ(arena.size(), total);
562}
563
564// =============================================================================
565// Type Alias Tests
566// =============================================================================
567
569{
570 static_assert(std::is_same_v<MapArena::iterator, char*>);
571 static_assert(std::is_same_v<MapArena::const_iterator, const char*>);
572 static_assert(std::is_same_v<MapArena::size_type, size_t>);
573}
574
575// =============================================================================
576// Static Constants Tests
577// =============================================================================
578
583
584// =============================================================================
585// Main
586// =============================================================================
587
588int main(int argc, char ** argv)
589{
590 ::testing::InitGoogleTest(&argc, argv);
591 return RUN_ALL_TESTS();
592}
Memory-mapped file arena allocator.
TEST_F(MapArenaTest, DefaultConstruction)
int main()
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Memory-mapped file arena allocator.
size_type size() const noexcept
Get the total committed (allocated) size.
size_type capacity() const noexcept
Get the current capacity (mapped region size).
char * base() const noexcept
Get the base address of the mapped region.
void init_and_erase(const std::string &file_path_name)
Initialize and erase any existing data.
void init(const std::string &file_path_name, void *addr=nullptr)
Initialize the arena with a backing file.
int file_descriptor() const noexcept
Get the file descriptor.
void commit(const size_type sz) noexcept
Commit a previous reservation.
char * reserve(const size_type sz)
Reserve memory for allocation.
bool empty() const noexcept
Check if the arena is empty.
iterator end() noexcept
Get iterator past the last allocated byte.
bool is_initialized() const noexcept
Check if the arena has been initialized.
static constexpr size_t initial_rgn_size
Initial region size in bytes (4 KB).
size_type avail() const noexcept
Get the available memory in the current mapping.
void * mapped_addr() const noexcept
Get the mapped memory address.
iterator begin() noexcept
Get iterator to the beginning of allocated memory.
void sync() noexcept
Ensure data is persisted to disk.
void TearDown() override
std::string test_file
void SetUp() override
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
#define TEST(name)
#define N
Definition fib.C:294
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:100
DynList< T > maps(const C &c, Op op)
Classic map operation.
ofstream output
Definition writeHeap.C:213