--- /dev/null
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "varenaalloc.h"
+#include <algorithm>
+#include <new>
+
+static char* end_chain(char*) { return nullptr; }
+
+static uint32_t first_allocated_block(uint32_t blockSize, uint32_t firstHeapAllocation) {
+ return firstHeapAllocation > 0 ? firstHeapAllocation :
+ blockSize > 0 ? blockSize : 1024;
+}
+
+VArenaAlloc::VArenaAlloc(char* block, size_t size, size_t firstHeapAllocation)
+ : fDtorCursor {block}
+ , fCursor {block}
+ , fEnd {block + ToU32(size)}
+ , fFirstBlock {block}
+ , fFirstSize {ToU32(size)}
+ , fFirstHeapAllocationSize {first_allocated_block(ToU32(size), ToU32(firstHeapAllocation))}
+{
+ if (size < sizeof(Footer)) {
+ fEnd = fCursor = fDtorCursor = nullptr;
+ }
+
+ if (fCursor != nullptr) {
+ this->installFooter(end_chain, 0);
+ }
+}
+
+VArenaAlloc::~VArenaAlloc() {
+ RunDtorsOnBlock(fDtorCursor);
+}
+
+void VArenaAlloc::reset() {
+ this->~VArenaAlloc();
+ new (this) VArenaAlloc{fFirstBlock, fFirstSize, fFirstHeapAllocationSize};
+}
+
+void VArenaAlloc::installFooter(FooterAction* action, uint32_t padding) {
+ assert(padding < 64);
+ int64_t actionInt = (int64_t)(intptr_t)action;
+
+ // The top 14 bits should be either all 0s or all 1s. Check this.
+ assert((actionInt << 6) >> 6 == actionInt);
+ Footer encodedFooter = (actionInt << 6) | padding;
+ memmove(fCursor, &encodedFooter, sizeof(Footer));
+ fCursor += sizeof(Footer);
+ fDtorCursor = fCursor;
+}
+
+void VArenaAlloc::installPtrFooter(FooterAction* action, char* ptr, uint32_t padding) {
+ memmove(fCursor, &ptr, sizeof(char*));
+ fCursor += sizeof(char*);
+ this->installFooter(action, padding);
+}
+
+char* VArenaAlloc::SkipPod(char* footerEnd) {
+ char* objEnd = footerEnd - (sizeof(Footer) + sizeof(int32_t));
+ int32_t skip;
+ memmove(&skip, objEnd, sizeof(int32_t));
+ return objEnd - skip;
+}
+
+void VArenaAlloc::RunDtorsOnBlock(char* footerEnd) {
+ while (footerEnd != nullptr) {
+ Footer footer;
+ memcpy(&footer, footerEnd - sizeof(Footer), sizeof(Footer));
+
+ FooterAction* action = (FooterAction*)(footer >> 6);
+ ptrdiff_t padding = footer & 63;
+
+ footerEnd = action(footerEnd) - padding;
+ }
+}
+
+char* VArenaAlloc::NextBlock(char* footerEnd) {
+ char* objEnd = footerEnd - (sizeof(Footer) + sizeof(char*));
+ char* next;
+ memmove(&next, objEnd, sizeof(char*));
+ RunDtorsOnBlock(next);
+ delete [] objEnd;
+ return nullptr;
+}
+
+void VArenaAlloc::installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding) {
+ memmove(fCursor, &value, sizeof(uint32_t));
+ fCursor += sizeof(uint32_t);
+ this->installFooter(action, padding);
+}
+
+void VArenaAlloc::ensureSpace(uint32_t size, uint32_t alignment) {
+ constexpr uint32_t headerSize = sizeof(Footer) + sizeof(ptrdiff_t);
+ // The chrome c++ library we use does not define std::max_align_t.
+ // This must be conservative to add the right amount of extra memory to handle the alignment
+ // padding.
+ constexpr uint32_t alignof_max_align_t = 8;
+ constexpr uint32_t maxSize = std::numeric_limits<uint32_t>::max();
+ constexpr uint32_t overhead = headerSize + sizeof(Footer);
+ AssertRelease(size <= maxSize - overhead);
+ uint32_t objSizeAndOverhead = size + overhead;
+ if (alignment > alignof_max_align_t) {
+ uint32_t alignmentOverhead = alignment - 1;
+ AssertRelease(objSizeAndOverhead <= maxSize - alignmentOverhead);
+ objSizeAndOverhead += alignmentOverhead;
+ }
+
+ uint32_t minAllocationSize;
+ if (fFirstHeapAllocationSize <= maxSize / fFib0) {
+ minAllocationSize = fFirstHeapAllocationSize * fFib0;
+ fFib0 += fFib1;
+ std::swap(fFib0, fFib1);
+ } else {
+ minAllocationSize = maxSize;
+ }
+ uint32_t allocationSize = std::max(objSizeAndOverhead, minAllocationSize);
+
+ // Round up to a nice size. If > 32K align to 4K boundary else up to max_align_t. The > 32K
+ // heuristic is from the JEMalloc behavior.
+ {
+ uint32_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 16 - 1;
+ AssertRelease(allocationSize <= maxSize - mask);
+ allocationSize = (allocationSize + mask) & ~mask;
+ }
+
+ char* newBlock = new char[allocationSize];
+
+ auto previousDtor = fDtorCursor;
+ fCursor = newBlock;
+ fDtorCursor = newBlock;
+ fEnd = fCursor + allocationSize;
+ this->installPtrFooter(NextBlock, previousDtor, 0);
+}
+
+char* VArenaAlloc::allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment) {
+ uintptr_t mask = alignment - 1;
+
+restart:
+ uint32_t skipOverhead = 0;
+ bool needsSkipFooter = fCursor != fDtorCursor;
+ if (needsSkipFooter) {
+ skipOverhead = sizeof(Footer) + sizeof(uint32_t);
+ }
+ char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
+ uint32_t totalSize = sizeIncludingFooter + skipOverhead;
+ //std::cout<<"non POD object size = "<<totalSize<<"\n";
+ if ((ptrdiff_t)totalSize > fEnd - objStart) {
+ this->ensureSpace(totalSize, alignment);
+ goto restart;
+ }
+
+ AssertRelease((ptrdiff_t)totalSize <= fEnd - objStart);
+
+ // Install a skip footer if needed, thus terminating a run of POD data. The calling code is
+ // responsible for installing the footer after the object.
+ if (needsSkipFooter) {
+ this->installUint32Footer(SkipPod, ToU32(fCursor - fDtorCursor), 0);
+ }
+
+ return objStart;
+}
--- /dev/null
+/*
+ * 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 VARENAALLOC_H
+#define VARENAALLOC_H
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <new>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+// SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed
+// to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an
+// (optional) user-provided block of memory, and when that's exhausted it allocates on the heap,
+// starting with an allocation of firstHeapAllocation bytes. If your data (plus a small overhead)
+// fits in the user-provided block, SkArenaAlloc never uses the heap, and if it fits in
+// firstHeapAllocation bytes, it'll use the heap only once. If 0 is specified for
+// firstHeapAllocation, then blockSize is used unless that too is 0, then 1024 is used.
+//
+// Examples:
+//
+// char block[mostCasesSize];
+// SkArenaAlloc arena(block, mostCasesSize);
+//
+// If mostCasesSize is too large for the stack, you can use the following pattern.
+//
+// std::unique_ptr<char[]> block{new char[mostCasesSize]};
+// SkArenaAlloc arena(block.get(), mostCasesSize, almostAllCasesSize);
+//
+// If the program only sometimes allocates memory, use the following pattern.
+//
+// SkArenaAlloc arena(nullptr, 0, almostAllCasesSize);
+//
+// The storage does not necessarily need to be on the stack. Embedding the storage in a class also
+// works.
+//
+// class Foo {
+// char storage[mostCasesSize];
+// SkArenaAlloc arena (storage, mostCasesSize);
+// };
+//
+// In addition, the system is optimized to handle POD data including arrays of PODs (where
+// POD is really data with no destructors). For POD data it has zero overhead per item, and a
+// typical per block overhead of 8 bytes. For non-POD objects there is a per item overhead of 4
+// bytes. For arrays of non-POD objects there is a per array overhead of typically 8 bytes. There
+// is an addition overhead when switching from POD data to non-POD data of typically 8 bytes.
+//
+// If additional blocks are needed they are increased exponentially. This strategy bounds the
+// recursion of the RunDtorsOnBlock to be limited to O(log size-of-memory). Block size grow using
+// the Fibonacci sequence which means that for 2^32 memory there are 48 allocations, and for 2^48
+// there are 71 allocations.
+class VArenaAlloc {
+public:
+ VArenaAlloc(char* block, size_t blockSize, size_t firstHeapAllocation);
+
+ explicit VArenaAlloc(size_t firstHeapAllocation)
+ : VArenaAlloc(nullptr, 0, firstHeapAllocation)
+ {}
+
+ ~VArenaAlloc();
+
+ template <typename T, typename... Args>
+ T* make(Args&&... args) {
+ uint32_t size = ToU32(sizeof(T));
+ uint32_t alignment = ToU32(alignof(T));
+ char* objStart;
+ if (std::is_trivially_destructible<T>::value) {
+ objStart = this->allocObject(size, alignment);
+ fCursor = objStart + size;
+ } else {
+ objStart = this->allocObjectWithFooter(size + sizeof(Footer), alignment);
+ // Can never be UB because max value is alignof(T).
+ uint32_t padding = ToU32(objStart - fCursor);
+
+ // Advance to end of object to install footer.
+ fCursor = objStart + size;
+ FooterAction* releaser = [](char* objEnd) {
+ char* objStart = objEnd - (sizeof(T) + sizeof(Footer));
+ ((T*)objStart)->~T();
+ return objStart;
+ };
+ this->installFooter(releaser, padding);
+ }
+
+ // This must be last to make objects with nested use of this allocator work.
+ return new(objStart) T(std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ T* makeArrayDefault(size_t count) {
+ uint32_t safeCount = ToU32(count);
+ T* array = (T*)this->commonArrayAlloc<T>(safeCount);
+
+ // If T is primitive then no initialization takes place.
+ for (size_t i = 0; i < safeCount; i++) {
+ new (&array[i]) T;
+ }
+ return array;
+ }
+
+ template <typename T>
+ T* makeArray(size_t count) {
+ uint32_t safeCount = ToU32(count);
+ T* array = (T*)this->commonArrayAlloc<T>(safeCount);
+
+ // If T is primitive then the memory is initialized. For example, an array of chars will
+ // be zeroed.
+ for (size_t i = 0; i < safeCount; i++) {
+ new (&array[i]) T();
+ }
+ return array;
+ }
+
+ // Only use makeBytesAlignedTo if none of the typed variants are impractical to use.
+ void* makeBytesAlignedTo(size_t size, size_t align) {
+ auto objStart = this->allocObject(ToU32(size), ToU32(align));
+ fCursor = objStart + size;
+ return objStart;
+ }
+
+ // Destroy all allocated objects, free any heap allocations.
+ void reset();
+
+private:
+ static void AssertRelease(bool cond) { if (!cond) { ::abort(); } }
+ static uint32_t ToU32(size_t v) {
+ return (uint32_t)v;
+ }
+
+ using Footer = int64_t;
+ using FooterAction = char* (char*);
+
+ static char* SkipPod(char* footerEnd);
+ static void RunDtorsOnBlock(char* footerEnd);
+ static char* NextBlock(char* footerEnd);
+
+ void installFooter(FooterAction* releaser, uint32_t padding);
+ void installUint32Footer(FooterAction* action, uint32_t value, uint32_t padding);
+ void installPtrFooter(FooterAction* action, char* ptr, uint32_t padding);
+
+ void ensureSpace(uint32_t size, uint32_t alignment);
+
+ char* allocObject(uint32_t size, uint32_t alignment) {
+ uintptr_t mask = alignment - 1;
+ uintptr_t alignedOffset = (~reinterpret_cast<uintptr_t>(fCursor) + 1) & mask;
+ uintptr_t totalSize = size + alignedOffset;
+ AssertRelease(totalSize >= size);
+
+ if (totalSize > static_cast<uintptr_t>(fEnd - fCursor)) {
+ this->ensureSpace(size, alignment);
+ alignedOffset = (~reinterpret_cast<uintptr_t>(fCursor) + 1) & mask;
+ }
+ return fCursor + alignedOffset;
+ }
+
+ char* allocObjectWithFooter(uint32_t sizeIncludingFooter, uint32_t alignment);
+
+ template <typename T>
+ char* commonArrayAlloc(uint32_t count) {
+ char* objStart;
+ AssertRelease(count <= std::numeric_limits<uint32_t>::max() / sizeof(T));
+ uint32_t arraySize = ToU32(count * sizeof(T));
+ uint32_t alignment = ToU32(alignof(T));
+
+ if (std::is_trivially_destructible<T>::value) {
+ objStart = this->allocObject(arraySize, alignment);
+ fCursor = objStart + arraySize;
+ } else {
+ constexpr uint32_t overhead = sizeof(Footer) + sizeof(uint32_t);
+ AssertRelease(arraySize <= std::numeric_limits<uint32_t>::max() - overhead);
+ uint32_t totalSize = arraySize + overhead;
+ objStart = this->allocObjectWithFooter(totalSize, alignment);
+
+ // Can never be UB because max value is alignof(T).
+ uint32_t padding = ToU32(objStart - fCursor);
+
+ // Advance to end of array to install footer.?
+ fCursor = objStart + arraySize;
+ this->installUint32Footer(
+ [](char* footerEnd) {
+ char* objEnd = footerEnd - (sizeof(Footer) + sizeof(uint32_t));
+ uint32_t count;
+ memmove(&count, objEnd, sizeof(uint32_t));
+ char* objStart = objEnd - count * sizeof(T);
+ T* array = (T*) objStart;
+ for (uint32_t i = 0; i < count; i++) {
+ array[i].~T();
+ }
+ return objStart;
+ },
+ ToU32(count),
+ padding);
+ }
+
+ return objStart;
+ }
+
+ char* fDtorCursor;
+ char* fCursor;
+ char* fEnd;
+ char* const fFirstBlock;
+ const uint32_t fFirstSize;
+ const uint32_t fFirstHeapAllocationSize;
+
+ // Use the Fibonacci sequence as the growth factor for block size. The size of the block
+ // allocated is fFib0 * fFirstHeapAllocationSize. Using 2 ^ n * fFirstHeapAllocationSize
+ // had too much slop for Android.
+ uint32_t fFib0 {1}, fFib1 {1};
+};
+
+// Helper for defining allocators with inline/reserved storage.
+// For argument declarations, stick to the base type (SkArenaAlloc).
+template <size_t InlineStorageSize>
+class VSTArenaAlloc : public VArenaAlloc {
+public:
+ explicit VSTArenaAlloc(size_t firstHeapAllocation = InlineStorageSize)
+ : VArenaAlloc(fInlineStorage, InlineStorageSize, firstHeapAllocation) {}
+
+private:
+ char fInlineStorage[InlineStorageSize];
+};
+
+#endif // VARENAALLOC_H