blob: e1fb4d1bc075a8f30f6bcd70b1cd1ee7f5fb2a9e [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
// -----------------------------------------------------------------------------
//
// Vector implementation
//
// Authors: Yannis Guyon (yguyon@google.com)
#ifndef WP2_UTILS_VECTOR_H_
#define WP2_UTILS_VECTOR_H_
#include <cassert>
#include <cstddef>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
#include "src/utils/utils.h"
#include "src/wp2/base.h"
namespace WP2 {
//------------------------------------------------------------------------------
namespace Internal {
static constexpr size_t kMinVectorAllocation = 16;
// Returns the smallest power of two greater or equal to 'value'.
static size_t NextPow2(size_t value) {
if (value == 0) return 0;
--value;
for (size_t i = 1; i < sizeof(size_t) * 8; i *= 2) value |= value >> i;
return value + 1;
}
// Returns the smallest capacity greater or equal to 'value'.
static inline size_t NextCapacity(size_t value) {
if (value == 0) return 0;
if (value <= kMinVectorAllocation) return kMinVectorAllocation;
return NextPow2(value);
}
//------------------------------------------------------------------------------
// Data structure equivalent to std::vector but returning false and to its last
// valid state on memory allocation failure.
// std::vector with a custom allocator does not fill this need without
// exceptions.
template <typename T>
class VectorBase {
public:
typedef T* iterator;
typedef const T* const_iterator;
VectorBase() noexcept = default;
VectorBase(const VectorBase&) = delete;
VectorBase(VectorBase&& other) noexcept { TrivialMoveCtor(this, &other); }
~VectorBase() {
clear();
WP2Free(items_);
}
// Reallocates just enough memory if needed so that 'new_cap' items can fit.
WP2_NO_DISCARD bool reserve(size_t new_cap) {
if (capacity_ < new_cap) {
// Not using realloc() because only 'num_items_' need to be moved.
T* const new_items = (T*)WP2Malloc(new_cap, sizeof(T));
if (new_items == nullptr) return false;
if (num_items_ > 0) {
if (std::is_trivially_copyable<T>::value &&
std::is_trivially_destructible<T>::value) {
std::memcpy((void*)new_items, (const void*)items_,
num_items_ * sizeof(T));
} else {
for (size_t i = 0; i < num_items_; ++i) {
new (&new_items[i]) T(std::move(items_[i]));
items_[i].~T();
}
}
}
WP2Free(items_);
items_ = new_items;
capacity_ = new_cap;
}
return true;
}
// Reallocates less memory so that only the existing items can fit.
bool shrink_to_fit() {
if (capacity_ == num_items_) return true;
if (num_items_ == 0) {
WP2Free(items_);
items_ = nullptr;
capacity_ = 0;
return true;
}
const size_t previous_capacity = capacity_;
capacity_ = 0; // Force reserve() to allocate and copy.
if (reserve(num_items_)) return true;
capacity_ = previous_capacity;
return false;
}
// Sets the new size assuming there's enough capacity. Warning! No check.
// Content is unmodified.
void resize_no_check(size_t num_items) {
if (capacity_ < num_items) assert(false);
num_items_ = num_items;
}
// Constructs a new item by copy ctor. May reallocate if 'resize_if_needed'.
WP2_NO_DISCARD bool push_back(const T& value, bool resize_if_needed = true) {
if (num_items_ >= capacity_ &&
(!resize_if_needed ||
!reserve(Internal::NextCapacity(num_items_ + 1)))) {
return false;
}
new (&items_[num_items_]) T(value);
++num_items_;
return true;
}
// Constructs a new item by move ctor. May reallocate if 'resize_if_needed'.
WP2_NO_DISCARD bool push_back(T&& value, bool resize_if_needed = true) {
if (num_items_ >= capacity_ &&
(!resize_if_needed ||
!reserve(Internal::NextCapacity(num_items_ + 1)))) {
return false;
}
new (&items_[num_items_]) T(std::move(value));
++num_items_;
return true;
}
inline void push_back_no_resize(const T& value) {
if (!push_back(value, /*resize_if_needed=*/false)) assert(false);
}
inline void push_back_no_resize(T&& value) {
if (!push_back(std::move(value), /*resize_if_needed=*/false)) assert(false);
}
// Destructs the last item.
void pop_back() {
--num_items_;
items_[num_items_].~T();
}
// Destructs the item at 'pos'.
void erase(iterator pos) { erase(pos, pos + 1); }
// Destructs the items in [first,last).
void erase(iterator first, iterator last) {
for (iterator it = first; it != last; ++it) it->~T();
if (last != end()) {
if (std::is_trivial<T>::value) {
std::memmove((void*)first, (const void*)last,
(end() - last) * sizeof(T));
} else {
for (iterator it_src = last, it_dst = first; it_src != end();
++it_src, ++it_dst) {
new (it_dst) T(std::move(*it_src));
it_src->~T();
}
}
}
num_items_ -= std::distance(first, last);
}
// Appends 'size' elements by copy. Returns false on error.
bool append(const T data[], size_t size) {
if (size == 0) return true;
if (!reserve(Internal::NextCapacity(num_items_ + size))) return false;
std::copy(data, data + size, &items_[num_items_]);
num_items_ += size;
return true;
}
// Destructs all the items.
void clear() { erase(begin(), end()); }
// Destroys (including deallocating) all the items.
void reset() {
clear();
if (!shrink_to_fit()) assert(false);
}
// Accessors
bool empty() const { return (num_items_ == 0); }
size_t size() const { return num_items_; }
size_t capacity() const { return capacity_; }
T* data() { return items_; }
T& front() { return items_[0]; }
T& back() { return items_[num_items_ - 1]; }
T& operator[](size_t i) { return items_[i]; }
T& at(size_t i) { return items_[i]; }
const T* data() const { return items_; }
const T& front() const { return items_[0]; }
const T& back() const { return items_[num_items_ - 1]; }
const T& operator[](size_t i) const { return items_[i]; }
const T& at(size_t i) const { return items_[i]; }
iterator begin() { return &items_[0]; }
const_iterator begin() const { return &items_[0]; }
iterator end() { return &items_[num_items_]; }
const_iterator end() const { return &items_[num_items_]; }
void swap(VectorBase& b) {
using std::swap;
swap(items_, b.items_);
swap(capacity_, b.capacity_);
swap(num_items_, b.num_items_);
}
bool copy_from(const VectorBase& src) {
clear();
if (!reserve(src.capacity())) return false;
for (const auto& elmt : src) {
if (!push_back(elmt, false)) return false;
}
return true;
}
protected:
T* items_ = nullptr;
size_t capacity_ = 0;
size_t num_items_ = 0;
};
} // namespace Internal
//------------------------------------------------------------------------------
// Vector class that does *NOT* construct/destruct the content on resize().
// Should be reserved to plain old data.
template <class T>
class VectorNoCtor : public Internal::VectorBase<T> {
static_assert(std::is_trivially_destructible<T>::value,
"Type should not have destructor.");
public:
// Creates or removes items so that 'new_num_items' exist.
// Allocated memory grows every power-of-two items.
WP2_NO_DISCARD bool resize(size_t new_num_items) {
typedef Internal::VectorBase<T> super;
if (!super::reserve(new_num_items)) return false;
super::num_items_ = new_num_items;
return true;
}
};
// This generic vector class will call the constructors.
template <class T>
class Vector : public Internal::VectorBase<T> {
public:
// Constructs or destructs items so that 'new_num_items' exist.
// Allocated memory grows every power-of-two items.
WP2_NO_DISCARD bool resize(size_t new_num_items) {
typedef Internal::VectorBase<T> super;
if (super::num_items_ < new_num_items) {
if (!super::reserve(new_num_items)) {
return false;
}
while (super::num_items_ < new_num_items) {
new (&super::items_[super::num_items_]) T();
++super::num_items_;
}
} else {
while (super::num_items_ > new_num_items) {
--super::num_items_;
super::items_[super::num_items_].~T();
}
}
return true;
}
};
typedef VectorNoCtor<uint8_t> Vector_u8;
typedef VectorNoCtor<int8_t> Vector_s8;
typedef VectorNoCtor<uint16_t> Vector_u16;
typedef VectorNoCtor<int16_t> Vector_s16;
typedef VectorNoCtor<uint32_t> Vector_u32;
typedef VectorNoCtor<int32_t> Vector_s32;
typedef VectorNoCtor<uint64_t> Vector_u64;
typedef VectorNoCtor<int64_t> Vector_s64;
typedef VectorNoCtor<float> Vector_f;
//------------------------------------------------------------------------------
template <typename T>
void swap(VectorNoCtor<T>& a, VectorNoCtor<T>& b) {
a.swap(b);
}
template <typename T>
void swap(Vector<T>& a, Vector<T>& b) {
a.swap(b);
}
//------------------------------------------------------------------------------
} // namespace WP2
#endif /* WP2_UTILS_VECTOR_H_ */