Add a LICENSE file for folly
[folly.git] / folly / Arena.h
1 /*
2  * Copyright 2012 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_ARENA_H_
18 #define FOLLY_ARENA_H_
19
20 #include <cassert>
21 #include <utility>
22 #include <limits>
23 #include <boost/intrusive/slist.hpp>
24
25 #include "folly/Likely.h"
26 #include "folly/Malloc.h"
27
28 namespace folly {
29
30 /**
31  * Simple arena: allocate memory which gets freed when the arena gets
32  * destroyed.
33  *
34  * The arena itself allocates memory using a custom allocator which provides
35  * the following interface (same as required by StlAllocator in StlAllocator.h)
36  *
37  *   void* allocate(size_t size);
38  *      Allocate a block of size bytes, properly aligned to the maximum
39  *      alignment required on your system; throw std::bad_alloc if the
40  *      allocation can't be satisfied.
41  *
42  *   void deallocate(void* ptr);
43  *      Deallocate a previously allocated block.
44  *
45  * You may also specialize ArenaAllocatorTraits for your allocator type to
46  * provide:
47  *
48  *   size_t goodSize(const Allocator& alloc, size_t size) const;
49  *      Return a size (>= the provided size) that is considered "good" for your
50  *      allocator (for example, if your allocator allocates memory in 4MB
51  *      chunks, size should be rounded up to 4MB).  The provided value is
52  *      guaranteed to be rounded up to a multiple of the maximum alignment
53  *      required on your system; the returned value must be also.
54  *
55  * An implementation that uses malloc() / free() is defined below, see
56  * SysAlloc / SysArena.
57  */
58 template <class Alloc> struct ArenaAllocatorTraits;
59 template <class Alloc>
60 class Arena {
61  public:
62   explicit Arena(const Alloc& alloc,
63                  size_t minBlockSize = kDefaultMinBlockSize)
64     : allocAndSize_(alloc, minBlockSize),
65       ptr_(nullptr),
66       end_(nullptr) {
67   }
68
69   ~Arena();
70
71   void* allocate(size_t size) {
72     size = roundUp(size);
73
74     if (LIKELY(end_ - ptr_ >= size)) {
75       // Fast path: there's enough room in the current block
76       char* r = ptr_;
77       ptr_ += size;
78       assert(isAligned(r));
79       return r;
80     }
81
82     // Not enough room in the current block
83     void* r = allocateSlow(size);
84     assert(isAligned(r));
85     return r;
86   }
87
88   void deallocate(void* p) {
89     // Deallocate? Never!
90   }
91
92   // Transfer ownership of all memory allocated from "other" to "this".
93   void merge(Arena&& other);
94
95  private:
96   // not copyable
97   Arena(const Arena&) = delete;
98   Arena& operator=(const Arena&) = delete;
99
100   // movable
101   Arena(Arena&&) = default;
102   Arena& operator=(Arena&&) = default;
103
104   struct Block;
105   typedef boost::intrusive::slist_member_hook<
106     boost::intrusive::tag<Arena>> BlockLink;
107
108   struct Block {
109     BlockLink link;
110
111     // Allocate a block with at least size bytes of storage.
112     // If allowSlack is true, allocate more than size bytes if convenient
113     // (via ArenaAllocatorTraits::goodSize()) as we'll try to pack small
114     // allocations in this block.
115     static std::pair<Block*, size_t> allocate(
116         Alloc& alloc, size_t size, bool allowSlack);
117     void deallocate(Alloc& alloc);
118
119     char* start() {
120       return reinterpret_cast<char*>(this + 1);
121     }
122
123    private:
124     Block() { }
125     ~Block() { }
126   } __attribute__((aligned));
127   // This should be alignas(std::max_align_t) but neither alignas nor
128   // max_align_t are supported by gcc 4.6.2.
129
130  public:
131   static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof(Block);
132
133  private:
134   static constexpr size_t maxAlign = alignof(Block);
135   static constexpr bool isAligned(uintptr_t address) {
136     return (address & (maxAlign - 1)) == 0;
137   }
138   static bool isAligned(void* p) {
139     return isAligned(reinterpret_cast<uintptr_t>(p));
140   }
141
142   // Round up size so it's properly aligned
143   static constexpr size_t roundUp(size_t size) {
144     return (size + maxAlign - 1) & ~(maxAlign - 1);
145   }
146
147   // cache_last<true> makes the list keep a pointer to the last element, so we
148   // have push_back() and constant time splice_after()
149   typedef boost::intrusive::slist<
150     Block,
151     boost::intrusive::member_hook<Block, BlockLink, &Block::link>,
152     boost::intrusive::constant_time_size<false>,
153     boost::intrusive::cache_last<true>> BlockList;
154
155   void* allocateSlow(size_t size);
156
157   // Empty member optimization: package Alloc with a non-empty member
158   // in case Alloc is empty (as it is in the case of SysAlloc).
159   struct AllocAndSize : public Alloc {
160     explicit AllocAndSize(const Alloc& a, size_t s)
161       : Alloc(a), minBlockSize(s) {
162     }
163
164     size_t minBlockSize;
165   };
166
167   size_t minBlockSize() const {
168     return allocAndSize_.minBlockSize;
169   }
170   Alloc& alloc() { return allocAndSize_; }
171   const Alloc& alloc() const { return allocAndSize_; }
172
173   AllocAndSize allocAndSize_;
174   BlockList blocks_;
175   char* ptr_;
176   char* end_;
177 };
178
179 /**
180  * By default, don't pad the given size.
181  */
182 template <class Alloc>
183 struct ArenaAllocatorTraits {
184   static size_t goodSize(const Alloc& alloc, size_t size) {
185     return size;
186   }
187 };
188
189 /**
190  * Arena-compatible allocator that calls malloc() and free(); see
191  * goodMallocSize() in Malloc.h for goodSize().
192  */
193 class SysAlloc {
194  public:
195   void* allocate(size_t size) {
196     void* mem = malloc(size);
197     if (!mem) throw std::bad_alloc();
198     return mem;
199   }
200
201   void deallocate(void* p) {
202     free(p);
203   }
204 };
205
206 template <>
207 struct ArenaAllocatorTraits<SysAlloc> {
208   static size_t goodSize(const SysAlloc& alloc, size_t size) {
209     return goodMallocSize(size);
210   }
211 };
212
213 /**
214  * Arena that uses the system allocator (malloc / free)
215  */
216 class SysArena : public Arena<SysAlloc> {
217  public:
218   explicit SysArena(size_t minBlockSize = kDefaultMinBlockSize)
219     : Arena<SysAlloc>(SysAlloc(), minBlockSize) {
220   }
221 };
222
223 }  // namespace folly
224
225 #include "folly/Arena-inl.h"
226
227 #endif /* FOLLY_ARENA_H_ */