Libosmium  2.17.0
Fast and flexible C++ library for working with OpenStreetMap data
Loading...
Searching...
No Matches
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-2021 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
37#include <osmium/util/file.hpp>
38
39#include <cassert>
40#include <cerrno>
41#include <cstddef>
42#include <stdexcept>
43#include <system_error>
44
45#ifndef _WIN32
46# include <sys/mman.h>
47# include <sys/statvfs.h>
48#else
49# include <fcntl.h>
50# include <io.h>
51# include <windows.h>
52# include <sys/types.h>
53#endif
54
55namespace osmium {
56
57 inline namespace util {
58
97
98 public:
99
100 enum class mapping_mode {
101 readonly = 0,
102 write_private = 1,
103 write_shared = 2
104 };
105
106 private:
107
109 std::size_t m_size;
110
112 off_t m_offset;
113
115 int m_fd;
116
119
120#ifdef _WIN32
121 HANDLE m_handle;
122#endif
123
125 void* m_addr;
126
127 bool is_valid() const noexcept;
128
129 void make_invalid() noexcept;
130
131#ifdef _WIN32
132 using flag_type = DWORD;
133#else
134 using flag_type = int;
135#endif
136
137 flag_type get_protection() const noexcept;
138
139 flag_type get_flags() const noexcept;
140
141 static std::size_t check_size(std::size_t size) {
142 if (size == 0) {
143 return osmium::get_pagesize();
144 }
145 return size;
146 }
147
148#ifdef _WIN32
149 HANDLE get_handle() const noexcept;
150 HANDLE create_file_mapping() const noexcept;
151 void* map_view_of_file() const noexcept;
152#endif
153
154 // Get the available space on the file system where the file
155 // behind fd is on. Return 0 if it can't be determined.
156 static std::size_t available_space(int fd) {
157#ifdef _WIN32
158 return 0;
159#else
160 struct statvfs stat;
161 const int result = ::fstatvfs(fd, &stat);
162 if (result != 0) {
163 return 0;
164 }
165 return stat.f_bsize * stat.f_bavail;
166#endif
167 }
168
169 int resize_fd(int fd) const {
170 // Anonymous mapping doesn't need resizing.
171 if (fd == -1) {
172 return -1;
173 }
174
175 // Make sure the file backing this mapping is large enough.
176 auto const current_file_size = osmium::file_size(fd);
177 if (current_file_size < m_size + m_offset) {
178 const auto available = available_space(fd);
179 if (available > 0 && current_file_size + available <= m_size) {
180 throw std::system_error{ENOSPC, std::system_category(), "Could not resize file: Not enough space on filesystem"};
181 }
182
184 }
185 return fd;
186 }
187
188 public:
189
206 MemoryMapping(std::size_t size, mapping_mode mode, int fd = -1, off_t offset = 0);
207
213 // cppcheck-suppress noExplicitConstructor
214 OSMIUM_DEPRECATED MemoryMapping(std::size_t size, bool writable = true, int fd = -1, off_t offset = 0) : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
216 }
217
219 MemoryMapping(const MemoryMapping&) = delete;
220
223
228 MemoryMapping(MemoryMapping&& other) noexcept;
229
233 MemoryMapping& operator=(MemoryMapping&& other) noexcept;
234
239 ~MemoryMapping() noexcept {
240 try {
241 unmap();
242 } catch (const std::system_error&) {
243 // Ignore any exceptions because destructor must not throw.
244 }
245 }
246
253 void unmap();
254
266 void resize(std::size_t new_size);
267
272 explicit operator bool() const noexcept {
273 return is_valid();
274 }
275
281 std::size_t size() const noexcept {
282 return m_size;
283 }
284
290 int fd() const noexcept {
291 return m_fd;
292 }
293
297 bool writable() const noexcept {
299 }
300
306 template <typename T = void>
307 T* get_addr() const noexcept {
308 return reinterpret_cast<T*>(m_addr);
309 }
310
311 }; // class MemoryMapping
312
324
325 public:
326
327 explicit AnonymousMemoryMapping(std::size_t size) :
329 }
330
331#ifndef __linux__
336 void resize(std::size_t) = delete;
337#endif
338
339 }; // class AnonymousMemoryMapping
340
350 template <typename T>
352
354
355 public:
356
363 explicit TypedMemoryMapping(std::size_t size) :
364 m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) {
365 }
366
377 TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset = 0) :
378 m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) {
379 }
380
386 OSMIUM_DEPRECATED TypedMemoryMapping(std::size_t size, bool writable, int fd, off_t offset = 0) :
387 m_mapping(sizeof(T) * size,
388 writable ? MemoryMapping::mapping_mode::write_shared : MemoryMapping::mapping_mode::readonly,
389 fd,
390 sizeof(T) * offset) {
391 }
392
395
398
403 TypedMemoryMapping(TypedMemoryMapping&& other) noexcept = default;
404
408 TypedMemoryMapping& operator=(TypedMemoryMapping&& other) noexcept = default;
409
414 ~TypedMemoryMapping() noexcept = default;
415
422 void unmap() {
424 }
425
436 void resize(std::size_t new_size) {
437 m_mapping.resize(sizeof(T) * new_size);
438 }
439
444 explicit operator bool() const noexcept {
445 return !!m_mapping;
446 }
447
453 std::size_t size() const noexcept {
454 assert(m_mapping.size() % sizeof(T) == 0);
455 return m_mapping.size() / sizeof(T);
456 }
457
463 int fd() const noexcept {
464 return m_mapping.fd();
465 }
466
470 bool writable() const noexcept {
471 return m_mapping.writable();
472 }
473
479 T* begin() noexcept {
480 return m_mapping.get_addr<T>();
481 }
482
488 T* end() noexcept {
489 return m_mapping.get_addr<T>() + size();
490 }
491
497 const T* cbegin() const noexcept {
498 return m_mapping.get_addr<T>();
499 }
500
506 const T* cend() const noexcept {
507 return m_mapping.get_addr<T>() + size();
508 }
509
515 const T* begin() const noexcept {
516 return m_mapping.get_addr<T>();
517 }
518
524 const T* end() const noexcept {
525 return m_mapping.get_addr<T>() + size();
526 }
527
528 }; // class TypedMemoryMapping
529
530 template <typename T>
532
533 public:
534
535 explicit AnonymousTypedMemoryMapping(std::size_t size) :
537 }
538
539#ifndef __linux__
544 void resize(std::size_t) = delete;
545#endif
546
547 }; // class AnonymousTypedMemoryMapping
548
549 } // namespace util
550
551} // namespace osmium
552
553#ifndef _WIN32
554
555// =========== Unix implementation =============
556
557// MAP_FAILED is often a macro containing an old style cast
558#pragma GCC diagnostic push
559#pragma GCC diagnostic ignored "-Wold-style-cast"
560
561inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
562 return m_addr != MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
563}
564
566 m_addr = MAP_FAILED; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
567}
568
569#pragma GCC diagnostic pop
570
571// for BSD systems
572#ifndef MAP_ANONYMOUS
573# define MAP_ANONYMOUS MAP_ANON
574#endif
575
577 if (m_mapping_mode == mapping_mode::readonly) {
578 return PROT_READ;
579 }
580 return PROT_READ | PROT_WRITE; // NOLINT(hicpp-signed-bitwise)
581}
582
583inline int osmium::util::MemoryMapping::get_flags() const noexcept {
584 if (m_fd == -1) {
585 return MAP_PRIVATE | MAP_ANONYMOUS; // NOLINT(hicpp-signed-bitwise)
586 }
587 if (m_mapping_mode == mapping_mode::write_shared) {
588 return MAP_SHARED;
589 }
590 return MAP_PRIVATE;
591}
592
593inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, mapping_mode mode, int fd, off_t offset) :
594 m_size(check_size(size)),
595 m_offset(offset),
596 m_fd(resize_fd(fd)),
597 m_mapping_mode(mode),
598 m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) {
599 assert(!(fd == -1 && mode == mapping_mode::readonly));
600 if (!is_valid()) {
601 throw std::system_error{errno, std::system_category(), "mmap failed"};
602 }
603}
604
606 m_size(other.m_size),
607 m_offset(other.m_offset),
608 m_fd(other.m_fd),
609 m_mapping_mode(other.m_mapping_mode),
610 m_addr(other.m_addr) {
611 other.make_invalid();
612}
613
615 try {
616 unmap();
617 } catch (const std::system_error&) {
618 // Ignore unmap error. It should never happen anyway and we can't do
619 // anything about it here.
620 }
621 m_size = other.m_size;
622 m_offset = other.m_offset;
623 m_fd = other.m_fd;
624 m_mapping_mode = other.m_mapping_mode;
625 m_addr = other.m_addr;
626 other.make_invalid();
627 return *this;
628}
629
631 if (is_valid()) {
632 if (::munmap(m_addr, m_size) != 0) {
633 throw std::system_error{errno, std::system_category(), "munmap failed"};
634 }
635 make_invalid();
636 }
637}
638
639inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
640 assert(new_size > 0 && "can not resize to zero size");
641 if (m_fd == -1) { // anonymous mapping
642#ifdef __linux__
643 m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE);
644 if (!is_valid()) {
645 throw std::system_error{errno, std::system_category(), "mremap failed"};
646 }
647 m_size = new_size;
648#else
649 assert(false && "can't resize anonymous mappings on non-linux systems");
650#endif
651 } else { // file-based mapping
652 unmap();
653 m_size = new_size;
654 resize_fd(m_fd);
655 m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset);
656 if (!is_valid()) {
657 throw std::system_error{errno, std::system_category(), "mmap (remap) failed"};
658 }
659 }
660}
661
662#else
663
664// =========== Windows implementation =============
665
666/* References:
667 * CreateFileMapping: https://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
668 * CloseHandle: https://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
669 * MapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
670 * UnmapViewOfFile: https://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
671 */
672
673namespace osmium {
674
675 inline namespace util {
676
677 inline DWORD dword_hi(uint64_t x) {
678 return static_cast<DWORD>(x >> 32);
679 }
680
681 inline DWORD dword_lo(uint64_t x) {
682 return static_cast<DWORD>(x & 0xffffffff);
683 }
684
685 } // namespace util
686
687} // namespace osmium
688
689inline DWORD osmium::util::MemoryMapping::get_protection() const noexcept {
690 switch (m_mapping_mode) {
691 case mapping_mode::readonly:
692 return PAGE_READONLY;
693 case mapping_mode::write_private:
694 return PAGE_WRITECOPY;
695 default: // mapping_mode::write_shared
696 break;
697 }
698 return PAGE_READWRITE;
699}
700
701inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept {
702 switch (m_mapping_mode) {
703 case mapping_mode::readonly:
704 return FILE_MAP_READ;
705 case mapping_mode::write_private:
706 return FILE_MAP_COPY;
707 default: // mapping_mode::write_shared
708 break;
709 }
710 return FILE_MAP_WRITE;
711}
712
713inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept {
714 if (m_fd == -1) {
715 return INVALID_HANDLE_VALUE;
716 }
717 return reinterpret_cast<HANDLE>(_get_osfhandle(m_fd));
718}
719
720inline HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept {
721 if (m_fd != -1) {
722 _setmode(m_fd, _O_BINARY);
723 }
724 return CreateFileMapping(get_handle(),
725 nullptr,
726 get_protection(),
727 osmium::dword_hi(static_cast<uint64_t>(m_size) + m_offset),
728 osmium::dword_lo(static_cast<uint64_t>(m_size) + m_offset),
729 nullptr);
730}
731
732inline void* osmium::util::MemoryMapping::map_view_of_file() const noexcept {
733 return MapViewOfFile(m_handle,
734 get_flags(),
735 osmium::dword_hi(m_offset),
736 osmium::dword_lo(m_offset),
737 m_size);
738}
739
740inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
741 return m_addr != nullptr;
742}
743
744inline void osmium::util::MemoryMapping::make_invalid() noexcept {
745 m_addr = nullptr;
746}
747
748// GetLastError() returns a DWORD (A 32-bit unsigned integer), but the error
749// code for std::system_error is an int. So we convert this here and hope
750// it all works.
751inline int last_error() noexcept {
752 return static_cast<int>(GetLastError());
753}
754
755inline osmium::util::MemoryMapping::MemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) :
756 m_size(check_size(size)),
757 m_offset(offset),
758 m_fd(resize_fd(fd)),
759 m_mapping_mode(mode),
760 m_handle(create_file_mapping()),
761 m_addr(nullptr) {
762
763 if (!m_handle) {
764 throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
765 }
766
767 m_addr = map_view_of_file();
768 if (!is_valid()) {
769 throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
770 }
771}
772
773inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) noexcept :
774 m_size(other.m_size),
775 m_offset(other.m_offset),
776 m_fd(other.m_fd),
777 m_mapping_mode(other.m_mapping_mode),
778 m_handle(std::move(other.m_handle)),
779 m_addr(other.m_addr) {
780 other.make_invalid();
781 other.m_handle = nullptr;
782}
783
785 try {
786 unmap();
787 } catch (const std::system_error&) {
788 // Ignore unmap error. It should never happen anyway and we can't do
789 // anything about it here.
790 }
791 m_size = other.m_size;
792 m_offset = other.m_offset;
793 m_fd = other.m_fd;
794 m_mapping_mode = other.m_mapping_mode;
795 m_handle = std::move(other.m_handle);
796 m_addr = other.m_addr;
797 other.make_invalid();
798 other.m_handle = nullptr;
799 return *this;
800}
801
803 if (is_valid()) {
804 if (!UnmapViewOfFile(m_addr)) {
805 throw std::system_error{last_error(), std::system_category(), "UnmapViewOfFile failed"};
806 }
807 make_invalid();
808 }
809
810 if (m_handle) {
811 if (!CloseHandle(m_handle)) {
812 throw std::system_error{last_error(), std::system_category(), "CloseHandle failed"};
813 }
814 m_handle = nullptr;
815 }
816}
817
818inline void osmium::util::MemoryMapping::resize(std::size_t new_size) {
819 unmap();
820
821 m_size = new_size;
822 resize_fd(m_fd);
823
824 m_handle = create_file_mapping();
825 if (!m_handle) {
826 throw std::system_error{last_error(), std::system_category(), "CreateFileMapping failed"};
827 }
828
829 m_addr = map_view_of_file();
830 if (!is_valid()) {
831 throw std::system_error{last_error(), std::system_category(), "MapViewOfFile failed"};
832 }
833}
834
835#endif
836
837#endif // OSMIUM_UTIL_MEMORY_MAPPING_HPP
Definition: memory_mapping.hpp:323
AnonymousMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:327
Definition: memory_mapping.hpp:531
AnonymousTypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:535
Definition: memory_mapping.hpp:96
int resize_fd(int fd) const
Definition: memory_mapping.hpp:169
void unmap()
Definition: memory_mapping.hpp:630
bool is_valid() const noexcept
Definition: memory_mapping.hpp:561
MemoryMapping & operator=(const MemoryMapping &)=delete
You can not copy a MemoryMapping.
mapping_mode
Definition: memory_mapping.hpp:100
std::size_t size() const noexcept
Definition: memory_mapping.hpp:281
OSMIUM_DEPRECATED MemoryMapping(std::size_t size, bool writable=true, int fd=-1, off_t offset=0)
Definition: memory_mapping.hpp:214
MemoryMapping(const MemoryMapping &)=delete
You can not copy construct a MemoryMapping.
flag_type get_protection() const noexcept
Definition: memory_mapping.hpp:576
off_t m_offset
Offset into the file.
Definition: memory_mapping.hpp:112
~MemoryMapping() noexcept
Definition: memory_mapping.hpp:239
std::size_t m_size
The size of the mapping.
Definition: memory_mapping.hpp:109
static std::size_t available_space(int fd)
Definition: memory_mapping.hpp:156
int m_fd
File handle we got the mapping from.
Definition: memory_mapping.hpp:115
int flag_type
Definition: memory_mapping.hpp:134
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:639
bool writable() const noexcept
Definition: memory_mapping.hpp:297
T * get_addr() const noexcept
Definition: memory_mapping.hpp:307
flag_type get_flags() const noexcept
Definition: memory_mapping.hpp:583
mapping_mode m_mapping_mode
Mapping mode.
Definition: memory_mapping.hpp:118
static std::size_t check_size(std::size_t size)
Definition: memory_mapping.hpp:141
void make_invalid() noexcept
Definition: memory_mapping.hpp:565
void * m_addr
The address where the memory is mapped.
Definition: memory_mapping.hpp:125
int fd() const noexcept
Definition: memory_mapping.hpp:290
MemoryMapping(std::size_t size, mapping_mode mode, int fd=-1, off_t offset=0)
Definition: memory_mapping.hpp:593
Definition: memory_mapping.hpp:351
~TypedMemoryMapping() noexcept=default
int fd() const noexcept
Definition: memory_mapping.hpp:463
void resize(std::size_t new_size)
Definition: memory_mapping.hpp:436
TypedMemoryMapping & operator=(TypedMemoryMapping &&other) noexcept=default
const T * end() const noexcept
Definition: memory_mapping.hpp:524
const T * begin() const noexcept
Definition: memory_mapping.hpp:515
void unmap()
Definition: memory_mapping.hpp:422
TypedMemoryMapping(std::size_t size)
Definition: memory_mapping.hpp:363
bool writable() const noexcept
Definition: memory_mapping.hpp:470
TypedMemoryMapping(const TypedMemoryMapping &)=delete
You can not copy construct a TypedMemoryMapping.
const T * cend() const noexcept
Definition: memory_mapping.hpp:506
std::size_t size() const noexcept
Definition: memory_mapping.hpp:453
TypedMemoryMapping(std::size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset=0)
Definition: memory_mapping.hpp:377
MemoryMapping m_mapping
Definition: memory_mapping.hpp:353
const T * cbegin() const noexcept
Definition: memory_mapping.hpp:497
TypedMemoryMapping(TypedMemoryMapping &&other) noexcept=default
T * end() noexcept
Definition: memory_mapping.hpp:488
TypedMemoryMapping & operator=(const TypedMemoryMapping &)=delete
You can not copy a TypedMemoryMapping.
T * begin() noexcept
Definition: memory_mapping.hpp:479
OSMIUM_DEPRECATED TypedMemoryMapping(std::size_t size, bool writable, int fd, off_t offset=0)
Definition: memory_mapping.hpp:386
#define OSMIUM_DEPRECATED
Definition: compatibility.hpp:51
#define MAP_ANONYMOUS
Definition: memory_mapping.hpp:573
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:551