/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkFixedAlloc_DEFINED #define SkFixedAlloc_DEFINED #include "SkTFitsIn.h" #include "SkTypes.h" #include #include #include // SkFixedAlloc allocates objects out of a fixed-size buffer and destroys them when destroyed. class SkFixedAlloc { public: SkFixedAlloc(void* ptr, size_t len); ~SkFixedAlloc() { this->reset(); } // Allocates a new T in the buffer if possible. If not, returns nullptr. // Assumptions: // * max alignment value is 32 - if alignment is greater than 32, the allocation is best effort. // * footer is 32 bits - 5 bits of alignment and 27 bits of deleter difference from Base. // * deleter difference - the difference D is -2^26 <= D < 2^26. template T* make(Args&&... args) { auto mask = alignof(T) - 1; // Align fCursor for this allocation. char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask); ptrdiff_t padding = objStart - fCursor; Releaser releaser = [](char* objEnd) { char* objStart = objEnd - (sizeof(T) + sizeof(Footer)); ((T*)objStart)->~T(); return objStart; }; ptrdiff_t deleterDiff = (char*)releaser - (char*)Base; if (objStart + sizeof(T) + sizeof(Footer) > fEnd || padding >= 32 || deleterDiff >= (1 << 26) || deleterDiff < -(1 << 26)) { // Ran out of space, or code not store info in the Footer. return nullptr; } // Advance cursor to end of the object. fCursor = objStart + sizeof(T); Footer footer = (Footer)(SkLeftShift((int64_t)deleterDiff, 5) | padding); memcpy(fCursor, &footer, sizeof(Footer)); fCursor += sizeof(Footer); return new (objStart) T(std::forward(args)...); } // Destroys the last object allocated and frees its space in the buffer. void undo(); // Destroys all objects and frees all space in the buffer. void reset(); private: using Footer = int32_t; using Releaser = char*(*)(char*); // A function pointer to use for offsets of releasers. static void Base(); char* const fStorage; char* fCursor; char* const fEnd; }; class SkFallbackAlloc { public: explicit SkFallbackAlloc(SkFixedAlloc*); ~SkFallbackAlloc() { this->reset(); } // Allocates a new T with the SkFixedAlloc if possible. If not, uses the heap. template T* make(Args&&... args) { // Once we go heap we never go back to fixed. This keeps destructor ordering sane. if (fHeapAllocs.empty()) { if (T* ptr = fFixedAlloc->make(std::forward(args)...)) { return ptr; } } char* ptr = new char[sizeof(T)]; fHeapAllocs.push_back({[](char* ptr) { ((T*)ptr)->~T(); delete [] ptr; }, ptr}); return new (ptr) T(std::forward(args)...); } // Destroys the last object allocated and frees any space it used in the SkFixedAlloc. void undo(); // Destroys all objects and frees all space in the SkFixedAlloc. void reset(); private: struct HeapAlloc { void (*deleter)(char*); char* ptr; }; SkFixedAlloc* fFixedAlloc; std::vector fHeapAllocs; }; #endif//SkFixedAlloc_DEFINED