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.h"
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 namespace detail
79 {
80 bool create_file(std::filesystem::path const& path, size_t size, std::error_code& error) noexcept;
81 }
82
86
88 template <typename VALUE_TYPE>
89 //requires std::is_integral_v<VALUE_TYPE>
90 struct basic_mmap_base
91 {
92 static_assert(sizeof(VALUE_TYPE) == sizeof(std::byte));
93
94 using value_type = VALUE_TYPE;
95 using size_type = size_t;
96 using reference = value_type&;
97 using const_reference = const value_type&;
98 using pointer = value_type*;
99 using const_pointer = const value_type*;
100 using difference_type = std::ptrdiff_t;
101 using iterator = pointer;
102 using const_iterator = const_pointer;
103 using reverse_iterator = std::reverse_iterator<iterator>;
104 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
105 using iterator_category = std::random_access_iterator_tag;
106 using handle_type = file_handle_type;
107
108 basic_mmap_base() noexcept = default;
109 basic_mmap_base(basic_mmap_base&& other) noexcept
110 : data_(std::exchange(other.data_, nullptr))
111 , length_(std::exchange(other.length_, 0))
112 , mapped_length_(std::exchange(other.mapped_length_, 0))
113 , file_handle_(std::exchange(other.file_handle_, invalid_handle))
114 , file_mapping_handle_(std::exchange(other.file_mapping_handle_, invalid_handle))
115 {
116 }
117
118 [[nodiscard]] handle_type file_handle() const noexcept { return file_handle_; }
119 [[nodiscard]] handle_type mapping_handle() const noexcept { return file_mapping_handle_ == invalid_handle ? file_handle_ : file_mapping_handle_; }
120
121 [[nodiscard]] bool is_open() const noexcept { return file_handle_ != invalid_handle; }
122
123 [[nodiscard]] bool empty() const noexcept { return length() == 0; }
124
125 [[nodiscard]] bool is_mapped() const noexcept
126 {
127#ifdef _WIN32
128 return file_mapping_handle_ != invalid_handle;
129#else // POSIX
130 return is_open();
131#endif
132 }
133
134 template <typename T = VALUE_TYPE>
135 [[nodiscard]] std::span<T const> to_span() const noexcept { return { reinterpret_cast<T const*>(data()), size() }; }
136
137 [[nodiscard]] size_type size() const noexcept { return length(); }
138 [[nodiscard]] size_type length() const noexcept { return length_; }
139 [[nodiscard]] size_type mapped_length() const noexcept { return mapped_length_; }
140
141 [[nodiscard]] size_type mapping_offset() const noexcept
142 {
143 return mapped_length_ - length_;
144 }
145
146 [[nodiscard]] const_pointer data() const noexcept { return data_; }
147
148 [[nodiscard]] const_iterator begin() const noexcept { return data(); }
149 [[nodiscard]] const_iterator cbegin() const noexcept { return data(); }
150
151 [[nodiscard]] const_iterator end() const noexcept { return data() + length(); }
152 [[nodiscard]] const_iterator cend() const noexcept { return data() + length(); }
153
154 [[nodiscard]] const_reverse_iterator rbegin() const noexcept
155 {
156 return const_reverse_iterator(end());
157 }
158
159 [[nodiscard]] const_reverse_iterator crbegin() const noexcept
160 {
161 return const_reverse_iterator(end());
162 }
163
164 [[nodiscard]] const_reverse_iterator rend() const noexcept
165 {
166 return const_reverse_iterator(begin());
167 }
168 [[nodiscard]] const_reverse_iterator crend() const noexcept
169 {
170 return const_reverse_iterator(begin());
171 }
172
173 [[nodiscard]] const_reference operator[](const size_type i) const noexcept { return data_[i]; }
174
175 void swap(basic_mmap_base& other) noexcept
176 {
177 if (this != &other)
178 {
179 using std::swap;
180 swap(data_, other.data_);
181 swap(file_handle_, other.file_handle_);
182 swap(file_mapping_handle_, other.file_mapping_handle_);
183 swap(length_, other.length_);
184 swap(mapped_length_, other.mapped_length_);
185 }
186 }
187
188 void unmap() noexcept;
189
190 protected:
191
192 struct mmap_context
193 {
194 VALUE_TYPE* data;
195 int64_t length;
196 int64_t mapped_length;
197 file_handle_type file_mapping_handle;
198 };
199
200 pointer data_ = nullptr;
201
202 size_type length_ = 0;
203 size_type mapped_length_ = 0;
204
205 handle_type file_handle_ = invalid_handle;
206 handle_type file_mapping_handle_ = invalid_handle;
207
208 const_pointer get_mapping_start() const noexcept
209 {
210 return !data() ? nullptr : data() - mapping_offset();
211 }
212
213 };
214
215 template <typename CRTP, typename VALUE_TYPE = std::byte>
216 struct basic_mmap : public basic_mmap_base<VALUE_TYPE>
217 {
218 using typename basic_mmap_base<VALUE_TYPE>::size_type;
219 using typename basic_mmap_base<VALUE_TYPE>::handle_type;
220 using typename basic_mmap_base<VALUE_TYPE>::pointer;
221
222 basic_mmap() noexcept = default;
223 basic_mmap(const std::filesystem::path& path, const size_type offset = 0, const size_t length = map_entire_file)
224 {
225 std::error_code error;
226 map(path, offset, length, error);
227 if (error) { throw std::system_error{ error }; }
228 }
229 basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
230 {
231 std::error_code error;
232 map(handle, offset, length, error);
233 if (error) { throw std::system_error{ error }; }
234 }
235
236 basic_mmap(const basic_mmap&) = delete;
237 basic_mmap(basic_mmap&&) noexcept = default;
238 basic_mmap& operator=(const basic_mmap&) = delete;
239 basic_mmap& operator=(basic_mmap&& other) noexcept
240 {
241 if (this != &other)
242 {
243 this->unmap();
244
245 this->data_ = std::exchange(other.data_, nullptr);
246 this->length_ = std::exchange(other.length_, 0);
247 this->mapped_length_ = std::exchange(other.mapped_length_, 0);
248 this->file_handle_ = std::exchange(other.file_handle_, invalid_handle);
249 this->file_mapping_handle_ = std::exchange(other.file_mapping_handle_, invalid_handle);
250 }
251 return *this;
252 }
253
254 ~basic_mmap() noexcept
255 {
256 static_cast<CRTP*>(this)->conditional_sync();
257 this->unmap();
258 }
259
260 protected:
261
262 void map(const std::filesystem::path& path, const size_type offset, const size_type length, std::error_code& error) noexcept
263 {
264 error.clear();
265 if (path.empty())
266 {
267 error = std::make_error_code(std::errc::invalid_argument);
268 return;
269 }
270
271 error.clear();
272 const auto file_size = std::filesystem::file_size(path, error);
273 if (error)
274 return;
275
276 if (file_size == 0)
277 {
278 error = std::make_error_code(std::errc::file_too_large);
279 return;
280 }
281
282 error.clear();
283 const auto handle = static_cast<CRTP*>(this)->open_file(path, error);
284 if (error)
285 return;
286
287 error.clear();
288 if (handle == invalid_handle)
289 {
290 error = std::make_error_code(std::errc::bad_file_descriptor);
291 return;
292 }
293
294 if (offset + length > file_size)
295 {
296 error = std::make_error_code(std::errc::invalid_argument);
297 return;
298 }
299
300 const auto ctx = static_cast<CRTP*>(this)->memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, error);
301 if (!error)
302 {
303 // We must unmap the previous mapping that may have existed prior to this call.
304 // Note that this must only be invoked after a new mapping has been created in
305 // order to provide the strong guarantee that, should the new mapping fail, the
306 // `map` function leaves this instance in a state as though the function had
307 // never been invoked.
308 this->unmap();
309
310 this->file_handle_ = handle;
311 this->data_ = reinterpret_cast<pointer>(ctx.data);
312 this->length_ = ctx.length;
313 this->mapped_length_ = ctx.mapped_length;
314 this->file_mapping_handle_ = ctx.file_mapping_handle;
315 }
316 }
317
318 void map(const std::filesystem::path& path, std::error_code& error) noexcept
319 {
320 this->map(path, 0, map_entire_file, error);
321 }
322
323 };
324
327 template <typename VALUE_TYPE = std::byte>
328 struct mmap_source : public basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>
329 {
330 using basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>::basic_mmap;
331 using typename basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>::mmap_context;
332
333 template <typename VALUE_TYPE_>
334 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;
335
336 operator std::basic_string_view<VALUE_TYPE>() const noexcept
337 {
338 return std::basic_string_view<VALUE_TYPE>{ this->data(), this->size() };
339 }
340
341 protected:
342
343 friend struct basic_mmap<mmap_source<VALUE_TYPE>, VALUE_TYPE>;
344
345 static file_handle_type open_file(const std::filesystem::path& path, std::error_code& error) noexcept;
346
347 static mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, std::error_code& error) noexcept;
348
349 void conditional_sync() {}
350 };
351
352
353 using byte_mmap_source = mmap_source<std::byte>;
354 using u8_mmap_source = mmap_source<std::uint8_t>;
355 using char_mmap_source = mmap_source<char>;
356 using char8_mmap_source = mmap_source<char8_t>;
357
359 template <typename VALUE_TYPE = std::byte>
360 struct mmap_sink : public basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>
361 {
362 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::basic_mmap;
363 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::reference;
364 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::size_type;
365 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::mmap_context;
366 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::pointer;
367 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::iterator;
368 using typename basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::reverse_iterator;
369
370 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::operator[];
371 [[nodiscard]] reference operator[](const size_type i) const noexcept { return this->data_[i]; }
372
373 //void unmap();
374
375 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::data;
376 [[nodiscard]] pointer data() noexcept { return this->data_; }
377
378 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::begin;
379 [[nodiscard]] iterator begin() noexcept { return this->data(); }
380
381 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::end;
382 [[nodiscard]] iterator end() noexcept { return this->data() + this->length(); }
383
384 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::rbegin;
385 [[nodiscard]] reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
386
387 using basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>::rend;
388 [[nodiscard]] reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
389
390 void sync(std::error_code& error) noexcept;
391
392 protected:
393
394 friend struct basic_mmap<mmap_sink<VALUE_TYPE>, VALUE_TYPE>;
395
396 static file_handle_type open_file(const std::filesystem::path& path, std::error_code& error) noexcept;
397
398 static mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, std::error_code& error) noexcept;
399
400 pointer get_mapping_start() noexcept
401 {
402 return !this->data() ? nullptr : this->data() - this->mapping_offset();
403 }
404
405 void conditional_sync()
406 {
407 std::error_code ec;
408 sync(ec);
409 }
410 };
411
412 template <typename VALUE_TYPE = char>
413 [[nodiscard]] 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
414 {
416 mmap.map(path, offset, length, error);
417 return mmap;
418 }
419
420 template <typename VALUE_TYPE = char>
421 [[nodiscard]] mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path, std::error_code& error) noexcept
422 {
423 return make_mmap_source<VALUE_TYPE>(path, 0, map_entire_file, error);
424 }
425
426 template <typename VALUE_TYPE = char>
427 [[nodiscard]] 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)
428 {
429 return mmap_source<VALUE_TYPE>{ path, offset, length };
430 }
431
432 template <typename VALUE_TYPE = char>
433 [[nodiscard]] mmap_source<VALUE_TYPE> make_mmap_source(const std::filesystem::path& path)
434 {
435 return make_mmap_source<VALUE_TYPE>(path, 0, map_entire_file);
436 }
437
438 template <typename VALUE_TYPE>
439 [[nodiscard]] 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
440 {
441 if (!detail::create_file(path, offset + length, error))
442 return mmap_sink<VALUE_TYPE>{};
443
445 mmap.map(path, offset, length, error);
446 return mmap;
447 }
448
449 template <typename VALUE_TYPE>
450 [[nodiscard]] mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path, std::error_code& error) noexcept
451 {
452 return make_mmap_sink<VALUE_TYPE>(path, 0, map_entire_file, error);
453 }
454
455 template <typename VALUE_TYPE>
456 [[nodiscard]] 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)
457 {
458 std::error_code error;
459 if (!detail::create_file(path, offset + length, error))
460 return mmap_sink<VALUE_TYPE>{};
461
462 return mmap_sink<VALUE_TYPE>{ path, offset, length };
463 }
464
465 template <typename VALUE_TYPE>
466 [[nodiscard]] mmap_sink<VALUE_TYPE> make_mmap_sink(const std::filesystem::path& path)
467 {
468 return make_mmap_sink<VALUE_TYPE>(path, 0, map_entire_file);
469 }
470
472}
473
474#ifdef GHASSANPL_MMAP_IMPL
475#include "mmap_impl.h"
476#endif
Primary namespace for everything in this library.
Definition align+rec2.h:10
Base for mmap_source and mmap_sink with a specific value type.
Definition mmap.h:91
A read-write view over a file.
Definition mmap.h:361
A read-only memory view over a file.
Definition mmap.h:329