1 // Copyright Notice (GNU Affero GPL) {{{
2 /* Cyndir - (Awesome) Memory Mapped Dictionary
3 * Copyright (C) 2010 Jay Freeman (saurik)
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.
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.
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/>.
37 #define _assert(test) do \
39 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
46 static const uint32_t Magic = 'cynd';
55 template <typename Target_>
66 Offset(uint32_t offset) :
71 Offset &operator =(uint32_t offset) {
76 uint32_t GetOffset() const {
86 Cytore::Offset<void> reserved_;
89 template <typename Type_>
90 static _finline Type_ Round(Type_ value, Type_ size) {
92 return value + mask & ~mask;
95 template <typename Base_>
98 static const unsigned Shift_ = 17;
99 static const size_t Block_ = 1 << Shift_;
100 static const size_t Mask_ = Block_ - 1;
105 typedef std::vector<uint8_t *> BlockVector_;
106 BlockVector_ blocks_;
112 Mapping_(uint8_t *data, size_t size) :
119 typedef std::vector<Mapping_> MappingVector_;
120 MappingVector_ maps_;
123 return *reinterpret_cast<Header *>(blocks_[0]);
127 return Header_().size_;
130 void Map_(size_t size) {
131 size_t before(blocks_.size() * Block_);
132 size_t extend(size - before);
134 void *data(mmap(NULL, extend, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, file_, before));
135 _assert(data != MAP_FAILED);
136 uint8_t *bytes(reinterpret_cast<uint8_t *>(data));
138 maps_.push_back(Mapping_(bytes, extend));
139 for (size_t i(0); i != extend >> Shift_; ++i)
140 blocks_.push_back(bytes + Block_ * i);
143 bool Truncate_(size_t capacity) {
144 capacity = Round(capacity, Block_);
146 int error(ftruncate(file_, capacity));
160 File(const char *path) :
167 for (typename MappingVector_::const_iterator map(maps_.begin()); map != maps_.end(); ++map)
168 munmap(map->data_, map->size_);
173 for (typename MappingVector_::const_iterator map(maps_.begin()); map != maps_.end(); ++map)
174 msync(map->data_, map->size_, MS_SYNC);
177 size_t Capacity() const {
178 return blocks_.size() * Block_;
181 void Open(const char *path) { open:
182 _assert(file_ == -1);
183 file_ = open(path, O_RDWR | O_CREAT | O_EXLOCK, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
184 _assert(file_ != -1);
187 _assert(fstat(file_, &stat) == 0);
189 size_t core(sizeof(Header) + sizeof(Base_));
191 size_t size(stat.st_size);
193 if (!Truncate_(core)) {
198 Header_().magic_ = Magic;
200 } else if (size < core) {
206 // XXX: this involves an unneccessary call to ftruncate()
207 _assert(Truncate_(size));
208 _assert(Header_().magic_ == Magic);
209 _assert(Header_().version_ == 0);
213 bool Reserve(size_t capacity) {
214 if (capacity <= Capacity())
217 uint8_t *block(blocks_.back());
220 if (Truncate_(capacity))
223 blocks_.push_back(block);
228 template <typename Target_>
229 Target_ &Get(uint32_t offset) {
230 return *reinterpret_cast<Target_ *>(offset == 0 ? NULL : blocks_[offset >> Shift_] + (offset & Mask_));
233 template <typename Target_>
234 Target_ &Get(Offset<Target_> &ref) {
235 return Get<Target_>(ref.GetOffset());
238 Base_ *operator ->() {
239 return &Get<Base_>(sizeof(Header));
242 template <typename Target_>
243 Offset<Target_> New(size_t extra = 0) {
244 size_t size(sizeof(Target_) + extra);
245 size = Round(size, sizeof(uintptr_t));
248 if (!Reserve(Size_() + size))
255 return Offset<Target_>(offset);