xref: /aosp_15_r20/external/libtextclassifier/native/utils/base/arena.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1*993b0882SAndroid Build Coastguard Worker /*
2*993b0882SAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*993b0882SAndroid Build Coastguard Worker  *
4*993b0882SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*993b0882SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*993b0882SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*993b0882SAndroid Build Coastguard Worker  *
8*993b0882SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*993b0882SAndroid Build Coastguard Worker  *
10*993b0882SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*993b0882SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*993b0882SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*993b0882SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*993b0882SAndroid Build Coastguard Worker  * limitations under the License.
15*993b0882SAndroid Build Coastguard Worker  */
16*993b0882SAndroid Build Coastguard Worker 
17*993b0882SAndroid Build Coastguard Worker // This approach to arenas overcomes many of the limitations described
18*993b0882SAndroid Build Coastguard Worker // in the "Specialized allocators" section of
19*993b0882SAndroid Build Coastguard Worker //     http://www.pdos.lcs.mit.edu/~dm/c++-new.html
20*993b0882SAndroid Build Coastguard Worker //
21*993b0882SAndroid Build Coastguard Worker // A somewhat similar approach to Gladiator, but for heap-detection, was
22*993b0882SAndroid Build Coastguard Worker // suggested by Ron van der Wal and Scott Meyers at
23*993b0882SAndroid Build Coastguard Worker //     http://www.aristeia.com/BookErrata/M27Comments_frames.html
24*993b0882SAndroid Build Coastguard Worker 
25*993b0882SAndroid Build Coastguard Worker #include "utils/base/arena.h"
26*993b0882SAndroid Build Coastguard Worker 
27*993b0882SAndroid Build Coastguard Worker #include "utils/base/logging.h"
28*993b0882SAndroid Build Coastguard Worker #include "utils/base/macros.h"
29*993b0882SAndroid Build Coastguard Worker 
30*993b0882SAndroid Build Coastguard Worker namespace libtextclassifier3 {
31*993b0882SAndroid Build Coastguard Worker 
32*993b0882SAndroid Build Coastguard Worker #ifndef __cpp_aligned_new
aligned_malloc(size_t size,int minimum_alignment)33*993b0882SAndroid Build Coastguard Worker static void *aligned_malloc(size_t size, int minimum_alignment) {
34*993b0882SAndroid Build Coastguard Worker   void *ptr = nullptr;
35*993b0882SAndroid Build Coastguard Worker   // posix_memalign requires that the requested alignment be at least
36*993b0882SAndroid Build Coastguard Worker   // sizeof(void*). In this case, fall back on malloc which should return memory
37*993b0882SAndroid Build Coastguard Worker   // aligned to at least the size of a pointer.
38*993b0882SAndroid Build Coastguard Worker   const int required_alignment = sizeof(void*);
39*993b0882SAndroid Build Coastguard Worker   if (minimum_alignment < required_alignment)
40*993b0882SAndroid Build Coastguard Worker     return malloc(size);
41*993b0882SAndroid Build Coastguard Worker   if (posix_memalign(&ptr, static_cast<size_t>(minimum_alignment), size) != 0)
42*993b0882SAndroid Build Coastguard Worker     return nullptr;
43*993b0882SAndroid Build Coastguard Worker   else
44*993b0882SAndroid Build Coastguard Worker     return ptr;
45*993b0882SAndroid Build Coastguard Worker }
46*993b0882SAndroid Build Coastguard Worker #endif  // !__cpp_aligned_new
47*993b0882SAndroid Build Coastguard Worker 
48*993b0882SAndroid Build Coastguard Worker // The value here doesn't matter until page_aligned_ is supported.
49*993b0882SAndroid Build Coastguard Worker static const int kPageSize = 8192;   // should be getpagesize()
50*993b0882SAndroid Build Coastguard Worker 
51*993b0882SAndroid Build Coastguard Worker // We used to only keep track of how much space has been allocated in
52*993b0882SAndroid Build Coastguard Worker // debug mode. Now we track this for optimized builds, as well. If you
53*993b0882SAndroid Build Coastguard Worker // want to play with the old scheme to see if this helps performance,
54*993b0882SAndroid Build Coastguard Worker // change this TC3_ARENASET() macro to a NOP. However, NOTE: some
55*993b0882SAndroid Build Coastguard Worker // applications of arenas depend on this space information (exported
56*993b0882SAndroid Build Coastguard Worker // via bytes_allocated()).
57*993b0882SAndroid Build Coastguard Worker #define TC3_ARENASET(x) (x)
58*993b0882SAndroid Build Coastguard Worker 
59*993b0882SAndroid Build Coastguard Worker namespace {
60*993b0882SAndroid Build Coastguard Worker 
61*993b0882SAndroid Build Coastguard Worker #ifdef __cpp_aligned_new
62*993b0882SAndroid Build Coastguard Worker 
AllocateBytes(size_t size)63*993b0882SAndroid Build Coastguard Worker char* AllocateBytes(size_t size) {
64*993b0882SAndroid Build Coastguard Worker   return static_cast<char*>(::operator new(size));
65*993b0882SAndroid Build Coastguard Worker }
66*993b0882SAndroid Build Coastguard Worker 
67*993b0882SAndroid Build Coastguard Worker // REQUIRES: alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__
68*993b0882SAndroid Build Coastguard Worker //
69*993b0882SAndroid Build Coastguard Worker // For alignments <=__STDCPP_DEFAULT_NEW_ALIGNMENT__, AllocateBytes() will
70*993b0882SAndroid Build Coastguard Worker // provide the correct alignment.
AllocateAlignedBytes(size_t size,size_t alignment)71*993b0882SAndroid Build Coastguard Worker char* AllocateAlignedBytes(size_t size, size_t alignment) {
72*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_GT(alignment, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
73*993b0882SAndroid Build Coastguard Worker   return static_cast<char*>(::operator new(size, std::align_val_t(alignment)));
74*993b0882SAndroid Build Coastguard Worker }
75*993b0882SAndroid Build Coastguard Worker 
DeallocateBytes(void * ptr,size_t size,size_t alignment)76*993b0882SAndroid Build Coastguard Worker void DeallocateBytes(void* ptr, size_t size, size_t alignment) {
77*993b0882SAndroid Build Coastguard Worker   if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
78*993b0882SAndroid Build Coastguard Worker #ifdef __cpp_sized_deallocation
79*993b0882SAndroid Build Coastguard Worker     ::operator delete(ptr, size, std::align_val_t(alignment));
80*993b0882SAndroid Build Coastguard Worker #else  // !__cpp_sized_deallocation
81*993b0882SAndroid Build Coastguard Worker     ::operator delete(ptr, std::align_val_t(alignment));
82*993b0882SAndroid Build Coastguard Worker #endif  // !__cpp_sized_deallocation
83*993b0882SAndroid Build Coastguard Worker   } else {
84*993b0882SAndroid Build Coastguard Worker #ifdef __cpp_sized_deallocation
85*993b0882SAndroid Build Coastguard Worker     ::operator delete(ptr, size);
86*993b0882SAndroid Build Coastguard Worker #else  // !__cpp_sized_deallocation
87*993b0882SAndroid Build Coastguard Worker     ::operator delete(ptr);
88*993b0882SAndroid Build Coastguard Worker #endif  // !__cpp_sized_deallocation
89*993b0882SAndroid Build Coastguard Worker   }
90*993b0882SAndroid Build Coastguard Worker }
91*993b0882SAndroid Build Coastguard Worker 
92*993b0882SAndroid Build Coastguard Worker #else  // !__cpp_aligned_new
93*993b0882SAndroid Build Coastguard Worker 
94*993b0882SAndroid Build Coastguard Worker char* AllocateBytes(size_t size) {
95*993b0882SAndroid Build Coastguard Worker   return static_cast<char*>(malloc(size));
96*993b0882SAndroid Build Coastguard Worker }
97*993b0882SAndroid Build Coastguard Worker 
98*993b0882SAndroid Build Coastguard Worker char* AllocateAlignedBytes(size_t size, size_t alignment) {
99*993b0882SAndroid Build Coastguard Worker   return static_cast<char*>(aligned_malloc(size, alignment));
100*993b0882SAndroid Build Coastguard Worker }
101*993b0882SAndroid Build Coastguard Worker 
102*993b0882SAndroid Build Coastguard Worker void DeallocateBytes(void* ptr, size_t size, size_t alignment) {
103*993b0882SAndroid Build Coastguard Worker   free(ptr);
104*993b0882SAndroid Build Coastguard Worker }
105*993b0882SAndroid Build Coastguard Worker 
106*993b0882SAndroid Build Coastguard Worker #endif  // !__cpp_aligned_new
107*993b0882SAndroid Build Coastguard Worker 
108*993b0882SAndroid Build Coastguard Worker }  // namespace
109*993b0882SAndroid Build Coastguard Worker 
110*993b0882SAndroid Build Coastguard Worker const int BaseArena::kDefaultAlignment;
111*993b0882SAndroid Build Coastguard Worker 
112*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
113*993b0882SAndroid Build Coastguard Worker // BaseArena::BaseArena()
114*993b0882SAndroid Build Coastguard Worker // BaseArena::~BaseArena()
115*993b0882SAndroid Build Coastguard Worker //    Destroying the arena automatically calls Reset()
116*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
117*993b0882SAndroid Build Coastguard Worker 
BaseArena(char * first,const size_t orig_block_size,bool align_to_page)118*993b0882SAndroid Build Coastguard Worker BaseArena::BaseArena(char* first, const size_t orig_block_size,
119*993b0882SAndroid Build Coastguard Worker                      bool align_to_page)
120*993b0882SAndroid Build Coastguard Worker     : remaining_(0),
121*993b0882SAndroid Build Coastguard Worker       block_size_(orig_block_size),
122*993b0882SAndroid Build Coastguard Worker       freestart_(nullptr),  // set for real in Reset()
123*993b0882SAndroid Build Coastguard Worker       last_alloc_(nullptr),
124*993b0882SAndroid Build Coastguard Worker       overflow_blocks_(nullptr),
125*993b0882SAndroid Build Coastguard Worker       first_block_externally_owned_(first != nullptr),
126*993b0882SAndroid Build Coastguard Worker       page_aligned_(align_to_page),
127*993b0882SAndroid Build Coastguard Worker       blocks_alloced_(1) {
128*993b0882SAndroid Build Coastguard Worker   // Trivial check that aligned objects can actually be allocated.
129*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_GT(block_size_, kDefaultAlignment)
130*993b0882SAndroid Build Coastguard Worker       << "orig_block_size = " << orig_block_size;
131*993b0882SAndroid Build Coastguard Worker   if (page_aligned_) {
132*993b0882SAndroid Build Coastguard Worker     // kPageSize must be power of 2, so make sure of this.
133*993b0882SAndroid Build Coastguard Worker     TC3_CHECK(kPageSize > 0 && 0 == (kPageSize & (kPageSize - 1)))
134*993b0882SAndroid Build Coastguard Worker                               << "kPageSize[ " << kPageSize << "] is not "
135*993b0882SAndroid Build Coastguard Worker                               << "correctly initialized: not a power of 2.";
136*993b0882SAndroid Build Coastguard Worker   }
137*993b0882SAndroid Build Coastguard Worker 
138*993b0882SAndroid Build Coastguard Worker   if (first) {
139*993b0882SAndroid Build Coastguard Worker     TC3_CHECK(!page_aligned_ ||
140*993b0882SAndroid Build Coastguard Worker           (reinterpret_cast<uintptr_t>(first) & (kPageSize - 1)) == 0);
141*993b0882SAndroid Build Coastguard Worker     first_blocks_[0].mem = first;
142*993b0882SAndroid Build Coastguard Worker     first_blocks_[0].size = orig_block_size;
143*993b0882SAndroid Build Coastguard Worker   } else {
144*993b0882SAndroid Build Coastguard Worker     if (page_aligned_) {
145*993b0882SAndroid Build Coastguard Worker       // Make sure the blocksize is page multiple, as we need to end on a page
146*993b0882SAndroid Build Coastguard Worker       // boundary.
147*993b0882SAndroid Build Coastguard Worker       TC3_CHECK_EQ(block_size_ & (kPageSize - 1), 0) << "block_size is not a"
148*993b0882SAndroid Build Coastguard Worker                                                  << "multiple of kPageSize";
149*993b0882SAndroid Build Coastguard Worker       first_blocks_[0].mem = AllocateAlignedBytes(block_size_, kPageSize);
150*993b0882SAndroid Build Coastguard Worker       first_blocks_[0].alignment = kPageSize;
151*993b0882SAndroid Build Coastguard Worker       TC3_CHECK(nullptr != first_blocks_[0].mem);
152*993b0882SAndroid Build Coastguard Worker     } else {
153*993b0882SAndroid Build Coastguard Worker       first_blocks_[0].mem = AllocateBytes(block_size_);
154*993b0882SAndroid Build Coastguard Worker       first_blocks_[0].alignment = 0;
155*993b0882SAndroid Build Coastguard Worker     }
156*993b0882SAndroid Build Coastguard Worker     first_blocks_[0].size = block_size_;
157*993b0882SAndroid Build Coastguard Worker   }
158*993b0882SAndroid Build Coastguard Worker 
159*993b0882SAndroid Build Coastguard Worker   Reset();
160*993b0882SAndroid Build Coastguard Worker }
161*993b0882SAndroid Build Coastguard Worker 
~BaseArena()162*993b0882SAndroid Build Coastguard Worker BaseArena::~BaseArena() {
163*993b0882SAndroid Build Coastguard Worker   FreeBlocks();
164*993b0882SAndroid Build Coastguard Worker   assert(overflow_blocks_ == nullptr);    // FreeBlocks() should do that
165*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
166*993b0882SAndroid Build Coastguard Worker   if (first_block_externally_owned_) {
167*993b0882SAndroid Build Coastguard Worker     ASAN_UNPOISON_MEMORY_REGION(first_blocks_[0].mem, first_blocks_[0].size);
168*993b0882SAndroid Build Coastguard Worker   }
169*993b0882SAndroid Build Coastguard Worker #endif
170*993b0882SAndroid Build Coastguard Worker   // The first X blocks stay allocated always by default.  Delete them now.
171*993b0882SAndroid Build Coastguard Worker   for (int i = first_block_externally_owned_ ? 1 : 0;
172*993b0882SAndroid Build Coastguard Worker        i < blocks_alloced_; ++i) {
173*993b0882SAndroid Build Coastguard Worker     DeallocateBytes(first_blocks_[i].mem, first_blocks_[i].size,
174*993b0882SAndroid Build Coastguard Worker                     first_blocks_[i].alignment);
175*993b0882SAndroid Build Coastguard Worker   }
176*993b0882SAndroid Build Coastguard Worker }
177*993b0882SAndroid Build Coastguard Worker 
178*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
179*993b0882SAndroid Build Coastguard Worker // BaseArena::block_count()
180*993b0882SAndroid Build Coastguard Worker //    Only reason this is in .cc file is because it involves STL.
181*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
182*993b0882SAndroid Build Coastguard Worker 
block_count() const183*993b0882SAndroid Build Coastguard Worker int BaseArena::block_count() const {
184*993b0882SAndroid Build Coastguard Worker   return (blocks_alloced_ +
185*993b0882SAndroid Build Coastguard Worker           (overflow_blocks_ ? static_cast<int>(overflow_blocks_->size()) : 0));
186*993b0882SAndroid Build Coastguard Worker }
187*993b0882SAndroid Build Coastguard Worker 
188*993b0882SAndroid Build Coastguard Worker // Returns true iff it advances freestart_ to the first position
189*993b0882SAndroid Build Coastguard Worker // satisfying alignment without exhausting the current block.
SatisfyAlignment(size_t alignment)190*993b0882SAndroid Build Coastguard Worker bool BaseArena::SatisfyAlignment(size_t alignment) {
191*993b0882SAndroid Build Coastguard Worker   const size_t overage =
192*993b0882SAndroid Build Coastguard Worker       reinterpret_cast<size_t>(freestart_) & (alignment - 1);
193*993b0882SAndroid Build Coastguard Worker   if (overage > 0) {
194*993b0882SAndroid Build Coastguard Worker     const size_t waste = alignment - overage;
195*993b0882SAndroid Build Coastguard Worker     if (waste >= remaining_) {
196*993b0882SAndroid Build Coastguard Worker       return false;
197*993b0882SAndroid Build Coastguard Worker     }
198*993b0882SAndroid Build Coastguard Worker     freestart_ += waste;
199*993b0882SAndroid Build Coastguard Worker     remaining_ -= waste;
200*993b0882SAndroid Build Coastguard Worker   }
201*993b0882SAndroid Build Coastguard Worker   TC3_DCHECK_EQ(0, reinterpret_cast<size_t>(freestart_) & (alignment - 1));
202*993b0882SAndroid Build Coastguard Worker   return true;
203*993b0882SAndroid Build Coastguard Worker }
204*993b0882SAndroid Build Coastguard Worker 
205*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
206*993b0882SAndroid Build Coastguard Worker // BaseArena::Reset()
207*993b0882SAndroid Build Coastguard Worker //    Clears all the memory an arena is using.
208*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
209*993b0882SAndroid Build Coastguard Worker 
Reset()210*993b0882SAndroid Build Coastguard Worker void BaseArena::Reset() {
211*993b0882SAndroid Build Coastguard Worker   FreeBlocks();
212*993b0882SAndroid Build Coastguard Worker   freestart_ = first_blocks_[0].mem;
213*993b0882SAndroid Build Coastguard Worker   remaining_ = first_blocks_[0].size;
214*993b0882SAndroid Build Coastguard Worker   last_alloc_ = nullptr;
215*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
216*993b0882SAndroid Build Coastguard Worker   ASAN_POISON_MEMORY_REGION(freestart_, remaining_);
217*993b0882SAndroid Build Coastguard Worker #endif
218*993b0882SAndroid Build Coastguard Worker 
219*993b0882SAndroid Build Coastguard Worker   TC3_ARENASET(status_.bytes_allocated_ = block_size_);
220*993b0882SAndroid Build Coastguard Worker 
221*993b0882SAndroid Build Coastguard Worker   // There is no guarantee the first block is properly aligned, so
222*993b0882SAndroid Build Coastguard Worker   // enforce that now.
223*993b0882SAndroid Build Coastguard Worker   TC3_CHECK(SatisfyAlignment(kDefaultAlignment));
224*993b0882SAndroid Build Coastguard Worker 
225*993b0882SAndroid Build Coastguard Worker   freestart_when_empty_ = freestart_;
226*993b0882SAndroid Build Coastguard Worker }
227*993b0882SAndroid Build Coastguard Worker 
228*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
229*993b0882SAndroid Build Coastguard Worker // BaseArena::MakeNewBlock()
230*993b0882SAndroid Build Coastguard Worker //    Our sbrk() equivalent.  We always make blocks of the same size
231*993b0882SAndroid Build Coastguard Worker //    (though GetMemory() can also make a new block for really big
232*993b0882SAndroid Build Coastguard Worker //    data.
233*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
234*993b0882SAndroid Build Coastguard Worker 
MakeNewBlock(const uint32 alignment)235*993b0882SAndroid Build Coastguard Worker void BaseArena::MakeNewBlock(const uint32 alignment) {
236*993b0882SAndroid Build Coastguard Worker   AllocatedBlock *block = AllocNewBlock(block_size_, alignment);
237*993b0882SAndroid Build Coastguard Worker   freestart_ = block->mem;
238*993b0882SAndroid Build Coastguard Worker   remaining_ = block->size;
239*993b0882SAndroid Build Coastguard Worker   TC3_CHECK(SatisfyAlignment(alignment));
240*993b0882SAndroid Build Coastguard Worker }
241*993b0882SAndroid Build Coastguard Worker 
242*993b0882SAndroid Build Coastguard Worker // The following simple numeric routines also exist in util/math/mathutil.h
243*993b0882SAndroid Build Coastguard Worker // but we don't want to depend on that library.
244*993b0882SAndroid Build Coastguard Worker 
245*993b0882SAndroid Build Coastguard Worker // Euclid's algorithm for Greatest Common Denominator.
GCD(uint32 x,uint32 y)246*993b0882SAndroid Build Coastguard Worker static uint32 GCD(uint32 x, uint32 y) {
247*993b0882SAndroid Build Coastguard Worker   while (y != 0) {
248*993b0882SAndroid Build Coastguard Worker     uint32 r = x % y;
249*993b0882SAndroid Build Coastguard Worker     x = y;
250*993b0882SAndroid Build Coastguard Worker     y = r;
251*993b0882SAndroid Build Coastguard Worker   }
252*993b0882SAndroid Build Coastguard Worker   return x;
253*993b0882SAndroid Build Coastguard Worker }
254*993b0882SAndroid Build Coastguard Worker 
LeastCommonMultiple(uint32 a,uint32 b)255*993b0882SAndroid Build Coastguard Worker static uint32 LeastCommonMultiple(uint32 a, uint32 b) {
256*993b0882SAndroid Build Coastguard Worker   if (a > b) {
257*993b0882SAndroid Build Coastguard Worker     return (a / GCD(a, b)) * b;
258*993b0882SAndroid Build Coastguard Worker   } else if (a < b) {
259*993b0882SAndroid Build Coastguard Worker     return (b / GCD(b, a)) * a;
260*993b0882SAndroid Build Coastguard Worker   } else {
261*993b0882SAndroid Build Coastguard Worker     return a;
262*993b0882SAndroid Build Coastguard Worker   }
263*993b0882SAndroid Build Coastguard Worker }
264*993b0882SAndroid Build Coastguard Worker 
265*993b0882SAndroid Build Coastguard Worker // -------------------------------------------------------------
266*993b0882SAndroid Build Coastguard Worker // BaseArena::AllocNewBlock()
267*993b0882SAndroid Build Coastguard Worker //    Adds and returns an AllocatedBlock.
268*993b0882SAndroid Build Coastguard Worker //    The returned AllocatedBlock* is valid until the next call
269*993b0882SAndroid Build Coastguard Worker //    to AllocNewBlock or Reset.  (i.e. anything that might
270*993b0882SAndroid Build Coastguard Worker //    affect overflow_blocks_).
271*993b0882SAndroid Build Coastguard Worker // -------------------------------------------------------------
272*993b0882SAndroid Build Coastguard Worker 
AllocNewBlock(const size_t block_size,const uint32 alignment)273*993b0882SAndroid Build Coastguard Worker BaseArena::AllocatedBlock*  BaseArena::AllocNewBlock(const size_t block_size,
274*993b0882SAndroid Build Coastguard Worker                                                      const uint32 alignment) {
275*993b0882SAndroid Build Coastguard Worker   AllocatedBlock *block;
276*993b0882SAndroid Build Coastguard Worker   // Find the next block.
277*993b0882SAndroid Build Coastguard Worker   if (blocks_alloced_ < TC3_ARRAYSIZE(first_blocks_)) {
278*993b0882SAndroid Build Coastguard Worker     // Use one of the pre-allocated blocks
279*993b0882SAndroid Build Coastguard Worker     block = &first_blocks_[blocks_alloced_++];
280*993b0882SAndroid Build Coastguard Worker   } else {  // oops, out of space, move to the vector
281*993b0882SAndroid Build Coastguard Worker     if (overflow_blocks_ == nullptr)
282*993b0882SAndroid Build Coastguard Worker       overflow_blocks_ = new std::vector<AllocatedBlock>;
283*993b0882SAndroid Build Coastguard Worker     // Adds another block to the vector.
284*993b0882SAndroid Build Coastguard Worker     overflow_blocks_->resize(overflow_blocks_->size()+1);
285*993b0882SAndroid Build Coastguard Worker     // block points to the last block of the vector.
286*993b0882SAndroid Build Coastguard Worker     block = &overflow_blocks_->back();
287*993b0882SAndroid Build Coastguard Worker   }
288*993b0882SAndroid Build Coastguard Worker 
289*993b0882SAndroid Build Coastguard Worker   // NOTE(tucker): this utility is made slightly more complex by
290*993b0882SAndroid Build Coastguard Worker   // not disallowing the case where alignment > block_size.
291*993b0882SAndroid Build Coastguard Worker   // Can we, without breaking existing code?
292*993b0882SAndroid Build Coastguard Worker 
293*993b0882SAndroid Build Coastguard Worker   // If page_aligned_, then alignment must be a multiple of page size.
294*993b0882SAndroid Build Coastguard Worker   // Otherwise, must be a multiple of kDefaultAlignment, unless
295*993b0882SAndroid Build Coastguard Worker   // requested alignment is 1, in which case we don't care at all.
296*993b0882SAndroid Build Coastguard Worker   const uint32 adjusted_alignment =
297*993b0882SAndroid Build Coastguard Worker       page_aligned_ ? LeastCommonMultiple(kPageSize, alignment)
298*993b0882SAndroid Build Coastguard Worker       : (alignment > 1 ? LeastCommonMultiple(alignment, kDefaultAlignment) : 1);
299*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_LE(adjusted_alignment, 1 << 20)
300*993b0882SAndroid Build Coastguard Worker       << "Alignment on boundaries greater than 1MB not supported.";
301*993b0882SAndroid Build Coastguard Worker 
302*993b0882SAndroid Build Coastguard Worker   // If block_size > alignment we force block_size to be a multiple
303*993b0882SAndroid Build Coastguard Worker   // of alignment; if block_size < alignment we make no adjustment, unless
304*993b0882SAndroid Build Coastguard Worker   // page_aligned_ is true, in which case it must be a multiple of
305*993b0882SAndroid Build Coastguard Worker   // kPageSize because SetProtect() will assume that.
306*993b0882SAndroid Build Coastguard Worker   size_t adjusted_block_size = block_size;
307*993b0882SAndroid Build Coastguard Worker #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
308*993b0882SAndroid Build Coastguard Worker   if (adjusted_alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
309*993b0882SAndroid Build Coastguard Worker #else
310*993b0882SAndroid Build Coastguard Worker   if (adjusted_alignment > 1) {
311*993b0882SAndroid Build Coastguard Worker #endif
312*993b0882SAndroid Build Coastguard Worker     if (adjusted_block_size > adjusted_alignment) {
313*993b0882SAndroid Build Coastguard Worker       const uint32 excess = adjusted_block_size % adjusted_alignment;
314*993b0882SAndroid Build Coastguard Worker       adjusted_block_size += (excess > 0 ? adjusted_alignment - excess : 0);
315*993b0882SAndroid Build Coastguard Worker     }
316*993b0882SAndroid Build Coastguard Worker     if (page_aligned_) {
317*993b0882SAndroid Build Coastguard Worker       size_t num_pages = ((adjusted_block_size - 1)/kPageSize) + 1;
318*993b0882SAndroid Build Coastguard Worker       adjusted_block_size = num_pages * kPageSize;
319*993b0882SAndroid Build Coastguard Worker     }
320*993b0882SAndroid Build Coastguard Worker     block->mem = AllocateAlignedBytes(adjusted_block_size, adjusted_alignment);
321*993b0882SAndroid Build Coastguard Worker   } else {
322*993b0882SAndroid Build Coastguard Worker     block->mem = AllocateBytes(adjusted_block_size);
323*993b0882SAndroid Build Coastguard Worker   }
324*993b0882SAndroid Build Coastguard Worker   block->size = adjusted_block_size;
325*993b0882SAndroid Build Coastguard Worker   block->alignment = adjusted_alignment;
326*993b0882SAndroid Build Coastguard Worker   TC3_CHECK(nullptr != block->mem)
327*993b0882SAndroid Build Coastguard Worker       << "block_size=" << block_size
328*993b0882SAndroid Build Coastguard Worker       << " adjusted_block_size=" << adjusted_block_size
329*993b0882SAndroid Build Coastguard Worker       << " alignment=" << alignment
330*993b0882SAndroid Build Coastguard Worker       << " adjusted_alignment=" << adjusted_alignment;
331*993b0882SAndroid Build Coastguard Worker 
332*993b0882SAndroid Build Coastguard Worker   TC3_ARENASET(status_.bytes_allocated_ += adjusted_block_size);
333*993b0882SAndroid Build Coastguard Worker 
334*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
335*993b0882SAndroid Build Coastguard Worker   ASAN_POISON_MEMORY_REGION(block->mem, block->size);
336*993b0882SAndroid Build Coastguard Worker #endif
337*993b0882SAndroid Build Coastguard Worker   return block;
338*993b0882SAndroid Build Coastguard Worker }
339*993b0882SAndroid Build Coastguard Worker 
340*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
341*993b0882SAndroid Build Coastguard Worker // BaseArena::IndexToBlock()
342*993b0882SAndroid Build Coastguard Worker //    Index encoding is as follows:
343*993b0882SAndroid Build Coastguard Worker //    For blocks in the first_blocks_ array, we use index of the block in
344*993b0882SAndroid Build Coastguard Worker //    the array.
345*993b0882SAndroid Build Coastguard Worker //    For blocks in the overflow_blocks_ vector, we use the index of the
346*993b0882SAndroid Build Coastguard Worker //    block in iverflow_blocks_, plus the size of the first_blocks_ array.
347*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
348*993b0882SAndroid Build Coastguard Worker 
349*993b0882SAndroid Build Coastguard Worker const BaseArena::AllocatedBlock *BaseArena::IndexToBlock(int index) const {
350*993b0882SAndroid Build Coastguard Worker   if (index < TC3_ARRAYSIZE(first_blocks_)) {
351*993b0882SAndroid Build Coastguard Worker     return &first_blocks_[index];
352*993b0882SAndroid Build Coastguard Worker   }
353*993b0882SAndroid Build Coastguard Worker   TC3_CHECK(overflow_blocks_ != nullptr);
354*993b0882SAndroid Build Coastguard Worker   int index_in_overflow_blocks = index - TC3_ARRAYSIZE(first_blocks_);
355*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_GE(index_in_overflow_blocks, 0);
356*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_LT(static_cast<size_t>(index_in_overflow_blocks),
357*993b0882SAndroid Build Coastguard Worker            overflow_blocks_->size());
358*993b0882SAndroid Build Coastguard Worker   return &(*overflow_blocks_)[index_in_overflow_blocks];
359*993b0882SAndroid Build Coastguard Worker }
360*993b0882SAndroid Build Coastguard Worker 
361*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
362*993b0882SAndroid Build Coastguard Worker // BaseArena::GetMemoryFallback()
363*993b0882SAndroid Build Coastguard Worker //    We take memory out of our pool, aligned on the byte boundary
364*993b0882SAndroid Build Coastguard Worker //    requested.  If we don't have space in our current pool, we
365*993b0882SAndroid Build Coastguard Worker //    allocate a new block (wasting the remaining space in the
366*993b0882SAndroid Build Coastguard Worker //    current block) and give you that.  If your memory needs are
367*993b0882SAndroid Build Coastguard Worker //    too big for a single block, we make a special your-memory-only
368*993b0882SAndroid Build Coastguard Worker //    allocation -- this is equivalent to not using the arena at all.
369*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
370*993b0882SAndroid Build Coastguard Worker 
371*993b0882SAndroid Build Coastguard Worker void* BaseArena::GetMemoryFallback(const size_t size, const int alignment) {
372*993b0882SAndroid Build Coastguard Worker   if (0 == size) {
373*993b0882SAndroid Build Coastguard Worker     return nullptr;             // stl/stl_alloc.h says this is okay
374*993b0882SAndroid Build Coastguard Worker   }
375*993b0882SAndroid Build Coastguard Worker 
376*993b0882SAndroid Build Coastguard Worker   // alignment must be a positive power of 2.
377*993b0882SAndroid Build Coastguard Worker   TC3_CHECK(alignment > 0 && 0 == (alignment & (alignment - 1)));
378*993b0882SAndroid Build Coastguard Worker 
379*993b0882SAndroid Build Coastguard Worker   // If the object is more than a quarter of the block size, allocate
380*993b0882SAndroid Build Coastguard Worker   // it separately to avoid wasting too much space in leftover bytes.
381*993b0882SAndroid Build Coastguard Worker   if (block_size_ == 0 || size > block_size_/4) {
382*993b0882SAndroid Build Coastguard Worker     // Use a block separate from all other allocations; in particular
383*993b0882SAndroid Build Coastguard Worker     // we don't update last_alloc_ so you can't reclaim space on this block.
384*993b0882SAndroid Build Coastguard Worker     AllocatedBlock* b = AllocNewBlock(size, alignment);
385*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
386*993b0882SAndroid Build Coastguard Worker     ASAN_UNPOISON_MEMORY_REGION(b->mem, b->size);
387*993b0882SAndroid Build Coastguard Worker #endif
388*993b0882SAndroid Build Coastguard Worker     return b->mem;
389*993b0882SAndroid Build Coastguard Worker   }
390*993b0882SAndroid Build Coastguard Worker 
391*993b0882SAndroid Build Coastguard Worker   // Enforce alignment on freestart_ then check for adequate space,
392*993b0882SAndroid Build Coastguard Worker   // which may require starting a new block.
393*993b0882SAndroid Build Coastguard Worker   if (!SatisfyAlignment(alignment) || size > remaining_) {
394*993b0882SAndroid Build Coastguard Worker     MakeNewBlock(alignment);
395*993b0882SAndroid Build Coastguard Worker   }
396*993b0882SAndroid Build Coastguard Worker   TC3_CHECK_LE(size, remaining_);
397*993b0882SAndroid Build Coastguard Worker 
398*993b0882SAndroid Build Coastguard Worker   remaining_ -= size;
399*993b0882SAndroid Build Coastguard Worker   last_alloc_ = freestart_;
400*993b0882SAndroid Build Coastguard Worker   freestart_ += size;
401*993b0882SAndroid Build Coastguard Worker 
402*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
403*993b0882SAndroid Build Coastguard Worker   ASAN_UNPOISON_MEMORY_REGION(last_alloc_, size);
404*993b0882SAndroid Build Coastguard Worker #endif
405*993b0882SAndroid Build Coastguard Worker   return reinterpret_cast<void*>(last_alloc_);
406*993b0882SAndroid Build Coastguard Worker }
407*993b0882SAndroid Build Coastguard Worker 
408*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
409*993b0882SAndroid Build Coastguard Worker // BaseArena::ReturnMemoryFallback()
410*993b0882SAndroid Build Coastguard Worker // BaseArena::FreeBlocks()
411*993b0882SAndroid Build Coastguard Worker //    Unlike GetMemory(), which does actual work, ReturnMemory() is a
412*993b0882SAndroid Build Coastguard Worker //    no-op: we don't "free" memory until Reset() is called.  We do
413*993b0882SAndroid Build Coastguard Worker //    update some stats, though.  Note we do no checking that the
414*993b0882SAndroid Build Coastguard Worker //    pointer you pass in was actually allocated by us, or that it
415*993b0882SAndroid Build Coastguard Worker //    was allocated for the size you say, so be careful here!
416*993b0882SAndroid Build Coastguard Worker //       FreeBlocks() does the work for Reset(), actually freeing all
417*993b0882SAndroid Build Coastguard Worker //    memory allocated in one fell swoop.
418*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
419*993b0882SAndroid Build Coastguard Worker 
420*993b0882SAndroid Build Coastguard Worker void BaseArena::FreeBlocks() {
421*993b0882SAndroid Build Coastguard Worker   for ( int i = 1; i < blocks_alloced_; ++i ) {  // keep first block alloced
422*993b0882SAndroid Build Coastguard Worker     DeallocateBytes(first_blocks_[i].mem, first_blocks_[i].size,
423*993b0882SAndroid Build Coastguard Worker                     first_blocks_[i].alignment);
424*993b0882SAndroid Build Coastguard Worker     first_blocks_[i].mem = nullptr;
425*993b0882SAndroid Build Coastguard Worker     first_blocks_[i].size = 0;
426*993b0882SAndroid Build Coastguard Worker   }
427*993b0882SAndroid Build Coastguard Worker   blocks_alloced_ = 1;
428*993b0882SAndroid Build Coastguard Worker   if (overflow_blocks_ != nullptr) {
429*993b0882SAndroid Build Coastguard Worker     std::vector<AllocatedBlock>::iterator it;
430*993b0882SAndroid Build Coastguard Worker     for (it = overflow_blocks_->begin(); it != overflow_blocks_->end(); ++it) {
431*993b0882SAndroid Build Coastguard Worker       DeallocateBytes(it->mem, it->size, it->alignment);
432*993b0882SAndroid Build Coastguard Worker     }
433*993b0882SAndroid Build Coastguard Worker     delete overflow_blocks_;             // These should be used very rarely
434*993b0882SAndroid Build Coastguard Worker     overflow_blocks_ = nullptr;
435*993b0882SAndroid Build Coastguard Worker   }
436*993b0882SAndroid Build Coastguard Worker }
437*993b0882SAndroid Build Coastguard Worker 
438*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
439*993b0882SAndroid Build Coastguard Worker // BaseArena::AdjustLastAlloc()
440*993b0882SAndroid Build Coastguard Worker //    If you realize you didn't want your last alloc to be for
441*993b0882SAndroid Build Coastguard Worker //    the size you asked, after all, you can fix it by calling
442*993b0882SAndroid Build Coastguard Worker //    this.  We'll grow or shrink the last-alloc region if we
443*993b0882SAndroid Build Coastguard Worker //    can (we can always shrink, but we might not be able to
444*993b0882SAndroid Build Coastguard Worker //    grow if you want to grow too big.
445*993b0882SAndroid Build Coastguard Worker //      RETURNS true if we successfully modified the last-alloc
446*993b0882SAndroid Build Coastguard Worker //    region, false if the pointer you passed in wasn't actually
447*993b0882SAndroid Build Coastguard Worker //    the last alloc or if you tried to grow bigger than we could.
448*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
449*993b0882SAndroid Build Coastguard Worker 
450*993b0882SAndroid Build Coastguard Worker bool BaseArena::AdjustLastAlloc(void *last_alloc, const size_t newsize) {
451*993b0882SAndroid Build Coastguard Worker   // It's only legal to call this on the last thing you alloced.
452*993b0882SAndroid Build Coastguard Worker   if (last_alloc == nullptr || last_alloc != last_alloc_)  return false;
453*993b0882SAndroid Build Coastguard Worker   // last_alloc_ should never point into a "big" block, w/ size >= block_size_
454*993b0882SAndroid Build Coastguard Worker   assert(freestart_ >= last_alloc_ && freestart_ <= last_alloc_ + block_size_);
455*993b0882SAndroid Build Coastguard Worker   assert(remaining_ >= 0);   // should be: it's a size_t!
456*993b0882SAndroid Build Coastguard Worker   if (newsize > (freestart_ - last_alloc_) + remaining_)
457*993b0882SAndroid Build Coastguard Worker     return false;  // not enough room, even after we get back last_alloc_ space
458*993b0882SAndroid Build Coastguard Worker   const char* old_freestart = freestart_;   // where last alloc used to end
459*993b0882SAndroid Build Coastguard Worker   freestart_ = last_alloc_ + newsize;       // where last alloc ends now
460*993b0882SAndroid Build Coastguard Worker   remaining_ -= (freestart_ - old_freestart);  // how much new space we've taken
461*993b0882SAndroid Build Coastguard Worker 
462*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
463*993b0882SAndroid Build Coastguard Worker   ASAN_UNPOISON_MEMORY_REGION(last_alloc_, newsize);
464*993b0882SAndroid Build Coastguard Worker   ASAN_POISON_MEMORY_REGION(freestart_, remaining_);
465*993b0882SAndroid Build Coastguard Worker #endif
466*993b0882SAndroid Build Coastguard Worker   return true;
467*993b0882SAndroid Build Coastguard Worker }
468*993b0882SAndroid Build Coastguard Worker 
469*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
470*993b0882SAndroid Build Coastguard Worker // UnsafeArena::Realloc()
471*993b0882SAndroid Build Coastguard Worker // SafeArena::Realloc()
472*993b0882SAndroid Build Coastguard Worker //    If you decide you want to grow -- or shrink -- a memory region,
473*993b0882SAndroid Build Coastguard Worker //    we'll do it for you here.  Typically this will involve copying
474*993b0882SAndroid Build Coastguard Worker //    the existing memory to somewhere else on the arena that has
475*993b0882SAndroid Build Coastguard Worker //    more space reserved.  But if you're reallocing the last-allocated
476*993b0882SAndroid Build Coastguard Worker //    block, we may be able to accommodate you just by updating a
477*993b0882SAndroid Build Coastguard Worker //    pointer.  In any case, we return a pointer to the new memory
478*993b0882SAndroid Build Coastguard Worker //    location, which may be the same as the pointer you passed in.
479*993b0882SAndroid Build Coastguard Worker //       Here's an example of how you might use Realloc():
480*993b0882SAndroid Build Coastguard Worker //
481*993b0882SAndroid Build Coastguard Worker //    compr_buf = arena->Alloc(uncompr_size);  // get too-much space
482*993b0882SAndroid Build Coastguard Worker //    int compr_size;
483*993b0882SAndroid Build Coastguard Worker //    zlib.Compress(uncompr_buf, uncompr_size, compr_buf, &compr_size);
484*993b0882SAndroid Build Coastguard Worker //    compr_buf = arena->Realloc(compr_buf, uncompr_size, compr_size);
485*993b0882SAndroid Build Coastguard Worker // ----------------------------------------------------------------------
486*993b0882SAndroid Build Coastguard Worker 
487*993b0882SAndroid Build Coastguard Worker char* UnsafeArena::Realloc(char* original, size_t oldsize, size_t newsize) {
488*993b0882SAndroid Build Coastguard Worker   assert(oldsize >= 0 && newsize >= 0);
489*993b0882SAndroid Build Coastguard Worker   // if original happens to be the last allocation we can avoid fragmentation.
490*993b0882SAndroid Build Coastguard Worker   if (AdjustLastAlloc(original, newsize)) {
491*993b0882SAndroid Build Coastguard Worker     return original;
492*993b0882SAndroid Build Coastguard Worker   }
493*993b0882SAndroid Build Coastguard Worker 
494*993b0882SAndroid Build Coastguard Worker   char* resized = original;
495*993b0882SAndroid Build Coastguard Worker   if (newsize > oldsize) {
496*993b0882SAndroid Build Coastguard Worker     resized = Alloc(newsize);
497*993b0882SAndroid Build Coastguard Worker     memcpy(resized, original, oldsize);
498*993b0882SAndroid Build Coastguard Worker   } else {
499*993b0882SAndroid Build Coastguard Worker     // no need to do anything; we're ain't reclaiming any memory!
500*993b0882SAndroid Build Coastguard Worker   }
501*993b0882SAndroid Build Coastguard Worker 
502*993b0882SAndroid Build Coastguard Worker #ifdef ADDRESS_SANITIZER
503*993b0882SAndroid Build Coastguard Worker   // Alloc already returns unpoisoned memory, but handling both cases here
504*993b0882SAndroid Build Coastguard Worker   // allows us to poison the old memory without worrying about whether or not it
505*993b0882SAndroid Build Coastguard Worker   // overlaps with the new memory.  Thus, we must poison the old memory first.
506*993b0882SAndroid Build Coastguard Worker   ASAN_POISON_MEMORY_REGION(original, oldsize);
507*993b0882SAndroid Build Coastguard Worker   ASAN_UNPOISON_MEMORY_REGION(resized, newsize);
508*993b0882SAndroid Build Coastguard Worker #endif
509*993b0882SAndroid Build Coastguard Worker   return resized;
510*993b0882SAndroid Build Coastguard Worker }
511*993b0882SAndroid Build Coastguard Worker 
512*993b0882SAndroid Build Coastguard Worker // Avoid weak vtables by defining a dummy key method.
513*993b0882SAndroid Build Coastguard Worker void UnsafeArena::UnusedKeyMethod() {}
514*993b0882SAndroid Build Coastguard Worker 
515*993b0882SAndroid Build Coastguard Worker }  // namespace libtextclassifier3
516