header_utils
Loading...
Searching...
No Matches
mmap.h
1
4
5#pragma once
6
7#include <iterator>
8#include <string>
9#include <system_error>
10#include <cstdint>
11#include <span>
12#include <filesystem>
13
15/*
16struct incremental_position_checkpoint
17{
18private:
19 mmap_sink& mapping;
20 struct checkpoint_struct
21 {
22 size_t position[4];
23 };
24 checkpoint_struct* mapped_checkpoint;
25 bool last_version_2 = false;
26public:
27 incremental_position_checkpoint(mmap_sink& in_file, size_at loc)
28 : mapping(in_file),
29 , mapped_checkpoint(mapping.data() + loc)
30 {
31 assert(mapping.size() <= loc + sizeof(checkpoint_struct));
32 }
33
34 void reset(size_t to_position = 0)
35 {
36 mapped_checkpoint = {0,0,0,0};
37 mapping.flush();
38 last_version_2 = false;
39 }
40
41 void checkpoint(size_t position)
42 {
43 mapped_checkpoint.position[last_version_2 * 2] = position;
44 mapping.flush();
45 mapped_checkpoint.position[last_version_2 * 2 + 1] = position;
46 mapping.flush();
47 last_version_2 = !last_version_2;
48 }
49
50 size_t position() const
51 {
52 const auto copy = *mapped_checkpoint;
53 const auto healthy[2] = { copy.position[0] == copy.position[1], copy.position[2] == copy.position[3] };
54 if (healthy[0] && healthy[1])
55 return std::max(copy.position[0], copy.position[2]);
56 else if (healthy[0])
57 return position[0];
58 else if (healthy[1])
59 return position[2];
60 else
61 throw "no valid position";
62 }
63};
64*/
65
66namespace ghassanpl
67{
68 static constexpr size_t map_entire_file = 0;
69
70#ifdef _WIN32
71 using file_handle_type = void*;
72#else
73 using file_handle_type = int;
74#endif
75
76 const inline file_handle_type invalid_handle = (file_handle_type)-1;
77
78
79 template <typename VALUE_TYPE>
80 //requires std::is_integral_v<VALUE_TYPE>
81 struct basic_mmap_base
82 {
83 static_assert(sizeof(VALUE_TYPE) == sizeof(std::byte));
84
85 using value_type = VALUE_TYPE;
86 using size_type = size_t;
87 using reference = value_type&;
88 using const_reference = const value_type&;
89 using pointer = value_type*;
90 using const_pointer = const value_type*;
91 using difference_type = std::ptrdiff_t;
92 using iterator = pointer;
93 using const_iterator = const_pointer;
94 using reverse_iterator = std::reverse_iterator<iterator>;
95 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
96 using iterator_category = std::random_access_iterator_tag;
97 using handle_type = file_handle_type;
98
99 basic_mmap_base() noexcept = default;
100 basic_mmap_base(basic_mmap_base&& other) noexcept
101 : data_(std::exchange(other.data_, nullptr))
102 , length_(std::exchange(other.length_, 0))
103 , mapped_length_(std::exchange(other.mapped_length_, 0))
104 , file_handle_(std::exchange(other.file_handle_, invalid_handle))
105 , file_mapping_handle_(std::exchange(other.file_mapping_handle_, invalid_handle))
106 {
107 }
108
109 handle_type file_handle() const noexcept { return file_handle_; }
110 handle_type mapping_handle() const noexcept { return file_mapping_handle_ == invalid_handle ? file_handle_ : file_mapping_handle_; }
111
112 bool is_open() const noexcept { return file_handle_ != invalid_handle; }
113
114 bool empty() const noexcept { return length() == 0; }
115
116 bool is_mapped() const noexcept
117 {
118#ifdef _WIN32
119 return file_mapping_handle_ != invalid_handle;
120#else // POSIX
121 return is_open();
122#endif
123 }
124
125 template <typename T = VALUE_TYPE>
126 std::span<T const> to_span() const noexcept { return { reinterpret_cast<T const*>(data()), size() }; }
127
128 size_type size() const noexcept { return length(); }
129 size_type length() const noexcept { return length_; }
130 size_type mapped_length() const noexcept { return mapped_length_; }
131
132 size_type mapping_offset() const noexcept
133 {
134 return mapped_length_ - length_;
135 }
136
137 const_pointer data() const noexcept { return data_; }
138
139 const_iterator begin() const noexcept { return data(); }
140 const_iterator cbegin() const noexcept { return data(); }
141
142 const_iterator end() const noexcept { return data() + length(); }
143 const_iterator cend() const noexcept { return data() + length(); }
144
145 const_reverse_iterator rbegin() const noexcept
146 {
147 return const_reverse_iterator(end());
148 }
149
150 const_reverse_iterator crbegin() const noexcept
151 {
152 return const_reverse_iterator(end());
153 }
154
155 const_reverse_iterator rend() const noexcept
156 {
157 return const_reverse_iterator(begin());
158 }
159 const_reverse_iterator crend() const noexcept
160 {
161 return const_reverse_iterator(begin());
162 }
163
164 const_reference operator[](const size_type i) const noexcept { return data_[i]; }
165
166 void swap(basic_mmap_base& other) noexcept
167 {
168 if (this != &other)
169 {
170 using std::swap;
171 swap(data_, other.data_);
172 swap(file_handle_, other.file_handle_);
173 swap(file_mapping_handle_, other.file_mapping_handle_);
174 swap(length_, other.length_);
175 swap(mapped_length_, other.mapped_length_);
176 }
177 }
178
179 void unmap() noexcept;
180
181 protected:
182
183 struct mmap_context
184 {
185 VALUE_TYPE* data;
186 int64_t length;
187 int64_t mapped_length;
188 file_handle_type file_mapping_handle;
189 };
190
191 pointer data_ = nullptr;
192
193 size_type length_ = 0;
194 size_type mapped_length_ = 0;
195
196 handle_type file_handle_ = invalid_handle;
197 handle_type file_mapping_handle_ = invalid_handle;
198
199 const_pointer get_mapping_start() const noexcept
200 {
201 return !data() ? nullptr : data() - mapping_offset();
202 }
203
204 };
205
206 template <typename CRTP, typename VALUE_TYPE = std::byte>
207 struct basic_mmap : public basic_mmap_base<VALUE_TYPE>
208 {
209 using typename basic_mmap_base<VALUE_TYPE>::size_type;
210 using typename basic_mmap_base<VALUE_TYPE>::handle_type;
211 using typename basic_mmap_base<VALUE_TYPE>::pointer;
212
213 basic_mmap() noexcept = default;
214 basic_mmap(const std::filesystem::path& path, const size_type offset = 0, const size_t length = map_entire_file)
215 {
216 std::error_code error;
217 map(path, offset, length, error);
218 if (error) { throw std::system_error{ error }; }
219 }
220 basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
221 {
222 std::error_code error;
223 map(handle, offset, length, error);
224 if (error) { throw std::system_error{ error }; }
225 }
226
227 basic_mmap(const basic_mmap&) = delete;
228 basic_mmap(basic_mmap&&) noexcept = default;
229 basic_mmap& operator=(const basic_mmap&) = delete;
230 basic_mmap& operator=(basic_mmap&& other) noexcept
231 {
232 if (this != &other)
233 {
234 this->unmap();
235
236 this->data_ = std::exchange(other.data_, nullptr);
237 this->length_ = std::exchange(other.length_, 0);
238 this->mapped_length_ = std::exchange(other.mapped_length_, 0);
239 this->file_handle_ = std::exchange(other.file_handle_, invalid_handle);
240 this->file_mapping_handle_ = std::exchange(other.file_mapping_handle_, invalid_handle);
241 }
242 return *this;
243 }
244
245 ~basic_mmap() noexcept
246 {
247 static_cast<CRTP*>(this)->conditional_sync();
248 this->unmap();
249 }
250
251 protected:
252
253 void map(const std::filesystem::path& path, const size_type offset, const size_type length, std::error_code& error) noexcept
254 {
255 error.clear();
256 if (path.empty())
257 {
258 error = std::make_error_code(std::errc::invalid_argument);
259 return;
260 }
261
262 error.clear();
263 const auto file_size = std::filesystem::file_size(path, error);
264 if (error)
265 return;
266
267 if (file_size == 0)
268 {
269 error = std::make_error_code(std::errc::file_too_large);
270 return;
271 }
272
273 error.clear();
274 const auto handle = static_cast<CRTP*>(this)->open_file(path, error);
275 if (error)
276 return;
277
278 error.clear();
279 if (handle == invalid_handle)
280 {
281 error = std::make_error_code(std::errc::bad_file_descriptor);
282 return;
283 }
284
285 if (offset + length > file_size)
286 {
287 error = std::make_error_code(std::errc::invalid_argument);
288 return;
289 }
290
291 const auto ctx = static_cast<CRTP*>(this)->memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, error);
292 if (!error)
293 {
294 // We must unmap the previous mapping that may have existed prior to this call.
295 // Note that this must only be invoked after a new mapping has been created in
296 // order to provide the strong guarantee that, should the new mapping fail, the
297 // `map` function leaves this instance in a state as though the function had
298 // never been invoked.
299 this->unmap();
300
301 this->file_handle_ = handle;
302 this->data_ = reinterpret_cast<pointer>(ctx.data);
303 this->length_ = ctx.length;
304 this->mapped_length_ = ctx.mapped_length;
305 this->file_mapping_handle_ = ctx.file_mapping_handle;
306 }
307 }
308
309 void map(const std::filesystem::path& path, std::error_code& error) noexcept
310 {
311 this->map(path, 0, map_entire_file, error);
312 }
313
314 };
315
318
321 template <typename VALUE_TYPE = std::byte>
322 struct mmap_source : public basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>
323 {
324 using basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>::basic_mmap;
325 using typename basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>::mmap_context;
326
327 template <typename VALUE_TYPE_>
328 friend mmap_source<VALUE_TYPE_> make_mmap_source(const std::filesystem::path& path, typename mmap_source<VALUE_TYPE_>::size_type offset, typename mmap_source<VALUE_TYPE_>::size_type length, std::error_code& error) noexcept;
329
330 protected:
331
332 friend struct basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>;
333
334 static file_handle_type open_file(const std::filesystem::path& path, std::error_code& error) noexcept;
335
336 static mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, std::error_code& error) noexcept;
337
338 void conditional_sync() {}
339 };
340
341
346
348 template <typename VALUE_TYPE = std::byte>
349 struct mmap_sink : public basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>
350 {
351 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::basic_mmap;
352 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::reference;
353 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::size_type;
354 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::mmap_context;
355 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::pointer;
356 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::iterator;
357 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::reverse_iterator;
358
359 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::operator[];
360 reference operator[](const size_type i) const noexcept { return this->data_[i]; }
361
362 //void unmap();
363
364 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::data;
365 pointer data() noexcept { return this->data_; }
366
367 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::begin;
368 iterator begin() noexcept { return this->data(); }
369
370 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::end;
371 iterator end() noexcept { return this->data() + this->length(); }
372
373 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::rbegin;
374 reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
375
376 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::rend;
377 reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
378
379 void sync(std::error_code& error) noexcept;
380
381 protected:
382
383 friend struct basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>;
384
385 static file_handle_type open_file(const std::filesystem::path& path, std::error_code& error) noexcept;
386
387 static mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, std::error_code& error) noexcept;
388
389 pointer get_mapping_start() noexcept
390 {
391 return !this->data() ? nullptr : this->data() - this->mapping_offset();
392 }
393
394 void conditional_sync()
395 {
396 std::error_code ec;
397 sync(ec);
398 }
399 };
400
401 template <typename VALUE_TYPE>
402 inline mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path, typename mmap_source<VALUE_TYPE>::size_type offset, typename mmap_source<VALUE_TYPE>::size_type length, std::error_code& error) noexcept
403 {
405 mmap.map(path, offset, length, error);
406 return mmap;
407 }
408
409 template <typename VALUE_TYPE>
410 inline mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path, std::error_code& error) noexcept
411 {
412 return make_mmap_source<VALUE_TYPE>(path, 0, map_entire_file, error);
413 }
414
415 template <typename VALUE_TYPE>
416 inline mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path, typename mmap_source<VALUE_TYPE>::size_type offset, typename mmap_source<VALUE_TYPE>::size_type length)
417 {
418 return mmap_source<VALUE_TYPE>{ path, offset, length };
419 }
420
421 template <typename VALUE_TYPE>
422 inline mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path)
423 {
424 return make_mmap_source<VALUE_TYPE>(path, 0, map_entire_file);
425 }
426
427 template <typename VALUE_TYPE>
428 inline mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path, typename mmap_sink<VALUE_TYPE>::size_type offset, typename mmap_sink<VALUE_TYPE>::size_type length, std::error_code& error) noexcept
429 {
431 mmap.map(path, offset, length, error);
432 return mmap;
433 }
434
435 template <typename VALUE_TYPE>
436 inline mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path, std::error_code& error) noexcept
437 {
438 return make_mmap_sink<VALUE_TYPE>(path, 0, map_entire_file, error);
439 }
440
441 template <typename VALUE_TYPE>
442 inline mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path, typename mmap_sink<VALUE_TYPE>::size_type offset, typename mmap_sink<VALUE_TYPE>::size_type length)
443 {
444 return mmap_sink<VALUE_TYPE>{ path, offset, length };
445 }
446
447 template <typename VALUE_TYPE>
448 inline mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path)
449 {
450 return make_mmap_sink<VALUE_TYPE>(path, 0, map_entire_file);
451 }
452
454}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
Primary namespace for everything in this library.
Definition align+rec2.h:10
A read-write view over a file.
Definition mmap.h:350
A read-only memory view over a file.
Definition mmap.h:323