PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
memory_mapped_file.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
11
12#if defined(_WIN32)
13 #ifndef WIN32_LEAN_AND_MEAN
14 #define WIN32_LEAN_AND_MEAN
15 #endif
16 #include <windows.h>
17#else
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22#endif
23
24namespace kcenon::pacs::core {
25
26auto memory_mapped_file::open(const std::filesystem::path& path)
28
29 memory_mapped_file result;
30
31#if defined(_WIN32)
32 // Open the file
33 result.file_handle_ = CreateFileW(
34 path.c_str(),
35 GENERIC_READ,
36 FILE_SHARE_READ,
37 nullptr,
38 OPEN_EXISTING,
39 FILE_ATTRIBUTE_NORMAL,
40 nullptr);
41
42 if (result.file_handle_ == INVALID_HANDLE_VALUE) {
43 result.file_handle_ = nullptr;
46 "Failed to open file: " + path.string());
47 }
48
49 // Get file size
50 LARGE_INTEGER file_size;
51 if (!GetFileSizeEx(result.file_handle_, &file_size)) {
52 CloseHandle(result.file_handle_);
53 result.file_handle_ = nullptr;
56 "Failed to get file size: " + path.string());
57 }
58
59 result.size_ = static_cast<std::size_t>(file_size.QuadPart);
60
61 if (result.size_ == 0) {
62 CloseHandle(result.file_handle_);
63 result.file_handle_ = nullptr;
66 "File is empty: " + path.string());
67 }
68
69 // Create file mapping
70 result.mapping_handle_ = CreateFileMappingW(
71 result.file_handle_,
72 nullptr,
73 PAGE_READONLY,
74 0, 0,
75 nullptr);
76
77 if (result.mapping_handle_ == nullptr) {
78 CloseHandle(result.file_handle_);
79 result.file_handle_ = nullptr;
82 "Failed to create file mapping: " + path.string());
83 }
84
85 // Map view of file
86 auto* mapped = MapViewOfFile(
87 result.mapping_handle_,
88 FILE_MAP_READ,
89 0, 0,
90 0);
91
92 if (mapped == nullptr) {
93 CloseHandle(result.mapping_handle_);
94 CloseHandle(result.file_handle_);
95 result.mapping_handle_ = nullptr;
96 result.file_handle_ = nullptr;
99 "Failed to map file into memory: " + path.string());
100 }
101
102 result.data_ = static_cast<const std::uint8_t*>(mapped);
103
104#else // POSIX
105 // Open the file
106 result.fd_ = ::open(path.c_str(), O_RDONLY);
107 if (result.fd_ < 0) {
110 "Failed to open file: " + path.string());
111 }
112
113 // Get file size
114 struct stat st {};
115 if (::fstat(result.fd_, &st) != 0) {
116 ::close(result.fd_);
117 result.fd_ = -1;
120 "Failed to stat file: " + path.string());
121 }
122
123 result.size_ = static_cast<std::size_t>(st.st_size);
124
125 if (result.size_ == 0) {
126 ::close(result.fd_);
127 result.fd_ = -1;
130 "File is empty: " + path.string());
131 }
132
133 // Memory-map the file
134 auto* mapped = ::mmap(nullptr, result.size_, PROT_READ, MAP_PRIVATE,
135 result.fd_, 0);
136 if (mapped == MAP_FAILED) {
137 ::close(result.fd_);
138 result.fd_ = -1;
141 "Failed to mmap file: " + path.string());
142 }
143
144 result.data_ = static_cast<const std::uint8_t*>(mapped);
145#endif
146
147 return kcenon::pacs::Result<memory_mapped_file>::ok(std::move(result));
148}
149
150auto memory_mapped_file::data() const noexcept -> const std::uint8_t* {
151 return data_;
152}
153
154auto memory_mapped_file::size() const noexcept -> std::size_t {
155 return size_;
156}
157
158auto memory_mapped_file::as_span() const noexcept -> std::span<const std::uint8_t> {
159 return {data_, size_};
160}
161
163 : data_{other.data_}
164 , size_{other.size_}
165#if defined(_WIN32)
166 , file_handle_{other.file_handle_}
167 , mapping_handle_{other.mapping_handle_}
168#else
169 , fd_{other.fd_}
170#endif
171{
172 other.data_ = nullptr;
173 other.size_ = 0;
174#if defined(_WIN32)
175 other.file_handle_ = nullptr;
176 other.mapping_handle_ = nullptr;
177#else
178 other.fd_ = -1;
179#endif
180}
181
184 if (this != &other) {
185 unmap();
186
187 data_ = other.data_;
188 size_ = other.size_;
189#if defined(_WIN32)
190 file_handle_ = other.file_handle_;
191 mapping_handle_ = other.mapping_handle_;
192 other.file_handle_ = nullptr;
193 other.mapping_handle_ = nullptr;
194#else
195 fd_ = other.fd_;
196 other.fd_ = -1;
197#endif
198 other.data_ = nullptr;
199 other.size_ = 0;
200 }
201 return *this;
202}
203
207
209 if (data_ == nullptr) {
210 return;
211 }
212
213#if defined(_WIN32)
214 UnmapViewOfFile(data_);
215 if (mapping_handle_ != nullptr) {
216 CloseHandle(mapping_handle_);
217 }
218 if (file_handle_ != nullptr) {
219 CloseHandle(file_handle_);
220 }
221 mapping_handle_ = nullptr;
222 file_handle_ = nullptr;
223#else
224 ::munmap(const_cast<std::uint8_t*>(data_), size_);
225 if (fd_ >= 0) {
226 ::close(fd_);
227 }
228 fd_ = -1;
229#endif
230
231 data_ = nullptr;
232 size_ = 0;
233}
234
235} // namespace kcenon::pacs::core
static auto open(const std::filesystem::path &path) -> kcenon::pacs::Result< memory_mapped_file >
Open and memory-map a file for reading.
auto as_span() const noexcept -> std::span< const std::uint8_t >
Get a span over the mapped data.
auto size() const noexcept -> std::size_t
Get the size of the mapped file.
auto operator=(const memory_mapped_file &) -> memory_mapped_file &=delete
auto data() const noexcept -> const std::uint8_t *
Get pointer to the mapped data.
size_t size_
const uint8_t * data_
RAII wrapper for memory-mapped file I/O.
constexpr int file_read_error
Definition result.h:60
constexpr int invalid_dicom_file
Definition result.h:62
constexpr int file_not_found
Definition result.h:59
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234