yapb-noob-edition/ext/crlib/cr-array.h

415 lines
8.9 KiB
C
Raw Normal View History

2019-07-27 17:36:24 +03:00
//
2020-06-12 18:52:38 +03:00
// CRLib - Simple library for STL replacement in private projects.
// Copyright <20> 2020 YaPB Development Team <team@yapb.ru>.
2019-07-27 17:36:24 +03:00
//
2020-06-12 18:52:38 +03:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2019-07-27 17:36:24 +03:00
//
2020-06-12 18:52:38 +03:00
// 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.
//
2019-07-27 17:36:24 +03:00
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-random.h>
2019-07-29 23:11:49 +03:00
#include <initializer_list>
2019-07-27 17:36:24 +03:00
// policy to reserve memory
CR_DECLARE_SCOPED_ENUM (ReservePolicy,
2019-07-29 23:13:47 +03:00
Multiple,
2019-07-29 23:11:49 +03:00
Single,
)
2019-07-27 17:36:24 +03:00
CR_NAMESPACE_BEGIN
// simple array class like std::vector
2019-07-29 23:13:47 +03:00
template <typename T, ReservePolicy R = ReservePolicy::Multiple, size_t S = 0> class Array : public DenyCopying {
2020-06-12 18:52:38 +03:00
private:
T *contents_ {};
size_t capacity_ {};
size_t length_ {};
2019-07-27 17:36:24 +03:00
public:
2019-07-29 23:11:49 +03:00
explicit Array () {
if (fix (S > 0)) {
reserve (S);
}
}
2019-07-27 17:36:24 +03:00
Array (const size_t amount) {
reserve (amount);
}
Array (Array &&rhs) noexcept {
contents_ = rhs.contents_;
length_ = rhs.length_;
capacity_ = rhs.capacity_;
2019-07-27 17:36:24 +03:00
rhs.reset ();
}
Array (const std::initializer_list <T> &list) {
2019-07-29 23:11:49 +03:00
for (const auto &elem : list) {
push (elem);
}
}
2019-07-27 17:36:24 +03:00
~Array () {
destroy ();
}
private:
void destructElements () noexcept {
for (size_t i = 0; i < length_; ++i) {
alloc.destruct (&contents_[i]);
2019-07-27 17:36:24 +03:00
}
}
void transferElements (T *dest, T *src, size_t length) noexcept {
for (size_t i = 0; i < length; ++i) {
alloc.construct (&dest[i], cr::move (src[i]));
alloc.destruct (&src[i]);
}
}
void destroy () {
destructElements ();
alloc.deallocate (contents_);
2019-07-27 17:36:24 +03:00
}
void reset () {
contents_ = nullptr;
capacity_ = 0;
length_ = 0;
2019-07-27 17:36:24 +03:00
}
public:
bool reserve (const size_t amount) {
if (length_ + amount < capacity_) {
2019-07-27 17:36:24 +03:00
return true;
}
auto capacity = capacity_ ? capacity_ : 12;
2019-07-27 17:36:24 +03:00
2019-07-29 23:13:47 +03:00
if (cr::fix (R == ReservePolicy::Multiple)) {
while (length_ + amount > capacity) {
2019-07-27 17:36:24 +03:00
capacity *= 2;
}
}
else {
capacity = amount + capacity_ + 1;
2019-07-27 17:36:24 +03:00
}
auto data = alloc.allocate <T> (capacity);
if (contents_) {
transferElements (data, contents_, length_);
alloc.deallocate (contents_);
2020-06-12 18:52:38 +03:00
}
2019-07-27 17:36:24 +03:00
contents_ = data;
capacity_ = capacity;
2019-07-27 17:36:24 +03:00
return true;
}
bool resize (const size_t amount) {
if (amount < length_) {
while (amount < length_) {
2019-07-27 17:36:24 +03:00
discard ();
}
}
else if (amount > length_) {
2019-07-27 17:36:24 +03:00
if (!ensure (amount)) {
return false;
}
size_t resizeLength = amount - length_;
2019-07-27 17:36:24 +03:00
2019-07-29 23:11:49 +03:00
while (resizeLength--) {
emplace ();
2019-07-27 17:36:24 +03:00
}
}
return true;
}
bool ensure (const size_t amount) {
if (amount <= length_) {
2019-07-27 17:36:24 +03:00
return true;
}
return reserve (amount - length_);
2019-07-27 17:36:24 +03:00
}
2020-06-12 18:52:38 +03:00
template <typename U = size_t> U length () const {
return static_cast <U> (length_);
2019-07-27 17:36:24 +03:00
}
size_t capacity () const {
return capacity_;
2019-07-27 17:36:24 +03:00
}
template <typename U> bool set (size_t index, U &&object) {
if (index >= capacity_) {
2019-07-27 17:36:24 +03:00
if (!reserve (index + 1)) {
return false;
}
}
alloc.construct (&contents_[index], cr::forward <U> (object));
2019-07-27 17:36:24 +03:00
if (index >= length_) {
length_ = index + 1;
2019-07-27 17:36:24 +03:00
}
return true;
}
template <typename U> bool insert (size_t index, U &&object) {
return insert (index, &object, 1);
}
template <typename U> bool insert (size_t index, U *objects, size_t count = 1) {
if (!objects || !count) {
return false;
}
const size_t capacity = (length_ > index ? length_ : index) + count;
2019-07-27 17:36:24 +03:00
if (capacity >= capacity_ && !reserve (capacity)) {
2019-07-27 17:36:24 +03:00
return false;
}
if (index >= length_) {
2019-07-27 17:36:24 +03:00
for (size_t i = 0; i < count; ++i) {
alloc.construct (&contents_[i + index], cr::forward <U> (objects[i]));
2019-07-27 17:36:24 +03:00
}
length_ = capacity;
2019-07-27 17:36:24 +03:00
}
else {
size_t i = 0;
for (i = length_; i > index; --i) {
contents_[i + count - 1] = cr::move (contents_[i - 1]);
2019-07-27 17:36:24 +03:00
}
for (i = 0; i < count; ++i) {
alloc.construct (&contents_[i + index], cr::forward <U> (objects[i]));
2019-07-27 17:36:24 +03:00
}
length_ += count;
2019-07-27 17:36:24 +03:00
}
return true;
}
bool insert (size_t at, const Array &rhs) {
if (&rhs == this) {
return false;
}
return insert (at, &rhs.contents_[0], rhs.length_);
2019-07-27 17:36:24 +03:00
}
bool erase (const size_t index, const size_t count) {
if (index + count > capacity_) {
2019-07-27 17:36:24 +03:00
return false;
}
for (size_t i = index; i < index + count; ++i) {
alloc.destruct (&contents_[i]);
2019-07-29 23:11:49 +03:00
}
length_ -= count;
2019-07-27 17:36:24 +03:00
for (size_t i = index; i < length_; ++i) {
contents_[i] = cr::move (contents_[i + count]);
2019-07-27 17:36:24 +03:00
}
return true;
}
bool shift () {
return erase (0, 1);
}
template <typename U> bool unshift (U &&object) {
return insert (0, &object);
}
bool remove (const T &object) {
return erase (index (object), 1);
}
template <typename U> bool push (U &&object) {
if (!reserve (1)) {
return false;
}
alloc.construct (&contents_[length_], cr::forward <U> (object));
++length_;
2019-07-27 17:36:24 +03:00
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!reserve (1)) {
return false;
}
alloc.construct (&contents_[length_], cr::forward <Args> (args)...);
++length_;
2019-07-27 17:36:24 +03:00
return true;
}
T pop () {
auto object = cr::move (contents_[length_ - 1]);
2019-07-27 17:36:24 +03:00
discard ();
return object;
}
void discard () {
erase (length_ - 1, 1);
2019-07-27 17:36:24 +03:00
}
size_t index (const T &object) const {
return &object - &contents_[0];
2019-07-27 17:36:24 +03:00
}
void shuffle () {
for (size_t i = length_; i >= 1; --i) {
cr::swap (contents_[i - 1], contents_[rg.int_ (i, length_ - 2)]);
2019-07-27 17:36:24 +03:00
}
}
void reverse () {
for (size_t i = 0; i < length_ / 2; ++i) {
cr::swap (contents_[i], contents_[length_ - 1 - i]);
2019-07-27 17:36:24 +03:00
}
}
template <typename U> bool extend (U &&rhs) {
if (length_ == 0) {
2019-07-27 17:36:24 +03:00
*this = cr::move (rhs);
}
else {
for (size_t i = 0; i < rhs.length (); ++i) {
if (!push (cr::move (rhs[i]))) {
return false;
}
}
}
return true;
}
template <typename U> bool assign (U &&rhs) {
clear ();
return extend (cr::move (rhs));
}
void clear () {
destructElements ();
length_ = 0;
2019-07-27 17:36:24 +03:00
}
bool empty () const {
return length_ == 0;
2019-07-27 17:36:24 +03:00
}
bool shrink () {
if (length_ == capacity_ || !length_) {
2019-07-27 17:36:24 +03:00
return false;
}
auto data = alloc.allocate <T> (length_);
transferElements (data, contents_, length_);
2019-07-27 17:36:24 +03:00
alloc.deallocate (contents_);
2019-07-27 17:36:24 +03:00
contents_ = data;
capacity_ = length_;
2019-07-27 17:36:24 +03:00
return true;
}
const T &at (size_t index) const {
return contents_[index];
2019-07-27 17:36:24 +03:00
}
T &at (size_t index) {
return contents_[index];
2019-07-27 17:36:24 +03:00
}
const T &first () const {
return contents_[0];
2019-07-27 17:36:24 +03:00
}
T &first () {
return contents_[0];
2019-07-27 17:36:24 +03:00
}
T &last () {
return contents_[length_ - 1];
2019-07-27 17:36:24 +03:00
}
const T &last () const {
return contents_[length_ - 1];
2019-07-27 17:36:24 +03:00
}
const T &random () const {
return contents_[rg.int_ <size_t> (0, length_ - 1)];
2019-07-27 17:36:24 +03:00
}
T &random () {
return contents_[rg.int_ <size_t> (0u, length_ - 1u)];
2019-07-27 17:36:24 +03:00
}
T *data () {
return contents_;
2019-07-27 17:36:24 +03:00
}
T *data () const {
return contents_;
2019-07-27 17:36:24 +03:00
}
public:
Array &operator = (Array &&rhs) noexcept {
if (this != &rhs) {
destroy ();
contents_ = rhs.contents_;
length_ = rhs.length_;
capacity_ = rhs.capacity_;
2019-07-27 17:36:24 +03:00
rhs.reset ();
}
return *this;
}
public:
const T &operator [] (size_t index) const {
return at (index);
}
T &operator [] (size_t index) {
return at (index);
}
// for range-based loops
public:
T *begin () {
return contents_;
2019-07-27 17:36:24 +03:00
}
T *begin () const {
return contents_;
2019-07-27 17:36:24 +03:00
}
T *end () {
return contents_ + length_;
2019-07-27 17:36:24 +03:00
}
T *end () const {
return contents_ + length_;
2019-07-27 17:36:24 +03:00
}
};
2019-07-29 23:11:49 +03:00
// small array (with minimal reserve policy, something like fixed array, but still able to grow, by default allocates 64 elements)
template <typename T> using SmallArray = Array <T, ReservePolicy::Single, 64>;
2019-07-27 17:36:24 +03:00
CR_NAMESPACE_END