Returns an allocation that is at least 'size'. If the current allocation backing 'ptr' is big enough, 'ptr' is returned. Otherwise a new one is made and the contents of ptr are copied into it. nullptr will be returned on allocation failure. It's the caller's responsibility to free the memory buffer pointed to by "ptr" in this case.
| 123 | /// nullptr will be returned on allocation failure. It's the caller's responsibility to |
| 124 | /// free the memory buffer pointed to by "ptr" in this case. |
| 125 | uint8_t* Reallocate(uint8_t* ptr, int64_t size) { |
| 126 | if (UNLIKELY(ptr == nullptr || ptr == mem_pool_->EmptyAllocPtr())) return Allocate(size); |
| 127 | FreeListNode* node = reinterpret_cast<FreeListNode*>(ptr - sizeof(FreeListNode)); |
| 128 | FreeListNode* list = node->list; |
| 129 | #ifndef NDEBUG |
| 130 | CheckValidAllocation(list, ptr); |
| 131 | #endif |
| 132 | int bucket_idx = (list - &lists_[0]); |
| 133 | DCHECK_LT(bucket_idx, NUM_LISTS); |
| 134 | // This is the actual size of ptr. |
| 135 | int64_t allocation_size = 1LL << bucket_idx; |
| 136 | |
| 137 | // If it's already big enough, just return the ptr. |
| 138 | if (allocation_size >= size) { |
| 139 | // Ensure that only first size bytes are unpoisoned. Need to poison whole region |
| 140 | // first in case size is smaller than original allocation's size. |
| 141 | #ifdef ADDRESS_SANITIZER |
| 142 | DCHECK(alloc_to_size_.find(ptr) != alloc_to_size_.end()); |
| 143 | int64_t prev_allocation_size = alloc_to_size_[ptr]; |
| 144 | if (prev_allocation_size > size) { |
| 145 | // Allocation is shrinking: poison the 'freed' bytes. |
| 146 | ASAN_POISON_MEMORY_REGION(ptr + size, prev_allocation_size - size); |
| 147 | } else { |
| 148 | // Allocation is growing: unpoison the newly allocated bytes. |
| 149 | ASAN_UNPOISON_MEMORY_REGION( |
| 150 | ptr + prev_allocation_size, size - prev_allocation_size); |
| 151 | } |
| 152 | alloc_to_size_[ptr] = size; |
| 153 | #endif |
| 154 | return ptr; |
| 155 | } |
| 156 | |
| 157 | // Make a new one. Since Allocate() already rounds up to powers of 2, this effectively |
| 158 | // doubles for the caller. |
| 159 | uint8_t* new_ptr = Allocate(size); |
| 160 | if (LIKELY(new_ptr != nullptr)) { |
| 161 | #ifdef ADDRESS_SANITIZER |
| 162 | DCHECK(alloc_to_size_.find(ptr) != alloc_to_size_.end()); |
| 163 | // Unpoison the region so that we can copy the old allocation to the new one. |
| 164 | ASAN_UNPOISON_MEMORY_REGION(ptr, allocation_size); |
| 165 | #endif |
| 166 | memcpy(new_ptr, ptr, allocation_size); |
| 167 | Free(ptr); |
| 168 | } |
| 169 | return new_ptr; |
| 170 | } |
| 171 | |
| 172 | MemTracker* mem_tracker() { return mem_pool_->mem_tracker(); } |
| 173 | int64_t net_allocations() const { return net_allocations_; } |