692 lines
13 KiB
C++
692 lines
13 KiB
C++
/*************************************************************************/
|
|
/* list.h */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* http://www.godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
#ifndef GLOBALS_LIST_H
|
|
#define GLOBALS_LIST_H
|
|
|
|
#include "os/memory.h"
|
|
#include "sort.h"
|
|
|
|
/**
|
|
* Generic Templatized Linked List Implementation.
|
|
* The implementation differs from the STL one because
|
|
* a compatible preallocated linked list can be written
|
|
* using the same API, or features such as erasing an element
|
|
* from the iterator.
|
|
*/
|
|
|
|
template <class T,class A=DefaultAllocator>
|
|
class List {
|
|
struct _Data;
|
|
public:
|
|
|
|
|
|
class Element {
|
|
|
|
private:
|
|
friend class List<T,A>;
|
|
|
|
T value;
|
|
Element* next_ptr;
|
|
Element* prev_ptr;
|
|
_Data *data;
|
|
public:
|
|
|
|
/**
|
|
* Get NEXT Element iterator, for constant lists.
|
|
*/
|
|
_FORCE_INLINE_ const Element* next() const {
|
|
|
|
return next_ptr;
|
|
};
|
|
/**
|
|
* Get NEXT Element iterator,
|
|
*/
|
|
_FORCE_INLINE_ Element* next() {
|
|
|
|
return next_ptr;
|
|
};
|
|
|
|
/**
|
|
* Get PREV Element iterator, for constant lists.
|
|
*/
|
|
_FORCE_INLINE_ const Element* prev() const {
|
|
|
|
return prev_ptr;
|
|
};
|
|
/**
|
|
* Get PREV Element iterator,
|
|
*/
|
|
_FORCE_INLINE_ Element* prev() {
|
|
|
|
return prev_ptr;
|
|
};
|
|
|
|
/**
|
|
* * operator, for using as *iterator, when iterators are defined on stack.
|
|
*/
|
|
_FORCE_INLINE_ const T& operator *() const {
|
|
return value;
|
|
};
|
|
/**
|
|
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
|
*/
|
|
_FORCE_INLINE_ const T* operator->() const {
|
|
|
|
return &value;
|
|
};
|
|
/**
|
|
* * operator, for using as *iterator, when iterators are defined on stack,
|
|
*/
|
|
_FORCE_INLINE_ T& operator *() {
|
|
return value;
|
|
};
|
|
/**
|
|
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
|
*/
|
|
_FORCE_INLINE_ T* operator->() {
|
|
return &value;
|
|
};
|
|
|
|
/**
|
|
* get the value stored in this element.
|
|
*/
|
|
_FORCE_INLINE_ T& get() {
|
|
return value;
|
|
};
|
|
/**
|
|
* get the value stored in this element, for constant lists
|
|
*/
|
|
_FORCE_INLINE_ const T& get() const {
|
|
return value;
|
|
};
|
|
/**
|
|
* set the value stored in this element.
|
|
*/
|
|
_FORCE_INLINE_ void set(const T& p_value) {
|
|
value = (T&)p_value;
|
|
};
|
|
|
|
void erase() {
|
|
|
|
data->erase(this);
|
|
}
|
|
|
|
_FORCE_INLINE_ Element() {
|
|
next_ptr = 0;
|
|
prev_ptr = 0;
|
|
data=NULL;
|
|
};
|
|
};
|
|
|
|
private:
|
|
|
|
struct _Data {
|
|
|
|
Element* first;
|
|
Element* last;
|
|
int size_cache;
|
|
|
|
|
|
bool erase(const Element* p_I) {
|
|
|
|
ERR_FAIL_COND_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>( const_cast<Element*>(p_I) );
|
|
size_cache--;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
_Data *_data;
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
* return an const iterator to the begining of the list.
|
|
*/
|
|
_FORCE_INLINE_ const Element* front() const {
|
|
|
|
return _data?_data->first:0;
|
|
};
|
|
|
|
/**
|
|
* return an iterator to the begining of the list.
|
|
*/
|
|
_FORCE_INLINE_ Element* front() {
|
|
return _data?_data->first:0;
|
|
};
|
|
|
|
/**
|
|
* return an const iterator to the last member of the list.
|
|
*/
|
|
_FORCE_INLINE_ const Element* back() const {
|
|
|
|
return _data?_data->last:0;
|
|
};
|
|
|
|
/**
|
|
* return an iterator to the last member of the list.
|
|
*/
|
|
_FORCE_INLINE_ Element* back() {
|
|
|
|
return _data?_data->last:0;
|
|
};
|
|
|
|
/**
|
|
* store a new element at the end of the list
|
|
*/
|
|
Element* push_back(const T& value) {
|
|
|
|
if (!_data) {
|
|
|
|
_data=memnew_allocator(_Data,A);
|
|
_data->first=NULL;
|
|
_data->last=NULL;
|
|
_data->size_cache=0;
|
|
}
|
|
|
|
Element* n = memnew_allocator(Element,A);
|
|
n->value = (T&)value;
|
|
|
|
n->prev_ptr=_data->last;
|
|
n->next_ptr=0;
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* store a new element at the begining of the list
|
|
*/
|
|
Element* push_front(const T& value) {
|
|
|
|
if (!_data) {
|
|
|
|
_data=memnew_allocator(_Data,A);
|
|
_data->first=NULL;
|
|
_data->last=NULL;
|
|
_data->size_cache=0;
|
|
}
|
|
|
|
Element* n = memnew_allocator(Element,A);
|
|
n->value = (T&)value;
|
|
n->prev_ptr = 0;
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* find an element in the list,
|
|
*/
|
|
template<class 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 NULL;
|
|
};
|
|
|
|
/**
|
|
* erase an element in the list, by iterator pointing to it. Return true if it was found/erased.
|
|
*/
|
|
bool erase(const Element* p_I) {
|
|
|
|
if (_data) {
|
|
bool ret = _data->erase(p_I);
|
|
|
|
if (_data->size_cache==0) {
|
|
memdelete_allocator<_Data,A>(_data);
|
|
_data=NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* erase the first element in the list, that contains value
|
|
*/
|
|
bool erase(const T& value) {
|
|
|
|
Element* I = find(value);
|
|
return erase(I);
|
|
};
|
|
|
|
/**
|
|
* return wether the list is empty
|
|
*/
|
|
_FORCE_INLINE_ bool empty() const {
|
|
|
|
return (!_data || !_data->size_cache);
|
|
}
|
|
|
|
/**
|
|
* clear the list
|
|
*/
|
|
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);
|
|
|
|
Element* A_prev=p_A->prev_ptr;
|
|
Element* A_next=p_A->next_ptr;
|
|
|
|
p_A->next_ptr=p_B->next_ptr;
|
|
p_A->prev_ptr=p_B->prev_ptr;
|
|
|
|
p_B->next_ptr=A_next;
|
|
p_B->prev_ptr=A_prev;
|
|
|
|
if (p_A->prev_ptr)
|
|
p_A->prev_ptr->next_ptr=p_A;
|
|
if (p_A->next_ptr)
|
|
p_A->next_ptr->prev_ptr=p_A;
|
|
|
|
if (p_B->prev_ptr)
|
|
p_B->prev_ptr->next_ptr=p_B;
|
|
if (p_B->next_ptr)
|
|
p_B->next_ptr->prev_ptr=p_B;
|
|
|
|
}
|
|
/**
|
|
* copy the list
|
|
*/
|
|
void operator=(const List& p_list) {
|
|
|
|
clear();
|
|
const Element *it=p_list.front();
|
|
while (it) {
|
|
|
|
push_back( it->get() );
|
|
it=it->next();
|
|
}
|
|
|
|
}
|
|
|
|
T& operator[](int p_index) {
|
|
|
|
if (p_index<0 || p_index>=size()) {
|
|
T& aux=*((T*)0); //nullreturn
|
|
ERR_FAIL_COND_V(p_index<0 || p_index>=size(),aux);
|
|
}
|
|
|
|
Element *I=front();
|
|
int c=0;
|
|
while(I) {
|
|
|
|
if (c==p_index) {
|
|
|
|
return I->get();
|
|
}
|
|
I=I->next();
|
|
c++;
|
|
}
|
|
|
|
ERR_FAIL_V( *((T*)0) ); // bug!!
|
|
}
|
|
|
|
const T& operator[](int p_index) const {
|
|
|
|
if (p_index<0 || p_index>=size()) {
|
|
T& aux=*((T*)0); //nullreturn
|
|
ERR_FAIL_COND_V(p_index<0 || p_index>=size(),aux);
|
|
}
|
|
|
|
const Element *I=front();
|
|
int c=0;
|
|
while(I) {
|
|
|
|
if (c==p_index) {
|
|
|
|
return I->get();
|
|
}
|
|
I=I->next();
|
|
c++;
|
|
}
|
|
|
|
ERR_FAIL_V( *((T*)0) ); // bug!
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
if (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=NULL;
|
|
_data->last=p_I;
|
|
|
|
}
|
|
|
|
void invert() {
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
_data->first->prev_ptr=p_I;
|
|
p_I->next_ptr=_data->first;
|
|
p_I->prev_ptr=NULL;
|
|
_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;
|
|
};
|
|
|
|
/**
|
|
* simple insertion sort
|
|
*/
|
|
|
|
void sort() {
|
|
|
|
sort_custom< Comparator<T> >();
|
|
}
|
|
|
|
template<class C>
|
|
void sort_custom_inplace() {
|
|
|
|
if(size()<2)
|
|
return;
|
|
|
|
Element *from=front();
|
|
Element *current=from;
|
|
Element *to=from;
|
|
|
|
while(current) {
|
|
|
|
Element *next=current->next_ptr;
|
|
|
|
//disconnect
|
|
current->next_ptr=NULL;
|
|
|
|
if (from!=current) {
|
|
|
|
current->prev_ptr=NULL;
|
|
current->next_ptr=from;
|
|
|
|
Element *find=from;
|
|
C less;
|
|
while( find && less(find->value,current->value) ) {
|
|
|
|
current->prev_ptr=find;
|
|
current->next_ptr=find->next_ptr;
|
|
find=find->next_ptr;
|
|
}
|
|
|
|
if (current->prev_ptr)
|
|
current->prev_ptr->next_ptr=current;
|
|
else
|
|
from=current;
|
|
|
|
if (current->next_ptr)
|
|
current->next_ptr->prev_ptr=current;
|
|
else
|
|
to=current;
|
|
} else {
|
|
|
|
current->prev_ptr=NULL;
|
|
current->next_ptr=NULL;
|
|
|
|
}
|
|
|
|
current=next;
|
|
}
|
|
_data->first=from;
|
|
_data->last=to;
|
|
}
|
|
|
|
template<class C>
|
|
struct AuxiliaryComparator {
|
|
|
|
C compare;
|
|
_FORCE_INLINE_ bool operator()(const Element *a,const Element* b) const {
|
|
|
|
return compare(a->value,b->value);
|
|
}
|
|
};
|
|
|
|
template<class C>
|
|
void sort_custom() {
|
|
|
|
//this version uses auxiliary memory for speed.
|
|
//if you don't want to use auxiliary memory, use the in_place version
|
|
|
|
int s = size();
|
|
if(s<2)
|
|
return;
|
|
|
|
|
|
Element **aux_buffer = memnew_arr(Element*,s);
|
|
|
|
int idx=0;
|
|
for(Element *E=front();E;E=E->next_ptr) {
|
|
|
|
aux_buffer[idx]=E;
|
|
idx++;
|
|
}
|
|
|
|
SortArray<Element*,AuxiliaryComparator<C> > sort;
|
|
sort.sort(aux_buffer,s);
|
|
|
|
_data->first=aux_buffer[0];
|
|
aux_buffer[0]->prev_ptr=NULL;
|
|
aux_buffer[0]->next_ptr=aux_buffer[1];
|
|
|
|
_data->last=aux_buffer[s-1];
|
|
aux_buffer[s-1]->prev_ptr=aux_buffer[s-2];
|
|
aux_buffer[s-1]->next_ptr=NULL;
|
|
|
|
for(int i=1;i<s-1;i++) {
|
|
|
|
aux_buffer[i]->prev_ptr=aux_buffer[i-1];
|
|
aux_buffer[i]->next_ptr=aux_buffer[i+1];
|
|
|
|
}
|
|
|
|
memdelete_arr(aux_buffer);
|
|
}
|
|
|
|
|
|
/**
|
|
* copy constructor for the list
|
|
*/
|
|
List(const List& p_list) {
|
|
|
|
_data=NULL;
|
|
const Element *it=p_list.front();
|
|
while (it) {
|
|
|
|
push_back( it->get() );
|
|
it=it->next();
|
|
}
|
|
|
|
}
|
|
|
|
List() {
|
|
_data=NULL;
|
|
};
|
|
~List() {
|
|
clear();
|
|
if (_data) {
|
|
|
|
ERR_FAIL_COND(_data->size_cache);
|
|
memdelete_allocator<_Data,A>(_data);
|
|
}
|
|
};
|
|
};
|
|
|
|
#endif
|