blob: f969986713f95fcc462bb5e57b7db2586d697764 [file] [log] [blame]
/*
* 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 <new>
#include <utility>
#include <vector>
// 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 <typename T, typename... Args>
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>(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 <typename T, typename... Args>
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<T>(std::forward<Args>(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>(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<HeapAlloc> fHeapAllocs;
};
#endif//SkFixedAlloc_DEFINED