1 // Copyright 2016 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include "android/base/TypeTraits.h"
18 
19 #include <algorithm>
20 #include <initializer_list>
21 #include <memory>
22 #include <type_traits>
23 #include <utility>
24 
25 #include <stddef.h>
26 #include <stdlib.h>
27 
28 //
29 // SmallVector<T>, SmallFixedVector<T, SmallSize>
30 //
31 // This header defines a replacement for a std::vector<> that uses small buffer
32 // optimization technique - for some preset number of elements |SmallSize| it
33 // stores them inside of the object, and falls back to the dynamically allocated
34 // array only if one needs to add more elements.
35 // This is useful for the performance-critical places where common number of
36 // processed items is small, but it may still be quite large for a stack array.
37 //
38 // SmallFixedVector<> is the class you use to store elements, while
39 // SmallVector<> is its base class that erases the small size from the type.
40 //
41 // NOTE: SmallVector<> cannot guarantee std::vector<>'s iterator invalidation
42 //   rules for move() and swap() operations - std::vector<>s just exchange
43 //   their iterators on swap() and pass the moved ones over, while SmallVector<>
44 //   may leave the iterators pointing to nowhere if they were for the in-place
45 //   array storage.
46 //
47 // Currenly only a limited subset of std::vector<>'s operations is implemented,
48 // but fill free to add the ones you need.
49 //
50 
51 namespace android {
52 namespace base {
53 
54 //
55 // Forward-declare the 'real' small vector class.
56 template <class T, size_t S>
57 class SmallFixedVector;
58 
59 //
60 // SmallVector<T> - an interface for a small-buffer-optimized vector.
61 // It hides the fixed size from its type, so one can use it to pass small
62 // vectors around and not leak the buffer size to all callers:
63 //
64 //  void process(SmallVector<Foo>& data);
65 //  ...
66 //  ...
67 //  SmallFixedVector<Foo, 100> aLittleBitOfFoos = ...;
68 //  process(aLittleBitOfFoos);
69 //  ...
70 //  SmallFixedVector<Foo, 1000> moreFoos = ...;
71 //  process(moreFoos);
72 //
73 template <class T>
74 class SmallVector {
75     // Make them friends so SmallFixedVector is able to refer to SmallVector's
76     // protected members in static_assert()s.
77     template <class U, size_t S>
78     friend class SmallFixedVector;
79 
80 public:
81     // Common set of type aliases.
82     using value_type = T;
83     using iterator = T*;
84     using const_iterator = const T*;
85     using pointer = T*;
86     using const_pointer = const T*;
87     using reference = T&;
88     using const_reference = const T&;
89     using size_type = size_t;
90 
91     // It's ok to delete SmallVector<> through the base class - dtor() actually
92     // takes care of all living elements and the allocated memory.
~SmallVector()93     ~SmallVector() { dtor(); }
94 
95     // std::vector<> interface operations.
begin()96     iterator begin() { return mBegin; }
begin()97     const_iterator begin() const { return mBegin; }
cbegin()98     const_iterator cbegin() const { return mBegin; }
99 
end()100     iterator end() { return mEnd; }
end()101     const_iterator end() const { return mEnd; }
cend()102     const_iterator cend() const { return mEnd; }
103 
size()104     size_type size() const { return end() - begin(); }
capacity()105     size_type capacity() const { return mCapacity; }
empty()106     bool empty() const { return begin() == end(); }
107 
front()108     reference front() { return *begin(); }
front()109     const_reference front() const { return *cbegin(); }
back()110     reference back() { return *(end() - 1); }
back()111     const_reference back() const { return *(cend() - 1); }
112 
113     reference operator[](size_t i) { return *(begin() + i); }
114     const_reference operator[](size_t i) const { return *(cbegin() + i); }
115 
data()116     pointer data() { return mBegin; }
data()117     const_pointer data() const { return mBegin; }
cdata()118     const_pointer cdata() const { return mBegin; }
119 
120     template <class... Args>
emplace_back(Args &&...args)121     void emplace_back(Args&&... args) {
122         grow_for_size(size() + 1);
123         new (mEnd) T(std::forward<Args>(args)...);
124         ++mEnd;
125     }
126 
push_back(const T & t)127     void push_back(const T& t) { emplace_back(t); }
push_back(T && t)128     void push_back(T&& t) { emplace_back(std::move(t)); }
129 
pop_back()130     void pop_back() {
131         destruct(mEnd - 1, mEnd);
132         --mEnd;
133     }
134 
clear()135     void clear() {
136         destruct(begin(), end());
137         mEnd = mBegin;
138     }
139 
reserve(size_type newCap)140     void reserve(size_type newCap) {
141         if (newCap <= this->capacity()) {
142             return;
143         }
144         set_capacity(newCap);
145     }
146 
resize(size_type newSize)147     void resize(size_type newSize) { resize_impl<true>(newSize); }
148 
149     // This version of resizing doesn't initialize the newly allocated elements
150     // Useful for the cases when value-initialization is noticeably slow and
151     // one wants to directly construct or memcpy the elements into the resized
152     // place.
resize_noinit(size_type newSize)153     void resize_noinit(size_type newSize) { resize_impl<false>(newSize); }
154 
155     // Returns if the current vector's buffer is dynamically allocated.
isAllocated()156     bool isAllocated() const { return this->cbegin() != smallBufferStart(); }
157 
158 protected:
159     // Hide the default constructor so only SmallFixedVector can be
160     // instantiated.
161     SmallVector() = default;
162 
163     // Destroy all elements in the vector and free the array if it was allocated
164     // dynamically.
dtor()165     void dtor() {
166         this->destruct(this->begin(), this->end());
167         if (isAllocated()) {
168             free(this->mBegin);
169         }
170     }
171 
172     // Just a convenience setter function to init all members at once.
init(iterator begin,iterator end,size_type capacity)173     void init(iterator begin, iterator end, size_type capacity) {
174         this->mBegin = begin;
175         this->mEnd = end;
176         this->mCapacity = capacity;
177     }
178 
179     // An implementation of different resizing versions.
180     template <bool init>
resize_impl(size_type newSize)181     void resize_impl(size_type newSize) {
182         if (newSize < this->size()) {
183             const auto newEnd = this->begin() + newSize;
184             this->destruct(newEnd, this->end());
185             this->mEnd = newEnd;
186         } else if (newSize > this->size()) {
187             grow_for_size(newSize);
188             const auto newEnd = this->begin() + newSize;
189             if (init) {
190                 std::uninitialized_fill(this->end(), newEnd, T());
191             }
192             this->mEnd = newEnd;
193         }
194     }
195 
196     // Templated append operation for a range of elements.
197     template <class Iter>
insert_back(Iter b,Iter e)198     void insert_back(Iter b, Iter e) {
199         if (b == e) {
200             return;
201         }
202         const auto newSize = this->size() + (e - b);
203         grow_for_size(newSize);
204         this->mEnd = std::uninitialized_copy(b, e, this->mEnd);
205     }
206 
207     // Multiplicative grow for the internal array so it can hold |newSize|
208     // elements.
209     // Doesn't change size(), only capacity().
grow_for_size(size_type newSize)210     void grow_for_size(size_type newSize) {
211         // Grow by 1.5x by default.
212         if (newSize > capacity()) {
213             set_capacity(std::max(newSize, capacity() + capacity() / 2));
214         }
215     }
216 
217     // Sets the capacity() to be exacly |newCap|. Allocates the array
218     // dynamically, moves all elements over and (potentially) deallocates the
219     // old array.
220     // Doesn't change size(), only capacity().
set_capacity(size_type newCap)221     void set_capacity(size_type newCap) {
222         // Here we can only be switching to the dynamic vector, as static one
223         // always has its capacity on the maximum.
224         const auto newBegin = (T*)malloc(sizeof(T) * newCap);
225         if (!newBegin) {
226             abort();  // what else can we do here?
227         }
228         const auto newEnd = std::uninitialized_copy(
229                 std::make_move_iterator(this->begin()),
230                 std::make_move_iterator(this->end()), newBegin);
231         dtor();
232         this->mBegin = newBegin;
233         this->mEnd = newEnd;
234         this->mCapacity = newCap;
235     }
236 
237     // A convenience function to call destructor for a range of elements.
destruct(T * b,T * e)238     static void destruct(T* b, T* e) {
239         if (!std::is_trivially_destructible<T>::value) {
240             for (; b != e; ++b) {
241                 b->~T();
242             }
243         }
244     }
245 
246     // By design of the class, SmallFixedVector<> will be inheriting from
247     // SmallVector<>, so its in-place storage array is going to be the very next
248     // member after the last one here.
249     // This function returns that address, and SmallFixedVector<> has a static
250     // assert to make sure it remains correct.
smallBufferStart()251     constexpr const void* smallBufferStart() const {
252         return (const void*)(&mCapacity + 1);
253     }
254 
255     // Standard set of members for a vector - begin, end and capacity.
256     // These point to the currently used chunk of memory, no matter if it's a
257     // heap-allocated one or an in-place array.
258     iterator mBegin;
259     iterator mEnd;
260     size_type mCapacity;
261 };
262 
263 // The implementation of a SmallVector with a fixed in-place size, |SmallSize|.
264 template <class T, size_t SmallSize>
265 class SmallFixedVector : public SmallVector<T> {
266     using base = SmallVector<T>;
267 
268 public:
269     // Grab these from the base class.
270     using value_type = typename base::value_type;
271     using iterator = typename base::iterator;
272     using const_iterator = typename base::const_iterator;
273     using pointer = typename base::pointer;
274     using const_pointer = typename base::const_pointer;
275     using reference = typename base::reference;
276     using const_reference = typename base::const_reference;
277     using size_type = typename base::size_type;
278 
279     static constexpr size_type kSmallSize = SmallSize;
280 
281     // Default constructor - set up an empty vector with capacity at full
282     // internal array size.
SmallFixedVector()283     SmallFixedVector() {
284         // Make sure that the small array starts exactly where base class
285         // expects it: right after the |mCapacity|.
286 
287         // We can't use a static_assert with offsetof() because in msvc, it uses
288         // reinterpret_cast.
289         // TODO: Add runtime assertion instead?
290         // https://developercommunity.visualstudio.com/content/problem/22196/static-assert-cannot-compile-constexprs-method-tha.html
291 #ifndef _MSC_VER
292         static_assert(offsetof(base, mCapacity) + sizeof(base::mCapacity) ==
293                                       offsetof(SmallFixedVector, mData) &&
294                               offsetof(Data, array) == 0,
295                       "SmallFixedVector<> class layout is wrong, "
296                       "|mData| needs to follow |mCapacity|");
297 #endif
298 
299         init_inplace();
300     }
301 
302     // Ctor from a range of iterators
303     template <class Iter>
SmallFixedVector(Iter b,Iter e)304     SmallFixedVector(Iter b, Iter e) : SmallFixedVector() {
305         this->insert_back(b, e);
306     }
307 
308     // Ctor from a range - anything that has begin and end.
309     // Note: template constructor is never a copy/move-ctor.
310     template <class Range,
311               class = enable_if_c<!std::is_same<Range, T>::value &&
312                                   is_range<Range>::value>>
SmallFixedVector(const Range & r)313     explicit SmallFixedVector(const Range& r)
314         : SmallFixedVector(std::begin(r), std::end(r)) {}
315     template <class Range,
316               class = enable_if_c<!std::is_same<Range, T>::value &&
317                                   is_range<Range>::value>>
SmallFixedVector(Range && r)318     explicit SmallFixedVector(Range&& r)
319         : SmallFixedVector(std::make_move_iterator(std::begin(r)),
320                            std::make_move_iterator(std::end(r))) {}
321     template <class U, class = enable_if_convertible<U, T>>
SmallFixedVector(std::initializer_list<U> list)322     SmallFixedVector(std::initializer_list<U> list)
323         : SmallFixedVector(std::begin(list), std::end(list)) {}
324 
SmallFixedVector(const SmallFixedVector & other)325     SmallFixedVector(const SmallFixedVector& other)
326         : SmallFixedVector(other.begin(), other.end()) {}
327 
SmallFixedVector(SmallFixedVector && other)328     SmallFixedVector(SmallFixedVector&& other) {
329         if (other.isAllocated()) {
330             // Just steal the allocated memory from the |other|.
331             this->mBegin = other.mBegin;
332             this->mEnd = other.mEnd;
333             this->mCapacity = other.mCapacity;
334             other.init_inplace();
335         } else {
336             // Have to move individual elements.
337             this->mBegin = mData.array;
338             this->mEnd = std::uninitialized_copy(
339                     std::make_move_iterator(other.begin()),
340                     std::make_move_iterator(other.end()), this->begin());
341             this->mCapacity = kSmallSize;
342         }
343     }
344 
345     SmallFixedVector& operator=(const SmallFixedVector& other) {
346         if (&other != this) {
347             this->clear();
348             this->insert_back(other.begin(), other.end());
349         }
350         return *this;
351     }
352 
353     SmallFixedVector& operator=(SmallFixedVector&& other) {
354         if (other.isAllocated()) {
355             // Steal it and we're done.
356             this->dtor();
357             this->mBegin = other.mBegin;
358             this->mEnd = other.mEnd;
359             this->mCapacity = other.mCapacity;
360             other.init_inplace();
361             return *this;
362         }
363 
364         if (this->isAllocated() && this->mCapacity < other.size()) {
365             // Not enough dynamic memory, switch to in-place.
366             this->dtor();
367             init_inplace();
368         } else {
369             // This could potentially be improved by move-assigning
370             // only needed items and destroying the rest, but
371             // destroy-all+construct-all is just simpler. For PODs it actually
372             // is even faster as it's always a single memcpy().
373             this->destruct(this->begin(), this->end());
374         }
375 
376         // Move the whole |other| into the pre-cleaned memory
377         const auto newEnd = std::uninitialized_copy(
378                 std::make_move_iterator(other.begin()),
379                 std::make_move_iterator(other.end()), this->mBegin);
380         this->mEnd = newEnd;
381         // |other| is valid as-is.
382         return *this;
383     }
384 
385     // Make sure we don't end up trying to move from an interface - it's just
386     // inefficient with the current code.
387     SmallFixedVector(base&& other) = delete;
388     SmallFixedVector& operator=(base&& other) = delete;
389 
390 private:
391     // A shortcut for initialization for in-place storage.
init_inplace()392     void init_inplace() { this->init(mData.array, mData.array, kSmallSize); }
393 
394     // A union with empty constructor and destructor makes sure that the array
395     // elements are not default-constructed in ctor and not destructed in dtor:
396     // the class needs to be able manage their lifetime more precisely.
397     union Data {
398         alignas(size_type) T array[kSmallSize];
399 
Data()400         Data() {}
~Data()401         ~Data() {}
402     } mData;
403 };
404 
405 }  // namespace base
406 }  // namespace android
407