Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
map_arena_persistence_example.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
77#include <ah-map-arena.H>
78
79#include <cstddef>
80#include <cstdint>
81#include <cstring>
82#include <filesystem>
83#include <iostream>
84#include <limits>
85#include <stdexcept>
86#include <string>
87#include <vector>
88
89using namespace std;
90using namespace Aleph;
91namespace fs = std::filesystem;
92
93namespace
94{
95
96constexpr std::uint32_t kMagic = 0x414C5048U; // "ALPH"
97constexpr std::uint32_t kVersion = 1;
98
99struct FileHeader
100{
101 // Must be the first field: MapArena reads a size_t from file offset 0.
102 size_t end;
103 std::uint32_t magic;
104 std::uint32_t version;
105};
106
107static_assert(offsetof(FileHeader, end) == 0);
108
109FileHeader read_header(const MapArena & arena)
110{
111 FileHeader header{};
112 if (!arena.is_initialized() || arena.capacity() < sizeof(FileHeader))
113 return header;
114 std::memcpy(&header, arena.begin(), sizeof(header));
115 return header;
116}
117
118void write_header(MapArena & arena, const FileHeader & header)
119{
120 std::memcpy(arena.begin(), &header, sizeof(header));
121}
122
123bool header_is_valid(const FileHeader & header)
124{
125 return header.magic == kMagic && header.version == kVersion &&
126 header.end >= sizeof(FileHeader);
127}
128
129void init_or_recover(MapArena & arena)
130{
131 if (const auto header = read_header(arena); header_is_valid(header))
132 return;
133
134 // New file: initialize a clean header.
135 //
136 // Note: if the file existed but is not in the expected format, `MapArena`
137 // will have loaded an arbitrary `end_` (size()) from offset 0. In that case
138 // it is not safe to "auto-repair" the file here; it's better to abort and
139 // ask the user to delete the file.
140 if (not arena.empty())
141 throw std::runtime_error("existing file with unknown format; delete it and retry");
142 FileHeader fresh{};
143 fresh.end = sizeof(FileHeader);
144 fresh.magic = kMagic;
145 fresh.version = kVersion;
146
147 char * header_write_ptr = arena.reserve(sizeof(FileHeader));
148 if (header_write_ptr == nullptr)
149 throw std::runtime_error("MapArena: reserve() failed while initializing the header");
150 std::memcpy(header_write_ptr, &fresh, sizeof(fresh));
151 arena.commit(sizeof(FileHeader));
152
153 // Persist the offset so the next open() can recover end_ correctly.
154 arena.sync();
155}
156
157// Writes a record as [len][payload] and updates header.end so it can be
158// recovered after reopening the file.
159void append_record(MapArena & arena, const std::string & payload)
160{
161 if (payload.size() > std::numeric_limits<std::uint32_t>::max())
162 throw std::runtime_error("payload too large");
163
164 const auto len = static_cast<std::uint32_t>(payload.size());
165
166 // 1) Reserve and write len
167 {
168 char * record_len_write_ptr = arena.reserve(sizeof(len));
169 if (record_len_write_ptr == nullptr)
170 throw std::runtime_error("MapArena: reserve() failed (len)");
171 std::memcpy(record_len_write_ptr, &len, sizeof(len));
172 arena.commit(sizeof(len));
173 }
174
175 // 2) Reserve and write payload
176 if (len > 0)
177 {
178 char * record_payload_write_ptr = arena.reserve(len);
179 if (record_payload_write_ptr == nullptr)
180 throw std::runtime_error("MapArena: reserve() failed (payload)");
181 std::memcpy(record_payload_write_ptr, payload.data(), len);
182 arena.commit(len);
183 }
184
185 // 3) Update header.end (persist the logical end pointer)
186 auto header = read_header(arena);
187 if (!header_is_valid(header))
188 throw std::runtime_error("invalid header while appending");
189 header.end = arena.size();
190 write_header(arena, header);
191
192 // For a demo we sync every append; in production you might batch + MS_ASYNC.
193 arena.sync();
194}
195
197{
199
200 const auto header = read_header(arena);
201 if (!header_is_valid(header))
202 return out;
203
204 const char * cursor = arena.begin() + sizeof(FileHeader);
205 const char * const end_ptr = arena.begin() + header.end;
206
207 while (cursor < end_ptr)
208 {
209 if (static_cast<size_t>(end_ptr - cursor) < sizeof(std::uint32_t))
210 throw std::runtime_error("truncated file: missing length field");
211
212 std::uint32_t len = 0;
213 std::memcpy(&len, cursor, sizeof(len));
214 cursor += sizeof(len);
215
216 if (static_cast<size_t>(end_ptr - cursor) < len)
217 throw std::runtime_error("truncated file: incomplete payload");
218
219 out.emplace_back(cursor, cursor + len);
220 cursor += len;
221 }
222
223 return out;
224}
225
226void dump_records(const vector<string> & recs, const string & title)
227{
228 cout << "\n" << title << "\n";
229 cout << string(title.size(), '-') << "\n";
230 for (size_t index = 0; index < recs.size(); ++index)
231 cout << " [" << index << "] " << recs[index] << "\n";
232}
233
234} // namespace
235
236int main()
237{
238 const string arena_file = "/tmp/aleph_map_arena_persist_demo.dat";
239 fs::remove(arena_file);
240
241 cout << "Aleph-w MapArena: persistence example\n";
242 cout << "File: " << arena_file << "\n";
243
244 // ----------------------------------------------------------------------------
245 // Phase 1: create and append a few records
246 // ----------------------------------------------------------------------------
247 {
248 MapArena arena(arena_file);
249 init_or_recover(arena);
250
251 append_record(arena, "hello");
252 append_record(arena, "This is persistently in the file1");
253 append_record(arena, "record #3");
254
255 auto recs = read_all_records(arena);
256 dump_records(recs, "Phase 1: records written");
257 }
258
259 // ----------------------------------------------------------------------------
260 // Phase 2: "restart" (reopen) and read what was written before
261 // ----------------------------------------------------------------------------
262 {
263 MapArena arena(arena_file);
264 init_or_recover(arena);
265
266 auto recs = read_all_records(arena);
267 dump_records(recs, "Phase 2: records recovered after reopening");
268
269 append_record(arena, "new record after reopening");
270 }
271
272 // ----------------------------------------------------------------------------
273 // Phase 3: reopen again and validate there are more records now
274 // ----------------------------------------------------------------------------
275 {
276 MapArena arena(arena_file);
277 init_or_recover(arena);
278
279 auto recs = read_all_records(arena);
280 dump_records(recs, "Phase 3: records after second reopening");
281 }
282
283 fs::remove(arena_file);
284 cout << "\nOK\n";
285 return 0;
286}
Memory-mapped file arena allocator.
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).
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.
bool is_initialized() const noexcept
Check if the arena has been initialized.
iterator begin() noexcept
Get iterator to the beginning of allocated memory.
void sync() noexcept
Ensure data is persisted to disk.
iterator end() noexcept
Return an STL-compatible end iterator.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.