/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#ifndef mozilla_ArenaAllocator_h#define mozilla_ArenaAllocator_h#include<algorithm>#include<cstdint>#include<sstream>#include"mozilla/Assertions.h"#include"mozilla/fallible.h"#include"mozilla/Likely.h"#include"mozilla/MemoryChecking.h"#include"mozilla/MemoryReporting.h"#include"mozilla/OperatorNewExtensions.h"#include"mozilla/Poison.h"#include"mozilla/TemplateLib.h"#include"nsDebug.h"namespacemozilla{/** * A very simple arena allocator based on NSPR's PLArena. * * The arena allocator only provides for allocation, all memory is retained * until the allocator is destroyed. It's useful for situations where a large * amount of small transient allocations are expected. * * Example usage: * * // Define an allocator that is page sized and returns allocations that are * // 8-byte aligned. * ArenaAllocator<4096, 8> a; * for (int i = 0; i < 1000; i++) { * DoSomething(a.Allocate(i)); * } */template<size_tArenaSize,size_tAlignment=1>classArenaAllocator{public:constexprArenaAllocator():mHead(),mCurrent(nullptr){static_assert(mozilla::tl::FloorLog2<Alignment>::value==mozilla::tl::CeilingLog2<Alignment>::value,"ArenaAllocator alignment must be a power of two");}ArenaAllocator(constArenaAllocator&)=delete;ArenaAllocator&operator=(constArenaAllocator&)=delete;/** * Frees all internal arenas but does not call destructors for objects * allocated out of the arena. */~ArenaAllocator(){Clear();}/** * Fallibly allocates a chunk of memory with the given size from the internal * arenas. If the allocation size is larger than the chosen arena a size an * entire arena is allocated and used. */MOZ_ALWAYS_INLINEvoid*Allocate(size_taSize,constfallible_t&){MOZ_RELEASE_ASSERT(aSize,"Allocation size must be non-zero");returnInternalAllocate(AlignedSize(aSize));}void*Allocate(size_taSize){void*p=Allocate(aSize,fallible);if(MOZ_UNLIKELY(!p)){NS_ABORT_OOM(std::max(aSize,ArenaSize));}returnp;}/** * Frees all entries. The allocator can be reused after this is called. * * NB: This will not run destructors of any objects that were allocated from * the arena. */voidClear(){// Free all chunks.autoa=mHead.next;while(a){autotmp=a;a=a->next;free(tmp);}// Reset the list head.mHead.next=nullptr;mCurrent=nullptr;}/** * Adjusts the given size to the required alignment. */staticconstexprsize_tAlignedSize(size_taSize){return(aSize+(Alignment-1))&~(Alignment-1);}size_tSizeOfExcludingThis(MallocSizeOfaMallocSizeOf)const{size_ts=0;for(autoarena=mHead.next;arena;arena=arena->next){s+=aMallocSizeOf(arena);}returns;}voidCheck(){if(mCurrent){mCurrent->canary.Check();}}private:structArenaHeader{/** * The location in memory of the data portion of the arena. */uintptr_toffset;/** * The location in memory of the end of the data portion of the arena. */uintptr_ttail;};structArenaChunk{constexprArenaChunk():header{0,0},next(nullptr){}explicitArenaChunk(size_taSize):header{AlignedSize(uintptr_t(this+1)),uintptr_t(this)+aSize},next(nullptr){}CorruptionCanarycanary;ArenaHeaderheader;ArenaChunk*next;/** * Allocates a chunk of memory out of the arena and advances the offset. */void*Allocate(size_taSize){MOZ_ASSERT(aSize<=Available());char*p=reinterpret_cast<char*>(header.offset);MOZ_RELEASE_ASSERT(p);header.offset+=aSize;canary.Check();MOZ_MAKE_MEM_UNDEFINED(p,aSize);returnp;}/** * Calculates the amount of space available for allocation in this chunk. */size_tAvailable()const{returnheader.tail-header.offset;}};/** * Allocates an arena chunk of the given size and initializes its header. */ArenaChunk*AllocateChunk(size_taSize){staticconstsize_tkOffset=AlignedSize(sizeof(ArenaChunk));MOZ_ASSERT(kOffset<aSize);constsize_tchunkSize=aSize+kOffset;void*p=malloc(chunkSize);if(!p){returnnullptr;}ArenaChunk*arena=new(KnownNotNull,p)ArenaChunk(chunkSize);MOZ_MAKE_MEM_NOACCESS((void*)arena->header.offset,arena->header.tail-arena->header.offset);// Insert into the head of the list.arena->next=mHead.next;mHead.next=arena;// Only update |mCurrent| if this is a standard allocation, large// allocations will always end up full so there's no point in updating// |mCurrent| in that case.if(aSize==ArenaSize-kOffset){mCurrent=arena;}returnarena;}MOZ_ALWAYS_INLINEvoid*InternalAllocate(size_taSize){static_assert(ArenaSize>AlignedSize(sizeof(ArenaChunk)),"Arena size must be greater than the header size");staticconstsize_tkMaxArenaCapacity=ArenaSize-AlignedSize(sizeof(ArenaChunk));if(mCurrent&&aSize<=mCurrent->Available()){returnmCurrent->Allocate(aSize);}ArenaChunk*arena=AllocateChunk(std::max(kMaxArenaCapacity,aSize));returnarena?arena->Allocate(aSize):nullptr;}ArenaChunkmHead;ArenaChunk*mCurrent;};}// namespace mozilla#endif // mozilla_ArenaAllocator_h