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