1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #pragma once
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
20*d57664e9SAndroid Build Coastguard Worker #include <array>
21*d57664e9SAndroid Build Coastguard Worker #include <cinttypes>
22*d57664e9SAndroid Build Coastguard Worker #include <cstddef>
23*d57664e9SAndroid Build Coastguard Worker #include <cstdlib>
24*d57664e9SAndroid Build Coastguard Worker #include <type_traits>
25*d57664e9SAndroid Build Coastguard Worker #include <utility>
26*d57664e9SAndroid Build Coastguard Worker
27*d57664e9SAndroid Build Coastguard Worker namespace android::uirenderer {
28*d57664e9SAndroid Build Coastguard Worker
29*d57664e9SAndroid Build Coastguard Worker template <typename T>
30*d57664e9SAndroid Build Coastguard Worker struct OpBufferItemHeader {
31*d57664e9SAndroid Build Coastguard Worker T type : 8;
32*d57664e9SAndroid Build Coastguard Worker uint32_t size : 24;
33*d57664e9SAndroid Build Coastguard Worker };
34*d57664e9SAndroid Build Coastguard Worker
35*d57664e9SAndroid Build Coastguard Worker struct OpBufferAllocationHeader {
36*d57664e9SAndroid Build Coastguard Worker // Used size, including header size
37*d57664e9SAndroid Build Coastguard Worker size_t used = 0;
38*d57664e9SAndroid Build Coastguard Worker // Capacity, including header size
39*d57664e9SAndroid Build Coastguard Worker size_t capacity = 0;
40*d57664e9SAndroid Build Coastguard Worker // Offset relative to `this` at which the first item is
41*d57664e9SAndroid Build Coastguard Worker size_t startOffset = 0;
42*d57664e9SAndroid Build Coastguard Worker // Offset relative to `this` at which the last item is
43*d57664e9SAndroid Build Coastguard Worker size_t endOffset = 0;
44*d57664e9SAndroid Build Coastguard Worker };
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker #define BE_OPBUFFERS_FRIEND() \
47*d57664e9SAndroid Build Coastguard Worker template <typename ItemTypes, template <ItemTypes> typename, typename, typename> \
48*d57664e9SAndroid Build Coastguard Worker friend class OpBuffer
49*d57664e9SAndroid Build Coastguard Worker
50*d57664e9SAndroid Build Coastguard Worker template <typename ItemTypes, template <ItemTypes> typename ItemContainer,
51*d57664e9SAndroid Build Coastguard Worker typename BufferHeader = OpBufferAllocationHeader,
52*d57664e9SAndroid Build Coastguard Worker typename ItemTypesSequence = std::make_index_sequence<static_cast<int>(ItemTypes::COUNT)>>
53*d57664e9SAndroid Build Coastguard Worker class OpBuffer {
54*d57664e9SAndroid Build Coastguard Worker // Instead of re-aligning individual inserts, just pad the size of everything
55*d57664e9SAndroid Build Coastguard Worker // to a multiple of pointer alignment. This assumes we never work with doubles.
56*d57664e9SAndroid Build Coastguard Worker // Which we don't.
57*d57664e9SAndroid Build Coastguard Worker static constexpr size_t Alignment = alignof(void*);
58*d57664e9SAndroid Build Coastguard Worker
PadAlign(size_t size)59*d57664e9SAndroid Build Coastguard Worker static constexpr size_t PadAlign(size_t size) {
60*d57664e9SAndroid Build Coastguard Worker return (size + (Alignment - 1)) & -Alignment;
61*d57664e9SAndroid Build Coastguard Worker }
62*d57664e9SAndroid Build Coastguard Worker
63*d57664e9SAndroid Build Coastguard Worker public:
64*d57664e9SAndroid Build Coastguard Worker static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
65*d57664e9SAndroid Build Coastguard Worker using ItemHeader = OpBufferItemHeader<ItemTypes>;
66*d57664e9SAndroid Build Coastguard Worker
67*d57664e9SAndroid Build Coastguard Worker explicit OpBuffer() = default;
68*d57664e9SAndroid Build Coastguard Worker
69*d57664e9SAndroid Build Coastguard Worker // Prevent copying by default
70*d57664e9SAndroid Build Coastguard Worker OpBuffer(const OpBuffer&) = delete;
71*d57664e9SAndroid Build Coastguard Worker void operator=(const OpBuffer&) = delete;
72*d57664e9SAndroid Build Coastguard Worker
OpBuffer(OpBuffer && other)73*d57664e9SAndroid Build Coastguard Worker OpBuffer(OpBuffer&& other) {
74*d57664e9SAndroid Build Coastguard Worker mBuffer = other.mBuffer;
75*d57664e9SAndroid Build Coastguard Worker other.mBuffer = nullptr;
76*d57664e9SAndroid Build Coastguard Worker }
77*d57664e9SAndroid Build Coastguard Worker
78*d57664e9SAndroid Build Coastguard Worker void operator=(OpBuffer&& other) {
79*d57664e9SAndroid Build Coastguard Worker destroy();
80*d57664e9SAndroid Build Coastguard Worker mBuffer = other.mBuffer;
81*d57664e9SAndroid Build Coastguard Worker other.mBuffer = nullptr;
82*d57664e9SAndroid Build Coastguard Worker }
83*d57664e9SAndroid Build Coastguard Worker
~OpBuffer()84*d57664e9SAndroid Build Coastguard Worker ~OpBuffer() {
85*d57664e9SAndroid Build Coastguard Worker destroy();
86*d57664e9SAndroid Build Coastguard Worker }
87*d57664e9SAndroid Build Coastguard Worker
capacity()88*d57664e9SAndroid Build Coastguard Worker constexpr size_t capacity() const { return mBuffer ? mBuffer->capacity : 0; }
89*d57664e9SAndroid Build Coastguard Worker
size()90*d57664e9SAndroid Build Coastguard Worker constexpr size_t size() const { return mBuffer ? mBuffer->used : 0; }
91*d57664e9SAndroid Build Coastguard Worker
remaining()92*d57664e9SAndroid Build Coastguard Worker constexpr size_t remaining() const { return capacity() - size(); }
93*d57664e9SAndroid Build Coastguard Worker
94*d57664e9SAndroid Build Coastguard Worker // TODO: Add less-copy'ing variants of this. emplace_back? deferred initialization?
95*d57664e9SAndroid Build Coastguard Worker template <ItemTypes T>
push_container(ItemContainer<T> && op)96*d57664e9SAndroid Build Coastguard Worker void push_container(ItemContainer<T>&& op) {
97*d57664e9SAndroid Build Coastguard Worker static_assert(alignof(ItemContainer<T>) <= Alignment);
98*d57664e9SAndroid Build Coastguard Worker static_assert(offsetof(ItemContainer<T>, header) == 0);
99*d57664e9SAndroid Build Coastguard Worker
100*d57664e9SAndroid Build Coastguard Worker constexpr auto padded_size = PadAlign(sizeof(ItemContainer<T>));
101*d57664e9SAndroid Build Coastguard Worker if (remaining() < padded_size) {
102*d57664e9SAndroid Build Coastguard Worker resize(std::max(padded_size, capacity()) * 2);
103*d57664e9SAndroid Build Coastguard Worker }
104*d57664e9SAndroid Build Coastguard Worker mBuffer->endOffset = mBuffer->used;
105*d57664e9SAndroid Build Coastguard Worker mBuffer->used += padded_size;
106*d57664e9SAndroid Build Coastguard Worker
107*d57664e9SAndroid Build Coastguard Worker void* allocateAt = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->endOffset;
108*d57664e9SAndroid Build Coastguard Worker auto temp = new (allocateAt) ItemContainer<T>{std::move(op)};
109*d57664e9SAndroid Build Coastguard Worker temp->header = {.type = T, .size = padded_size};
110*d57664e9SAndroid Build Coastguard Worker }
111*d57664e9SAndroid Build Coastguard Worker
resize(size_t newsize)112*d57664e9SAndroid Build Coastguard Worker void resize(size_t newsize) {
113*d57664e9SAndroid Build Coastguard Worker // Add the header size to newsize
114*d57664e9SAndroid Build Coastguard Worker const size_t adjustedSize = newsize + STARTING_SIZE;
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard Worker if (adjustedSize < size()) {
117*d57664e9SAndroid Build Coastguard Worker // todo: throw?
118*d57664e9SAndroid Build Coastguard Worker return;
119*d57664e9SAndroid Build Coastguard Worker }
120*d57664e9SAndroid Build Coastguard Worker if (newsize == 0) {
121*d57664e9SAndroid Build Coastguard Worker free(mBuffer);
122*d57664e9SAndroid Build Coastguard Worker mBuffer = nullptr;
123*d57664e9SAndroid Build Coastguard Worker } else {
124*d57664e9SAndroid Build Coastguard Worker if (mBuffer) {
125*d57664e9SAndroid Build Coastguard Worker mBuffer = reinterpret_cast<BufferHeader*>(realloc(mBuffer, adjustedSize));
126*d57664e9SAndroid Build Coastguard Worker mBuffer->capacity = adjustedSize;
127*d57664e9SAndroid Build Coastguard Worker } else {
128*d57664e9SAndroid Build Coastguard Worker mBuffer = new (malloc(adjustedSize)) BufferHeader();
129*d57664e9SAndroid Build Coastguard Worker mBuffer->capacity = adjustedSize;
130*d57664e9SAndroid Build Coastguard Worker mBuffer->used = STARTING_SIZE;
131*d57664e9SAndroid Build Coastguard Worker mBuffer->startOffset = STARTING_SIZE;
132*d57664e9SAndroid Build Coastguard Worker }
133*d57664e9SAndroid Build Coastguard Worker }
134*d57664e9SAndroid Build Coastguard Worker }
135*d57664e9SAndroid Build Coastguard Worker
136*d57664e9SAndroid Build Coastguard Worker template <typename F>
for_each(F && f)137*d57664e9SAndroid Build Coastguard Worker void for_each(F&& f) const {
138*d57664e9SAndroid Build Coastguard Worker do_for_each(std::forward<F>(f), ItemTypesSequence{});
139*d57664e9SAndroid Build Coastguard Worker }
140*d57664e9SAndroid Build Coastguard Worker
141*d57664e9SAndroid Build Coastguard Worker void clear();
142*d57664e9SAndroid Build Coastguard Worker
first()143*d57664e9SAndroid Build Coastguard Worker ItemHeader* first() const { return isEmpty() ? nullptr : itemAt(mBuffer->startOffset); }
144*d57664e9SAndroid Build Coastguard Worker
last()145*d57664e9SAndroid Build Coastguard Worker ItemHeader* last() const { return isEmpty() ? nullptr : itemAt(mBuffer->endOffset); }
146*d57664e9SAndroid Build Coastguard Worker
147*d57664e9SAndroid Build Coastguard Worker class sentinal {
148*d57664e9SAndroid Build Coastguard Worker public:
sentinal(const uint8_t * end)149*d57664e9SAndroid Build Coastguard Worker explicit sentinal(const uint8_t* end) : end(end) {}
150*d57664e9SAndroid Build Coastguard Worker private:
151*d57664e9SAndroid Build Coastguard Worker const uint8_t* const end;
152*d57664e9SAndroid Build Coastguard Worker };
153*d57664e9SAndroid Build Coastguard Worker
end()154*d57664e9SAndroid Build Coastguard Worker sentinal end() const {
155*d57664e9SAndroid Build Coastguard Worker return sentinal{end_ptr()};
156*d57664e9SAndroid Build Coastguard Worker }
157*d57664e9SAndroid Build Coastguard Worker
158*d57664e9SAndroid Build Coastguard Worker template <ItemTypes T>
159*d57664e9SAndroid Build Coastguard Worker class filtered_iterator {
160*d57664e9SAndroid Build Coastguard Worker public:
filtered_iterator(uint8_t * start,const uint8_t * end)161*d57664e9SAndroid Build Coastguard Worker explicit filtered_iterator(uint8_t* start, const uint8_t* end)
162*d57664e9SAndroid Build Coastguard Worker : mCurrent(start), mEnd(end) {
163*d57664e9SAndroid Build Coastguard Worker ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
164*d57664e9SAndroid Build Coastguard Worker if (header->type != T) {
165*d57664e9SAndroid Build Coastguard Worker advance();
166*d57664e9SAndroid Build Coastguard Worker }
167*d57664e9SAndroid Build Coastguard Worker }
168*d57664e9SAndroid Build Coastguard Worker
169*d57664e9SAndroid Build Coastguard Worker filtered_iterator& operator++() {
170*d57664e9SAndroid Build Coastguard Worker advance();
171*d57664e9SAndroid Build Coastguard Worker return *this;
172*d57664e9SAndroid Build Coastguard Worker }
173*d57664e9SAndroid Build Coastguard Worker
174*d57664e9SAndroid Build Coastguard Worker // Although this iterator self-terminates, we need a placeholder to compare against
175*d57664e9SAndroid Build Coastguard Worker // to make for-each loops happy
176*d57664e9SAndroid Build Coastguard Worker bool operator!=(const sentinal& other) const {
177*d57664e9SAndroid Build Coastguard Worker return mCurrent != mEnd;
178*d57664e9SAndroid Build Coastguard Worker }
179*d57664e9SAndroid Build Coastguard Worker
180*d57664e9SAndroid Build Coastguard Worker ItemContainer<T>& operator*() {
181*d57664e9SAndroid Build Coastguard Worker return *reinterpret_cast<ItemContainer<T>*>(mCurrent);
182*d57664e9SAndroid Build Coastguard Worker }
183*d57664e9SAndroid Build Coastguard Worker private:
advance()184*d57664e9SAndroid Build Coastguard Worker void advance() {
185*d57664e9SAndroid Build Coastguard Worker ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
186*d57664e9SAndroid Build Coastguard Worker do {
187*d57664e9SAndroid Build Coastguard Worker mCurrent += header->size;
188*d57664e9SAndroid Build Coastguard Worker header = reinterpret_cast<ItemHeader*>(mCurrent);
189*d57664e9SAndroid Build Coastguard Worker } while (mCurrent != mEnd && header->type != T);
190*d57664e9SAndroid Build Coastguard Worker }
191*d57664e9SAndroid Build Coastguard Worker uint8_t* mCurrent;
192*d57664e9SAndroid Build Coastguard Worker const uint8_t* const mEnd;
193*d57664e9SAndroid Build Coastguard Worker };
194*d57664e9SAndroid Build Coastguard Worker
195*d57664e9SAndroid Build Coastguard Worker template <ItemTypes T>
196*d57664e9SAndroid Build Coastguard Worker class filtered_view {
197*d57664e9SAndroid Build Coastguard Worker public:
filtered_view(uint8_t * start,const uint8_t * end)198*d57664e9SAndroid Build Coastguard Worker explicit filtered_view(uint8_t* start, const uint8_t* end) : mStart(start), mEnd(end) {}
199*d57664e9SAndroid Build Coastguard Worker
begin()200*d57664e9SAndroid Build Coastguard Worker filtered_iterator<T> begin() const {
201*d57664e9SAndroid Build Coastguard Worker return filtered_iterator<T>{mStart, mEnd};
202*d57664e9SAndroid Build Coastguard Worker }
203*d57664e9SAndroid Build Coastguard Worker
end()204*d57664e9SAndroid Build Coastguard Worker sentinal end() const {
205*d57664e9SAndroid Build Coastguard Worker return sentinal{mEnd};
206*d57664e9SAndroid Build Coastguard Worker }
207*d57664e9SAndroid Build Coastguard Worker private:
208*d57664e9SAndroid Build Coastguard Worker uint8_t* mStart;
209*d57664e9SAndroid Build Coastguard Worker const uint8_t* const mEnd;
210*d57664e9SAndroid Build Coastguard Worker };
211*d57664e9SAndroid Build Coastguard Worker
212*d57664e9SAndroid Build Coastguard Worker template <ItemTypes T>
filter()213*d57664e9SAndroid Build Coastguard Worker filtered_view<T> filter() const {
214*d57664e9SAndroid Build Coastguard Worker return filtered_view<T>{start_ptr(), end_ptr()};
215*d57664e9SAndroid Build Coastguard Worker }
216*d57664e9SAndroid Build Coastguard Worker
217*d57664e9SAndroid Build Coastguard Worker private:
218*d57664e9SAndroid Build Coastguard Worker
start_ptr()219*d57664e9SAndroid Build Coastguard Worker uint8_t* start_ptr() const {
220*d57664e9SAndroid Build Coastguard Worker return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->startOffset;
221*d57664e9SAndroid Build Coastguard Worker }
222*d57664e9SAndroid Build Coastguard Worker
end_ptr()223*d57664e9SAndroid Build Coastguard Worker const uint8_t* end_ptr() const {
224*d57664e9SAndroid Build Coastguard Worker return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->used;
225*d57664e9SAndroid Build Coastguard Worker }
226*d57664e9SAndroid Build Coastguard Worker
227*d57664e9SAndroid Build Coastguard Worker template <typename F, std::size_t... I>
do_for_each(F && f,std::index_sequence<I...>)228*d57664e9SAndroid Build Coastguard Worker void do_for_each(F&& f, std::index_sequence<I...>) const {
229*d57664e9SAndroid Build Coastguard Worker // Validate we're not empty
230*d57664e9SAndroid Build Coastguard Worker if (isEmpty()) return;
231*d57664e9SAndroid Build Coastguard Worker
232*d57664e9SAndroid Build Coastguard Worker // Setup the jump table, mapping from each type to a springboard that invokes the template
233*d57664e9SAndroid Build Coastguard Worker // function with the appropriate concrete type
234*d57664e9SAndroid Build Coastguard Worker using F_PTR = decltype(&f);
235*d57664e9SAndroid Build Coastguard Worker using THUNK = void (*)(F_PTR, void*);
236*d57664e9SAndroid Build Coastguard Worker static constexpr auto jump = std::array<THUNK, sizeof...(I)>{[](F_PTR fp, void* t) {
237*d57664e9SAndroid Build Coastguard Worker (*fp)(reinterpret_cast<const ItemContainer<static_cast<ItemTypes>(I)>*>(t));
238*d57664e9SAndroid Build Coastguard Worker }...};
239*d57664e9SAndroid Build Coastguard Worker
240*d57664e9SAndroid Build Coastguard Worker // Do the actual iteration of each item
241*d57664e9SAndroid Build Coastguard Worker uint8_t* current = start_ptr();
242*d57664e9SAndroid Build Coastguard Worker const uint8_t* end = end_ptr();
243*d57664e9SAndroid Build Coastguard Worker while (current != end) {
244*d57664e9SAndroid Build Coastguard Worker auto header = reinterpret_cast<ItemHeader*>(current);
245*d57664e9SAndroid Build Coastguard Worker // `f` could be a destructor, so ensure all accesses to the OP happen prior to invoking
246*d57664e9SAndroid Build Coastguard Worker // `f`
247*d57664e9SAndroid Build Coastguard Worker auto it = (void*)current;
248*d57664e9SAndroid Build Coastguard Worker current += header->size;
249*d57664e9SAndroid Build Coastguard Worker jump[static_cast<int>(header->type)](&f, it);
250*d57664e9SAndroid Build Coastguard Worker }
251*d57664e9SAndroid Build Coastguard Worker }
252*d57664e9SAndroid Build Coastguard Worker
destroy()253*d57664e9SAndroid Build Coastguard Worker void destroy() {
254*d57664e9SAndroid Build Coastguard Worker clear();
255*d57664e9SAndroid Build Coastguard Worker resize(0);
256*d57664e9SAndroid Build Coastguard Worker }
257*d57664e9SAndroid Build Coastguard Worker
offsetIsValid(size_t offset)258*d57664e9SAndroid Build Coastguard Worker bool offsetIsValid(size_t offset) const {
259*d57664e9SAndroid Build Coastguard Worker return offset >= mBuffer->startOffset && offset < mBuffer->used;
260*d57664e9SAndroid Build Coastguard Worker }
261*d57664e9SAndroid Build Coastguard Worker
itemAt(size_t offset)262*d57664e9SAndroid Build Coastguard Worker ItemHeader* itemAt(size_t offset) const {
263*d57664e9SAndroid Build Coastguard Worker if (!offsetIsValid(offset)) return nullptr;
264*d57664e9SAndroid Build Coastguard Worker return reinterpret_cast<ItemHeader*>(reinterpret_cast<uint8_t*>(mBuffer) + offset);
265*d57664e9SAndroid Build Coastguard Worker }
266*d57664e9SAndroid Build Coastguard Worker
isEmpty()267*d57664e9SAndroid Build Coastguard Worker bool isEmpty() const { return mBuffer == nullptr || mBuffer->used == STARTING_SIZE; }
268*d57664e9SAndroid Build Coastguard Worker
269*d57664e9SAndroid Build Coastguard Worker BufferHeader* mBuffer = nullptr;
270*d57664e9SAndroid Build Coastguard Worker };
271*d57664e9SAndroid Build Coastguard Worker
272*d57664e9SAndroid Build Coastguard Worker template <typename ItemTypes, template <ItemTypes> typename ItemContainer, typename BufferHeader,
273*d57664e9SAndroid Build Coastguard Worker typename ItemTypeSequence>
clear()274*d57664e9SAndroid Build Coastguard Worker void OpBuffer<ItemTypes, ItemContainer, BufferHeader, ItemTypeSequence>::clear() {
275*d57664e9SAndroid Build Coastguard Worker
276*d57664e9SAndroid Build Coastguard Worker // Don't need to do anything if we don't have a buffer
277*d57664e9SAndroid Build Coastguard Worker if (!mBuffer) return;
278*d57664e9SAndroid Build Coastguard Worker
279*d57664e9SAndroid Build Coastguard Worker for_each([](auto op) {
280*d57664e9SAndroid Build Coastguard Worker using T = std::remove_reference_t<decltype(*op)>;
281*d57664e9SAndroid Build Coastguard Worker op->~T();
282*d57664e9SAndroid Build Coastguard Worker });
283*d57664e9SAndroid Build Coastguard Worker mBuffer->used = STARTING_SIZE;
284*d57664e9SAndroid Build Coastguard Worker mBuffer->startOffset = STARTING_SIZE;
285*d57664e9SAndroid Build Coastguard Worker mBuffer->endOffset = 0;
286*d57664e9SAndroid Build Coastguard Worker }
287*d57664e9SAndroid Build Coastguard Worker
288*d57664e9SAndroid Build Coastguard Worker } // namespace android::uirenderer