Pixie
Loading...
Searching...
No Matches
mapped_file.h
1#pragma once
2
3#if !defined(__unix__) && !defined(__APPLE__)
4#error "pixie/io/mapped_file.h requires POSIX mmap support"
5#endif
6
7#include <fcntl.h>
8#include <sys/mman.h>
9#include <sys/stat.h>
10#include <unistd.h>
11
12#include <cerrno>
13#include <cstddef>
14#include <cstdint>
15#include <filesystem>
16#include <limits>
17#include <span>
18#include <stdexcept>
19#include <system_error>
20#include <utility>
21
22namespace pixie::io {
23
30 public:
37 explicit MappedFile(const std::filesystem::path& path) {
38 const int descriptor = ::open(path.c_str(), O_RDONLY);
39 if (descriptor == -1) {
40 throw std::system_error(errno, std::generic_category(),
41 "Failed to open mapped file");
42 }
43
44 struct stat status {};
45 if (::fstat(descriptor, &status) == -1) {
46 const int error = errno;
47 close_descriptor(descriptor);
48 throw std::system_error(error, std::generic_category(),
49 "Failed to inspect mapped file");
50 }
51
52 const auto file_size = static_cast<std::uintmax_t>(status.st_size);
53 if (status.st_size < 0 ||
54 file_size > std::numeric_limits<std::size_t>::max()) {
55 close_descriptor(descriptor);
56 throw std::length_error("Mapped file is too large");
57 }
58
59 const auto size = static_cast<std::size_t>(file_size);
60 if (size == 0) {
61 close_descriptor(descriptor);
62 return;
63 }
64
65 void* const mapping =
66 ::mmap(nullptr, size, PROT_READ, MAP_SHARED, descriptor, 0);
67 const int error = mapping == MAP_FAILED ? errno : 0;
68 close_descriptor(descriptor);
69 if (mapping == MAP_FAILED) {
70 throw std::system_error(error, std::generic_category(),
71 "Failed to map file");
72 }
73
74 data_ = mapping;
75 size_bytes_ = size;
76 }
77
78 MappedFile(const MappedFile&) = delete;
79 MappedFile& operator=(const MappedFile&) = delete;
80
82 MappedFile(MappedFile&& other) noexcept
83 : data_(std::exchange(other.data_, nullptr)),
84 size_bytes_(std::exchange(other.size_bytes_, 0)) {}
85
87 MappedFile& operator=(MappedFile&& other) noexcept {
88 if (this != &other) {
89 reset();
90 data_ = std::exchange(other.data_, nullptr);
91 size_bytes_ = std::exchange(other.size_bytes_, 0);
92 }
93 return *this;
94 }
95
97 ~MappedFile() { reset(); }
98
100 std::span<const std::byte> as_bytes() const noexcept {
101 if (data_ == nullptr) {
102 return {};
103 }
104 return {static_cast<const std::byte*>(data_), size_bytes_};
105 }
106
108 std::size_t size_bytes() const noexcept { return size_bytes_; }
109
110 private:
111 static void close_descriptor(int descriptor) noexcept {
112 if (descriptor != -1) {
113 ::close(descriptor);
114 }
115 }
116
117 void reset() noexcept {
118 if (data_ != nullptr) {
119 ::munmap(data_, size_bytes_);
120 data_ = nullptr;
121 size_bytes_ = 0;
122 }
123 }
124
125 void* data_ = nullptr;
126 std::size_t size_bytes_ = 0;
127};
128
129} // namespace pixie::io
Move-only owner of a read-only POSIX memory-mapped file.
Definition mapped_file.h:29
MappedFile & operator=(MappedFile &&other) noexcept
Release this mapping and transfer ownership from other.
Definition mapped_file.h:87
MappedFile(MappedFile &&other) noexcept
Transfer ownership of a mapping.
Definition mapped_file.h:82
std::span< const std::byte > as_bytes() const noexcept
Return the mapped file contents.
Definition mapped_file.h:100
std::size_t size_bytes() const noexcept
Return the mapped file size in bytes.
Definition mapped_file.h:108
MappedFile(const std::filesystem::path &path)
Map path for reading.
Definition mapped_file.h:37
~MappedFile()
Unmap the file contents, if any.
Definition mapped_file.h:97