Update to v094r09 release.

byuu says:

This will easily be the biggest diff in the history of higan. And not in
a good way.

* target-higan and target-loki have been blown away completely
* nall and ruby massively updated
* phoenix replaced with hiro (pretty near a total rewrite)
* target-higan restarted using hiro (just a window for now)
* all emulation cores updated to compile again
* installation changed to not require root privileges (installs locally)

For the foreseeable future (maybe even permanently?), the new higan UI
will only build under Linux/BSD with GTK+ 2.20+. Probably the most
likely route for Windows/OS X will be to try and figure out how to build
hiro/GTK on those platforms, as awful as that would be. The other
alternative would be to produce new UIs for those platforms ... which
would actually be a good opportunity to make something much more user
friendly.

Being that I just started on this a few hours ago, that means that for
at least a few weeks, don't expect to be able to actually play any
games. Right now, you can pretty much just compile the binary and that's
it. It's quite possible that some nall changes didn't produce
compilation errors, but will produce runtime errors. So until the UI can
actually load games, we won't know if anything is broken. But we should
mostly be okay. It was mostly just trim<1> -> trim changes, moving to
Hash::SHA256 (much cleaner), and patching some reckless memory copy
functions enough to compile.

Progress isn't going to be like it was before: I'm now dividing my time
much thinner between studying and other hobbies.

My aim this time is not to produce a binary for everyone to play games
on. Rather, it's to keep the emulator alive. I want to be able to apply
critical patches again. And I would also like the base of the emulator
to live on, for use in other emulator frontends that utilize higan.
This commit is contained in:
Tim Allen
2015-02-26 21:10:46 +11:00
parent 1a7bc6bb87
commit a512d14628
793 changed files with 20182 additions and 19416 deletions

View File

@@ -0,0 +1,123 @@
#ifdef NALL_STRING_INTERNAL_HPP
/*****
adaptive allocator
sizeof(string) == SSO + 8
aggressively tries to avoid heap allocations
small strings are stored on the stack
large strings are shared via copy-on-write
SSO alone is very slow on large strings due to copying
SSO alone is very slightly faster than this allocator on small strings
COW alone is very slow on small strings due to heap allocations
COW alone is very slightly faster than this allocator on large strings
adaptive is thus very fast for all string sizes
*****/
namespace nall {
string::string() : _data(nullptr), _capacity(SSO - 1), _size(0) {
}
auto string::pointer() -> char* {
if(_capacity < SSO) return _text;
if(*_refs > 1) _copy();
return _data;
}
auto string::data() const -> const char* {
if(_capacity < SSO) return _text;
return _data;
}
auto string::reset() -> type& {
if(_capacity >= SSO && !--*_refs) memory::free(_data);
_data = nullptr;
_capacity = SSO - 1;
_size = 0;
return *this;
}
auto string::reserve(unsigned capacity) -> type& {
if(capacity <= _capacity) return *this;
capacity = bit::round(capacity + 1) - 1;
if(_capacity < SSO) {
_capacity = capacity;
_allocate();
} else if(*_refs > 1) {
_capacity = capacity;
_copy();
} else {
_capacity = capacity;
_resize();
}
return *this;
}
auto string::resize(unsigned size) -> type& {
reserve(size);
pointer()[_size = size] = 0;
return *this;
}
auto string::operator=(const string& source) -> type& {
if(&source == this) return *this;
reset();
if(source._capacity >= SSO) {
_data = source._data;
_refs = source._refs;
_capacity = source._capacity;
_size = source._size;
++*_refs;
} else {
memory::copy(_text, source._text, SSO);
_capacity = source._capacity;
_size = source._size;
}
return *this;
}
auto string::operator=(string&& source) -> type& {
if(&source == this) return *this;
reset();
memory::copy(this, &source, sizeof(string));
source._data = nullptr;
source._capacity = SSO - 1;
source._size = 0;
return *this;
}
//SSO -> COW
auto string::_allocate() -> void {
char _temp[SSO];
memory::copy(_temp, _text, SSO);
_data = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
memory::copy(_data, _temp, SSO);
_refs = (unsigned*)(_data + _capacity + 1); //always aligned by 32 via reserve()
*_refs = 1;
}
//COW -> Unique
auto string::_copy() -> void {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
memory::copy(_temp, _data, _size = min(_capacity, _size));
_temp[_size] = 0;
--*_refs;
_data = _temp;
_refs = (unsigned*)(_data + _capacity + 1);
*_refs = 1;
}
//COW -> Resize
auto string::_resize() -> void {
_data = (char*)memory::resize(_data, _capacity + 1 + sizeof(unsigned));
_refs = (unsigned*)(_data + _capacity + 1);
*_refs = 1;
}
}
#endif

View File

@@ -1,102 +1,90 @@
#ifdef NALL_STRING_INTERNAL_HPP
/*
copy on write (COW) allocator
sizeof(string) == 24 (amd64)
utilizes a shared_ptr to reference count strings
allows string copies to execute as fast as string moves
requires extra computations, which will be slower for all string sizes
pros:
* lower memory usage
* pass-by-value does not require heap allocation; obviates pass-by-const-reference
cons:
* added overhead to fetch data()
* added heap allocation for reference-count pool
* no potential for in-place resize (always copies)
* larger sizeof(string)
*/
namespace nall {
char* string::data() {
if(!_data.unique()) _copy();
return _data.get();
string::string() : _data(nullptr), _refs(nullptr), _capacity(0), _size(0) {
}
const char* string::data() const {
if(!_data) return "";
return _data.get();
auto string::pointer() -> char* {
static char _null[] = "";
if(!_data) return _null;
if(*_refs > 1) _data = _copy(); //make unique for write operations
return _data;
}
//copy _data (to make unique or to grow in size)
void string::_copy() {
auto copy = new char[_capacity + 1];
if(_data.get()) memcpy(copy, _data.get(), min(_capacity, _size));
copy[_size] = 0;
copy[_capacity] = 0;
_data.reset(copy);
auto string::data() const -> const char* {
static const char _null[] = "";
if(!_data) return _null;
return _data;
}
//amortize growth to O(log n)
//allocate one extra byte to always store null-terminator for libc usage
void string::reserve(unsigned capacity) {
if(capacity > _capacity) {
_capacity = bit::round(capacity + 1) - 1;
_copy();
auto string::reset() -> type& {
if(_data && !--*_refs) {
memory::free(_data);
_data = nullptr; //_refs = nullptr; is unnecessary
}
}
void string::resize(unsigned size) {
reserve(size);
data()[_size = size] = 0;
}
void string::reset() {
_data.reset();
_capacity = 0;
_size = 0;
}
string& string::operator=(const string& source) {
if(&source == this) return *this;
reset();
_data = source._data;
_capacity = source._capacity;
_size = source._size;
return *this;
}
string& string::operator=(string&& source) {
auto string::reserve(unsigned capacity) -> type& {
if(capacity > _capacity) {
_capacity = bit::round(max(31u, capacity) + 1) - 1;
_data = _data ? _copy() : _allocate();
}
return *this;
}
auto string::resize(unsigned size) -> type& {
reserve(size);
pointer()[_size = size] = 0;
return *this;
}
auto string::operator=(const string& source) -> string& {
if(&source == this) return *this;
reset();
_data = std::move(source._data);
if(source._data) {
_data = source._data;
_refs = source._refs;
_capacity = source._capacity;
_size = source._size;
++*_refs;
}
return *this;
}
auto string::operator=(string&& source) -> string& {
if(&source == this) return *this;
reset();
_data = source._data;
_refs = source._refs;
_capacity = source._capacity;
_size = source._size;
source._data = nullptr;
source._refs = nullptr;
source._capacity = 0;
source._size = 0;
return *this;
}
template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
construct();
sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
auto string::_allocate() -> char* {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
*_temp = 0;
_refs = (unsigned*)(_temp + _capacity + 1); //this will always be aligned by 32 via reserve()
*_refs = 1;
return _temp;
}
string::string() {
construct();
}
string::~string() {
reset();
}
void string::construct() {
_capacity = 0;
_size = 0;
auto string::_copy() -> char* {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
memory::copy(_temp, _data, _size = min(_capacity, _size));
_temp[_size] = 0;
--*_refs;
_refs = (unsigned*)(_temp + _capacity + 1);
*_refs = 1;
return _temp;
}
}

View File

@@ -2,7 +2,7 @@
/*
small string optimization (SSO) allocator
sizeof(string) == 16 (amd64)
sizeof(string) == 8 + string::SSO
utilizes a union to store small strings directly into text pointer
bypasses the need to allocate heap memory for small strings
@@ -10,102 +10,86 @@ requires extra computations, which can be slower for large strings
pros:
* potential for in-place resize
* no heap allocation when (capacity < 8)
* no heap allocation when (capacity < SSO)
cons:
* added overhead to fetch data()
* 32-bit platforms limited to (capacity < 4)
* pass-by-value requires heap allocation
* pass-by-value requires heap allocation when (capacity >= SSO)
*/
namespace nall {
char* string::data() {
if(_capacity < SSO) return _text;
return _data;
}
const char* string::data() const {
if(_capacity < SSO) return _text;
return _data;
}
void string::reserve(unsigned capacity) {
if(capacity > _capacity) {
if(capacity >= SSO) {
capacity = bit::round(capacity + 1) - 1;
if(_capacity < SSO) {
char temp[SSO];
memcpy(temp, _text, SSO);
_data = (char*)malloc(capacity + 1);
memcpy(_data, temp, SSO);
} else {
_data = (char*)realloc(_data, capacity + 1);
}
}
_capacity = capacity;
data()[_capacity] = 0;
}
}
void string::resize(unsigned size) {
reserve(size);
data()[_size = size] = 0;
}
void string::reset() {
if(_capacity >= SSO) free(_data);
string::string() {
_data = nullptr;
_capacity = SSO - 1;
_size = 0;
}
string& string::operator=(const string& source) {
if(&source == this) return *this;
reset();
if(source._capacity >= SSO) {
_data = (char*)malloc(source._capacity + 1);
_capacity = source._capacity;
_size = source._size;
memcpy(_data, source.data(), source.size() + 1);
auto string::pointer() -> char* {
if(_capacity < SSO) return _text;
return _data;
}
auto string::data() const -> const char* {
if(_capacity < SSO) return _text;
return _data;
}
auto string::reset() -> type& {
if(_capacity >= SSO) memory::free(_data);
_data = nullptr;
_capacity = SSO - 1;
_size = 0;
return *this;
}
auto string::reserve(unsigned capacity) -> type& {
if(capacity <= _capacity) return *this;
capacity = bit::round(capacity + 1) - 1;
if(_capacity < SSO) {
char _temp[SSO];
memory::copy(_temp, _text, SSO);
_data = (char*)memory::allocate(_capacity = capacity + 1);
memory::copy(_data, _temp, SSO);
} else {
memcpy(_text, source._text, SSO);
_capacity = SSO - 1;
_size = strlen(_text);
_data = (char*)memory::resize(_data, _capacity = capacity + 1);
}
return *this;
}
string& string::operator=(string&& source) {
auto string::resize(unsigned size) -> type& {
reserve(size);
pointer()[_size = size] = 0;
return *this;
}
auto string::operator=(const string& source) -> type& {
if(&source == this) return *this;
reset();
memcpy(this, &source, sizeof(string));
if(source._capacity >= SSO) {
_data = (char*)memory::allocate(source._capacity + 1);
_capacity = source._capacity;
_size = source._size;
memory::copy(_data, source._data, source._size + 1);
} else {
memory::copy(_text, source._text, SSO);
_capacity = SSO - 1;
_size = source._size;
}
return *this;
}
auto string::operator=(string&& source) -> type& {
if(&source == this) return *this;
reset();
memory::copy(this, &source, sizeof(string));
source._data = nullptr;
source._capacity = SSO - 1;
source._size = 0;
return *this;
}
template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
construct();
sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
}
string::string() {
construct();
}
string::~string() {
reset();
}
void string::construct() {
_data = nullptr;
_capacity = SSO - 1;
_size = 0;
}
}
#endif

View File

@@ -19,46 +19,49 @@ cons:
namespace nall {
char* string::data() {
auto string::pointer() -> char* {
if(_capacity == 0) reserve(1);
return _data;
}
const char* string::data() const {
auto string::data() const -> const char* {
if(_capacity == 0) return "";
return _data;
}
void string::reserve(unsigned capacity) {
if(capacity > _capacity) {
_capacity = bit::round(capacity + 1) - 1;
_data = (char*)realloc(_data, _capacity + 1);
_data[_capacity] = 0;
}
}
void string::resize(unsigned size) {
reserve(size);
data()[_size = size] = 0;
}
void string::reset() {
if(_data) { free(_data); _data = nullptr; }
auto string::reset() -> type& {
if(_data) { memory::free(_data); _data = nullptr; }
_capacity = 0;
_size = 0;
}
string& string::operator=(const string& source) {
if(&source == this) return *this;
reset();
_data = (char*)malloc(source._size + 1);
_capacity = source._size;
_size = source._size;
memcpy(_data, source.data(), source.size() + 1);
return *this;
}
string& string::operator=(string&& source) {
auto string::reserve(unsigned capacity) -> type& {
if(capacity > _capacity) {
_capacity = bit::round(capacity + 1) - 1;
_data = (char*)memory::resize(_data, _capacity + 1);
_data[_capacity] = 0;
}
return *this;
}
auto string::resize(unsigned size) -> type& {
reserve(size);
pointer()[_size = size] = 0;
return *this;
}
auto string::operator=(const string& source) -> type& {
if(&source == this) return *this;
reset();
_data = (char*)memory::allocate(source._size + 1);
_capacity = source._size;
_size = source._size;
memory::copy(_data, source.data(), source.size() + 1);
return *this;
}
auto string::operator=(string&& source) -> type& {
if(&source == this) return *this;
reset();
_data = source._data;
@@ -70,20 +73,7 @@ string& string::operator=(string&& source) {
return *this;
}
template<typename T, typename... Args> string::string(T&& source, Args&&... args) {
construct();
sprint(*this, std::forward<T>(source), std::forward<Args>(args)...);
}
string::string() {
construct();
}
string::~string() {
reset();
}
void string::construct() {
_data = nullptr;
_capacity = 0;
_size = 0;