Skip to content

Commit

Permalink
Initial implementation of List for scripting purposes
Browse files Browse the repository at this point in the history
  • Loading branch information
Xrayez committed Sep 10, 2020
1 parent 14f8f89 commit 1912927
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ if env["goost_math_enabled"]:
SConscript("math/SCsub", exports="env_goost")

env_goost.add_source_files(env.modules_sources, "*.cpp")
env_goost.add_source_files(env.modules_sources, "bind/*.cpp")
125 changes: 125 additions & 0 deletions core/bind/list.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include "list.h"

bool VariantListData::erase(VariantListElement *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(p_I);
size_cache--;

return true;
}

VariantListElement *VariantList::front() {
return _data ? _data->first : 0;
}

VariantListElement *VariantList::back() {
return _data ? _data->last : 0;
}

VariantListElement* VariantList::push_back(const Variant &value) {
if (!_data) {
_data = memnew_allocator(VariantListData, DefaultAllocator);
_data->first = nullptr;
_data->last = nullptr;
_data->size_cache = 0;
}
VariantListElement *n = memnew(VariantListElement);
n->value = 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 VariantList::pop_back() {
if (_data && _data->last) {
erase(_data->last);
}
}

VariantListElement *VariantList::push_front(const Variant &value) {
if (!_data) {
_data = memnew_allocator(VariantListData, DefaultAllocator);
_data->first = nullptr;
_data->last = nullptr;
_data->size_cache = 0;
}
VariantListElement *n = memnew_allocator(VariantListElement, DefaultAllocator);
n->value = 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 VariantList::pop_front() {
if (_data && _data->first) {
erase(_data->first);
}
}

bool VariantList::erase(VariantListElement *p_I) {
if (_data) {
bool ret = _data->erase(p_I);
if (_data->size_cache == 0) {
memdelete_allocator<VariantListData, DefaultAllocator>(_data);
_data = nullptr;
}
return ret;
}
return false;
};

void VariantList::_bind_methods() {
ClassDB::bind_method(D_METHOD("front"), &VariantList::front);
ClassDB::bind_method(D_METHOD("back"), &VariantList::back);
ClassDB::bind_method(D_METHOD("push_back", "value"), &VariantList::push_back);
ClassDB::bind_method(D_METHOD("pop_back"), &VariantList::pop_back);
ClassDB::bind_method(D_METHOD("push_front", "value"), &VariantList::push_front);
ClassDB::bind_method(D_METHOD("pop_front"), &VariantList::pop_front);
ClassDB::bind_method(D_METHOD("erase", "element"), &VariantList::erase);
}

void VariantListElement::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_value", "value"), &VariantListElement::set_value);
ClassDB::bind_method(D_METHOD("get_value"), &VariantListElement::get_value);

ADD_PROPERTY(PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_value", "get_value");
}
183 changes: 183 additions & 0 deletions core/bind/list.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#ifndef GOOST_LIST_H
#define GOOST_LIST_H

// Implementation based on Godot's `core/list.h`, which is mostly
// an instantiation of the `List<Variant>` template class for performance,
// which also allows to register relevant classes to scripting.

#include "core/object.h"
#include "core/sort_array.h"

class VariantListElement;

struct VariantListData {
VariantListElement *first = nullptr;
VariantListElement *last = nullptr;
int size_cache = 0;
bool erase(VariantListElement *p_I);
};

class VariantListElement : public Object {
GDCLASS(VariantListElement, Object);

protected:
static void _bind_methods();

private:
friend class VariantList;
friend struct VariantListData;

Variant value;
VariantListElement *next_ptr = nullptr;
VariantListElement *prev_ptr = nullptr;
VariantListData *data = nullptr;

public:
VariantListElement *next();
VariantListElement *prev();
Variant get_value() { return value; }
void set_value(const Variant &p_value) { value = p_value; }
void erase() { data->erase(this); }

VariantListElement() {}
};

class VariantList : public Object {
GDCLASS(VariantList, Object);

protected:
static void _bind_methods();

private:
VariantListData *_data = nullptr;

public:
VariantListElement *front();
VariantListElement *back();
VariantListElement *push_back(const Variant &value);
void pop_back();
VariantListElement *push_front(const Variant &value);
void pop_front();

VariantListElement *insert_after(VariantListElement *p_element, const Variant &p_value);
VariantListElement *insert_before(VariantListElement *p_element, const Variant &p_value);

VariantListElement *find(const Variant &p_val);

bool erase(VariantListElement *p_I);
// bool erase(const Variant &value); // TODO: rename to erase_first_found?
bool empty() const;
void clear();
int size() const;

void swap(VariantListElement *p_A, VariantListElement *p_B);

void move_to_back(VariantListElement *p_I);

void invert();

void move_to_front(VariantListElement *p_I);
void move_before(VariantListElement *value, VariantListElement *where);

void sort() {
sort_custom<Comparator<Variant> >();
}

template <class C>
void sort_custom_inplace() {

if (size() < 2)
return;

VariantListElement *from = front();
VariantListElement *current = from;
VariantListElement *to = from;

while (current) {

VariantListElement *next = current->next_ptr;

if (from != current) {

current->prev_ptr = nullptr;
current->next_ptr = from;

VariantListElement *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 = nullptr;
current->next_ptr = nullptr;
}

current = next;
}
_data->first = from;
_data->last = to;
}

template <class C>
struct AuxiliaryComparator {

C compare;
bool operator()(const VariantListElement *a, const VariantListElement *b) const {

return compare(a->value, b->value);
}
};

template <class C>
void sort_custom() {
int s = size();
if (s < 2)
return;

VariantListElement **aux_buffer = memnew_arr(VariantListElement *, s);

int idx = 0;
for (VariantListElement *E = front(); E; E = E->next_ptr) {

aux_buffer[idx] = E;
idx++;
}

SortArray<VariantListElement *, AuxiliaryComparator<C> > sort;
sort.sort(aux_buffer, s);

_data->first = aux_buffer[0];
aux_buffer[0]->prev_ptr = nullptr;
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 = nullptr;

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);
}
// VariantList(const VariantList &p_list);
VariantList() {}
};

#endif // GOOST_LIST_H
4 changes: 4 additions & 0 deletions core/register_core_types.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include "register_core_types.h"

#include "core/bind/list.h"

#include "image/register_image_types.h"
#include "math/register_math_types.h"

namespace goost {

void register_core_types() {
ClassDB::register_class<VariantList>();
ClassDB::register_class<VariantListElement>();
#ifdef GOOST_IMAGE_ENABLED
register_image_types();
#endif
Expand Down
11 changes: 11 additions & 0 deletions tests/project/goost/core/test_list.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extends "res://addons/gut/test.gd"

func test_push_back():
var list = VariantList.new()
var n: VariantListElement
n = list.push_back("Goost")
assert_eq(n.value, "Goost")
n = list.push_back(37)
assert_eq(n.value, 37)
n = list.push_back(Color.red)
assert_eq(n.value, Color.red)

0 comments on commit 1912927

Please sign in to comment.