aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkArenaAlloc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/SkArenaAlloc.cpp')
-rw-r--r--src/core/SkArenaAlloc.cpp148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/core/SkArenaAlloc.cpp b/src/core/SkArenaAlloc.cpp
new file mode 100644
index 0000000000..d6c249b573
--- /dev/null
+++ b/src/core/SkArenaAlloc.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <algorithm>
+#include "SkArenaAlloc.h"
+
+struct Skipper {
+ char* operator()(char* objEnd, ptrdiff_t size) { return objEnd + size; }
+};
+
+struct NextBlock {
+ char* operator()(char* objEnd, ptrdiff_t size) { delete [] objEnd; return objEnd + size; }
+};
+
+SkArenaAlloc::SkArenaAlloc(char* block, size_t size, size_t extraSize)
+ : fDtorCursor{block}
+ , fCursor {block}
+ , fEnd {block + size}
+ , fExtraSize {extraSize}
+{
+ if (size < sizeof(Footer)) {
+ fEnd = fCursor = fDtorCursor = nullptr;
+ }
+
+ if (fCursor != nullptr) {
+ this->installFooter(EndChain, 0);
+ }
+}
+
+SkArenaAlloc::~SkArenaAlloc() {
+ this->reset();
+}
+
+void SkArenaAlloc::reset() {
+ Footer f;
+ memmove(&f, fDtorCursor - sizeof(Footer), sizeof(Footer));
+ char* releaser = fDtorCursor;
+ while (releaser != nullptr) {
+ releaser = this->callFooterAction(releaser);
+ }
+}
+
+void SkArenaAlloc::installFooter(FooterAction* releaser, ptrdiff_t padding) {
+ ptrdiff_t releaserDiff = (char *)releaser - (char *)EndChain;
+ ptrdiff_t footerData = SkLeftShift((int64_t)releaserDiff, 5) | padding;
+ if (padding >= 32 || !SkTFitsIn<int32_t>(footerData)) {
+ // Footer data will not fit.
+ SkFAIL("Constraints are busted.");
+ }
+
+ Footer footer = (Footer)(footerData);
+ memmove(fCursor, &footer, sizeof(Footer));
+ Footer check;
+ memmove(&check, fCursor, sizeof(Footer));
+ fCursor += sizeof(Footer);
+ fDtorCursor = fCursor;
+}
+
+void SkArenaAlloc::ensureSpace(size_t size, size_t alignment) {
+ constexpr size_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 size_t alignof_max_align_t = 8;
+ auto objSizeAndOverhead = size + headerSize + sizeof(Footer);
+ if (alignment > alignof_max_align_t) {
+ objSizeAndOverhead += alignment - 1;
+ }
+
+ auto allocationSize = std::max(objSizeAndOverhead, fExtraSize);
+
+ // 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.
+ {
+ size_t mask = allocationSize > (1 << 15) ? (1 << 12) - 1 : 32 - 1;
+ allocationSize = (allocationSize + mask) & ~mask;
+ }
+
+ char* newBlock = new char[allocationSize];
+
+ auto previousDtor = fDtorCursor;
+ fCursor = newBlock;
+ fDtorCursor = newBlock;
+ fEnd = fCursor + allocationSize;
+ this->installIntFooter<NextBlock>(previousDtor - fCursor, 0);
+}
+
+char* SkArenaAlloc::allocObject(size_t size, size_t alignment) {
+ size_t mask = alignment - 1;
+ char* objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
+ if (objStart + size > fEnd) {
+ this->ensureSpace(size, alignment);
+ objStart = (char*)((uintptr_t)(fCursor + mask) & ~mask);
+ }
+ return objStart;
+}
+
+// * sizeAndFooter - the memory for the footer in addition to the size for the object.
+// * alignment - alignment needed by the object.
+char* SkArenaAlloc::allocObjectWithFooter(size_t sizeIncludingFooter, size_t alignment) {
+ size_t mask = alignment - 1;
+
+ restart:
+ size_t skipOverhead = 0;
+ bool needsSkipFooter = fCursor != fDtorCursor;
+ if (needsSkipFooter) {
+ size_t skipSize = SkTFitsIn<int32_t>(fDtorCursor - fCursor)
+ ? sizeof(int32_t)
+ : sizeof(ptrdiff_t);
+ skipOverhead = sizeof(Footer) + skipSize;
+ }
+ char* objStart = (char*)((uintptr_t)(fCursor + skipOverhead + mask) & ~mask);
+ size_t totalSize = sizeIncludingFooter + skipOverhead;
+
+ if (objStart + totalSize > fEnd) {
+ this->ensureSpace(totalSize, alignment);
+ goto restart;
+ }
+
+ SkASSERT(objStart + totalSize <= fEnd);
+
+ // 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->installIntFooter<Skipper>(fDtorCursor - fCursor, 0);
+ }
+
+ return objStart;
+}
+
+char* SkArenaAlloc::callFooterAction(char* end) {
+ Footer footer;
+ memcpy(&footer, end - sizeof(Footer), sizeof(Footer));
+
+ FooterAction* releaser = (FooterAction*)((char*)EndChain + (footer >> 5));
+ ptrdiff_t padding = footer & 31;
+
+ char* r = releaser(end) - padding;
+
+ return r;
+}
+
+char* SkArenaAlloc::EndChain(char*) { return nullptr; }
+