1717#pragma once
1818#define FOLLY_ARENA_H_
1919
20+ #include < algorithm>
2021#include < cassert>
2122#include < limits>
2223#include < stdexcept>
2324#include < utility>
2425
25- #include < boost/intrusive/slist.hpp>
26-
2726#include < folly/Conv.h>
2827#include < folly/Likely.h>
2928#include < folly/Memory.h>
@@ -67,7 +66,6 @@ class Arena {
6766 size_t sizeLimit = kNoSizeLimit ,
6867 size_t maxAlign = kDefaultMaxAlign )
6968 : allocAndSize_(alloc, minBlockSize),
70- currentBlock_(blocks_.last()),
7169 ptr_(nullptr ),
7270 end_(nullptr ),
7371 totalAllocatedSize_(0 ),
@@ -99,7 +97,7 @@ class Arena {
9997 }
10098
10199 if (canReuseExistingBlock (size)) {
102- currentBlock_++ ;
100+ currentBlock_ = currentBlock_-> next ;
103101 char * r = align (currentBlock_->start ());
104102 ptr_ = r + size;
105103 end_ = currentBlock_->start () + blockGoodAllocSize () - sizeof (Block);
@@ -124,10 +122,10 @@ class Arena {
124122 void clear () {
125123 bytesUsed_ = 0 ;
126124 freeLargeBlocks (); // We don't reuse large blocks
127- if (blocks_. empty () ) {
125+ if (!firstBlock_ ) {
128126 return ;
129127 }
130- currentBlock_ = blocks_. begin () ;
128+ currentBlock_ = firstBlock_ ;
131129 ptr_ = align (currentBlock_->start ());
132130 end_ = currentBlock_->start () + blockGoodAllocSize () - sizeof (Block);
133131 assert (ptr_ <= end_);
@@ -151,10 +149,8 @@ class Arena {
151149 private:
152150 using AllocTraits =
153151 typename std::allocator_traits<Alloc>::template rebind_traits<char >;
154- using BlockLink = boost::intrusive::slist_member_hook<>;
155-
156152 struct alignas (max_align_v) Block {
157- BlockLink link ;
153+ Block* next = nullptr ;
158154
159155 char * start () { return reinterpret_cast <char *>(this + 1 ); }
160156
@@ -164,11 +160,11 @@ class Arena {
164160
165161 constexpr size_t blockGoodAllocSize () {
166162 return ArenaAllocatorTraits<Alloc>::goodSize (
167- alloc (), roundUp ( sizeof ( Block) ) + minBlockSize ());
163+ alloc (), headerSize< Block>( ) + minBlockSize ());
168164 }
169165
170166 struct alignas (max_align_v) LargeBlock {
171- BlockLink link ;
167+ LargeBlock* next = nullptr ;
172168 const size_t allocSize;
173169
174170 char * start () { return reinterpret_cast <char *>(this + 1 ); }
@@ -182,35 +178,39 @@ class Arena {
182178 // We don't reuse large blocks
183179 return false ;
184180 }
185- if (blocks_.empty () || currentBlock_ == blocks_.last ()) {
186- // No regular blocks to reuse
187- return false ;
188- }
189- return true ;
181+ // No regular blocks to reuse past the current one
182+ return currentBlock_ != lastBlock_;
190183 }
191184
192185 void freeBlocks () {
193- blocks_.clear_and_dispose ([this ](Block* b) {
186+ for (Block* b = firstBlock_; b;) {
187+ Block* const next = b->next ;
194188 b->~Block ();
195189 AllocTraits::deallocate (
196190 alloc (), reinterpret_cast <char *>(b), blockGoodAllocSize ());
197- });
191+ b = next;
192+ }
193+ firstBlock_ = lastBlock_ = currentBlock_ = nullptr ;
198194 }
199195
200196 void freeLargeBlocks () {
201- largeBlocks_.clear_and_dispose ([this ](LargeBlock* b) {
197+ for (LargeBlock* b = firstLargeBlock_; b;) {
198+ LargeBlock* const next = b->next ;
202199 auto size = b->allocSize ;
203200 totalAllocatedSize_ -= size;
204201 b->~LargeBlock ();
205202 AllocTraits::deallocate (alloc (), reinterpret_cast <char *>(b), size);
206- });
203+ b = next;
204+ }
205+ firstLargeBlock_ = lastLargeBlock_ = nullptr ;
207206 }
208207
209208 public:
210209 static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof (Block);
211210 static constexpr size_t kNoSizeLimit = 0 ;
212211 static constexpr size_t kDefaultMaxAlign = alignof (Block);
213212 static constexpr size_t kBlockOverhead = sizeof (Block);
213+ static constexpr size_t kLargeBlockOverhead = sizeof (LargeBlock);
214214
215215 private:
216216 bool isAligned (uintptr_t address) const {
@@ -232,19 +232,23 @@ class Arena {
232232
233233 char * align (char * ptr) { return align_ceil (ptr, maxAlign_); }
234234
235- // cache_last<true> makes the list keep a pointer to the last element, so we
236- // have push_back() and constant time splice_after()
237- using BlockList = boost::intrusive::slist<
238- Block,
239- boost::intrusive::member_hook<Block, BlockLink, &Block::link>,
240- boost::intrusive::constant_time_size< false >,
241- boost::intrusive::cache_last< true >>;
235+ // A block header plus the most padding align() can add after it, since the
236+ // allocator only guarantees alignment for the header.
237+ template < typename Header>
238+ size_t headerSize () const {
239+ return sizeof (Header) + std::max (maxAlign_, alignof (Header)) -
240+ alignof (Header);
241+ }
242242
243- using LargeBlockList = boost::intrusive::slist<
244- LargeBlock,
245- boost::intrusive::member_hook<LargeBlock, BlockLink, &LargeBlock::link>,
246- boost::intrusive::constant_time_size<false >,
247- boost::intrusive::cache_last<true >>;
243+ // Appends the chain [first, last], if not empty, to the chain [head, tail].
244+ template <typename B>
245+ static void append (B*& head, B*& tail, B* first, B* last) {
246+ if (!first) {
247+ return ;
248+ }
249+ (tail ? tail->next : head) = first;
250+ tail = last;
251+ }
248252
249253 void * allocateSlow (size_t size);
250254
@@ -262,9 +266,14 @@ class Arena {
262266 const Alloc& alloc () const { return allocAndSize_; }
263267
264268 AllocAndSize allocAndSize_;
265- BlockList blocks_;
266- typename BlockList::iterator currentBlock_;
267- LargeBlockList largeBlocks_;
269+ // Blocks are chained through their next pointers. Blocks after
270+ // currentBlock_ are free for reuse; currentBlock_ is null only when there
271+ // are no blocks.
272+ Block* firstBlock_ = nullptr ;
273+ Block* lastBlock_ = nullptr ;
274+ Block* currentBlock_ = nullptr ;
275+ LargeBlock* firstLargeBlock_ = nullptr ;
276+ LargeBlock* lastLargeBlock_ = nullptr ;
268277 char * ptr_;
269278 char * end_;
270279 size_t totalAllocatedSize_;
0 commit comments