blob: 22f1232c343169e3c174152e9cc7fbf16d7be2e4 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
#define INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
#include <algorithm>
#include <type_traits>
#include <utility>
#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
// Uses inline storage first, switches to dynamic storage when it overflows.
template <typename T, size_t kSize>
class SmallVector {
public:
static constexpr size_t kInlineSize = kSize;
explicit SmallVector() = default;
~SmallVector() {
clear();
if (PERFETTO_UNLIKELY(is_using_heap()))
AlignedFree(begin_);
begin_ = end_ = end_of_storage_ = nullptr;
}
// Move operators.
SmallVector(SmallVector&& other) noexcept(
std::is_nothrow_move_constructible<T>::value) {
if (other.is_using_heap()) {
// Move the heap content, no need to move the individual objects as their
// location won't change.
begin_ = other.begin_;
end_ = other.end_;
end_of_storage_ = other.end_of_storage_;
} else {
const size_t other_size = other.size();
PERFETTO_DCHECK(other_size <= capacity());
for (size_t i = 0; i < other_size; i++) {
// Move the entries and destroy the ones in the moved-from object.
new (&begin_[i]) T(std::move(other.begin_[i]));
other.begin_[i].~T();
}
end_ = begin_ + other_size;
}
auto* const other_inline_storage = other.inline_storage_begin();
other.end_ = other.begin_ = other_inline_storage;
other.end_of_storage_ = other_inline_storage + kInlineSize;
}
SmallVector& operator=(SmallVector&& other) noexcept(
std::is_nothrow_move_constructible<T>::value) {
this->~SmallVector();
new (this) SmallVector<T, kSize>(std::move(other));
return *this;
}
// Copy operators.
SmallVector(const SmallVector& other) {
const size_t other_size = other.size();
if (other_size > capacity())
Grow(other_size);
// Copy-construct the elements.
for (size_t i = 0; i < other_size; ++i)
new (&begin_[i]) T(other.begin_[i]);
end_ = begin_ + other_size;
}
SmallVector& operator=(const SmallVector& other) {
if (PERFETTO_UNLIKELY(this == &other))
return *this;
this->~SmallVector();
new (this) SmallVector<T, kSize>(other);
return *this;
}
T* data() { return begin_; }
const T* data() const { return begin_; }
T* begin() { return begin_; }
const T* begin() const { return begin_; }
T* end() { return end_; }
const T* end() const { return end_; }
size_t size() const { return static_cast<size_t>(end_ - begin_); }
bool empty() const { return end_ == begin_; }
size_t capacity() const {
return static_cast<size_t>(end_of_storage_ - begin_);
}
T& front() {
PERFETTO_DCHECK(!empty());
return begin_[0];
}
const T& front() const {
PERFETTO_DCHECK(!empty());
return begin_[0];
}
T& back() {
PERFETTO_DCHECK(!empty());
return end_[-1];
}
const T& back() const {
PERFETTO_DCHECK(!empty());
return end_[-1];
}
T& operator[](size_t index) {
PERFETTO_DCHECK(index < size());
return begin_[index];
}
const T& operator[](size_t index) const {
PERFETTO_DCHECK(index < size());
return begin_[index];
}
template <typename... Args>
void emplace_back(Args&&... args) {
T* end = end_;
if (PERFETTO_UNLIKELY(end == end_of_storage_))
end = Grow();
new (end) T(std::forward<Args>(args)...);
end_ = end + 1;
}
void pop_back() {
PERFETTO_DCHECK(!empty());
back().~T();
--end_;
}
// Clear without reverting back to inline storage.
void clear() {
while (!empty())
pop_back();
}
private:
PERFETTO_NO_INLINE T* Grow(size_t desired_capacity = 0) {
size_t cur_size = size();
size_t new_capacity = desired_capacity;
if (desired_capacity <= cur_size)
new_capacity = std::max(capacity() * 2, size_t(128));
T* new_storage =
static_cast<T*>(AlignedAlloc(alignof(T), new_capacity * sizeof(T)));
for (size_t i = 0; i < cur_size; ++i) {
// Move the elements into the new heap buffer and destroy the old ones.
new (&new_storage[i]) T(std::move(begin_[i]));
begin_[i].~T();
}
if (is_using_heap())
AlignedFree(begin_);
begin_ = new_storage;
end_ = new_storage + cur_size;
end_of_storage_ = new_storage + new_capacity;
return end_;
}
T* inline_storage_begin() { return reinterpret_cast<T*>(&inline_storage_); }
bool is_using_heap() { return begin_ != inline_storage_begin(); }
T* begin_ = inline_storage_begin();
T* end_ = begin_;
T* end_of_storage_ = begin_ + kInlineSize;
typename std::aligned_storage<sizeof(T) * kInlineSize, alignof(T)>::type
inline_storage_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_