]>
Commit | Line | Data |
---|---|---|
94b0b3e5 JF |
1 | // Copyright Notice (GNU Affero GPL) {{{ |
2 | /* Cyndir - (Awesome) Memory Mapped Dictionary | |
3 | * Copyright (C) 2010 Jay Freeman (saurik) | |
4 | */ | |
5 | ||
6 | /* | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU Affero General Public License as | |
9 | * published by the Free Software Foundation, either version 3 of the | |
10 | * License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU Affero General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Affero General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | // }}} | |
21 | ||
22 | #ifndef CYTORE_HPP | |
23 | #define CYTORE_HPP | |
24 | ||
25 | #include <fcntl.h> | |
26 | ||
27 | #include <sys/mman.h> | |
28 | #include <sys/stat.h> | |
29 | ||
30 | #include <cstdio> | |
31 | #include <cstdlib> | |
32 | ||
33 | #include <errno.h> | |
34 | #include <stdint.h> | |
35 | #include <unistd.h> | |
36 | ||
37 | #define _assert(test) do \ | |
38 | if (!(test)) { \ | |
39 | fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \ | |
40 | exit(-1); \ | |
41 | } \ | |
42 | while (false) | |
43 | ||
44 | namespace Cytore { | |
45 | ||
46 | static const uint32_t Magic = 'cynd'; | |
47 | ||
48 | struct Header { | |
49 | uint32_t magic_; | |
50 | uint32_t version_; | |
51 | uint32_t size_; | |
52 | uint32_t reserved_; | |
53 | }; | |
54 | ||
55 | struct Block { | |
56 | }; | |
57 | ||
58 | template <typename Target_> | |
59 | class Offset { | |
60 | private: | |
61 | uint32_t offset_; | |
62 | ||
63 | public: | |
64 | Offset() : | |
65 | offset_(0) | |
66 | { | |
67 | } | |
68 | ||
69 | Offset(uint32_t offset) : | |
70 | offset_(offset) | |
71 | { | |
72 | } | |
73 | ||
74 | Offset &operator =(uint32_t offset) { | |
75 | offset_ = offset; | |
76 | return *this; | |
77 | } | |
78 | ||
79 | uint32_t GetOffset() const { | |
80 | return offset_; | |
81 | } | |
82 | ||
83 | bool IsNull() const { | |
84 | return offset_ == 0; | |
85 | } | |
86 | }; | |
87 | ||
88 | template <typename Type_> | |
89 | static _finline Type_ Round(Type_ value, Type_ size) { | |
90 | Type_ mask(size - 1); | |
91 | return value + mask & ~mask; | |
92 | } | |
93 | ||
94 | template <typename Base_> | |
95 | class File { | |
96 | private: | |
97 | static const unsigned Shift_ = 17; | |
98 | static const size_t Block_ = 1 << Shift_; | |
99 | static const size_t Mask_ = Block_ - 1; | |
100 | ||
101 | private: | |
102 | int file_; | |
103 | std::vector<uint8_t *> blocks_; | |
104 | ||
105 | Header &Header_() { | |
106 | return *reinterpret_cast<Header *>(blocks_[0]); | |
107 | } | |
108 | ||
109 | uint32_t &Size_() { | |
110 | return Header_().size_; | |
111 | } | |
112 | ||
113 | void Map_(size_t size) { | |
114 | size_t before(blocks_.size() * Block_); | |
115 | size_t extend(size - before); | |
116 | ||
117 | void *data(mmap(NULL, extend, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, file_, before)); | |
118 | for (size_t i(0); i != extend >> Shift_; ++i) | |
119 | blocks_.push_back(reinterpret_cast<uint8_t *>(data) + Block_ * i); | |
120 | } | |
121 | ||
122 | void Truncate_(size_t capacity) { | |
123 | capacity = Round(capacity, Block_); | |
124 | ftruncate(file_, capacity); | |
125 | Map_(capacity); | |
126 | } | |
127 | ||
128 | public: | |
129 | File() : | |
130 | file_(-1) | |
131 | { | |
132 | } | |
133 | ||
134 | File(const char *path) : | |
135 | file_(-1) | |
136 | { | |
137 | Open(path); | |
138 | } | |
139 | ||
140 | ~File() { | |
141 | // XXX: this object is never deconstructed. if it were, this should unmap the memory | |
142 | close(file_); | |
143 | } | |
144 | ||
145 | size_t Capacity() const { | |
146 | return blocks_.size() * Block_; | |
147 | } | |
148 | ||
149 | void Open(const char *path) { | |
150 | _assert(file_ == -1); | |
151 | file_ = open(path, O_RDWR | O_CREAT | O_EXLOCK, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | |
152 | ||
153 | struct stat stat; | |
154 | fstat(file_, &stat); | |
155 | ||
156 | size_t core(sizeof(Header) + sizeof(Base_)); | |
157 | ||
158 | size_t size(stat.st_size); | |
159 | if (size == 0) { | |
160 | Truncate_(core); | |
161 | Header_().magic_ = Magic; | |
162 | Size_() = core; | |
163 | } else { | |
164 | _assert(size >= core); | |
165 | Truncate_(size); | |
166 | _assert(Header_().magic_ == Magic); | |
167 | _assert(Header_().version_ == 0); | |
168 | } | |
169 | } | |
170 | ||
171 | void Reserve(size_t capacity) { | |
172 | if (capacity <= Capacity()) | |
173 | return; | |
174 | blocks_.pop_back(); | |
175 | Truncate_(capacity); | |
176 | } | |
177 | ||
178 | template <typename Target_> | |
179 | Target_ &Get(uint32_t offset) { | |
180 | return *reinterpret_cast<Target_ *>(blocks_[offset >> Shift_] + (offset & Mask_)); | |
181 | } | |
182 | ||
183 | template <typename Target_> | |
184 | Target_ &Get(Offset<Target_> &ref) { | |
185 | return Get<Target_>(ref.GetOffset()); | |
186 | } | |
187 | ||
188 | Base_ *operator ->() { | |
189 | return &Get<Base_>(sizeof(Header)); | |
190 | } | |
191 | ||
192 | template <typename Target_> | |
193 | Offset<Target_> New(size_t extra = 0) { | |
194 | size_t size(sizeof(Target_) + extra); | |
195 | size = Round(size, sizeof(uintptr_t)); | |
196 | Reserve(Size_() + size); | |
197 | uint32_t offset(Size_()); | |
198 | Size_() += size; | |
199 | return Offset<Target_>(offset); | |
200 | } | |
201 | }; | |
202 | ||
203 | } | |
204 | ||
205 | #endif//CYTORE_HPP |