1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/utils/port/MemoryMapping.h"
8
9 #include "td/utils/misc.h"
10 #include "td/utils/SliceBuilder.h"
11
12 // TODO:
13 // windows,
14 // anonymous maps,
15 // huge pages?
16
17 #if TD_WINDOWS
18 #else
19 #include <sys/mman.h>
20 #include <unistd.h>
21 #endif
22
23 namespace td {
24
25 class MemoryMapping::Impl {
26 public:
Impl(MutableSlice data,int64 offset)27 Impl(MutableSlice data, int64 offset) : data_(data), offset_(offset) {
28 }
as_slice() const29 Slice as_slice() const {
30 return data_.substr(narrow_cast<size_t>(offset_));
31 }
as_mutable_slice() const32 MutableSlice as_mutable_slice() const {
33 return {};
34 }
35
36 private:
37 MutableSlice data_;
38 int64 offset_;
39 };
40
41 #if !TD_WINDOWS
get_page_size()42 static Result<int64> get_page_size() {
43 static Result<int64> page_size = []() -> Result<int64> {
44 auto page_size = sysconf(_SC_PAGESIZE);
45 if (page_size < 0) {
46 return OS_ERROR("Can't load page size from sysconf");
47 }
48 return page_size;
49 }();
50 return page_size.clone();
51 }
52 #endif
53
create_anonymous(const MemoryMapping::Options & options)54 Result<MemoryMapping> MemoryMapping::create_anonymous(const MemoryMapping::Options &options) {
55 return Status::Error("Unsupported yet");
56 }
57
create_from_file(const FileFd & file_fd,const MemoryMapping::Options & options)58 Result<MemoryMapping> MemoryMapping::create_from_file(const FileFd &file_fd, const MemoryMapping::Options &options) {
59 #if TD_WINDOWS
60 return Status::Error("Unsupported yet");
61 #else
62 if (file_fd.empty()) {
63 return Status::Error("Can't create memory mapping: file is empty");
64 }
65 TRY_RESULT(stat, file_fd.stat());
66 auto fd = file_fd.get_native_fd().fd();
67 auto begin = options.offset;
68 if (begin < 0) {
69 return Status::Error(PSLICE() << "Can't create memory mapping: negative offset " << options.offset);
70 }
71
72 int64 end;
73 if (options.size < 0) {
74 end = stat.size_;
75 } else {
76 end = begin + stat.size_;
77 }
78
79 TRY_RESULT(page_size, get_page_size());
80 auto fixed_begin = begin / page_size * page_size;
81
82 auto data_offset = begin - fixed_begin;
83 TRY_RESULT(data_size, narrow_cast_safe<size_t>(end - fixed_begin));
84
85 void *data = mmap(nullptr, data_size, PROT_READ, MAP_PRIVATE, fd, narrow_cast<off_t>(fixed_begin));
86 if (data == MAP_FAILED) {
87 return OS_ERROR("mmap call failed");
88 }
89
90 return MemoryMapping(make_unique<Impl>(MutableSlice(static_cast<char *>(data), data_size), data_offset));
91 #endif
92 }
93
94 MemoryMapping::MemoryMapping(MemoryMapping &&other) noexcept = default;
95 MemoryMapping &MemoryMapping::operator=(MemoryMapping &&other) noexcept = default;
96 MemoryMapping::~MemoryMapping() = default;
97
MemoryMapping(unique_ptr<Impl> impl)98 MemoryMapping::MemoryMapping(unique_ptr<Impl> impl) : impl_(std::move(impl)) {
99 }
100
as_slice() const101 Slice MemoryMapping::as_slice() const {
102 return impl_->as_slice();
103 }
104
as_mutable_slice()105 MutableSlice MemoryMapping::as_mutable_slice() {
106 return impl_->as_mutable_slice();
107 }
108
109 } // namespace td
110