1 #ifndef MMAP_ALLOCATOR_H
2 #define MMAP_ALLOCATOR_H
3 
4 #include "macros.h"
5 #include <cstdio>
6 #include <sys/types.h>
7 
8 #include <memory>
9 #include <limits>
10 #include <string>
11 #include <stdexcept>
12 
13 /* This is inspired from https://github.com/johannesthoma/mmap_allocator
14  * License is LGPL.
15  * The version here has been trimmed down significantly, look for uptream
16  * version for more features */
17 
18 namespace mmap_allocator_details
19 {
20     typedef off_t offset_type;
21     typedef size_t size_type;
22 
23     enum access_mode {
24         DEFAULT_STL_ALLOCATOR, /* Default STL allocator (malloc based). Reason is to have containers that do both and are compatible */
25         READ_ONLY,  /* Readonly modus. Segfaults when vector content is written to */
26         READ_WRITE_PRIVATE, /* Read/write access, writes are not propagated to disk */
27         READ_WRITE_SHARED  /* Read/write access, writes are propagated to disk (file is modified) */
28     };
29 
30     struct mmap_allocator_exception: public std::runtime_error {
mmap_allocator_exceptionmmap_allocator_details::mmap_allocator_exception31         mmap_allocator_exception(const char *msg_param):
32             std::runtime_error(msg_param) { }
33 
mmap_allocator_exceptionmmap_allocator_details::mmap_allocator_exception34         mmap_allocator_exception(std::string msg_param):
35             std::runtime_error(msg_param) { }
36 
~mmap_allocator_exceptionmmap_allocator_details::mmap_allocator_exception37         virtual ~mmap_allocator_exception(void) noexcept { }
38     };
39 
40     /* Only the mmapped_file is something we can use to grab mapping
41      * segments. Once the mmapped_file object is destroyed, the segments
42      * survive because of shared_ptr's.
43      */
44     class mmapped_file {
45         public:
46             private:
47             class mapping {
48                 int fd = -1;
49                 void * area = NULL;
50                 offset_type offset_mapped;     /* page-aligned */
51                 size_type length_mapped;       /* page-aligned */
52                 public:
53                 mapping(const char * fname, enum access_mode access_mode, offset_type offset, size_type length);
54                 ~mapping();
55                 void * get(offset_type, size_type);
56                 void put(void *, offset_type, size_type);
57             };
58 #if 0
59             /* use this to trace shared_ptr games. */
60             class shared_mapping : private std::shared_ptr<mapping> {
61                 public:
62                 template<typename... Args> shared_mapping(Args... args) :
63                     std::shared_ptr<mapping>(args...) {
64                         fprintf(stderr, "Creating a shared_ptr [%p]\n", (void*) get());
65                     }
66                 shared_mapping(std::shared_ptr<mapping> const & s) : std::shared_ptr<mapping>(s) {
67                     fprintf(stderr, "Copying a shared_ptr [%p]\n", (void*) get());
68                 }
69                 shared_mapping(shared_mapping const & s) : std::shared_ptr<mapping>(s) {
70                     fprintf(stderr, "Copying a shared_ptr [%p]\n", (void*) get());
71                 }
72                 ~shared_mapping() {
73                     fprintf(stderr, "Destroying a shared_ptr [%p]\n", (void*) get());
74                 }
75                 mapping * operator->() { return std::shared_ptr<mapping>::operator->(); }
76                 mapping const * operator->() const { return std::shared_ptr<mapping>::operator->(); }
77                 operator bool() const { return (std::shared_ptr<mapping> const&)(*this) != nullptr; }
78             };
79 #endif
80             std::shared_ptr<mapping> m;
81             public:
82             class segment {
83                 public:
84                 std::shared_ptr<mapping> file;
85                 offset_type offset;
86                 size_type length;
87                 /* with shared_mapping above that defines an operator
88                  * bool, we just say "return file" */
operator bool() const89                 operator bool() const { return file != nullptr; }
get(size_type s)90                 void * get(size_type s) {
91                     if (s == 0) return NULL;
92                     ASSERT_ALWAYS(s == length);
93                     return file->get(offset, length);
94                 }
put(void * p,size_type s)95                 void put(void * p, size_type s) {
96                     if (s == 0) return;
97                     ASSERT_ALWAYS(s == length);
98                     file->put(p, offset, length);
99                 }
100             };
101         public:
get_segment(offset_type offset,size_type length)102             segment get_segment(offset_type offset, size_type length) {
103                 return segment { m, offset, length };
104             }
mmapped_file(const char * fname,access_mode mode=READ_ONLY,offset_type offset=0,size_type length=std::numeric_limits<size_type>::max ())105             mmapped_file(const char * fname, access_mode mode = READ_ONLY, offset_type offset = 0, size_type length = std::numeric_limits<size_type>::max()) : m(std::make_shared<mapping>(fname, mode, offset, length)) {}
106     };
107 
108     template <typename T> class mmap_allocator: public std::allocator<T>
109     {
110         public:
111             using typename std::allocator<T>::size_type;
112             using typename std::allocator<T>::pointer;
113             using typename std::allocator<T>::const_pointer;
114             typedef mmap_allocator_details::offset_type offset_type;
115         private:
116             mmapped_file::segment s;
117         public:
has_defined_mapping() const118             bool has_defined_mapping() const { return s; }
119             template<typename _Tp1>
120             struct rebind { typedef mmap_allocator<_Tp1> other; };
121 
allocate(size_type n,const void * hint=0)122             pointer allocate(size_type n, const void *hint=0)
123             {
124                 if (!s)
125                     return std::allocator<T>::allocate(n, hint);
126 
127                 if (n == 0) return NULL;
128 
129                 return (pointer) s.get(n*sizeof(T));
130             }
131 
deallocate(pointer p,size_type n)132             void deallocate(pointer p, size_type n)
133             {
134                 if (!s) {
135                     std::allocator<T>::deallocate(p, n);
136                     return;
137                 }
138 
139                 if (n == 0) return;
140 
141                 s.put(p, n*sizeof(T));
142             }
143 
144             mmap_allocator() = default;
mmap_allocator(const std::allocator<T> & a)145             mmap_allocator(const std::allocator<T> &a): std::allocator<T>(a) {}
146             mmap_allocator(const mmap_allocator &) = default;
147             ~mmap_allocator() = default;
148 
149             /* These are the only ctors that enable a behaviour that
150              * differs from the stl container */
mmap_allocator(mmapped_file::segment const & s)151             mmap_allocator(mmapped_file::segment const & s) : s(s) {}
mmap_allocator(mmapped_file m,offset_type offset,size_type length)152             mmap_allocator(mmapped_file m, offset_type offset, size_type length) : s(m.get_segment(offset, length * sizeof(T))) {}
mmap_allocator(const char * f,access_mode mode,offset_type offset,size_type length)153             mmap_allocator(const char * f, access_mode mode, offset_type offset, size_type length) : mmap_allocator(mmapped_file(f, mode, offset, length*sizeof(T)), offset, length) {}
154 
155     };
156 }
157 
158 #endif /* MMAP_ALLOCATOR_H */
159