1 /* 2 * Copyright (C) 2024 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 #ifndef MINIKIN_PACKED_VECTOR_H 18 #define MINIKIN_PACKED_VECTOR_H 19 20 #include <log/log.h> 21 22 #include <type_traits> 23 #include <vector> 24 25 namespace minikin { 26 27 // PackedVector optimize short term allocations for small size objects. 28 // The public interfaces are following the std::vector. 29 template <typename T, size_t ARRAY_SIZE = 2, typename SIZE_TYPE = uint32_t> 30 class PackedVector { 31 private: 32 // At least two elements of pointer array is reserved. 33 static constexpr size_t PTR_ARRAY_SIZE = 34 std::max(static_cast<size_t>(2), 35 (ARRAY_SIZE * sizeof(T) + sizeof(uintptr_t) - 1) / sizeof(uintptr_t)); 36 // Number of elements can be stored into array. 37 static constexpr size_t ARRAY_CAPACITY = PTR_ARRAY_SIZE * sizeof(uintptr_t) / sizeof(T); 38 static_assert(std::is_pod<T>::value, "only POD can be stored in PackedVector."); 39 40 public: 41 typedef T value_type; 42 43 // Constructors PackedVector()44 PackedVector() : mSize(0), mCapacity(ARRAY_CAPACITY) {} PackedVector(const T * ptr,SIZE_TYPE size)45 PackedVector(const T* ptr, SIZE_TYPE size) : PackedVector() { copy(ptr, size); } PackedVector(const std::vector<T> & src)46 PackedVector(const std::vector<T>& src) : PackedVector() { 47 LOG_ALWAYS_FATAL_IF(src.size() >= std::numeric_limits<SIZE_TYPE>::max()); 48 copy(src.data(), src.size()); 49 } PackedVector(std::initializer_list<T> init)50 PackedVector(std::initializer_list<T> init) : PackedVector() { 51 copy(init.begin(), init.size()); 52 } 53 54 // Assignments PackedVector(const PackedVector & o)55 PackedVector(const PackedVector& o) : PackedVector() { copy(o.getPtr(), o.mSize); } 56 PackedVector& operator=(const PackedVector& o) { 57 copy(o.getPtr(), o.mSize); 58 return *this; 59 } 60 61 // Movement PackedVector(PackedVector && o)62 PackedVector(PackedVector&& o) : PackedVector() { move(std::move(o)); } 63 PackedVector& operator=(PackedVector&& o) { 64 move(std::move(o)); 65 return *this; 66 } 67 ~PackedVector()68 ~PackedVector() { free(); } 69 70 // Compare 71 inline bool operator==(const PackedVector& o) const { 72 return mSize == o.mSize && memcmp(getPtr(), o.getPtr(), mSize * sizeof(T)) == 0; 73 } 74 inline bool operator!=(const PackedVector& o) const { return !(*this == o); } 75 data()76 const T* data() const { return getPtr(); } data()77 T* data() { return getPtr(); } 78 79 const T& operator[](SIZE_TYPE i) const { return getPtr()[i]; } 80 T& operator[](SIZE_TYPE i) { return getPtr()[i]; } 81 reserve(SIZE_TYPE capacity)82 void reserve(SIZE_TYPE capacity) { ensureCapacity(capacity); } 83 84 void resize(SIZE_TYPE size, T value = T()) { 85 if (mSize == size) { 86 return; 87 } else if (mSize > size) { // reduce size 88 if (isArrayUsed()) { // array to array reduction, so no need to reallocate. 89 mSize = size; 90 } else if (size > ARRAY_CAPACITY) { // heap to heap reduction 91 T* newPtr = new T[size]; 92 const T* oldPtr = getPtr(); 93 std::copy(oldPtr, oldPtr + size, newPtr); 94 free(); 95 mArray[0] = reinterpret_cast<uintptr_t>(newPtr); 96 mSize = size; 97 mCapacity = size; 98 } else { // heap to array reduction. 99 const T* oldPtr = getPtr(); 100 T* newPtr = reinterpret_cast<T*>(&mArray[0]); 101 std::copy(oldPtr, oldPtr + size, newPtr); 102 delete[] oldPtr; // we cannot use free() here because we wrote data to mArray. 103 mSize = size; 104 mCapacity = ARRAY_CAPACITY; 105 } 106 } else { // mSize < size // increase size 107 ensureCapacity(size); 108 T* ptr = getPtr(); 109 for (SIZE_TYPE i = mSize; i < size; ++i) { 110 ptr[i] = value; 111 } 112 mSize = size; 113 } 114 } 115 push_back(const T & x)116 void push_back(const T& x) { 117 if (mSize >= mCapacity) [[unlikely]] { 118 // exponential backoff 119 constexpr SIZE_TYPE kMaxIncrease = static_cast<SIZE_TYPE>(4096 / sizeof(T)); 120 ensureCapacity(mCapacity + std::min(mCapacity, kMaxIncrease)); 121 } 122 *(getPtr() + mSize) = x; 123 mSize++; 124 } 125 shrink_to_fit()126 void shrink_to_fit() { 127 if (mSize == mCapacity || mCapacity == ARRAY_CAPACITY) { 128 return; 129 } 130 131 bool needFree = !isArrayUsed(); 132 133 const T* oldPtr = getPtr(); 134 T* newPtr; 135 if (mSize <= ARRAY_CAPACITY) { 136 newPtr = reinterpret_cast<T*>(&mArray[0]); 137 mCapacity = ARRAY_CAPACITY; 138 std::copy(oldPtr, oldPtr + mSize, newPtr); 139 } else { 140 newPtr = new T[mSize]; 141 mCapacity = mSize; 142 std::copy(oldPtr, oldPtr + mSize, newPtr); 143 mArray[0] = reinterpret_cast<uintptr_t>(newPtr); 144 } 145 if (needFree) { 146 delete[] oldPtr; 147 } 148 } 149 clear()150 void clear() { 151 mSize = 0; // don't free up until free is called. 152 } 153 empty()154 bool empty() const { return mSize == 0; } 155 size()156 SIZE_TYPE size() const { return mSize; } capacity()157 SIZE_TYPE capacity() const { return mCapacity; } 158 159 private: 160 uintptr_t mArray[PTR_ARRAY_SIZE]; 161 SIZE_TYPE mSize; 162 SIZE_TYPE mCapacity; 163 copy(const T * src,SIZE_TYPE count)164 void copy(const T* src, SIZE_TYPE count) { 165 clear(); 166 ensureCapacity(count); 167 mSize = count; 168 memcpy(getPtr(), src, count * sizeof(T)); 169 } 170 move(PackedVector && o)171 void move(PackedVector&& o) { 172 mSize = o.mSize; 173 o.mSize = 0; 174 mCapacity = o.mCapacity; 175 o.mCapacity = ARRAY_CAPACITY; 176 for (uint32_t i = 0; i < PTR_ARRAY_SIZE; ++i) { 177 mArray[i] = o.mArray[i]; 178 o.mArray[i] = 0; 179 } 180 } 181 isArrayUsed()182 inline bool isArrayUsed() const { return mCapacity <= ARRAY_CAPACITY; } 183 ensureCapacity(SIZE_TYPE capacity)184 void ensureCapacity(SIZE_TYPE capacity) { 185 if (capacity <= mCapacity) { 186 return; 187 } 188 189 if (capacity > ARRAY_CAPACITY) { 190 T* newPtr = new T[capacity]; 191 const T* oldPtr = getPtr(); 192 std::copy(oldPtr, oldPtr + mSize, newPtr); 193 free(); 194 mArray[0] = reinterpret_cast<uintptr_t>(newPtr); 195 mCapacity = capacity; 196 } else { 197 mCapacity = ARRAY_CAPACITY; 198 } 199 } 200 free()201 void free() { 202 if (!isArrayUsed()) { 203 delete[] reinterpret_cast<T*>(mArray[0]); 204 mArray[0] = 0; 205 mCapacity = ARRAY_CAPACITY; 206 } 207 } 208 getPtr()209 inline T* getPtr() { 210 return isArrayUsed() ? reinterpret_cast<T*>(&mArray[0]) : reinterpret_cast<T*>(mArray[0]); 211 } 212 getPtr()213 inline const T* getPtr() const { 214 return isArrayUsed() ? reinterpret_cast<const T*>(&mArray[0]) 215 : reinterpret_cast<const T*>(mArray[0]); 216 } 217 218 public: begin()219 inline const T* begin() const { return getPtr(); } end()220 inline const T* end() const { return getPtr() + mSize; } back()221 inline const T* back() const { return getPtr() + mSize - 1; } begin()222 inline T* begin() { return getPtr(); } end()223 inline T* end() { return getPtr() + mSize; } back()224 inline T* back() { return getPtr() + mSize - 1; } 225 }; 226 227 } // namespace minikin 228 #endif // MINIKIN_PACKED_VECTOR_H 229