2016-01-07 17:14:33 +09:00
|
|
|
#pragma once
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
//set
|
|
|
|
//implementation: red-black tree
|
2013-07-29 18:42:45 +09:00
|
|
|
//
|
2013-05-02 20:25:45 +09:00
|
|
|
//search: O(log n) average; O(log n) worst
|
|
|
|
//insert: O(log n) average; O(log n) worst
|
|
|
|
//remove: O(log n) average; O(log n) worst
|
2013-07-29 18:42:45 +09:00
|
|
|
//
|
|
|
|
//requirements:
|
|
|
|
// bool T::operator==(const T&) const;
|
|
|
|
// bool T::operator< (const T&) const;
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
#include <nall/utility.hpp>
|
|
|
|
#include <nall/vector.hpp>
|
|
|
|
|
|
|
|
namespace nall {
|
|
|
|
|
|
|
|
template<typename T> struct set {
|
|
|
|
struct node_t {
|
|
|
|
T value;
|
|
|
|
bool red = 1;
|
|
|
|
node_t* link[2] = {nullptr, nullptr};
|
|
|
|
node_t() = default;
|
|
|
|
node_t(const T& value) : value(value) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
node_t* root = nullptr;
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 nodes = 0;
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2015-08-02 15:23:13 +09:00
|
|
|
set() = default;
|
2013-05-02 20:25:45 +09:00
|
|
|
set(const set& source) { operator=(source); }
|
2022-09-13 16:47:47 +09:00
|
|
|
set(set&& source) { operator=(std::move(source)); }
|
2013-05-02 20:25:45 +09:00
|
|
|
set(std::initializer_list<T> list) { for(auto& value : list) insert(value); }
|
|
|
|
~set() { reset(); }
|
|
|
|
|
2015-08-02 15:23:13 +09:00
|
|
|
auto operator=(const set& source) -> set& {
|
Update to v106r95 release.
byuu says:
Another eleven hours in ... I think we're finally starting to see some
hope.
Unserialization of the tree works now. An absolutely heroic effort. The
design challenges were thus:
- given a text document, reproduce a tree of shared pointer objects
- the text document may have extra or bad node names (perhaps it's
from an older version) -- ignore these so they don't appear in the
actual tree
- the text document may be missing some node names (again, maybe an
older version) -- populate the actual tree nodes with default values
in this case
- any peripherals connected in text document nodes won't be in the
actual tree, so we have to connect them
- upon the creation of a new actual tree peripheral, we must populate
it with the metadata from the text document recursively, with the
above rules in effect
- we also need to allow the GUI to attach new peripherals, where it
would not know about the child nodes each peripheral contains (eg
the buttons on a gamepad)
- lastly, even though the tree is small, try not to take an absolute
eternity to import a document into the tree
There's a hell of a lot of restructuring that needs doing, in the SNES
core far more than anywhere else. We have to split bus mapping to occur
at power-on time, and manifest generation + slot attachments to happen
during load / system configuration. GBA also should move to detecting
save types at run-time. And honestly, that scan is pretty painfully slow
... it may make sense to suffix array it ... 13*4MiB isn't that much
memory. That or just write a smarter substring scanner. Sigh.
So now that things can be serialized, the next step is going to be
writing an input manager. It's going to be a radical departure from the
old way of doing things. Now, when the core wants input state, it'll
call inputPoll with a Node::Input object that we can directly write the
value to (well, after we `dynamic_cast` it to its specialized type. It'd
sure be nice if there were a `dynamic_multicast` switch construct, so we
didn't have to keep doing repeated `dynamic_cast` calls for each input
type. I digress.) I guess we can cache things ...
Thus, input polling will be more like this:
- produce a map of Node::Input to
`shared_pointer<InputManager::Binding>` objects
- maybe even do a multitap as a lazier way of handling multiple
input bindings
- have the InputManager::Binding hold a `shared_pointer<nall::HID>`
object, the group ID, and the input ID
- polling will thus be a map (or multitap) lookup; copying the
nall::HID value over to the Node::Input value
- it should be fine to just store `shared_pointers` here, to save
the overhead of constantly converting `weak_pointers`
And of course, we have to write a GUI for this. Oh, and before I can
even do the above ... I have to implement template importing, and
implement serialization support when attaching / detaching peripherals.
Hmm, what else ... I got rid of the Port specializations, as Port ends
up needing a lot of glue code and any special handling can be done
inside the emulation cores anyway. So now, a Port is a Port. It's just a
Node with the special property that it may contain either zero or one
Peripheral nodes. In addition to nodes of other types, of course.
I'm thinking we should probably also enforce a tree rule that no parent
node shall have two child nodes that have both the same type and the
same name. If we stick to that rule, we can simplify path traversal, and
allow the append function to be used as a "create if not exists"
function.
Also, we're really going to need a system to pick sensible default
peripheral and template paths for ports, including for dynamic ports.
Having to manually assign these, and having the values on dynamically
generated ports lost easily, is very annoying.
2019-02-03 16:53:32 +09:00
|
|
|
if(this == &source) return *this;
|
2015-08-02 15:23:13 +09:00
|
|
|
reset();
|
|
|
|
copy(root, source.root);
|
|
|
|
nodes = source.nodes;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto operator=(set&& source) -> set& {
|
Update to v106r95 release.
byuu says:
Another eleven hours in ... I think we're finally starting to see some
hope.
Unserialization of the tree works now. An absolutely heroic effort. The
design challenges were thus:
- given a text document, reproduce a tree of shared pointer objects
- the text document may have extra or bad node names (perhaps it's
from an older version) -- ignore these so they don't appear in the
actual tree
- the text document may be missing some node names (again, maybe an
older version) -- populate the actual tree nodes with default values
in this case
- any peripherals connected in text document nodes won't be in the
actual tree, so we have to connect them
- upon the creation of a new actual tree peripheral, we must populate
it with the metadata from the text document recursively, with the
above rules in effect
- we also need to allow the GUI to attach new peripherals, where it
would not know about the child nodes each peripheral contains (eg
the buttons on a gamepad)
- lastly, even though the tree is small, try not to take an absolute
eternity to import a document into the tree
There's a hell of a lot of restructuring that needs doing, in the SNES
core far more than anywhere else. We have to split bus mapping to occur
at power-on time, and manifest generation + slot attachments to happen
during load / system configuration. GBA also should move to detecting
save types at run-time. And honestly, that scan is pretty painfully slow
... it may make sense to suffix array it ... 13*4MiB isn't that much
memory. That or just write a smarter substring scanner. Sigh.
So now that things can be serialized, the next step is going to be
writing an input manager. It's going to be a radical departure from the
old way of doing things. Now, when the core wants input state, it'll
call inputPoll with a Node::Input object that we can directly write the
value to (well, after we `dynamic_cast` it to its specialized type. It'd
sure be nice if there were a `dynamic_multicast` switch construct, so we
didn't have to keep doing repeated `dynamic_cast` calls for each input
type. I digress.) I guess we can cache things ...
Thus, input polling will be more like this:
- produce a map of Node::Input to
`shared_pointer<InputManager::Binding>` objects
- maybe even do a multitap as a lazier way of handling multiple
input bindings
- have the InputManager::Binding hold a `shared_pointer<nall::HID>`
object, the group ID, and the input ID
- polling will thus be a map (or multitap) lookup; copying the
nall::HID value over to the Node::Input value
- it should be fine to just store `shared_pointers` here, to save
the overhead of constantly converting `weak_pointers`
And of course, we have to write a GUI for this. Oh, and before I can
even do the above ... I have to implement template importing, and
implement serialization support when attaching / detaching peripherals.
Hmm, what else ... I got rid of the Port specializations, as Port ends
up needing a lot of glue code and any special handling can be done
inside the emulation cores anyway. So now, a Port is a Port. It's just a
Node with the special property that it may contain either zero or one
Peripheral nodes. In addition to nodes of other types, of course.
I'm thinking we should probably also enforce a tree rule that no parent
node shall have two child nodes that have both the same type and the
same name. If we stick to that rule, we can simplify path traversal, and
allow the append function to be used as a "create if not exists"
function.
Also, we're really going to need a system to pick sensible default
peripheral and template paths for ports, including for dynamic ports.
Having to manually assign these, and having the values on dynamically
generated ports lost easily, is very annoying.
2019-02-03 16:53:32 +09:00
|
|
|
if(this == &source) return *this;
|
2022-09-03 19:52:53 +09:00
|
|
|
reset();
|
2015-08-02 15:23:13 +09:00
|
|
|
root = source.root;
|
|
|
|
nodes = source.nodes;
|
|
|
|
source.root = nullptr;
|
|
|
|
source.nodes = 0;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-05-16 18:51:12 +09:00
|
|
|
explicit operator bool() const { return nodes; }
|
2021-02-08 18:11:00 +09:00
|
|
|
auto size() const -> u32 { return nodes; }
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto reset() -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
reset(root);
|
|
|
|
nodes = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto find(const T& value) -> maybe<T&> {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(node_t* node = find(root, value)) return node->value;
|
2014-02-09 14:59:46 +09:00
|
|
|
return nothing;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto find(const T& value) const -> maybe<const T&> {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(node_t* node = find(root, value)) return node->value;
|
2014-02-09 14:59:46 +09:00
|
|
|
return nothing;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto insert(const T& value) -> maybe<T&> {
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 count = size();
|
2013-07-29 18:42:45 +09:00
|
|
|
node_t* v = insert(root, value);
|
2013-05-02 20:25:45 +09:00
|
|
|
root->red = 0;
|
2014-02-09 14:59:46 +09:00
|
|
|
if(size() == count) return nothing;
|
|
|
|
return v->value;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
2015-08-02 15:23:13 +09:00
|
|
|
template<typename... P> auto insert(const T& value, P&&... p) -> bool {
|
2013-05-02 20:25:45 +09:00
|
|
|
bool result = insert(value);
|
2022-09-13 16:47:47 +09:00
|
|
|
insert(std::forward<P>(p)...) | result;
|
2013-07-29 18:42:45 +09:00
|
|
|
return result;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto remove(const T& value) -> bool {
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 count = size();
|
2013-05-02 20:25:45 +09:00
|
|
|
bool done = 0;
|
|
|
|
remove(root, &value, done);
|
|
|
|
if(root) root->red = 0;
|
|
|
|
return size() < count;
|
|
|
|
}
|
|
|
|
|
2015-08-02 15:23:13 +09:00
|
|
|
template<typename... P> auto remove(const T& value, P&&... p) -> bool {
|
2013-05-02 20:25:45 +09:00
|
|
|
bool result = remove(value);
|
2022-09-13 16:47:47 +09:00
|
|
|
return remove(std::forward<P>(p)...) | result;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
struct base_iterator {
|
2015-07-14 18:32:43 +09:00
|
|
|
auto operator!=(const base_iterator& source) const -> bool { return position != source.position; }
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto operator++() -> base_iterator& {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(++position >= source.size()) { position = source.size(); return *this; }
|
|
|
|
|
2016-05-16 18:51:12 +09:00
|
|
|
if(stack.right()->link[1]) {
|
|
|
|
stack.append(stack.right()->link[1]);
|
|
|
|
while(stack.right()->link[0]) stack.append(stack.right()->link[0]);
|
2013-05-02 20:25:45 +09:00
|
|
|
} else {
|
|
|
|
node_t* child;
|
2016-05-16 18:51:12 +09:00
|
|
|
do child = stack.takeRight();
|
|
|
|
while(child == stack.right()->link[1]);
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:11:00 +09:00
|
|
|
base_iterator(const set& source, u32 position) : source(source), position(position) {
|
2013-05-02 20:25:45 +09:00
|
|
|
node_t* node = source.root;
|
|
|
|
while(node) {
|
|
|
|
stack.append(node);
|
|
|
|
node = node->link[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
const set& source;
|
2021-02-08 18:11:00 +09:00
|
|
|
u32 position;
|
2013-05-02 20:25:45 +09:00
|
|
|
vector<node_t*> stack;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct iterator : base_iterator {
|
2021-02-08 18:11:00 +09:00
|
|
|
iterator(const set& source, u32 position) : base_iterator(source, position) {}
|
2016-05-16 18:51:12 +09:00
|
|
|
auto operator*() const -> T& { return base_iterator::stack.right()->value; }
|
2013-05-02 20:25:45 +09:00
|
|
|
};
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto begin() -> iterator { return iterator(*this, 0); }
|
|
|
|
auto end() -> iterator { return iterator(*this, size()); }
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
struct const_iterator : base_iterator {
|
2021-02-08 18:11:00 +09:00
|
|
|
const_iterator(const set& source, u32 position) : base_iterator(source, position) {}
|
2016-05-16 18:51:12 +09:00
|
|
|
auto operator*() const -> const T& { return base_iterator::stack.right()->value; }
|
2013-05-02 20:25:45 +09:00
|
|
|
};
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto begin() const -> const const_iterator { return const_iterator(*this, 0); }
|
|
|
|
auto end() const -> const const_iterator { return const_iterator(*this, size()); }
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
private:
|
2015-07-14 18:32:43 +09:00
|
|
|
auto reset(node_t*& node) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(!node) return;
|
|
|
|
if(node->link[0]) reset(node->link[0]);
|
|
|
|
if(node->link[1]) reset(node->link[1]);
|
|
|
|
delete node;
|
|
|
|
node = nullptr;
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto copy(node_t*& target, const node_t* source) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(!source) return;
|
|
|
|
target = new node_t(source->value);
|
|
|
|
target->red = source->red;
|
|
|
|
copy(target->link[0], source->link[0]);
|
|
|
|
copy(target->link[1], source->link[1]);
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto find(node_t* node, const T& value) const -> node_t* {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(node == nullptr) return nullptr;
|
|
|
|
if(node->value == value) return node;
|
|
|
|
return find(node->link[node->value < value], value);
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto red(node_t* node) const -> bool { return node && node->red; }
|
|
|
|
auto black(node_t* node) const -> bool { return !red(node); }
|
2013-05-02 20:25:45 +09:00
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto rotate(node_t*& a, bool dir) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
node_t*& b = a->link[!dir];
|
|
|
|
node_t*& c = b->link[dir];
|
|
|
|
a->red = 1, b->red = 0;
|
|
|
|
std::swap(a, b);
|
|
|
|
std::swap(b, c);
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto rotateTwice(node_t*& node, bool dir) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
rotate(node->link[!dir], !dir);
|
|
|
|
rotate(node, dir);
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto insert(node_t*& node, const T& value) -> node_t* {
|
2013-07-29 18:42:45 +09:00
|
|
|
if(!node) { nodes++; node = new node_t(value); return node; }
|
|
|
|
if(node->value == value) { node->value = value; return node; } //prevent duplicate entries
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
bool dir = node->value < value;
|
2013-07-29 18:42:45 +09:00
|
|
|
node_t* v = insert(node->link[dir], value);
|
|
|
|
if(black(node->link[dir])) return v;
|
2013-05-02 20:25:45 +09:00
|
|
|
|
|
|
|
if(red(node->link[!dir])) {
|
|
|
|
node->red = 1;
|
|
|
|
node->link[0]->red = 0;
|
|
|
|
node->link[1]->red = 0;
|
|
|
|
} else if(red(node->link[dir]->link[dir])) {
|
|
|
|
rotate(node, !dir);
|
|
|
|
} else if(red(node->link[dir]->link[!dir])) {
|
|
|
|
rotateTwice(node, !dir);
|
|
|
|
}
|
2013-07-29 18:42:45 +09:00
|
|
|
|
|
|
|
return v;
|
2013-05-02 20:25:45 +09:00
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto balance(node_t*& node, bool dir, bool& done) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
node_t* p = node;
|
|
|
|
node_t* s = node->link[!dir];
|
|
|
|
if(!s) return;
|
|
|
|
|
|
|
|
if(red(s)) {
|
|
|
|
rotate(node, dir);
|
|
|
|
s = p->link[!dir];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(black(s->link[0]) && black(s->link[1])) {
|
|
|
|
if(red(p)) done = 1;
|
|
|
|
p->red = 0, s->red = 1;
|
|
|
|
} else {
|
|
|
|
bool save = p->red;
|
|
|
|
bool head = node == p;
|
|
|
|
|
|
|
|
if(red(s->link[!dir])) rotate(p, dir);
|
|
|
|
else rotateTwice(p, dir);
|
|
|
|
|
|
|
|
p->red = save;
|
|
|
|
p->link[0]->red = 0;
|
|
|
|
p->link[1]->red = 0;
|
|
|
|
|
|
|
|
if(head) node = p;
|
|
|
|
else node->link[dir] = p;
|
|
|
|
|
|
|
|
done = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:32:43 +09:00
|
|
|
auto remove(node_t*& node, const T* value, bool& done) -> void {
|
2013-05-02 20:25:45 +09:00
|
|
|
if(!node) { done = 1; return; }
|
|
|
|
|
|
|
|
if(node->value == *value) {
|
|
|
|
if(!node->link[0] || !node->link[1]) {
|
|
|
|
node_t* save = node->link[!node->link[0]];
|
|
|
|
|
|
|
|
if(red(node)) done = 1;
|
|
|
|
else if(red(save)) save->red = 0, done = 1;
|
|
|
|
|
|
|
|
nodes--;
|
|
|
|
delete node;
|
|
|
|
node = save;
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
node_t* heir = node->link[0];
|
|
|
|
while(heir->link[1]) heir = heir->link[1];
|
|
|
|
node->value = heir->value;
|
|
|
|
value = &heir->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dir = node->value < *value;
|
|
|
|
remove(node->link[dir], value, done);
|
|
|
|
if(!done) balance(node, dir, done);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|