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