Libosmium  2.20.0
Fast and flexible C++ library for working with OpenStreetMap data
memory_mapping.hpp
Go to the documentation of this file.
1#ifndef OSMIUM_UTIL_MEMORY_MAPPING_HPP
2#define OSMIUM_UTIL_MEMORY_MAPPING_HPP
3
4/*
5
6This file is part of Osmium (https://osmcode.org/libosmium).
7
8Copyright 2013-2023 Jochen Topf <jochen@topf.org> and others (see README).
9
10Boost Software License - Version 1.0 - August 17th, 2003
11
12Permission is hereby granted, free of charge, to any person or organization
13obtaining a copy of the software and accompanying documentation covered by
14this license (the "Software") to use, reproduce, display, distribute,
15execute, and transmit the Software, and to prepare derivative works of the
16Software, and to permit third-parties to whom the Software is furnished to
17do so, all subject to the following:
18
19The copyright notices in the Software and this entire statement, including
20the above license grant, this restriction and the following disclaimer,
21must be included in all copies of the Software, in whole or in part, and
22all derivative works of the Software, unless such copies or derivative
23works are solely in the form of machine-executable object code generated by
24a source language processor.
25
26THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32DEALINGS IN THE SOFTWARE.
33
34*/
35
36#include <osmium/util/file.hpp>
37
38#include <cassert>
39#include <cerrno>
40#include <cstddef>
41#include <stdexcept>
42#include <system_error>
43
44#ifndef _WIN32
45# include <sys/mman.h>
46# include <sys/statvfs.h>
47#else
48# include <fcntl.h>
49# include <io.h>
50# include <windows.h>
51# include <sys/types.h>
52#endif
53
54namespace osmium {
55
56 inline namespace util {
57
96
97 public:
98
99 enum class mapping_mode {
100 readonly = 0,
101 write_private = 1,
102 write_shared = 2
103 };
104
105 private:
106
108 std::size_t m_size;
109
111 off_t m_offset;
112
114 int m_fd;
115
118
119#ifdef _WIN32
120 HANDLE m_handle;
121#endif
122
124 void* m_addr;
125
126 bool is_valid() const noexcept;
127
128 void make_invalid() noexcept;
129
130#ifdef _WIN32
131 using flag_type = DWORD;
132#else
133 using flag_type = int;
134#endif
135
136 flag_type get_protection() const noexcept;
137
138 flag_type get_flags() const noexcept;
139
140 static std::size_t check_size(std::size_t size) {
141 if (size == 0) {
142 return osmium::get_pagesize();
143 }
144 return size;
145 }
146
147#ifdef _WIN32
148 HANDLE get_handle() const noexcept;
149 HANDLE create_file_mapping() const noexcept;
150 void* map_view_of_file() const noexcept;
151#endif
152
153 // Get the available space on the file system where the file
154 // behind fd is on. Return 0 if it can't be determined.
155 static std::size_t available_space(int fd) {
156#ifdef _WIN32
157 return 0;
158#else
159 struct statvfs stat{};
160 const int result = ::fstatvfs(fd, &stat);
161 if (result != 0) {
162 return 0;
163 }
164 return stat.f_bsize * stat.f_bavail;
165#endif
166 }
167
168 int resize_fd(int fd) const {
169 // Anonymous mapping doesn't need resizing.
170 if (fd == -1) {
171 return -1;
172 }
173
174 // Make sure the file backing this mapping is large enough.
175 auto const current_file_size = osmium::file_size(fd);
176 if (current_file_size < m_size + m_offset) {
177 const auto available = available_space(fd);
178 if (available > 0 && current_file_size + available <= m_size) {
179 throw std::system_error{ENOSPC, std::system_category(), "Could not resize file: Not enough space on filesystem"};
180 }
181
183 }
184 return fd;
185 }
186
187 public:
188
205 MemoryMapping(std::size_t size, mapping_mode mode, int fd = -1, off_t offset = 0);
206
208 MemoryMapping(const MemoryMapping&) = delete;
209
212
217 MemoryMapping(MemoryMapping&& other) noexcept;
218
222 MemoryMapping& operator=(MemoryMapping&& other) noexcept;
223
228 ~MemoryMapping() noexcept {
229 try {
230 unmap();
231 } catch (const std::system_error&) {
232 // Ignore any exceptions because destructor must not throw.
233 }
234 }
235
242 void unmap();
243
255 void resize(std::size_t new_size);
256
261 explicit operator bool() const noexcept {
262 return is_valid();
263 }
264
270 std::size_t size() const noexcept {
271 return m_size;
272 }
273
279 int fd() const noexcept {
280 return m_fd;
281 }
282
286 bool writable() const noexcept {
288 }
289
295 template <typename T = void>
296 T* get_addr() const noexcept {
297 return reinterpret_cast<T*>(m_addr);
298 }
299
300 }; // class MemoryMapping
301
313
314 public:
315
316 explicit AnonymousMemoryMapping(std::size_t size) :
317 MemoryMapping(size, mapping_mode::write_private) {
318 }
319
320#ifndef __linux__
325 void resize(std::size_t) = delete;
326#endif
327
328 }; // class AnonymousMemoryMapping
329
339 template <typename T>
341
343
344 public:
345
352 explicit TypedMemoryMapping(std::size_t size) :
353 m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) {
354 }
355
366 TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset = 0) :
367 m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) {
368 }
369
372
375
380 TypedMemoryMapping(TypedMemoryMapping&& other) noexcept = default;
381
385 TypedMemoryMapping& operator=(TypedMemoryMapping&& other) noexcept = default;
386
391 ~TypedMemoryMapping() noexcept = default;
392
399 void unmap() {
401 }
402
413 void resize(std::size_t new_size) {
414 m_mapping.resize(sizeof(T) * new_size);
415 }
416
421 explicit operator bool() const noexcept {
422 return !!m_mapping;
423 }
424
430 std::size_t size() const noexcept {
431 assert(m_mapping.size() % sizeof(T) == 0);
432 return m_mapping.size() / sizeof(T);
433 }
434
440 int fd() const noexcept {
441 return m_mapping.fd();
442 }
443
447 bool writable() const noexcept {
448 return m_mapping.writable();
449 }
450
456 T* begin() noexcept {
457 return m_mapping.get_addr<T>();
458 }
459
465 T* end() noexcept {
466 return m_mapping.get_addr<T>() + size();
467 }
468
474 const T* cbegin() const noexcept {
475 return m_mapping.get_addr<T>();
476 }
477
483 const T* cend() const noexcept {
484 return m_mapping.get_addr<T>() + size();
485 }
486
492 const T* begin() const noexcept {
493 return m_mapping.get_addr<T>();
494 }
495
501 const T* end() const noexcept {
502 return m_mapping.get_addr<T>() + size();
503 }
504
505 }; // class TypedMemoryMapping
506
507 template <typename T>
509
510 public:
511
512 explicit AnonymousTypedMemoryMapping(std::size_t size) :
514 }
515
516#ifndef __linux__
521 void resize(std::size_t) = delete;
522#endif
523
524 }; // class AnonymousTypedMemoryMapping
525
526 } // namespace util
527
528} // namespace osmium
529
530#ifndef _WIN32
531
532// =========== Unix implementation =============
533
534// MAP_FAILED is often a macro containing an old style cast
535#pragma GCC diagnostic push
536#pragma GCC diagnostic ignored "-Wold-style-cast"
537
538inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
539 return m_addr != MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
540}
541
543 m_addr = MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
544}
545
546#pragma GCC diagnostic pop
547
548// for BSD systems
549#ifndef MAP_ANONYMOUS
550# define MAP_ANONYMOUS MAP_ANON
551#endif
552
554 if (m_mapping_mode == mapping_mode::readonly) {
555 return PROT_READ;
556 }
557 return PROT_READ | PROT_WRITE; // NOLINT(hicpp-signed-bitwise)
558}
559
560inline int osmium::util::MemoryMapping::get_flags() const noexcept {
561 if (m_fd == -1) {
562 return MAP_PRIVATE | MAP_ANONYMOUS; // NOLINT(hicpp-signed-bitwise)
563 }
564 if (m_mapping_mode == mapping_mode::write_shared) {
565 return MAP_SHARED;
566 }
567 return MAP_PRIVATE;
568}
569
570inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, mapping_mode mode, int fd, off_t offset) :
571 m_size(check_size(size)),
572 m_offset(offset),
573 m_fd(resize_fd(fd)),
574 m_mapping_mode(mode),
575 m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) {
576 assert(!(fd == -1 && mode == mapping_mode::readonly));
577 if (!is_valid()) {
578 throw std::system_error{errno, std::system_category(), "mmap failed"};
579 }
580}
581
583 m_size(other.m_size),
584 m_offset(other.m_offset),
585 m_fd(other.m_fd),
586 m_mapping_mode(other.m_mapping_mode),
587 m_addr(other.m_addr) {
588 other.make_invalid();
589}
590
592 try {
593 unmap();
594 } catch (const std::system_error&) {
595 // Ignore unmap error. It should never happen anyway and we can't do
596 // anything about it here.
597 }
598 m_size = other.m_size;
599 m_offset = other.m_offset;
600 m_fd = other.m_fd;
601 m_mapping_mode = other.m_mapping_mode;
602 m_addr = other.m_addr;
603 other.make_invalid();
604 return *this;
605}
606
608 if (is_valid()) {
609 if (::munmap(m_addr, m_size) != 0) {
610 throw std::system_error{errno, std::system_category(), "munmap failed"};
611 }
612 make_invalid();
613 }
614}
615
616inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
617 assert(new_size > 0 && "can not resize to zero size");
618 if (m_fd == -1) { // anonymous mapping
619#ifdef __linux__
620 m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE);
621 if (!is_valid()) {
622 throw std::system_error{errno, std::system_category(), "mremap failed"};
623 }
624 m_size = new_size;
625#else
626 assert(false && "can't resize anonymous mappings on non-linux systems");
627#endif
628 } else { // file-based mapping
629 unmap();
630 m_size = new_size;
631 resize_fd(m_fd);
632 m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset);
633 if (!is_valid()) {
634 throw std::system_error{errno, std::system_category(), "mmap (remap) failed"};
635 }
636 }
637}
638
639#else
640
641// =========== Windows implementation =============
642
643/* References:
644 * CreateFileMapping: https://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
645 * CloseHandle: https://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
646 * MapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
647 * UnmapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
648 */
649
650namespace osmium {
651
652 inline namespace util {
653
654 inline DWORD dword_hi(uint64_t x) {
655 return static_cast<DWORD>(x >> 32);
656 }
657
658 inline DWORD dword_lo(uint64_t x) {
659 return static_cast<DWORD>(x & 0xffffffff);
660 }
661
662 } // namespace util
663
664} // namespace osmium
665
666inline DWORD osmium::util::MemoryMapping::get_protection() const noexcept {
667 switch (m_mapping_mode) {
668 case mapping_mode::readonly:
669 return PAGE_READONLY;
670 case mapping_mode::write_private:
671 return PAGE_WRITECOPY;
672 default: // mapping_mode::write_shared
673 break;
674 }
675 return PAGE_READWRITE;
676}
677
678inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept {
679 switch (m_mapping_mode) {
680 case mapping_mode::readonly:
681 return FILE_MAP_READ;
682 case mapping_mode::write_private:
683 return FILE_MAP_COPY;
684 default: // mapping_mode::write_shared
685 break;
686 }
687 return FILE_MAP_WRITE;
688}
689
690inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept {
691 if (m_fd == -1) {
692 return INVALID_HANDLE_VALUE;
693 }
694 return reinterpret_cast<HANDLE>(_get_osfhandle(m_fd));
695}
696
697inline HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept {
698 if (m_fd != -1) {
699 _setmode(m_fd, _O_BINARY);
700 }
701 return CreateFileMapping(get_handle(),
702 nullptr,
703 get_protection(),
704 osmium::dword_hi(static_cast<uint64_t>(m_size) + m_offset),
705 osmium::dword_lo(static_cast<uint64_t>(m_size) + m_offset),
706 nullptr);
707}
708
709inline void* osmium::util::MemoryMapping::map_view_of_file() const noexcept {
710 return MapViewOfFile(m_handle,
711 get_flags(),
712 osmium::dword_hi(m_offset),
713 osmium::dword_lo(m_offset),
714 m_size);
715}
716
717inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
718 return m_addr != nullptr;
719}
720
721inline void osmium::util::MemoryMapping::make_invalid() noexcept {
722 m_addr = nullptr;
723}
724
725// GetLastError() returns a DWORD (A 32-bit unsigned integer), but the error
726// code for std::system_error is an int. So we convert this here and hope
727// it all works.
728inline int last_error() noexcept {
729 return static_cast<int>(GetLastError());
730}
731
732inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) :
733 m_size(check_size(size)),
734 m_offset(offset),
735 m_fd(resize_fd(fd)),
736 m_mapping_mode(mode),
737 m_handle(create_file_mapping()),
738 m_addr(nullptr) {
739
740 if (!m_handle) {
741 throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
742 }
743
744 m_addr = map_view_of_file();
745 if (!is_valid()) {
746 throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
747 }
748}
749
750inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) noexcept :
751 m_size(other.m_size),
752 m_offset(other.m_offset),
753 m_fd(other.m_fd),
754 m_mapping_mode(other.m_mapping_mode),
755 m_handle(std::move(other.m_handle)),
756 m_addr(other.m_addr) {
757 other.make_invalid();
758 other.m_handle = nullptr;
759}
760
762 try {
763 unmap();
764 } catch (const std::system_error&) {
765 // Ignore unmap error. It should never happen anyway and we can't do
766 // anything about it here.
767 }
768 m_size = other.m_size;
769 m_offset = other.m_offset;
770 m_fd = other.m_fd;
771 m_mapping_mode = other.m_mapping_mode;
772 m_handle = std::move(other.m_handle);
773 m_addr = other.m_addr;
774 other.make_invalid();
775 other.m_handle = nullptr;
776 return *this;
777}
778
780 if (is_valid()) {
781 if (!UnmapViewOfFile(m_addr)) {
782 throw std::system_error{last_error(), std::system_category(), "UnmapViewOfFile failed"};
783 }
784 make_invalid();
785 }
786
787 if (m_handle) {
788 if (!CloseHandle(m_handle)) {
789 throw std::system_error{last_error(), std::system_category(), "CloseHandle failed"};
790 }
791 m_handle = nullptr;
792 }
793}
794
795inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
796 unmap();
797
798 m_size = new_size;
799 resize_fd(m_fd);
800
801 m_handle = create_file_mapping();
802 if (!m_handle) {
803 throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
804 }
805
806 m_addr = map_view_of_file();
807 if (!is_valid()) {
808 throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
809 }
810}
811
812#endif
813
814#endif // OSMIUM_UTIL_MEMORY_MAPPING_HPP
Definition: memory_mapping.hpp:312
AnonymousMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:316
Definition: memory_mapping.hpp:508
AnonymousTypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:512
Definition: memory_mapping.hpp:95
int resize_fd(int fd) const
Definition: memory_mapping.hpp:168
void unmap()
Definition: memory_mapping.hpp:607
bool is_valid() const noexcept
Definition: memory_mapping.hpp:538
MemoryMapping & operator=(const MemoryMapping &)=delete
You can not copy a MemoryMapping.
mapping_mode
Definition: memory_mapping.hpp:99
std::size_t size() const noexcept
Definition: memory_mapping.hpp:270
MemoryMapping(const MemoryMapping &)=delete
You can not copy construct a MemoryMapping.
flag_type get_protection() const noexcept
Definition: memory_mapping.hpp:553
off_t m_offset
Offset into the file.
Definition: memory_mapping.hpp:111
~MemoryMapping() noexcept
Definition: memory_mapping.hpp:228
std::size_t m_size
The size of the mapping.
Definition: memory_mapping.hpp:108
static std::size_t available_space(int fd)
Definition: memory_mapping.hpp:155
int m_fd
File handle we got the mapping from.
Definition: memory_mapping.hpp:114
int flag_type
Definition: memory_mapping.hpp:133
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:616
bool writable() const noexcept
Definition: memory_mapping.hpp:286
T * get_addr() const noexcept
Definition: memory_mapping.hpp:296
flag_type get_flags() const noexcept
Definition: memory_mapping.hpp:560
mapping_mode m_mapping_mode
Mapping mode.
Definition: memory_mapping.hpp:117
static std::size_t check_size(std::size_t size)
Definition: memory_mapping.hpp:140
void make_invalid() noexcept
Definition: memory_mapping.hpp:542
void * m_addr
The address where the memory is mapped.
Definition: memory_mapping.hpp:124
int fd() const noexcept
Definition: memory_mapping.hpp:279
MemoryMapping(std::size_t size, mapping_mode mode, int fd=-1, off_t offset=0)
Definition: memory_mapping.hpp:570
Definition: memory_mapping.hpp:340
~TypedMemoryMapping() noexcept=default
int fd() const noexcept
Definition: memory_mapping.hpp:440
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:413
TypedMemoryMapping & operator=(TypedMemoryMapping &&other) noexcept=default
const T * end() const noexcept
Definition: memory_mapping.hpp:501
const T * begin() const noexcept
Definition: memory_mapping.hpp:492
void unmap()
Definition: memory_mapping.hpp:399
TypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:352
bool writable() const noexcept
Definition: memory_mapping.hpp:447
TypedMemoryMapping(const TypedMemoryMapping &)=delete
You can not copy construct a TypedMemoryMapping.
const T * cend() const noexcept
Definition: memory_mapping.hpp:483
std::size_t size() const noexcept
Definition: memory_mapping.hpp:430
TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset=0)
Definition: memory_mapping.hpp:366
MemoryMapping m_mapping
Definition: memory_mapping.hpp:342
const T * cbegin() const noexcept
Definition: memory_mapping.hpp:474
TypedMemoryMapping(TypedMemoryMapping &&other) noexcept=default
T * end() noexcept
Definition: memory_mapping.hpp:465
TypedMemoryMapping & operator=(const TypedMemoryMapping &)=delete
You can not copy a TypedMemoryMapping.
T * begin() noexcept
Definition: memory_mapping.hpp:456
#define MAP_ANONYMOUS
Definition: memory_mapping.hpp:550
void resize_file(int fd, std::size_t new_size)
Definition: file.hpp:177
std::size_t file_size(int fd)
Definition: file.hpp:109
std::size_t get_pagesize() noexcept
Definition: file.hpp:193
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
Definition: location.hpp:555