#pragma once
#include "core/error/error_macros.h"
#include "core/os/memory.h"
#include "core/templates/sort_list.h"
#include <initializer_list>
template <typename T, typename A = DefaultAllocator>
class List {
struct _Data;
public:
class Element {
private:
friend class List<T, A>;
T value;
Element *next_ptr = nullptr;
Element *prev_ptr = nullptr;
_Data *data = nullptr;
public:
_FORCE_INLINE_ const Element *next() const {
return next_ptr;
}
_FORCE_INLINE_ Element *next() {
return next_ptr;
}
_FORCE_INLINE_ const Element *prev() const {
return prev_ptr;
}
_FORCE_INLINE_ Element *prev() {
return prev_ptr;
}
_FORCE_INLINE_ const T &operator*() const {
return value;
}
_FORCE_INLINE_ const T *operator->() const {
return &value;
}
_FORCE_INLINE_ T &operator*() {
return value;
}
_FORCE_INLINE_ T *operator->() {
return &value;
}
_FORCE_INLINE_ T &get() {
return value;
}
_FORCE_INLINE_ const T &get() const {
return value;
}
_FORCE_INLINE_ void set(const T &p_value) {
value = (T &)p_value;
}
void erase() {
data->erase(this);
}
void transfer_to_back(List<T, A> *p_dst_list);
_FORCE_INLINE_ Element() {}
};
typedef T ValueType;
struct ConstIterator {
_FORCE_INLINE_ const T &operator*() const {
return E->get();
}
_FORCE_INLINE_ const T *operator->() const { return &E->get(); }
_FORCE_INLINE_ ConstIterator &operator++() {
E = E->next();
return *this;
}
_FORCE_INLINE_ ConstIterator &operator--() {
E = E->prev();
return *this;
}
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
_FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; }
_FORCE_INLINE_ ConstIterator() {}
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
private:
const Element *E = nullptr;
};
struct Iterator {
_FORCE_INLINE_ T &operator*() const {
return E->get();
}
_FORCE_INLINE_ T *operator->() const { return &E->get(); }
_FORCE_INLINE_ Iterator &operator++() {
E = E->next();
return *this;
}
_FORCE_INLINE_ Iterator &operator--() {
E = E->prev();
return *this;
}
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
Iterator(Element *p_E) { E = p_E; }
Iterator() {}
Iterator(const Iterator &p_it) { E = p_it.E; }
operator ConstIterator() const {
return ConstIterator(E);
}
private:
Element *E = nullptr;
};
_FORCE_INLINE_ Iterator begin() {
return Iterator(front());
}
_FORCE_INLINE_ Iterator end() {
return Iterator(nullptr);
}
#if 0
_FORCE_INLINE_ Iterator find(const K &p_key) {
return Iterator(find(p_key));
}
#endif
_FORCE_INLINE_ ConstIterator begin() const {
return ConstIterator(front());
}
_FORCE_INLINE_ ConstIterator end() const {
return ConstIterator(nullptr);
}
#if 0
_FORCE_INLINE_ ConstIterator find(const K &p_key) const {
return ConstIterator(find(p_key));
}
#endif
private:
struct _Data {
Element *first = nullptr;
Element *last = nullptr;
int size_cache = 0;
bool erase(Element *p_I) {
ERR_FAIL_NULL_V(p_I, false);
ERR_FAIL_COND_V(p_I->data != this, false);
if (first == p_I) {
first = p_I->next_ptr;
}
if (last == p_I) {
last = p_I->prev_ptr;
}
if (p_I->prev_ptr) {
p_I->prev_ptr->next_ptr = p_I->next_ptr;
}
if (p_I->next_ptr) {
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
}
memdelete_allocator<Element, A>(p_I);
size_cache--;
return true;
}
};
_Data *_data = nullptr;
public:
_FORCE_INLINE_ const Element *front() const {
return _data ? _data->first : nullptr;
}
_FORCE_INLINE_ Element *front() {
return _data ? _data->first : nullptr;
}
_FORCE_INLINE_ const Element *back() const {
return _data ? _data->last : nullptr;
}
_FORCE_INLINE_ Element *back() {
return _data ? _data->last : nullptr;
}
Element *push_back(const T &value) {
if (!_data) {
_data = memnew_allocator(_Data, A);
_data->first = nullptr;
_data->last = nullptr;
_data->size_cache = 0;
}
Element *n = memnew_allocator(Element, A);
n->value = (T &)value;
n->prev_ptr = _data->last;
n->next_ptr = nullptr;
n->data = _data;
if (_data->last) {
_data->last->next_ptr = n;
}
_data->last = n;
if (!_data->first) {
_data->first = n;
}
_data->size_cache++;
return n;
}
void pop_back() {
if (_data && _data->last) {
erase(_data->last);
}
}
Element *push_front(const T &value) {
if (!_data) {
_data = memnew_allocator(_Data, A);
_data->first = nullptr;
_data->last = nullptr;
_data->size_cache = 0;
}
Element *n = memnew_allocator(Element, A);
n->value = (T &)value;
n->prev_ptr = nullptr;
n->next_ptr = _data->first;
n->data = _data;
if (_data->first) {
_data->first->prev_ptr = n;
}
_data->first = n;
if (!_data->last) {
_data->last = n;
}
_data->size_cache++;
return n;
}
void pop_front() {
if (_data && _data->first) {
erase(_data->first);
}
}
Element *insert_after(Element *p_element, const T &p_value) {
CRASH_COND(p_element && (!_data || p_element->data != _data));
if (!p_element) {
return push_back(p_value);
}
Element *n = memnew_allocator(Element, A);
n->value = (T &)p_value;
n->prev_ptr = p_element;
n->next_ptr = p_element->next_ptr;
n->data = _data;
if (!p_element->next_ptr) {
_data->last = n;
} else {
p_element->next_ptr->prev_ptr = n;
}
p_element->next_ptr = n;
_data->size_cache++;
return n;
}
Element *insert_before(Element *p_element, const T &p_value) {
CRASH_COND(p_element && (!_data || p_element->data != _data));
if (!p_element) {
return push_back(p_value);
}
Element *n = memnew_allocator(Element, A);
n->value = (T &)p_value;
n->prev_ptr = p_element->prev_ptr;
n->next_ptr = p_element;
n->data = _data;
if (!p_element->prev_ptr) {
_data->first = n;
} else {
p_element->prev_ptr->next_ptr = n;
}
p_element->prev_ptr = n;
_data->size_cache++;
return n;
}
template <typename T_v>
Element *find(const T_v &p_val) {
Element *it = front();
while (it) {
if (it->value == p_val) {
return it;
}
it = it->next();
}
return nullptr;
}
bool erase(Element *p_I) {
if (_data && p_I) {
bool ret = _data->erase(p_I);
if (_data->size_cache == 0) {
memdelete_allocator<_Data, A>(_data);
_data = nullptr;
}
return ret;
}
return false;
}
bool erase(const T &value) {
Element *I = find(value);
return erase(I);
}
_FORCE_INLINE_ bool is_empty() const {
return (!_data || !_data->size_cache);
}
void clear() {
while (front()) {
erase(front());
}
}
_FORCE_INLINE_ int size() const {
return _data ? _data->size_cache : 0;
}
void swap(Element *p_A, Element *p_B) {
ERR_FAIL_COND(!p_A || !p_B);
ERR_FAIL_COND(p_A->data != _data);
ERR_FAIL_COND(p_B->data != _data);
if (p_A == p_B) {
return;
}
Element *A_prev = p_A->prev_ptr;
Element *A_next = p_A->next_ptr;
Element *B_prev = p_B->prev_ptr;
Element *B_next = p_B->next_ptr;
if (A_prev) {
A_prev->next_ptr = p_B;
} else {
_data->first = p_B;
}
if (B_prev) {
B_prev->next_ptr = p_A;
} else {
_data->first = p_A;
}
if (A_next) {
A_next->prev_ptr = p_B;
} else {
_data->last = p_B;
}
if (B_next) {
B_next->prev_ptr = p_A;
} else {
_data->last = p_A;
}
p_A->prev_ptr = A_next == p_B ? p_B : B_prev;
p_A->next_ptr = B_next == p_A ? p_B : B_next;
p_B->prev_ptr = B_next == p_A ? p_A : A_prev;
p_B->next_ptr = A_next == p_B ? p_A : A_next;
}
void operator=(const List &p_list) {
clear();
const Element *it = p_list.front();
while (it) {
push_back(it->get());
it = it->next();
}
}
void operator=(List &&p_list) {
if (unlikely(this == &p_list)) {
return;
}
clear();
_data = p_list._data;
p_list._data = nullptr;
}
T &get(int p_index) {
CRASH_BAD_INDEX(p_index, size());
Element *I = front();
int c = 0;
while (c < p_index) {
I = I->next();
c++;
}
return I->get();
}
const T &get(int p_index) const {
CRASH_BAD_INDEX(p_index, size());
const Element *I = front();
int c = 0;
while (c < p_index) {
I = I->next();
c++;
}
return I->get();
}
void move_to_back(Element *p_I) {
ERR_FAIL_COND(p_I->data != _data);
if (!p_I->next_ptr) {
return;
}
if (_data->first == p_I) {
_data->first = p_I->next_ptr;
}
if (_data->last == p_I) {
_data->last = p_I->prev_ptr;
}
if (p_I->prev_ptr) {
p_I->prev_ptr->next_ptr = p_I->next_ptr;
}
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
_data->last->next_ptr = p_I;
p_I->prev_ptr = _data->last;
p_I->next_ptr = nullptr;
_data->last = p_I;
}
void reverse() {
int s = size() / 2;
Element *F = front();
Element *B = back();
for (int i = 0; i < s; i++) {
SWAP(F->value, B->value);
F = F->next();
B = B->prev();
}
}
void move_to_front(Element *p_I) {
ERR_FAIL_COND(p_I->data != _data);
if (!p_I->prev_ptr) {
return;
}
if (_data->first == p_I) {
_data->first = p_I->next_ptr;
}
if (_data->last == p_I) {
_data->last = p_I->prev_ptr;
}
p_I->prev_ptr->next_ptr = p_I->next_ptr;
if (p_I->next_ptr) {
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
}
_data->first->prev_ptr = p_I;
p_I->next_ptr = _data->first;
p_I->prev_ptr = nullptr;
_data->first = p_I;
}
void move_before(Element *value, Element *where) {
if (value->prev_ptr) {
value->prev_ptr->next_ptr = value->next_ptr;
} else {
_data->first = value->next_ptr;
}
if (value->next_ptr) {
value->next_ptr->prev_ptr = value->prev_ptr;
} else {
_data->last = value->prev_ptr;
}
value->next_ptr = where;
if (!where) {
value->prev_ptr = _data->last;
_data->last = value;
return;
}
value->prev_ptr = where->prev_ptr;
if (where->prev_ptr) {
where->prev_ptr->next_ptr = value;
} else {
_data->first = value;
}
where->prev_ptr = value;
}
void sort() {
sort_custom<Comparator<T>>();
}
template <typename C>
void sort_custom() {
if (size() < 2) {
return;
}
SortList<Element, T, &Element::value, &Element::prev_ptr, &Element::next_ptr, C> sorter;
sorter.sort(_data->first, _data->last);
}
const void *id() const {
return (void *)_data;
}
List(const List &p_list) {
const Element *it = p_list.front();
while (it) {
push_back(it->get());
it = it->next();
}
}
List(List &&p_list) {
_data = p_list._data;
p_list._data = nullptr;
}
List() {}
List(std::initializer_list<T> p_init) {
for (const T &E : p_init) {
push_back(E);
}
}
~List() {
clear();
if (_data) {
ERR_FAIL_COND(_data->size_cache);
memdelete_allocator<_Data, A>(_data);
}
}
};
template <typename T, typename A>
void List<T, A>::Element::transfer_to_back(List<T, A> *p_dst_list) {
if (data->first == this) {
data->first = data->first->next_ptr;
}
if (data->last == this) {
data->last = data->last->prev_ptr;
}
if (prev_ptr) {
prev_ptr->next_ptr = next_ptr;
}
if (next_ptr) {
next_ptr->prev_ptr = prev_ptr;
}
data->size_cache--;
if (!p_dst_list->_data) {
p_dst_list->_data = memnew_allocator(_Data, A);
p_dst_list->_data->first = this;
p_dst_list->_data->last = nullptr;
p_dst_list->_data->size_cache = 0;
prev_ptr = nullptr;
} else {
p_dst_list->_data->last->next_ptr = this;
prev_ptr = p_dst_list->_data->last;
}
p_dst_list->_data->last = this;
next_ptr = nullptr;
data = p_dst_list->_data;
p_dst_list->_data->size_cache++;
}