aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkArenaAlloc.cpp
blob: d6c249b57390936a259bedfcd663cda245d7a8f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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; }