--- /dev/null
+// Copyright 2016 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+#pragma once
+
+#include "anbox/common/type_traits.h"
+
+#include <algorithm>
+#include <initializer_list>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include <stddef.h>
+#include <stdlib.h>
+
+//
+// SmallVector<T>, SmallFixedVector<T, SmallSize>
+//
+// This header defines a replacement for a std::vector<> that uses small buffer
+// optimization technique - for some preset number of elements |SmallSize| it
+// stores them inside of the object, and falls back to the dynamically allocated
+// array only if one needs to add more elements.
+// This is useful for the performance-critical places where common number of
+// processed items is small, but it may still be quite large for a stack array.
+//
+// SmallFixedVector<> is the class you use to store elements, while
+// SmallVector<> is its base class that erases the small size from the type.
+//
+// NOTE: SmallVector<> cannot guarantee std::vector<>'s iterator invalidation
+// rules for move() and swap() operations - std::vector<>s just exchange
+// their iterators on swap() and pass the moved ones over, while SmallVector<>
+// may leave the iterators pointing to nowhere if they were for the in-place
+// array storage.
+//
+// Currenly only a limited subset of std::vector<>'s operations is implemented,
+// but fill free to add the ones you need.
+//
+
+namespace anbox {
+namespace common {
+
+//
+// Forward-declare the 'real' small vector class.
+template <class T, size_t S>
+class SmallFixedVector;
+
+//
+// SmallVector<T> - an interface for a small-buffer-optimized vector.
+// It hides the fixed size from its type, so one can use it to pass small
+// vectors around and not leak the buffer size to all callers:
+//
+// void process(SmallVector<Foo>& data);
+// ...
+// ...
+// SmallFixedVector<Foo, 100> aLittleBitOfFoos = ...;
+// process(aLittleBitOfFoos);
+// ...
+// SmallFixedVector<Foo, 1000> moreFoos = ...;
+// process(moreFoos);
+//
+template <class T>
+class SmallVector {
+ // Make them friends so SmallFixedVector is able to refer to SmallVector's
+ // protected members in static_assert()s.
+ template <class U, size_t S>
+ friend class SmallFixedVector;
+
+ public:
+ // Common set of type aliases.
+ using value_type = T;
+ using iterator = T*;
+ using const_iterator = const T*;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = size_t;
+
+ // It's ok to delete SmallVector<> through the base class - dtor() actually
+ // takes care of all living elements and the allocated memory.
+ ~SmallVector() { dtor(); }
+
+ // std::vector<> interface operations.
+ iterator begin() { return mBegin; }
+ const_iterator begin() const { return mBegin; }
+ const_iterator cbegin() const { return mBegin; }
+
+ iterator end() { return mEnd; }
+ const_iterator end() const { return mEnd; }
+ const_iterator cend() const { return mEnd; }
+
+ size_type size() const { return end() - begin(); }
+ size_type capacity() const { return mCapacity; }
+ bool empty() const { return begin() == end(); }
+
+ reference operator[](size_t i) { return *(begin() + i); }
+ const_reference operator[](size_t i) const { return *(cbegin() + i); }
+
+ pointer data() { return mBegin; }
+ const_pointer data() const { return mBegin; }
+ const_pointer cdata() const { return mBegin; }
+
+ template <class... Args>
+ void emplace_back(Args&&... args) {
+ grow_for_size(size() + 1);
+ new (mEnd) T(std::forward<Args>(args)...);
+ ++mEnd;
+ }
+
+ void push_back(const T& t) { emplace_back(t); }
+ void push_back(T&& t) { emplace_back(std::move(t)); }
+
+ void clear() {
+ destruct(begin(), end());
+ mEnd = mBegin;
+ }
+
+ void reserve(size_type newCap) {
+ if (newCap <= this->capacity()) {
+ return;
+ }
+ set_capacity(newCap);
+ }
+
+ void resize(size_type newSize) { resize_impl<true>(newSize); }
+
+ // This version of resizing doesn't initialize the newly allocated elements
+ // Useful for the cases when value-initialization is noticeably slow and
+ // one wants to directly construct or memcpy the elements into the resized
+ // place.
+ void resize_noinit(size_type newSize) { resize_impl<false>(newSize); }
+
+ // Returns if the current vector's buffer is dynamically allocated.
+ bool isAllocated() const { return this->cbegin() != smallBufferStart(); }
+
+ protected:
+ // Hide the default constructor so only SmallFixedVector can be
+ // instantiated.
+ SmallVector() = default;
+
+ // Destroy all elements in the vector and free the array if it was allocated
+ // dynamically.
+ void dtor() {
+ this->destruct(this->begin(), this->end());
+ if (isAllocated()) {
+ free(this->mBegin);
+ }
+ }
+
+ // Just a convenience setter function to init all members at once.
+ void init(iterator begin, iterator end, size_type capacity) {
+ this->mBegin = begin;
+ this->mEnd = end;
+ this->mCapacity = capacity;
+ }
+
+ // An implementation of different resizing versions.
+ template <bool init>
+ void resize_impl(size_type newSize) {
+ if (newSize < this->size()) {
+ const auto newEnd = this->begin() + newSize;
+ this->destruct(newEnd, this->end());
+ this->mEnd = newEnd;
+ } else if (newSize > this->size()) {
+ grow_for_size(newSize);
+ const auto newEnd = this->begin() + newSize;
+ if (init) {
+ std::uninitialized_fill(this->end(), newEnd, T());
+ }
+ this->mEnd = newEnd;
+ }
+ }
+
+ // Templated append operation for a range of elements.
+ template <class Iter>
+ void insert_back(Iter b, Iter e) {
+ if (b == e) {
+ return;
+ }
+ const auto newSize = this->size() + (e - b);
+ grow_for_size(newSize);
+ this->mEnd = std::uninitialized_copy(b, e, this->mEnd);
+ }
+
+ // Multiplicative grow for the internal array so it can hold |newSize|
+ // elements.
+ // Doesn't change size(), only capacity().
+ void grow_for_size(size_type newSize) {
+ // Grow by 1.5x by default.
+ if (newSize > capacity()) {
+ set_capacity(std::max(newSize, capacity() + capacity() / 2));
+ }
+ }
+
+ // Sets the capacity() to be exacly |newCap|. Allocates the array
+ // dynamically, moves all elements over and (potentially) deallocates the
+ // old array.
+ // Doesn't change size(), only capacity().
+ void set_capacity(size_type newCap) {
+ // Here we can only be switching to the dynamic vector, as static one
+ // always has its capacity on the maximum.
+ const auto newBegin = static_cast<T*>(malloc(sizeof(T) * newCap));
+ if (!newBegin) {
+ abort(); // what else can we do here?
+ }
+ const auto newEnd =
+ std::uninitialized_copy(std::make_move_iterator(this->begin()),
+ std::make_move_iterator(this->end()), newBegin);
+ dtor();
+ this->mBegin = newBegin;
+ this->mEnd = newEnd;
+ this->mCapacity = newCap;
+ }
+
+ // A convenience function to call destructor for a range of elements.
+ static void destruct(T* b, T* e) {
+ if (!std::is_trivially_destructible<T>::value) {
+ for (; b != e; ++b) {
+ b->~T();
+ }
+ }
+ }
+
+ // By design of the class, SmallFixedVector<> will be inheriting from
+ // SmallVector<>, so its in-place storage array is going to be the very next
+ // member after the last one here.
+ // This function returns that address, and SmallFixedVector<> has a static
+ // assert to make sure it remains correct.
+ constexpr const void* smallBufferStart() const {
+ return static_cast<const void*>(&mCapacity + 1);
+ }
+
+ // Standard set of members for a vector - begin, end and capacity.
+ // These point to the currently used chunk of memory, no matter if it's a
+ // heap-allocated one or an in-place array.
+ iterator mBegin;
+ iterator mEnd;
+ size_type mCapacity;
+};
+
+// The implementation of a SmallVector with a fixed in-place size, |SmallSize|.
+template <class T, size_t SmallSize>
+class SmallFixedVector : public SmallVector<T> {
+ using base = SmallVector<T>;
+
+ public:
+ // Grab these from the base class.
+ using value_type = typename base::value_type;
+ using iterator = typename base::iterator;
+ using const_iterator = typename base::const_iterator;
+ using pointer = typename base::pointer;
+ using const_pointer = typename base::const_pointer;
+ using reference = typename base::reference;
+ using const_reference = typename base::const_reference;
+ using size_type = typename base::size_type;
+
+ static constexpr size_type kSmallSize = SmallSize;
+
+ // Default constructor - set up an empty vector with capacity at full
+ // internal array size.
+ SmallFixedVector() {
+ init_inplace();
+ }
+
+ // Ctor from a range of iterators
+ template <class Iter>
+ SmallFixedVector(Iter b, Iter e) : SmallFixedVector() {
+ this->insert_back(b, e);
+ }
+
+ // Ctor from a range - anything that has begin and end.
+ // Note: template constructor is never a copy/move-ctor.
+ template <class Range, class = enable_if_c<!std::is_same<Range, T>::value &&
+ is_range<Range>::value>>
+ explicit SmallFixedVector(const Range& r)
+ : SmallFixedVector(std::begin(r), std::end(r)) {}
+ template <class Range, class = enable_if_c<!std::is_same<Range, T>::value &&
+ is_range<Range>::value>>
+ explicit SmallFixedVector(Range&& r)
+ : SmallFixedVector(std::make_move_iterator(std::begin(r)),
+ std::make_move_iterator(std::end(r))) {}
+ template <class U, class = enable_if_convertible<U, T>>
+ SmallFixedVector(std::initializer_list<U> list)
+ : SmallFixedVector(std::begin(list), std::end(list)) {}
+
+ SmallFixedVector(const SmallFixedVector& other)
+ : SmallFixedVector(other.begin(), other.end()) {}
+
+ SmallFixedVector(SmallFixedVector&& other) {
+ if (other.isAllocated()) {
+ // Just steal the allocated memory from the |other|.
+ this->mBegin = other.mBegin;
+ this->mEnd = other.mEnd;
+ this->mCapacity = other.mCapacity;
+ other.init_inplace();
+ } else {
+ // Have to move individual elements.
+ this->mBegin = mData.array;
+ this->mEnd = std::uninitialized_copy(
+ std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), this->begin());
+ this->mCapacity = kSmallSize;
+ }
+ }
+
+ SmallFixedVector& operator=(const SmallFixedVector& other) {
+ if (&other != this) {
+ this->clear();
+ this->insert_back(other.begin(), other.end());
+ }
+ return *this;
+ }
+
+ SmallFixedVector& operator=(SmallFixedVector&& other) {
+ if (other.isAllocated()) {
+ // Steal it and we're done.
+ this->dtor();
+ this->mBegin = other.mBegin;
+ this->mEnd = other.mEnd;
+ this->mCapacity = other.mCapacity;
+ other.init_inplace();
+ return *this;
+ }
+
+ if (this->isAllocated() && this->mCapacity < other.size()) {
+ // Not enough dynamic memory, switch to in-place.
+ this->dtor();
+ init_inplace();
+ } else {
+ // This could potentially be improved by move-assigning
+ // only needed items and destroying the rest, but
+ // destroy-all+construct-all is just simpler. For PODs it actually
+ // is even faster as it's always a single memcpy().
+ this->destruct(this->begin(), this->end());
+ }
+
+ // Move the whole |other| into the pre-cleaned memory
+ const auto newEnd = std::uninitialized_copy(
+ std::make_move_iterator(other.begin()),
+ std::make_move_iterator(other.end()), this->mBegin);
+ this->mEnd = newEnd;
+ // |other| is valid as-is.
+ return *this;
+ }
+
+ // Make sure we don't end up trying to move from an interface - it's just
+ // inefficient with the current code.
+ SmallFixedVector(base&& other) = delete;
+ SmallFixedVector& operator=(base&& other) = delete;
+
+ private:
+ // A shortcut for initialization for in-place storage.
+ void init_inplace() { this->init(mData.array, mData.array, kSmallSize); }
+
+ // A union with empty constructor and destructor makes sure that the array
+ // elements are not default-constructed in ctor and not destructed in dtor:
+ // the class needs to be able manage their lifetime more precisely.
+ union Data {
+ alignas(size_type) T array[kSmallSize];
+
+ Data() {}
+ ~Data() {}
+ } mData;
+};
+
+} // namespace common
+} // namespace anbox