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;

View File

@@ -3,19 +3,165 @@
namespace nall {
struct string;
struct format;
struct stringref;
struct lstring;
typedef const stringref& rstring;
using cstring = const string&;
using rstring = const stringref&;
#define NALL_STRING_ALLOCATOR_ADAPTIVE
//#define NALL_STRING_ALLOCATOR_COPY_ON_WRITE
#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION
//#define NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION
//#define NALL_STRING_ALLOCATOR_VECTOR
//cast.hpp
template<typename T> struct stringify;
//compare.hpp
inline auto compare(const string& self, rstring source) -> signed;
inline auto icompare(const string& self, rstring source) -> signed;
inline auto equals(const string& self, rstring source) -> bool;
inline auto iequals(const string& self, rstring source) -> bool;
inline auto beginsWith(const string& self, rstring source) -> bool;
inline auto ibeginsWith(const string& self, rstring source) -> bool;
inline auto endsWith(const string& self, rstring source) -> bool;
inline auto iendsWith(const string& self, rstring source) -> bool;
//convert.hpp
inline auto downcase(string& self) -> string&;
inline auto qdowncase(string& self) -> string&;
inline auto upcase(string& self) -> string&;
inline auto qupcase(string& self) -> string&;
inline auto transform(string& self, rstring from, rstring to) -> string&;
//core.hpp
template<typename... P> inline auto assign(string& self, P&&... p) -> string&;
template<typename T, typename... P> inline auto append(string& self, const T& value, P&&... p) -> string&;
template<typename... P> inline auto append(string& self, const format& value, P&&... p) -> string&;
inline auto append(string& self) -> string&;
template<typename T> inline auto _append(string& self, const stringify<T>& source) -> string&;
inline auto empty(const string& self) -> bool;
inline auto length(const string& self) -> unsigned;
//find.hpp
template<bool I, bool Q> inline auto _find(const string& self, signed offset, rstring source) -> maybe<unsigned>;
inline auto find(const string& self, rstring source) -> maybe<unsigned>;
inline auto ifind(const string& self, rstring source) -> maybe<unsigned>;
inline auto qfind(const string& self, rstring source) -> maybe<unsigned>;
inline auto iqfind(const string& self, rstring source) -> maybe<unsigned>;
inline auto findFrom(const string& self, signed offset, rstring source) -> maybe<unsigned>;
inline auto ifindFrom(const string& self, signed offset, rstring source) -> maybe<unsigned>;
//format.hpp
template<typename... P> inline auto print(P&&...) -> void;
template<signed precision = 0, char padchar = '0'> inline auto integer(intmax_t value) -> string;
template<signed precision = 0, char padchar = '0'> inline auto decimal(uintmax_t value) -> string;
template<signed precision = 0, char padchar = '0'> inline auto hex(uintmax_t value) -> string;
template<signed precision = 0, char padchar = '0'> inline auto octal(uintmax_t value) -> string;
template<signed precision = 0, char padchar = '0'> inline auto binary(uintmax_t value) -> string;
template<signed precision = 0, typename T> inline auto pointer(const T* value) -> string;
template<signed precision = 0> inline auto pointer(uintptr_t value) -> string;
inline auto real(long double value) -> string;
//hash.hpp
inline auto crc16(const string& self) -> string;
inline auto crc32(const string& self) -> string;
inline auto sha256(const string& self) -> string;
//match.hpp
inline auto match(const string& self, rstring source) -> bool;
inline auto imatch(const string& self, rstring source) -> bool;
inline auto tokenize(lstring& list, const char* s, const char* p) -> bool;
//path.hpp
inline auto pathname(const string& self) -> string;
inline auto filename(const string& self) -> string;
inline auto dirname(const string& self) -> string;
inline auto basename(const string& self) -> string;
inline auto prefixname(const string& self) -> string;
inline auto suffixname(const string& self) -> string;
//platform.hpp
inline auto activepath() -> string;
inline auto realpath(rstring name) -> string;
inline auto programpath() -> string;
inline auto userpath() -> string;
inline auto configpath() -> string;
inline auto sharedpath() -> string;
inline auto temppath() -> string;
//replace.hpp
template<unsigned L, bool I, bool Q> inline auto _replace(string& self, rstring from, rstring to) -> string&;
template<unsigned L = ~0u> inline auto replace(string& self, rstring from, rstring to) -> string&;
template<unsigned L = ~0u> inline auto ireplace(string& self, rstring from, rstring to) -> string&;
template<unsigned L = ~0u> inline auto qreplace(string& self, rstring from, rstring to) -> string&;
template<unsigned L = ~0u> inline auto iqreplace(string& self, rstring from, rstring to) -> string&;
//split.hpp
template<unsigned L, bool I, bool Q> inline auto _split(lstring& self, rstring source, rstring find) -> lstring&;
template<unsigned L = ~0u> inline auto split(string& self, rstring key) -> lstring;
template<unsigned L = ~0u> inline auto isplit(string& self, rstring key) -> lstring;
template<unsigned L = ~0u> inline auto qsplit(string& self, rstring key) -> lstring;
template<unsigned L = ~0u> inline auto iqsplit(string& self, rstring key) -> lstring;
//trim.hpp
inline auto trim(string& self, rstring lhs, rstring rhs) -> bool;
inline auto ltrim(string& self, rstring lhs) -> bool;
inline auto rtrim(string& self, rstring rhs) -> bool;
inline auto itrim(string& self, rstring lhs, rstring rhs) -> bool;
inline auto iltrim(string& self, rstring lhs) -> bool;
inline auto irtrim(string& self, rstring rhs) -> bool;
inline auto strip(string& self) -> bool;
inline auto lstrip(string& self) -> bool;
inline auto rstrip(string& self) -> bool;
//utility.hpp
inline auto fill(string& self, char fill = ' ') -> string&;
inline auto hash(const string& self) -> unsigned;
inline auto remove(string& self, unsigned offset, unsigned length) -> string&;
inline auto reverse(string& self) -> string&;
inline auto size(string& self, signed length, char fill = ' ') -> string&;
inline auto slice(const string& self, signed offset = 0, signed length = -1) -> string;
inline auto substr(rstring source, signed offset = 0, signed length = -1) -> string;
inline auto integer(char* result, intmax_t value) -> char*;
inline auto decimal(char* result, uintmax_t value) -> char*;
inline auto real(char* str, long double value) -> unsigned;
struct string {
using type = string;
struct exception_out_of_bounds{};
protected:
#if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
enum : unsigned { SSO = 24 };
union {
struct { //copy-on-write
char* _data;
unsigned* _refs;
};
struct { //small-string-optimization
char _text[SSO];
};
};
inline auto _allocate() -> void;
inline auto _copy() -> void;
inline auto _resize() -> void;
#endif
#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
inline void _copy();
std::shared_ptr<char> _data;
char* _data;
mutable unsigned* _refs;
inline auto _allocate() -> char*;
inline auto _copy() -> char*;
#endif
#if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
@@ -34,194 +180,213 @@ protected:
unsigned _size;
public:
inline string();
inline auto pointer() -> char*;
inline auto data() const -> const char*;
inline auto reset() -> type&;
inline auto reserve(unsigned) -> type&;
inline auto resize(unsigned) -> type&;
inline auto operator=(const string&) -> type&;
inline auto operator=(string&&) -> type&;
template<typename T, typename... P> string(T&& s, P&&... p) : string() { append(std::forward<T>(s), std::forward<P>(p)...); }
~string() { reset(); }
explicit operator bool() const { return _size; }
operator const uint8_t*() const { return (const uint8_t*)data(); }
operator const char*() const { return (const char*)data(); }
auto binary() const -> const uint8_t* { return (const uint8_t*)data(); }
auto size() const -> unsigned { return _size; }
auto capacity() const -> unsigned { return _capacity; }
auto operator==(const string& s) const -> bool { return size() == s.size() && memory::compare(data(), s.data(), s.size()) == 0; }
auto operator!=(const string& s) const -> bool { return size() != s.size() || memory::compare(data(), s.data(), s.size()) != 0; }
auto operator==(const char* s) const -> bool { return strcmp(data(), s) == 0; }
auto operator!=(const char* s) const -> bool { return strcmp(data(), s) != 0; }
auto operator< (const char* s) const -> bool { return strcmp(data(), s) < 0; }
auto operator<=(const char* s) const -> bool { return strcmp(data(), s) <= 0; }
auto operator> (const char* s) const -> bool { return strcmp(data(), s) > 0; }
auto operator>=(const char* s) const -> bool { return strcmp(data(), s) >= 0; }
string(const string& source) : string() { operator=(source); }
string(string&& source) : string() { operator=(std::move(source)); }
auto begin() -> char* { return &pointer()[0]; }
auto end() -> char* { return &pointer()[size()]; }
auto begin() const -> const char* { return &data()[0]; }
auto end() const -> const char* { return &data()[size()]; }
//nall/atoi.hpp
inline auto integer() const -> intmax_t { return nall::integer(*this); }
inline auto decimal() const -> uintmax_t { return nall::decimal(*this); }
inline auto hex() const -> uintmax_t { return nall::hex(*this); }
//core.hpp
inline char* data();
inline const char* data() const;
inline unsigned length() const;
inline unsigned size() const;
inline unsigned capacity() const;
inline bool empty() const;
inline void reset();
inline void reserve(unsigned);
inline void resize(unsigned);
inline void clear(char);
inline unsigned hash() const;
template<typename... Args> inline string& assign(Args&&... args);
template<typename... Args> inline string& append(Args&&... args);
//file.hpp
inline static string read(const string& filename);
inline auto operator[](signed) const -> const char&;
//datetime.hpp
inline static string date();
inline static string time();
inline static string datetime();
inline static auto date(time_t = 0) -> string;
inline static auto time(time_t = 0) -> string;
inline static auto datetime(time_t = 0) -> string;
//replace.hpp
template<unsigned Limit = 0> inline string& replace(rstring, rstring);
template<unsigned Limit = 0> inline string& ireplace(rstring, rstring);
template<unsigned Limit = 0> inline string& qreplace(rstring, rstring);
template<unsigned Limit = 0> inline string& iqreplace(rstring, rstring);
//format.hpp
inline auto format(const nall::format& params) -> type&;
//wrapper.hpp
template<unsigned Limit = 0> inline lstring split(rstring) const;
template<unsigned Limit = 0> inline lstring isplit(rstring) const;
template<unsigned Limit = 0> inline lstring qsplit(rstring) const;
template<unsigned Limit = 0> inline lstring iqsplit(rstring) const;
//utility.hpp
inline static auto read(const string& filename) -> string;
template<unsigned L> inline static auto repeat(const string& pattern) -> string;
inline signed compare(rstring) const;
inline signed icompare(rstring) const;
//extension methods
//=================
inline bool equals(rstring) const;
inline bool iequals(rstring) const;
//compare.hpp
auto compare(rstring source) const -> signed { return nall::compare(*this, source); }
auto icompare(rstring source) const -> signed { return nall::compare(*this, source); }
inline bool match(rstring) const;
inline bool imatch(rstring) const;
auto equals(rstring source) const -> bool { return nall::equals(*this, source); }
auto iequals(rstring source) const -> bool { return nall::iequals(*this, source); }
inline bool beginsWith(rstring) const;
inline bool ibeginsWith(rstring) const;
inline bool endsWith(rstring) const;
inline bool iendsWith(rstring) const;
auto beginsWith(rstring source) const -> bool { return nall::beginsWith(*this, source); }
auto ibeginsWith(rstring source) const -> bool { return nall::ibeginsWith(*this, source); }
inline string slice(unsigned offset, unsigned length = ~0u) const;
auto endsWith(rstring source) const -> bool { return nall::endsWith(*this, source); }
auto iendsWith(rstring source) const -> bool { return nall::iendsWith(*this, source); }
inline string& lower();
inline string& upper();
inline string& qlower();
inline string& qupper();
inline string& transform(rstring before, rstring after);
inline string& reverse();
//convert.hpp
auto downcase() -> type& { return nall::downcase(*this); }
auto upcase() -> type& { return nall::upcase(*this); }
template<unsigned limit = 0> inline string& ltrim() { return ltrim<limit>(" "); }
template<unsigned limit = 0> inline string& ltrim(rstring key);
auto qdowncase() -> type& { return nall::qdowncase(*this); }
auto qupcase() -> type& { return nall::qupcase(*this); }
template<unsigned limit = 0> inline string& rtrim() { return rtrim<limit>(" "); }
template<unsigned limit = 0> inline string& rtrim(rstring key);
template<unsigned limit = 0> inline string& trim() { return trim<limit>(" "); }
template<unsigned limit = 0> inline string& trim(rstring key);
template<unsigned limit = 0> inline string& trim(rstring key, rstring rkey);
inline string& strip();
inline maybe<unsigned> find(rstring key) const;
inline maybe<unsigned> ifind(rstring key) const;
inline maybe<unsigned> qfind(rstring key) const;
inline maybe<unsigned> iqfind(rstring key) const;
auto transform(rstring from, rstring to) -> type& { return nall::transform(*this, from, to); }
//core.hpp
inline explicit operator bool() const;
inline operator const char*() const;
inline char& operator[](signed);
inline const char& operator[](signed) const;
template<typename... P> auto assign(P&&... p) -> type& { return nall::assign(*this, std::forward<P>(p)...); }
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::forward<P>(p)...); }
auto empty() const -> bool { return nall::empty(*this); }
auto length() const -> unsigned { return nall::length(*this); }
inline bool operator==(const char*) const;
inline bool operator!=(const char*) const;
inline bool operator< (const char*) const;
inline bool operator<=(const char*) const;
inline bool operator> (const char*) const;
inline bool operator>=(const char*) const;
//find.hpp
auto find(rstring source) const -> maybe<unsigned> { return nall::find(*this, source); }
auto ifind(rstring source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
auto qfind(rstring source) const -> maybe<unsigned> { return nall::qfind(*this, source); }
auto iqfind(rstring source) const -> maybe<unsigned> { return nall::iqfind(*this, source); }
inline string& operator=(const string&);
inline string& operator=(string&&);
auto findFrom(signed offset, rstring source) const -> maybe<unsigned> { return nall::findFrom(*this, offset, source); }
auto ifindFrom(signed offset, rstring source) const -> maybe<unsigned> { return nall::ifindFrom(*this, offset, source); }
template<typename T, typename... Args> inline string(T&& source, Args&&... args);
inline string();
inline string(const string&);
inline string(string&&);
inline ~string();
//hash.hpp
auto crc16() const -> string { return nall::crc16(*this); }
auto crc32() const -> string { return nall::crc32(*this); }
auto sha256() const -> string { return nall::sha256(*this); }
inline char* begin() { return &data()[0]; }
inline char* end() { return &data()[size()]; }
inline const char* begin() const { return &data()[0]; }
inline const char* end() const { return &data()[size()]; }
//match.hpp
auto match(rstring source) const -> bool { return nall::match(*this, source); }
auto imatch(rstring source) const -> bool { return nall::imatch(*this, source); }
//protected:
struct exception_out_of_bounds{};
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(rstring, rstring);
inline string& _append(const char*);
//path.hpp
auto pathname() const -> string { return nall::pathname(*this); }
auto filename() const -> string { return nall::filename(*this); }
private:
inline void construct();
auto dirname() const -> string { return nall::dirname(*this); }
auto basename() const -> string { return nall::basename(*this); }
auto prefixname() const -> string { return nall::prefixname(*this); }
auto suffixname() const -> string { return nall::suffixname(*this); }
#if defined(QSTRING_H)
public:
//replace.hpp
template<unsigned L = ~0u> auto replace(rstring from, rstring to) -> type& { return nall::_replace<L, 0, 0>(*this, from, to); }
template<unsigned L = ~0u> auto ireplace(rstring from, rstring to) -> type& { return nall::_replace<L, 1, 0>(*this, from, to); }
template<unsigned L = ~0u> auto qreplace(rstring from, rstring to) -> type& { return nall::_replace<L, 0, 1>(*this, from, to); }
template<unsigned L = ~0u> auto iqreplace(rstring from, rstring to) -> type& { return nall::_replace<L, 1, 1>(*this, from, to); }
//split.hpp
template<unsigned L = ~0u> inline auto split(rstring key) const -> lstring;
template<unsigned L = ~0u> inline auto isplit(rstring key) const -> lstring;
template<unsigned L = ~0u> inline auto qsplit(rstring key) const -> lstring;
template<unsigned L = ~0u> inline auto iqsplit(rstring key) const -> lstring;
//trim.hpp
auto trim(rstring lhs, rstring rhs) -> type& { return nall::trim(*this, lhs, rhs), *this; }
auto ltrim(rstring lhs) -> type& { return nall::ltrim(*this, lhs), *this; }
auto rtrim(rstring rhs) -> type& { return nall::rtrim(*this, rhs), *this; }
auto itrim(rstring lhs, rstring rhs) -> type& { return nall::itrim(*this, lhs, rhs), *this; }
auto iltrim(rstring lhs) -> type& { return nall::iltrim(*this, lhs), *this; }
auto irtrim(rstring rhs) -> type& { return nall::irtrim(*this, rhs), *this; }
auto strip() -> type& { return nall::strip(*this), *this; }
auto lstrip() -> type& { return nall::lstrip(*this), *this; }
auto rstrip() -> type& { return nall::rstrip(*this), *this; }
//utility.hpp
auto fill(char fill = ' ') -> type& { return nall::fill(*this, fill); }
auto hash() const -> const type& { return nall::hash(*this), *this; }
auto remove(unsigned offset, unsigned length) -> type& { return nall::remove(*this, offset, length); }
auto reverse() -> type& { return nall::reverse(*this); }
auto size(signed length, char fill = ' ') -> type& { return nall::size(*this, length, fill); }
auto slice(signed offset = 0, signed length = -1) const -> string { return nall::slice(*this, offset, length); }
#if defined(QSTRING_H)
inline operator QString() const;
#endif
#endif
};
//list.hpp
template<typename... P> auto append(lstring& self, const string& value, P&&... p) -> lstring&;
inline auto append(lstring& self) -> lstring&;
inline auto find(const lstring& self, const string& source) -> maybe<unsigned>;
inline auto ifind(const lstring& self, const string& source) -> maybe<unsigned>;
inline auto merge(const lstring& self, const string& separator) -> string;
inline auto strip(lstring& self) -> lstring&;
struct lstring : vector<string> {
inline maybe<unsigned> find(rstring) const;
inline string merge(const string&) const;
inline lstring& isort();
inline lstring& strip();
inline void append() {}
template<typename... Args> inline void append(const string&, Args&&...);
using type = lstring;
inline bool operator==(const lstring&) const;
inline bool operator!=(const lstring&) const;
lstring(const lstring& source) { vector::operator=(source); }
lstring(lstring& source) { vector::operator=(source); }
lstring(lstring&& source) { vector::operator=(std::move(source)); }
template<typename... P> lstring(P&&... p) { append(std::forward<P>(p)...); }
inline lstring& operator=(const lstring&);
inline lstring& operator=(lstring&);
inline lstring& operator=(lstring&&);
//list.hpp
inline auto operator==(const lstring&) const -> bool;
inline auto operator!=(const lstring&) const -> bool;
template<typename... Args> inline lstring(Args&&... args);
inline lstring(const lstring&);
inline lstring(lstring&);
inline lstring(lstring&&);
inline auto operator=(const lstring& source) -> type& { return vector::operator=(source), *this; }
inline auto operator=(lstring& source) -> type& { return vector::operator=(source), *this; }
inline auto operator=(lstring&& source) -> type& { return vector::operator=(std::move(source)), *this; }
inline auto isort() -> type&;
//extension methods
//=================
//list.hpp
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::forward<P>(p)...); }
auto find(const string& source) const -> maybe<unsigned> { return nall::find(*this, source); }
auto ifind(const string& source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
auto merge(const string& separator) const -> string { return nall::merge(*this, separator); }
auto strip() -> type& { return nall::strip(*this); }
//split.hpp
template<unsigned Limit = 0> inline lstring& split(rstring, rstring);
template<unsigned Limit = 0> inline lstring& isplit(rstring, rstring);
template<unsigned Limit = 0> inline lstring& qsplit(rstring, rstring);
template<unsigned Limit = 0> inline lstring& iqsplit(rstring, rstring);
protected:
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(rstring, rstring);
template<unsigned L = ~0u> auto split(rstring source, rstring on) -> type& { return nall::_split<L, 0, 0>(*this, source, on); }
template<unsigned L = ~0u> auto isplit(rstring source, rstring on) -> type& { return nall::_split<L, 1, 0>(*this, source, on); }
template<unsigned L = ~0u> auto qsplit(rstring source, rstring on) -> type& { return nall::_split<L, 0, 1>(*this, source, on); }
template<unsigned L = ~0u> auto iqsplit(rstring source, rstring on) -> type& { return nall::_split<L, 1, 1>(*this, source, on); }
};
//filename.hpp
inline string dir(string name);
inline string notdir(string name);
inline string parentdir(string name);
inline string basename(string name);
inline string extension(string name);
inline string tempname();
//format.hpp
template<signed precision = 0, char padchar = ' '> inline string format(const string& value);
template<signed precision = 0, char padchar = '0'> inline string hex(uintmax_t value);
template<signed precision = 0, char padchar = '0'> inline string octal(uintmax_t value);
template<signed precision = 0, char padchar = '0'> inline string binary(uintmax_t value);
template<typename T, typename... P> inline auto append(format& self, const T& value, P&&... p) -> format&;
inline auto append(format& self) -> format&;
//platform.hpp
inline string activepath();
inline string realpath(const string& name);
inline string programpath();
inline string userpath();
inline string configpath();
inline string sharedpath();
inline string temppath();
struct format : vector<string> {
using type = format;
//utility.hpp
inline string substr(rstring source, unsigned offset = 0, unsigned length = ~0u);
inline string sha256(const uint8_t* data, unsigned size);
inline bool tokenize(lstring& list, const char* s, const char* p);
inline char* integer(char* result, intmax_t value);
inline char* decimal(char* result, uintmax_t value);
inline unsigned real(char* str, long double value);
inline string real(long double value);
//variadic.hpp
inline void sprint(string& output);
template<typename T, typename... Args> inline void sprint(string& output, const T& value, Args&&... args);
template<typename... Args> inline void print(Args&&... args);
template<typename... P> format(P&&... p) { reserve(sizeof...(p)); append(std::forward<P>(p)...); }
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, std::forward<P>(p)...); }
};
}

View File

@@ -1,185 +1,209 @@
#ifdef NALL_STRING_INTERNAL_HPP
//convert any (supported) type to a const char* without constructing a new nall::string
//this is used inside string{...} to build nall::string values
namespace nall {
//convert any (supported) type to a const char* without constructing a new nall::string
//this is used inside istring(...) to build nall::string values
template<typename T> struct stringify;
// base types
//base types
template<> struct stringify<bool> {
bool value;
operator const char*() const { return value ? "true" : "false"; }
stringify(bool value) : value(value) {}
bool _value;
auto data() const -> const char* { return _value ? "true" : "false"; }
auto size() const -> unsigned { return _value ? 4 : 5; }
stringify(bool value) : _value(value) {}
};
template<> struct stringify<char> {
char data[2];
operator const char*() const { return data; }
stringify(char value) { data[0] = value, data[1] = 0; }
char _data[2];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return 1; }
stringify(char source) { _data[0] = source; _data[1] = 0; }
};
// signed integers
//signed integers
template<> struct stringify<signed char> {
char data[256];
operator const char*() const { return data; }
stringify(signed char value) { integer(data, value); }
char _data[2 + sizeof(signed char) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(signed char source) { integer(_data, source); }
};
template<> struct stringify<signed short> {
char data[256];
operator const char*() const { return data; }
stringify(signed short value) { integer(data, value); }
char _data[2 + sizeof(signed short) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(signed short source) { integer(_data, source); }
};
template<> struct stringify<signed int> {
char data[256];
operator const char*() const { return data; }
stringify(signed int value) { integer(data, value); }
char _data[2 + sizeof(signed int) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(signed int source) { integer(_data, source); }
};
template<> struct stringify<signed long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long value) { integer(data, value); }
char _data[2 + sizeof(signed long) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(signed long source) { integer(_data, source); }
};
template<> struct stringify<signed long long> {
char data[256];
operator const char*() const { return data; }
stringify(signed long long value) { integer(data, value); }
char _data[2 + sizeof(signed long long) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(signed long long source) { integer(_data, source); }
};
template<unsigned bits> struct stringify<int_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(int_t<bits> value) { integer(data, value); }
char _data[2 + sizeof(intmax_t) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(int_t<bits> source) { integer(_data, source); }
};
// unsigned integers
//unsigned integers
template<> struct stringify<unsigned char> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned char value) { decimal(data, value); }
char _data[1 + sizeof(unsigned char) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(unsigned char source) { decimal(_data, source); }
};
template<> struct stringify<unsigned short> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned short value) { decimal(data, value); }
char _data[1 + sizeof(unsigned short) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(unsigned short source) { decimal(_data, source); }
};
template<> struct stringify<unsigned int> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned int value) { decimal(data, value); }
char _data[1 + sizeof(unsigned int) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(unsigned int source) { decimal(_data, source); }
};
template<> struct stringify<unsigned long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long value) { decimal(data, value); }
char _data[1 + sizeof(unsigned long) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(unsigned long source) { decimal(_data, source); }
};
template<> struct stringify<unsigned long long> {
char data[256];
operator const char*() const { return data; }
stringify(unsigned long long value) { decimal(data, value); }
char _data[1 + sizeof(unsigned long long) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(unsigned long long source) { decimal(_data, source); }
};
template<unsigned bits> struct stringify<uint_t<bits>> {
char data[256];
operator const char*() const { return data; }
stringify(uint_t<bits> value) { decimal(data, value); }
char _data[1 + sizeof(uintmax_t) * 3];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(uint_t<bits> source) { decimal(_data, source); }
};
// floating-point
//floating-point
template<> struct stringify<float> {
char data[256];
operator const char*() const { return data; }
stringify(float value) { real(data, value); }
char _data[256];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(float source) { real(_data, source); }
};
template<> struct stringify<double> {
char data[256];
operator const char*() const { return data; }
stringify(double value) { real(data, value); }
char _data[256];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(double source) { real(_data, source); }
};
template<> struct stringify<long double> {
char data[256];
operator const char*() const { return data; }
stringify(long double value) { real(data, value); }
char _data[256];
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(long double source) { real(_data, source); }
};
// arrays
//arrays
template<> struct stringify<vector<uint8_t>> {
char* text;
operator const char*() const { return text; }
stringify(vector<uint8_t> value) {
text = new char[value.size() + 1]();
memcpy(text, value.data(), value.size());
}
~stringify() {
delete[] text;
vector<char> _text;
auto data() const -> const char* { return _text.data(); }
auto size() const -> unsigned { return _text.size(); }
stringify(vector<uint8_t> source) {
_text.resize(source.size() + 1);
memory::copy(_text.data(), source.data(), source.size());
_text[_text.size()] = 0;
}
};
template<> struct stringify<const vector<uint8_t>&> {
char* text;
operator const char*() const { return text; }
stringify(const vector<uint8_t>& value) {
text = new char[value.size() + 1]();
memcpy(text, value.data(), value.size());
}
~stringify() {
delete[] text;
vector<char> _text;
auto data() const -> const char* { return _text.data(); }
auto size() const -> unsigned { return _text.size(); }
stringify(const vector<uint8_t>& source) {
_text.resize(source.size() + 1);
memory::copy(_text.data(), source.data(), source.size());
_text[_text.size()] = 0;
}
};
// strings
//char arrays
template<> struct stringify<char*> {
const char* value;
operator const char*() const { return value; }
stringify(char* value) : value(value) {}
const char* _data;
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(char* source) : _data(source) {}
};
template<> struct stringify<const char*> {
const char* value;
operator const char*() const { return value; }
stringify(const char* value) : value(value) {}
const char* _data;
auto data() const -> const char* { return _data; }
auto size() const -> unsigned { return strlen(_data); }
stringify(const char* source) : _data(source) {}
};
//strings
template<> struct stringify<string> {
const string& value;
operator const char*() const { return value; }
stringify(const string& value) : value(value) {}
const string& _text;
auto data() const -> const char* { return _text.data(); }
auto size() const -> unsigned { return _text.size(); }
stringify(const string& source) : _text(source) {}
};
template<> struct stringify<const string&> {
const string& value;
operator const char*() const { return value; }
stringify(const string& value) : value(value) {}
const string& _text;
auto data() const -> const char* { return _text.data(); }
auto size() const -> unsigned { return _text.size(); }
stringify(const string& source) : _text(source) {}
};
#if defined(QSTRING_H)
//Qt
template<> struct stringify<QString> {
const QString& value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString& value) : value(value) {}
const QString& _text;
auto data() const -> const char* { return _text.toUtf8().constData(); }
auto size() const -> unsigned { return _text.size(); }
stringify(const QString& source) : _text(source) {}
};
template<> struct stringify<const QString&> {
const QString& value;
operator const char*() const { return value.toUtf8().constData(); }
stringify(const QString& value) : value(value) {}
const QString& _text;
auto data() const -> const char* { return _text.toUtf8().constData(); }
auto size() const -> unsigned { return _text.size(); }
stringify(const QString& source) : _text(source) {}
};
string::operator QString() const {
@@ -190,7 +214,7 @@ string::operator QString() const {
//
template<typename T> stringify<T> make_string(T value) {
template<typename T> auto make_string(T value) -> stringify<T> {
return stringify<T>(std::forward<T>(value));
}

View File

@@ -1,13 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
#include <nall/string/char/base.hpp>
#include <nall/string/char/compare.hpp>
#include <nall/string/char/convert.hpp>
#include <nall/string/char/match.hpp>
#include <nall/string/char/strm.hpp>
#include <nall/string/char/strpos.hpp>
#include <nall/string/char/trim.hpp>
#include <nall/string/char/utf8.hpp>
#include <nall/string/char/utility.hpp>
#endif

View File

@@ -1,69 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//collection of functions to extend libc
//none of these functions require nall::string
//and thus, require no changes when nall::string is modified
namespace nall {
//compare.hpp
inline char chrlower(char c);
inline char chrupper(char c);
inline int imemcmp(const char* str1, const char* str2, unsigned size);
inline int istrcmp(const char* str1, const char* str2);
inline bool strbegin(const char* str, const char* key);
inline bool istrbegin(const char* str, const char* key);
inline bool strend(const char* str, const char* key);
inline bool istrend(const char* str, const char* key);
//convert.hpp
inline char* strlower(char* str);
inline char* strupper(char* str);
inline char* qstrlower(char* str);
inline char* qstrupper(char* str);
inline char* strtr(char* dest, const char* before, const char* after);
//match.hpp
inline bool strmatch(const char* str, const char* pattern);
inline bool istrmatch(const char* str, const char* pattern);
inline bool tokenize(const char* s, const char* p);
//strm.hpp
inline unsigned strmcpy(char* target, const char* source, unsigned length);
inline unsigned strmcat(char* target, const char* source, unsigned length);
inline bool strccpy(char* target, const char* source, unsigned length);
inline bool strccat(char* target, const char* source, unsigned length);
inline void strpcpy(char*& target, const char* source, unsigned& length);
//strpos.hpp
inline maybe<unsigned> strpos(const char* str, const char* key);
inline maybe<unsigned> istrpos(const char* str, const char* key);
inline maybe<unsigned> qstrpos(const char* str, const char* key);
inline maybe<unsigned> iqstrpos(const char* str, const char* key);
template<bool Insensitive = false, bool Quoted = false> inline maybe<unsigned> ustrpos(const char* str, const char* key);
//trim.hpp
template<unsigned Limit = 0> inline char* ltrim(char* str, const char* key = " ");
template<unsigned Limit = 0> inline char* rtrim(char* str, const char* key = " ");
template<unsigned Limit = 0> inline char* trim(char* str, const char* key = " ");
template<unsigned Limit = 0> inline char* trim(char* str, const char* lkey, const char* rkey);
inline char* strip(char* s);
//utf8.hpp
struct UTF8 {
unsigned size; //size of encoded codepoint
uint64_t data; //encoded codepoint
unsigned codepoint; //decoded codepoint
};
inline UTF8 utf8_read(const char* s);
inline void utf8_write(char* s, const UTF8& utf8);
//utility.hpp
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
template<bool Quoted, typename T> alwaysinline bool quoteskip(T*& p);
template<bool Quoted, typename T> alwaysinline bool quotecopy(char*& t, T*& p);
inline char* strduplicate(const char* s);
}
#endif

View File

@@ -1,81 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
char chrlower(char c) {
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
}
char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
int imemcmp(const char* str1, const char* str2, unsigned size) {
while(size--) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
}
return (int)chrlower(*str1) - (int)chrlower(*str2);
}
int istrcmp(const char* str1, const char* str2) {
while(*str1) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
}
return (int)chrlower(*str1) - (int)chrlower(*str2);
}
bool strbegin(const char* str, const char* key) {
if(!str || !key) return false;
int i, ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str, key, ksl));
}
bool istrbegin(const char* str, const char* key) {
if(!str || !key) return false;
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = 0; i < ksl; i++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[i] && str[i]+0x20 != key[i])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[i] && str[i]-0x20 != key[i])return false;
} else {
if(str[i] != key[i])return false;
}
}
return true;
}
bool strend(const char* str, const char* key) {
if(!str || !key) return false;
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
return (!memcmp(str + ssl - ksl, key, ksl));
}
bool istrend(const char* str, const char* key) {
if(!str || !key) return false;
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) {
if(str[i] >= 'A' && str[i] <= 'Z') {
if(str[i] != key[z] && str[i]+0x20 != key[z])return false;
} else if(str[i] >= 'a' && str[i] <= 'z') {
if(str[i] != key[z] && str[i]-0x20 != key[z])return false;
} else {
if(str[i] != key[z])return false;
}
}
return true;
}
}
#endif

View File

@@ -1,68 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
char* strlower(char* str) {
if(!str) return nullptr;
int i = 0;
while(str[i]) {
str[i] = chrlower(str[i]);
i++;
}
return str;
}
char* strupper(char* str) {
if(!str) return nullptr;
int i = 0;
while(str[i]) {
str[i] = chrupper(str[i]);
i++;
}
return str;
}
char* qstrlower(char* s) {
if(!s) return nullptr;
char* base = s;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
s++;
}
return base;
}
char* qstrupper(char* s) {
if(!s) return nullptr;
char* base = s;
bool quoted = false;
while(*s) {
if(*s == '\"' || *s == '\'') quoted ^= 1;
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
s++;
}
return base;
}
char* strtr(char* dest, const char* before, const char* after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace
for(unsigned i = 0; i < sl; i++) {
for(unsigned l = 0; l < bsl; l++) {
if(dest[i] == before[l]) {
dest[i] = after[l];
break;
}
}
}
return dest;
}
}
#endif

View File

@@ -1,41 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
//return = strlen(target)
unsigned strmcpy(char* target, const char* source, unsigned length) {
const char* origin = target;
if(length) {
while(*source && --length) *target++ = *source++;
*target = 0;
}
return target - origin;
}
//return = strlen(target)
unsigned strmcat(char* target, const char* source, unsigned length) {
const char* origin = target;
while(*target && length) target++, length--;
return (target - origin) + strmcpy(target, source, length);
}
//return = true when all of source was copied
bool strccpy(char* target, const char* source, unsigned length) {
return !source[strmcpy(target, source, length)];
}
//return = true when all of source was copied
bool strccat(char* target, const char* source, unsigned length) {
while(*target && length) target++, length--;
return !source[strmcpy(target, source, length)];
}
//return = reserved for future use
void strpcpy(char*& target, const char* source, unsigned& length) {
unsigned offset = strmcpy(target, source, length);
target += offset, length -= offset;
}
}
#endif

View File

@@ -1,33 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
//usage example:
//if(auto position = strpos(str, key)) print(position(), "\n");
//prints position of key within str; but only if it is found
namespace nall {
template<bool Insensitive, bool Quoted>
maybe<unsigned> ustrpos(const char* str, const char* key) {
const char* base = str;
while(*str) {
if(quoteskip<Quoted>(str)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) return (unsigned)(str - base);
if(str[n] == 0) return nothing;
if(!chrequal<Insensitive>(str[n], key[n])) break;
}
str++;
}
return nothing;
}
maybe<unsigned> strpos(const char* str, const char* key) { return ustrpos<false, false>(str, key); }
maybe<unsigned> istrpos(const char* str, const char* key) { return ustrpos<true, false>(str, key); }
maybe<unsigned> qstrpos(const char* str, const char* key) { return ustrpos<false, true>(str, key); }
maybe<unsigned> iqstrpos(const char* str, const char* key) { return ustrpos<true, true>(str, key); }
}
#endif

View File

@@ -1,62 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
//limit defaults to zero, which will underflow on first compare; equivalent to no limit
template<unsigned Limit> char* ltrim(char* str, const char* key) {
if(!str || !key || !*key) return str;
unsigned limit = Limit;
while(strbegin(str, key)) {
char* dest = str;
char* src = str + strlen(key);
while(true) {
*dest = *src++;
if(!*dest) break;
dest++;
}
if(--limit == 0) break;
}
return str;
}
template<unsigned Limit> char* rtrim(char* str, const char* key) {
if(!str || !key || !*key) return str;
unsigned limit = Limit;
while(strend(str, key)) {
str[strlen(str) - strlen(key)] = 0;
if(--limit == 0) break;
}
return str;
}
template<unsigned limit> char* trim(char* str, const char* key) {
return ltrim<limit>(rtrim<limit>(str, key), key);
}
template<unsigned limit> char* trim(char* str, const char* lkey, const char* rkey) {
return ltrim<limit>(rtrim<limit>(str, rkey), lkey);
}
//remove whitespace characters from both left and right sides of string
char* strip(char* s) {
if(!s) return nullptr;
signed n = 0, p = 0;
while(s[n]) {
if(s[n] != ' ' && s[n] != '\t' && s[n] != '\r' && s[n] != '\n') break;
n++;
}
while(s[n]) s[p++] = s[n++];
s[p--] = 0;
while(p >= 0) {
if(s[p] != ' ' && s[p] != '\t' && s[p] != '\r' && s[p] != '\n') break;
p--;
}
s[++p] = 0;
return s;
}
}
#endif

View File

@@ -1,37 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
UTF8 utf8_read(const char* s) {
UTF8 utf8;
if((*s & 0xfe) == 0xfc) utf8.size = 6;
else if((*s & 0xfc) == 0xf8) utf8.size = 5;
else if((*s & 0xf8) == 0xf0) utf8.size = 4;
else if((*s & 0xf0) == 0xe0) utf8.size = 3;
else if((*s & 0xe0) == 0xc0) utf8.size = 2;
else utf8.size = 1;
utf8.data = 0;
for(unsigned n = 0; n < utf8.size; n++) {
utf8.data = (utf8.data << 8) | (uint8_t)s[n];
}
static uint8_t mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
utf8.codepoint = s[0] & mask[utf8.size];
for(unsigned n = 1; n < utf8.size; n++) {
utf8.codepoint = (utf8.codepoint << 6) | (s[n] & 0x3f);
}
return utf8;
}
void utf8_write(char* s, const UTF8& utf8) {
for(signed n = utf8.size - 1, shift = 0; n >= 0; n--, shift += 8) {
s[n] = utf8.data >> shift;
}
}
}
#endif

View File

@@ -1,50 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
template<bool Insensitive>
bool chrequal(char x, char y) {
if(Insensitive) return chrlower(x) == chrlower(y);
return x == y;
}
template<bool Quoted, typename T>
bool quoteskip(T*& p) {
if(Quoted == false) return false;
if(*p != '\"') return false;
while(*p == '\"') {
char x = *p++;
while(*p && *p++ != x);
}
return true;
}
template<bool Quoted, typename T>
bool quotecopy(char*& t, T*& p) {
if(Quoted == false) return false;
if(*p != '\"') return false;
while(*p == '\"') {
char x = *p++;
*t++ = x;
while(*p && *p != x) *t++ = *p++;
*t++ = *p++;
}
return true;
}
//strdup() is not a standard function, so recreate it
char* strduplicate(const char* s) {
if(s == nullptr) return nullptr;
unsigned length = strlen(s);
char* result = (char*)malloc(length + 1);
strcpy(result, s);
return result;
}
}
#endif

50
nall/string/compare.hpp Normal file
View File

@@ -0,0 +1,50 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
template<bool Insensitive> inline auto _compare(const char* target, unsigned capacity, const char* source, unsigned size) -> signed {
if(Insensitive) return memory::icompare(target, capacity, source, size);
return memory::compare(target, capacity, source, size);
}
auto compare(const string& self, rstring source) -> signed {
return memory::compare(self.data(), self.size(), source.data(), source.size());
}
auto icompare(const string& self, rstring source) -> signed {
return memory::icompare(self.data(), self.size(), source.data(), source.size());
}
auto equals(const string& self, rstring source) -> bool {
if(self.size() != source.size()) return false;
return memory::compare(self.data(), source.data(), source.size()) == 0;
}
auto iequals(const string& self, rstring source) -> bool {
if(self.size() != source.size()) return false;
return memory::icompare(self.data(), source.data(), source.size()) == 0;
}
auto beginsWith(const string& self, rstring source) -> bool {
if(source.size() > self.size()) return false;
return memory::compare(self.data(), source.data(), source.size()) == 0;
}
auto ibeginsWith(const string& self, rstring source) -> bool {
if(source.size() > self.size()) return false;
return memory::icompare(self.data(), source.data(), source.size()) == 0;
}
auto endsWith(const string& self, rstring source) -> bool {
if(source.size() > self.size()) return false;
return memory::compare(self.data() + self.size() - source.size(), source.data(), source.size()) == 0;
}
auto iendsWith(const string& self, rstring source) -> bool {
if(source.size() > self.size()) return false;
return memory::icompare(self.data() + self.size() - source.size(), source.data(), source.size()) == 0;
}
}
#endif

55
nall/string/convert.hpp Normal file
View File

@@ -0,0 +1,55 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
auto downcase(string& self) -> string& {
char* p = self.pointer();
for(unsigned n = 0; n < self.size(); n++) {
if(p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
}
return self;
}
auto qdowncase(string& self) -> string& {
char* p = self.pointer();
for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
if(p[n] == '\"') quoted ^= 1;
if(!quoted && p[n] >= 'A' && p[n] <= 'Z') p[n] += 0x20;
}
return self;
}
auto upcase(string& self) -> string& {
char* p = self.pointer();
for(unsigned n = 0; n < self.size(); n++) {
if(p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
}
return self;
}
auto qupcase(string& self) -> string& {
char* p = self.pointer();
for(unsigned n = 0, quoted = 0; n < self.size(); n++) {
if(p[n] == '\"') quoted ^= 1;
if(!quoted && p[n] >= 'a' && p[n] <= 'z') p[n] -= 0x20;
}
return self;
}
auto transform(string& self, rstring from, rstring to) -> string& {
if(from.size() != to.size() || from.size() == 0) return self; //patterns must be the same length
char* p = self.pointer();
for(unsigned n = 0; n < self.size(); n++) {
for(unsigned s = 0; s < from.size(); s++) {
if(p[n] == from[s]) {
p[n] = to[s];
break;
}
}
}
return self;
}
}
#endif

View File

@@ -3,7 +3,9 @@
//only allocators may access _data or modify _size and _capacity
//all other functions must use data(), size(), capacity()
#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
#if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
#include <nall/string/allocator/adaptive.hpp>
#elif defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
#include <nall/string/allocator/copy-on-write.hpp>
#elif defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
#include <nall/string/allocator/small-string-optimization.hpp>
@@ -13,75 +15,44 @@
namespace nall {
unsigned string::length() const { return strlen(data()); }
unsigned string::size() const { return _size; }
unsigned string::capacity() const { return _capacity; }
bool string::empty() const { return _size == 0; }
void string::clear(char c) {
for(unsigned n = 0; n < size(); n++) data()[n] = c;
}
unsigned string::hash() const {
const char* p = data();
unsigned result = 5381;
while(*p) result = (result << 5) + result + *p++;
return result;
}
template<typename... Args> string& string::assign(Args&&... args) {
resize(0);
sprint(*this, std::forward<Args>(args)...);
return *this;
}
template<typename... Args> string& string::append(Args&&... args) {
sprint(*this, std::forward<Args>(args)...);
return *this;
}
string& string::_append(const char* s) {
if(s == nullptr) return *this;
unsigned basesize = size(), length = strlen(s);
reserve(basesize + length);
memcpy(data() + basesize, s, length);
resize(basesize + length);
return *this;
}
string::operator bool() const {
return !empty();
}
string::operator const char*() const {
return data();
}
char& string::operator[](signed position) {
auto string::operator[](signed position) const -> const char& {
if(position > size() + 1) throw exception_out_of_bounds{};
return data()[position];
}
const char& string::operator[](signed position) const {
if(position > size() + 1) throw exception_out_of_bounds{};
return data()[position];
template<typename... P> auto assign(string& self, P&&... p) -> string& {
self.resize(0);
return self.append(std::forward<P>(p)...);
}
bool string::operator==(const char* str) const { return strcmp(data(), str) == 0; }
bool string::operator!=(const char* str) const { return strcmp(data(), str) != 0; }
bool string::operator< (const char* str) const { return strcmp(data(), str) < 0; }
bool string::operator<=(const char* str) const { return strcmp(data(), str) <= 0; }
bool string::operator> (const char* str) const { return strcmp(data(), str) > 0; }
bool string::operator>=(const char* str) const { return strcmp(data(), str) >= 0; }
string::string(const string& source) {
construct();
operator=(source);
template<typename T, typename... P> auto append(string& self, const T& value, P&&... p) -> string& {
_append(self, make_string(value));
return self.append(std::forward<P>(p)...);
}
string::string(string&& source) {
construct();
operator=(std::move(source));
template<typename... P> auto append(string& self, const format& value, P&&... p) -> string& {
self.format(value);
return self.append(std::forward<P>(p)...);
}
auto append(string& self) -> string& {
return self;
}
template<typename T> auto _append(string& self, const stringify<T>& source) -> string& {
unsigned size = self.size();
unsigned length = source.size();
self.resize(size + length);
memory::copy(self.pointer() + size, source.data(), length);
return self;
}
auto empty(const string& self) -> bool {
return self.size() == 0;
}
auto length(const string& self) -> unsigned {
return strlen(self.data());
}
}

View File

@@ -2,28 +2,29 @@
namespace nall {
string string::date() {
time_t timestamp = ::time(nullptr);
auto string::date(time_t timestamp) -> string {
if(timestamp == 0) timestamp = ::time(nullptr);
tm* info = localtime(&timestamp);
return {
format<4, '0'>(1900 + info->tm_year), "-",
format<2, '0'>(1 + info->tm_mon), "-",
format<2, '0'>(info->tm_mday)
nall::decimal<4>(1900 + info->tm_year), "-",
nall::decimal<2>(1 + info->tm_mon), "-",
nall::decimal<2>(info->tm_mday)
};
}
string string::time() {
time_t timestamp = ::time(nullptr);
auto string::time(time_t timestamp) -> string {
if(timestamp == 0) timestamp = ::time(nullptr);
tm* info = localtime(&timestamp);
return {
format<2, '0'>(info->tm_hour), ":",
format<2, '0'>(info->tm_min), ":",
format<2, '0'>(info->tm_sec)
nall::decimal<2>(info->tm_hour), ":",
nall::decimal<2>(info->tm_min), ":",
nall::decimal<2>(info->tm_sec)
};
}
string string::datetime() {
return {string::date(), " ", string::time()};
auto string::datetime(time_t timestamp) -> string {
if(timestamp == 0) timestamp = ::time(nullptr);
return {string::date(timestamp), " ", string::time(timestamp)};
}
}

View File

@@ -29,7 +29,7 @@ inline string evaluateExpression(Node* node) {
for(auto& link : node->link) {
result.append(evaluateExpression(link), ", ");
}
return result.rtrim<1>(", ").append(")");
return result.rtrim(", ").append(")");
}
}
#undef p

View File

@@ -1,31 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
string string::read(const string& filename) {
string result;
#if !defined(_WIN32)
FILE* fp = fopen(filename, "rb");
#else
FILE* fp = _wfopen(utf16_t(filename), L"rb");
#endif
if(!fp) return result;
fseek(fp, 0, SEEK_END);
unsigned fsize = ftell(fp);
rewind(fp);
char* fdata = new char[fsize + 1];
unsigned unused = fread(fdata, 1, fsize, fp);
fclose(fp);
fdata[fsize] = 0;
result.resize(fsize);
memcpy(result.data(), fdata, fsize);
delete[] fdata;
return result;
}
}
#endif

View File

@@ -1,84 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
// "/foo/bar.c" -> "/foo/"
// "/foo/" -> "/foo/"
// "bar.c" -> "./"
string dir(string name) {
for(signed i = name.length(); i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') {
name.resize(i + 1);
break;
}
if(i == 0) name = "./";
}
return name;
}
// "/foo/bar.c" -> "bar.c"
// "/foo/" -> ""
// "bar.c" -> "bar.c"
string notdir(string name) {
for(signed i = name.length(); i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') {
return (const char*)name + i + 1;
}
}
return name;
}
// "/foo/bar/baz" -> "/foo/bar/"
// "/foo/bar/" -> "/foo/"
// "/foo/bar" -> "/foo/"
string parentdir(string name) {
unsigned length = name.length(), paths = 0, prev, last;
for(unsigned i = 0; i < length; i++) {
if(name[i] == '/' || name[i] == '\\') {
paths++;
prev = last;
last = i;
}
}
if(last + 1 == length) last = prev; //if name ends in slash; use previous slash
if(paths > 1) name.resize(last + 1);
return name;
}
// "/foo/bar.c" -> "/foo/bar"
string basename(string name) {
for(signed i = name.length(); i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') break; //file has no extension
if(name[i] == '.') {
name.resize(i);
break;
}
}
return name;
}
// "/foo/bar.c" -> "c"
// "/foo/bar" -> ""
string extension(string name) {
for(signed i = name.length(); i >= 0; i--) {
if(name[i] == '/' || name[i] == '\\') return ""; //file has no extension
if(name[i] == '.') {
return (const char*)name + i + 1;
}
}
return name;
}
string tempname() {
string path = temppath();
srand(time(nullptr));
while(true) {
uint32_t seed = rand();
string filename = {path, ".temporary-", hex<8>(seed)};
if(access(filename, F_OK) != 0) return filename;
}
}
}
#endif

30
nall/string/find.hpp Normal file
View File

@@ -0,0 +1,30 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
template<bool Insensitive, bool Quoted> auto _find(const string& self, signed offset, rstring source) -> maybe<unsigned> {
if(source.size() == 0) return nothing;
const char* p = self.data();
unsigned size = self.size();
for(unsigned n = offset, quoted = 0; n < size;) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size - n, source.data(), source.size())) { n++; continue; }
return n - offset;
}
return nothing;
}
auto find(const string& self, rstring source) -> maybe<unsigned> { return _find<0, 0>(self, 0, source); }
auto ifind(const string& self, rstring source) -> maybe<unsigned> { return _find<1, 0>(self, 0, source); }
auto qfind(const string& self, rstring source) -> maybe<unsigned> { return _find<0, 1>(self, 0, source); }
auto iqfind(const string& self, rstring source) -> maybe<unsigned> { return _find<1, 1>(self, 0, source); }
auto findFrom(const string& self, signed offset, rstring source) -> maybe<unsigned> { return _find<0, 0>(self, offset, source); }
auto ifindFrom(const string& self, signed offset, rstring source) -> maybe<unsigned> { return _find<1, 0>(self, offset, source); }
}
#endif

View File

@@ -2,69 +2,176 @@
namespace nall {
template<signed precision, char padchar> string format(const string& value) {
if(precision == 0) return value;
//nall::format is a vector<string> of parameters that can be applied to a string
//each {#} token will be replaced with its appropriate format parameter
bool padright = precision >= 0;
unsigned padding = abs(precision);
auto string::format(const nall::format& params) -> type& {
signed size = this->size();
char* data = (char*)memory::allocate(size);
memory::copy(data, this->data(), size);
if(padding <= value.size()) {
if(padright) return substr(value, value.size() - padding);
else return substr(value, 0, padding);
signed x = 0;
while(x < size - 2) { //2 = minimum tag length
if(data[x] != '{') { x++; continue; }
signed y = x + 1;
while(y < size - 1) { //-1 avoids going out of bounds on test after this loop
if(data[y] != '}') { y++; continue; }
break;
}
if(data[y++] != '}') { x++; continue; }
static auto isNumeric = [](char* s, char* e) -> bool {
if(s == e) return false; //ignore empty tags: {}
while(s < e) {
if(*s >= '0' && *s <= '9') { s++; continue; }
return false;
}
return true;
};
if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; }
unsigned index = nall::decimal(&data[x + 1]);
if(index >= params.size()) { x++; continue; }
unsigned sourceSize = y - x;
unsigned targetSize = params[index].size();
unsigned remaining = size - x;
if(sourceSize > targetSize) {
unsigned difference = sourceSize - targetSize;
memory::move(&data[x], &data[x + difference], remaining);
size -= difference;
} else if(targetSize > sourceSize) {
unsigned difference = targetSize - sourceSize;
data = (char*)realloc(data, size + difference);
size += difference;
memory::move(&data[x + difference], &data[x], remaining);
}
memory::copy(&data[x], params[index].data(), targetSize);
x += targetSize;
}
string buffer;
buffer.resize(padding);
buffer.clear(padchar);
resize(size);
memory::copy(pointer(), data, size);
memory::free(data);
return *this;
}
memcpy(buffer.data() + (padright ? padding - value.size() : 0), value, value.size());
template<typename T, typename... P> auto append(format& self, const T& value, P&&... p) -> format& {
self.vector<string>::append(value);
append(self, std::forward<P>(p)...);
}
auto append(format& self) -> format& {
return self;
}
template<typename... P> auto print(P&&... p) -> void {
string s{std::forward<P>(p)...};
fputs(s.data(), stdout);
}
template<signed precision, char padchar> auto integer(intmax_t value) -> string {
string buffer;
buffer.resize(1 + sizeof(intmax_t) * 3);
char* p = buffer.pointer();
bool negative = value < 0;
value = abs(value);
unsigned size = 0;
do {
p[size++] = '0' + (value % 10);
value /= 10;
} while(value);
if(negative) p[size++] = '-';
buffer.resize(size);
buffer.reverse();
if(precision) buffer.size(precision, padchar);
return buffer;
}
template<signed precision, char padchar> string hex(uintmax_t value) {
template<signed precision, char padchar> auto decimal(uintmax_t value) -> string {
string buffer;
buffer.resize(sizeof(uintmax_t) * 3);
char* p = buffer.pointer();
unsigned size = 0;
do {
p[size++] = '0' + (value % 10);
value /= 10;
} while(value);
buffer.resize(size);
buffer.reverse();
if(precision) buffer.size(precision, padchar);
return buffer;
}
template<signed precision, char padchar> auto hex(uintmax_t value) -> string {
string buffer;
buffer.resize(sizeof(uintmax_t) * 2);
char* p = buffer.pointer();
unsigned size = 0;
do {
unsigned n = value & 15;
buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10;
p[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4;
} while(value);
buffer.resize(size);
buffer.reverse();
return format<precision, padchar>(buffer);
if(precision) buffer.size(precision, padchar);
return buffer;
}
template<signed precision, char padchar> string octal(uintmax_t value) {
template<signed precision, char padchar> auto octal(uintmax_t value) -> string {
string buffer;
buffer.resize(sizeof(uintmax_t) * 3);
char* p = buffer.pointer();
unsigned size = 0;
do {
buffer[size++] = '0' + (value & 7);
p[size++] = '0' + (value & 7);
value >>= 3;
} while(value);
buffer.resize(size);
buffer.reverse();
return format<precision, padchar>(buffer);
if(precision) buffer.size(precision, padchar);
return buffer;
}
template<signed precision, char padchar> string binary(uintmax_t value) {
template<signed precision, char padchar> auto binary(uintmax_t value) -> string {
string buffer;
buffer.resize(sizeof(uintmax_t) * 8);
char* p = buffer.pointer();
unsigned size = 0;
do {
buffer[size++] = '0' + (value & 1);
p[size++] = '0' + (value & 1);
value >>= 1;
} while(value);
buffer.resize(size);
buffer.reverse();
if(precision) buffer.size(precision, padchar);
return buffer;
}
return format<precision, padchar>(buffer);
template<signed precision, typename T> auto pointer(const T* value) -> string {
if(value == nullptr) return "(null)";
return {"0x", hex<precision>((uintptr_t)value)};
}
template<signed precision> auto pointer(uintptr_t value) -> string {
if(value == 0) return "(null)";
return {"0x", hex<precision>(value)};
}
auto real(long double value) -> string {
string temp;
temp.resize(real(nullptr, value));
real(temp.pointer(), value);
return temp;
}
}

35
nall/string/hash.hpp Normal file
View File

@@ -0,0 +1,35 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
namespace Hash {
auto CRC16::digest() -> string {
return hex<4>(value());
}
auto CRC32::digest() -> string {
return hex<8>(value());
}
auto SHA256::digest() const -> string {
string result;
for(auto n : value()) result.append(hex<2>(n));
return result;
}
}
auto crc16(const string& self) -> string {
return Hash::CRC16(self.data(), self.size()).digest();
}
auto crc32(const string& self) -> string {
return Hash::CRC32(self.data(), self.size()).digest();
}
auto sha256(const string& self) -> string {
return Hash::SHA256(self.data(), self.size()).digest();
}
}
#endif

View File

@@ -2,42 +2,7 @@
namespace nall {
maybe<unsigned> lstring::find(rstring key) const {
for(unsigned i = 0; i < size(); i++) {
if(operator[](i) == key) return i;
}
return nothing;
}
string lstring::merge(const string& separator) const {
string output;
for(unsigned i = 0; i < size(); i++) {
output.append(operator[](i));
if(i < size() - 1) output.append(separator);
}
return output;
}
lstring& lstring::isort() {
nall::sort(pool, objectsize, [](const string& x, const string& y) {
return istrcmp(x, y) < 0;
});
return *this;
}
lstring& lstring::strip() {
for(unsigned n = 0; n < size(); n++) {
operator[](n).strip();
}
return *this;
}
template<typename... Args> void lstring::append(const string& data, Args&&... args) {
vector::append(data);
append(std::forward<Args>(args)...);
}
bool lstring::operator==(const lstring& source) const {
auto lstring::operator==(const lstring& source) const -> bool {
if(this == &source) return true;
if(size() != source.size()) return false;
for(unsigned n = 0; n < size(); n++) {
@@ -46,39 +11,55 @@ bool lstring::operator==(const lstring& source) const {
return true;
}
bool lstring::operator!=(const lstring& source) const {
auto lstring::operator!=(const lstring& source) const -> bool {
return !operator==(source);
}
lstring& lstring::operator=(const lstring& source) {
vector::operator=(source);
auto lstring::isort() -> lstring& {
nall::sort(pool, objectsize, [](const string& x, const string& y) {
return memory::icompare(x.data(), x.size(), y.data(), y.size()) < 0;
});
return *this;
}
lstring& lstring::operator=(lstring& source) {
vector::operator=(source);
return *this;
template<typename... P> auto append(lstring& self, const string& data, P&&... p) -> lstring& {
self.vector::append(data);
append(self, std::forward<P>(p)...);
return self;
}
lstring& lstring::operator=(lstring&& source) {
vector::operator=(std::move(source));
return *this;
auto append(lstring& self) -> lstring& {
return self;
}
template<typename... Args> lstring::lstring(Args&&... args) {
append(std::forward<Args>(args)...);
auto find(const lstring& self, const string& source) -> maybe<unsigned> {
for(unsigned n = 0; n < self.size(); n++) {
if(self[n].equals(source)) return n;
}
return nothing;
}
lstring::lstring(const lstring& source) {
vector::operator=(source);
auto ifind(const lstring& self, const string& source) -> maybe<unsigned> {
for(unsigned n = 0; n < self.size(); n++) {
if(self[n].iequals(source)) return n;
}
return nothing;
}
lstring::lstring(lstring& source) {
vector::operator=(source);
auto merge(const lstring& self, const string& separator) -> string {
string output;
for(unsigned n = 0; n < self.size(); n++) {
output.append(self[n]);
if(n < self.size() - 1) output.append(separator);
}
return output;
}
lstring::lstring(lstring&& source) {
vector::operator=(std::move(source));
auto strip(lstring& self) -> lstring& {
for(unsigned n = 0; n < self.size(); n++) {
self[n].strip();
}
return self;
}
}

View File

@@ -71,7 +71,7 @@ protected:
if(length == 0) throw "Invalid attribute name";
node.name = substr(p, 0, length);
node.parseData(p += length);
node.data.rtrim<1>("\n");
node.data.rtrim("\n");
children.append(node);
}
}
@@ -98,7 +98,7 @@ protected:
children.append(node);
}
data.rtrim<1>("\n");
data.rtrim("\n");
}
//read top-level nodes

View File

@@ -20,6 +20,10 @@ struct Node {
return string{data}.strip();
}
bool boolean() const {
return text() != "false";
}
intmax_t integer() const {
return numeral(text());
}
@@ -93,7 +97,7 @@ struct Node {
if(name.match("*[*]")) {
lstring side = name.split<1>("[");
name = side(0);
side = side(1).rtrim<1>("]").split<1>("-");
side = side(1).rtrim("]").split<1>("-");
lo = side(0).empty() ? 0u : numeral(side(0));
hi = side(1).empty() ? ~0u : numeral(side(1));
}
@@ -101,7 +105,7 @@ struct Node {
if(name.match("*(*)")) {
lstring side = name.split<1>("(");
name = side(0);
rule = side(1).rtrim<1>(")");
rule = side(1).rtrim(")");
}
unsigned position = 0;

View File

@@ -39,34 +39,34 @@ protected:
target.reserve(length + 1);
#if defined(NALL_XML_LITERAL)
memcpy(target(), source, length);
memory::copy(target.pointer(), source, length);
target[length] = 0;
return;
#endif
char* output = target.data();
char* output = target.pointer();
while(length) {
if(*source == '&') {
if(!memcmp(source, "&lt;", 4)) { *output++ = '<'; source += 4; length -= 4; continue; }
if(!memcmp(source, "&gt;", 4)) { *output++ = '>'; source += 4; length -= 4; continue; }
if(!memcmp(source, "&amp;", 5)) { *output++ = '&'; source += 5; length -= 5; continue; }
if(!memcmp(source, "&apos;", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
if(!memcmp(source, "&quot;", 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
if(!memory::compare(source, "&lt;", 4)) { *output++ = '<'; source += 4; length -= 4; continue; }
if(!memory::compare(source, "&gt;", 4)) { *output++ = '>'; source += 4; length -= 4; continue; }
if(!memory::compare(source, "&amp;", 5)) { *output++ = '&'; source += 5; length -= 5; continue; }
if(!memory::compare(source, "&apos;", 6)) { *output++ = '\''; source += 6; length -= 6; continue; }
if(!memory::compare(source, "&quot;", 6)) { *output++ = '\"'; source += 6; length -= 6; continue; }
}
if(attribute == false && source[0] == '<' && source[1] == '!') {
//comment
if(!memcmp(source, "<!--", 4)) {
if(!memory::compare(source, "<!--", 4)) {
source += 4, length -= 4;
while(memcmp(source, "-->", 3)) source++, length--;
while(memory::compare(source, "-->", 3)) source++, length--;
source += 3, length -= 3;
continue;
}
//CDATA
if(!memcmp(source, "<![CDATA[", 9)) {
if(!memory::compare(source, "<![CDATA[", 9)) {
source += 9, length -= 9;
while(memcmp(source, "]]>", 3)) *output++ = *source++, length--;
while(memory::compare(source, "]]>", 3)) *output++ = *source++, length--;
source += 3, length -= 3;
continue;
}
@@ -81,23 +81,23 @@ protected:
if(*(p + 1) != '!') return false;
//comment
if(!memcmp(p, "<!--", 4)) {
while(*p && memcmp(p, "-->", 3)) p++;
if(!memory::compare(p, "<!--", 4)) {
while(*p && memory::compare(p, "-->", 3)) p++;
if(!*p) throw "unclosed comment";
p += 3;
return true;
}
//CDATA
if(!memcmp(p, "<![CDATA[", 9)) {
while(*p && memcmp(p, "]]>", 3)) p++;
if(!memory::compare(p, "<![CDATA[", 9)) {
while(*p && memory::compare(p, "]]>", 3)) p++;
if(!*p) throw "unclosed CDATA";
p += 3;
return true;
}
//DOCTYPE
if(!memcmp(p, "<!DOCTYPE", 9)) {
if(!memory::compare(p, "<!DOCTYPE", 9)) {
unsigned counter = 0;
do {
char n = *p++;
@@ -171,7 +171,7 @@ protected:
while(*p && *p != '>') p++;
if(*p != '>') throw "unclosed closure element";
const char* nameEnd = p++;
if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
if(memory::compare(name.data(), nameStart, nameEnd - nameStart)) throw "closure element name mismatch";
return true;
}

View File

@@ -2,7 +2,12 @@
namespace nall {
bool strmatch(const char* s, const char* p) {
//todo: these functions are not binary-safe
auto match(const string& self, rstring source) -> bool {
const char* s = self.data();
const char* p = source.data();
const char* cp = nullptr;
const char* mp = nullptr;
while(*s && *p != '*') {
@@ -23,7 +28,14 @@ bool strmatch(const char* s, const char* p) {
return !*p;
}
bool istrmatch(const char* s, const char* p) {
auto imatch(const string& self, rstring source) -> bool {
static auto chrlower = [](char c) -> char {
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
};
const char* s = self.data();
const char* p = source.data();
const char* cp = nullptr;
const char* mp = nullptr;
while(*s && *p != '*') {
@@ -44,7 +56,7 @@ bool istrmatch(const char* s, const char* p) {
return !*p;
}
bool tokenize(const char* s, const char* p) {
inline bool tokenize(const char* s, const char* p) {
while(*s) {
if(*p == '*') {
while(*s) if(tokenize(s++, p + 1)) return true;
@@ -56,6 +68,25 @@ bool tokenize(const char* s, const char* p) {
return !*p;
}
auto tokenize(lstring& list, const char* s, const char* p) -> bool {
while(*s) {
if(*p == '*') {
const char* b = s;
while(*s) {
if(tokenize(list, s++, p + 1)) {
list.prepend(substr(b, 0, --s - b));
return true;
}
}
list.prepend(b);
return !*++p;
}
if(*s++ != *p++) return false;
}
while(*p == '*') { list.prepend(s); p++; }
return !*p;
}
}
#endif

73
nall/string/path.hpp Normal file
View File

@@ -0,0 +1,73 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
// (/parent/child.type/)
// (/parent/child.type/)name.type
auto pathname(const string& self) -> string {
const char* p = self.data() + self.size() - 1;
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
if(*p == '/') return slice(self, 0, offset + 1);
}
return "";
}
// /parent/child.type/()
// /parent/child.type/(name.type)
auto filename(const string& self) -> string {
const char* p = self.data() + self.size() - 1;
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
if(*p == '/') return slice(self, offset + 1);
}
return "";
}
// (/parent/)child.type/
// (/parent/child.type/)name.type
auto dirname(const string& self) -> string {
const char* p = self.data() + self.size() - 1, *last = p;
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
if(*p == '/' && p == last) continue;
if(*p == '/') return slice(self, 0, offset + 1);
}
return self; //this is the root directory
}
// /parent/(child.type/)
// /parent/child.type/(name.type)
auto basename(const string& self) -> string {
const char* p = self.data() + self.size() - 1, *last = p;
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
if(*p == '/' && p == last) continue;
if(*p == '/') return slice(self, offset + 1);
}
return "";
}
// /parent/(child).type/
// /parent/child.type/(name).type
auto prefixname(const string& self) -> string {
const char* p = self.data() + self.size() - 1, *last = p;
for(signed offset = self.size() - 1, suffix = 0; offset >= 0; offset--, p--) {
if(*p == '/' && p == last) continue;
if(*p == '/') return slice(self, offset + 1, suffix ? suffix - offset - 1 : 0).rtrim("/");
if(*p == '.' && suffix == 0) suffix = offset;
}
return "";
}
// /parent/child(.type)/
// /parent/child.type/name(.type)
auto suffixname(const string& self) -> string {
const char* p = self.data() + self.size() - 1, *last = p;
for(signed offset = self.size() - 1; offset >= 0; offset--, p--) {
if(*p == '/' && p == last) continue;
if(*p == '/') break;
if(*p == '.') return slice(self, offset).rtrim("/");
}
return "";
}
}
#endif

View File

@@ -2,7 +2,7 @@
namespace nall {
string activepath() {
auto activepath() -> string {
char path[PATH_MAX] = "";
auto unused = getcwd(path, PATH_MAX);
string result = path;
@@ -12,17 +12,17 @@ string activepath() {
return result;
}
string realpath(const string& name) {
auto realpath(rstring name) -> string {
string result;
char path[PATH_MAX] = "";
if(::realpath(name, path)) result = dir(path);
if(::realpath(name, path)) result = string{path}.pathname();
if(result.empty()) result = activepath();
result.transform("\\", "/");
if(result.endsWith("/") == false) result.append("/");
return result;
}
string programpath() {
auto programpath() -> string {
#if defined(PLATFORM_WINDOWS)
int argc = 0;
wchar_t** argv = CommandLineToArgvW(GetCommandLine(), &argc);
@@ -38,7 +38,7 @@ string programpath() {
// /home/username/
// c:/users/username/
string userpath() {
auto userpath() -> string {
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -55,7 +55,7 @@ string userpath() {
// /home/username/.config/
// c:/users/username/appdata/roaming/
string configpath() {
auto configpath() -> string {
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -74,7 +74,7 @@ string configpath() {
// /usr/share
// /Library/Application Support/
// c:/ProgramData/
string sharedpath() {
auto sharedpath() -> string {
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path);
@@ -92,7 +92,7 @@ string sharedpath() {
// /tmp
// c:/users/username/AppData/Local/Temp/
string temppath() {
auto temppath() -> string {
#if defined(PLATFORM_WINDOWS)
wchar_t path[PATH_MAX] = L"";
GetTempPathW(PATH_MAX, path);

View File

@@ -7,37 +7,54 @@ struct stringref {
return _data;
}
const char* data() const {
auto data() const -> const char* {
return _data;
}
unsigned size() const {
if(!_initialized) {
_initialized = true;
_size = strlen(_data);
}
auto size() const -> unsigned {
if(!_initialized) _size = strlen(_data), _initialized = true;
return _size;
}
stringref() = delete;
stringref(const stringref& source) = delete;
stringref(stringref&& source) = delete;
stringref() {
_string = nullptr;
_data = "";
_size = 0;
_initialized = true;
}
stringref(const char* source) {
_string = nullptr;
_data = source;
_initialized = false;
}
stringref(const string& source) {
_string = nullptr;
_data = source.data();
_size = source.size();
_initialized = true;
}
template<typename... P> stringref(P&&... p) {
_string = new string{std::forward<P>(p)...};
_data = _string->data();
_size = _string->size();
_initialized = true;
}
~stringref() {
if(_string) delete _string;
}
stringref(const stringref& source) = delete;
stringref(stringref&& source) = delete;
protected:
string* _string;
const char* _data;
mutable unsigned _size;
mutable bool _initialized;
mutable signed _size;
mutable unsigned _initialized;
};
}

View File

@@ -3,52 +3,93 @@
namespace nall {
template<unsigned Limit, bool Insensitive, bool Quoted>
string& string::ureplace(rstring key, rstring token) {
if(key.size() == 0) return *this;
enum : unsigned { limit = Limit ? Limit : ~0u };
auto _replace(string& self, rstring from, rstring to) -> string& {
if(Limit == 0 || from.size() == 0) return self;
const char* p = data();
unsigned counter = 0;
signed size = self.size();
signed matches = 0;
signed quoted = 0;
while(*p) {
if(quoteskip<Quoted>(p)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) { counter++; p += n; break; }
if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
//count matches first, so that we only need to reallocate memory once
//(recording matches would also require memory allocation, so this is not done)
{ const char* p = self.data();
for(signed n = 0; n <= size - (signed)from.size();) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
if(++matches >= Limit) break;
n += from.size();
}
}
if(counter == 0) return *this;
if(Limit) counter = min(counter, Limit);
if(matches == 0) return self;
char* t = data();
char* base = nullptr;
signed displacement = token.size() - key.size();
signed displacementSize = displacement * counter;
//in-place overwrite
if(to.size() == from.size()) {
char* p = self.pointer();
if(token.size() > key.size()) {
t = base = strduplicate(data());
reserve((unsigned)(p - data()) + displacementSize);
}
char* o = data();
for(signed n = 0, remaining = matches, quoted = 0; n <= size - (signed)from.size();) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
while(*t && counter) {
if(quotecopy<Quoted>(o, t)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) { counter--; memcpy(o, token, token.size()); t += key.size(); o += token.size(); break; }
if(!chrequal<Insensitive>(key[n], t[n])) { *o++ = *t++; break; }
memory::copy(p + n, to.data(), to.size());
if(!--remaining) break;
n += from.size();
}
}
do *o++ = *t; while(*t++);
if(base) free(base);
resize(_size + displacementSize);
return *this;
//left-to-right shrink
else if(to.size() < from.size()) {
char* p = self.pointer();
signed offset = 0;
signed base = 0;
for(signed n = 0, remaining = matches, quoted = 0; n <= size - (signed)from.size();) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }
if(offset) memory::move(p + offset, p + base, n - base);
memory::copy(p + offset + (n - base), to.data(), to.size());
offset += (n - base) + to.size();
n += from.size();
base = n;
if(!--remaining) break;
}
memory::move(p + offset, p + base, size - base);
self.resize(size - matches * (from.size() - to.size()));
}
//right-to-left expand
else if(to.size() > from.size()) {
self.resize(size + matches * (to.size() - from.size()));
char* p = self.pointer();
signed offset = self.size();
signed base = size;
for(signed n = size, remaining = matches; n >= (signed)from.size();) { //quoted reused from parent scope since we are iterating backward
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n--; continue; } if(quoted) { n--; continue; } }
if(_compare<Insensitive>(p + n - from.size(), size - n + from.size(), from.data(), from.size())) { n--; continue; }
memory::move(p + offset - (base - n), p + base - (base - n), base - n);
memory::copy(p + offset - (base - n) - to.size(), to.data(), to.size());
offset -= (base - n) + to.size();
if(!--remaining) break;
n -= from.size();
base = n;
}
}
return self;
}
template<unsigned Limit> string& string::replace(rstring key, rstring token) { return ureplace<Limit, false, false>(key, token); }
template<unsigned Limit> string& string::ireplace(rstring key, rstring token) { return ureplace<Limit, true, false>(key, token); }
template<unsigned Limit> string& string::qreplace(rstring key, rstring token) { return ureplace<Limit, false, true>(key, token); }
template<unsigned Limit> string& string::iqreplace(rstring key, rstring token) { return ureplace<Limit, true, true>(key, token); }
template<unsigned L> auto replace(string& self, rstring from, rstring to) -> string& { return _replace<L, 0, 0>(self, from, to); }
template<unsigned L> auto ireplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 1, 0>(self, from, to); }
template<unsigned L> auto qreplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 0, 1>(self, from, to); }
template<unsigned L> auto iqreplace(string& self, rstring from, rstring to) -> string& { return _replace<L, 1, 1>(self, from, to); }
};

View File

@@ -2,36 +2,46 @@
namespace nall {
template<unsigned Limit, bool Insensitive, bool Quoted> lstring& lstring::usplit(rstring key, rstring base) {
reset();
if(key.size() == 0) return *this;
template<unsigned Limit, bool Insensitive, bool Quoted> auto _split(lstring& self, rstring source, rstring find) -> lstring& {
self.reset();
if(find.size() == 0) return self;
const char* b = base;
const char* p = base;
const char* p = source.data();
signed size = source.size();
signed base = 0;
signed matches = 0;
while(*p) {
if(Limit) if(size() >= Limit) break;
if(quoteskip<Quoted>(p)) continue;
for(unsigned n = 0;; n++) {
if(key[n] == 0) {
append(substr(b, 0, p - b));
p += n;
b = p;
break;
}
if(!chrequal<Insensitive>(key[n], p[n])) { p++; break; }
}
for(signed n = 0, quoted = 0; n <= size - (signed)find.size();) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size - n, find.data(), find.size())) { n++; continue; }
if(matches >= Limit) break;
string& s = self(matches);
s.resize(n - base);
memory::copy(s.pointer(), p + base, n - base);
n += find.size();
base = n;
matches++;
}
append(b);
return *this;
string& s = self(matches);
s.resize(size - base);
memory::copy(s.pointer(), p + base, size - base);
return self;
}
template<unsigned Limit> lstring& lstring::split(rstring key, rstring src) { return usplit<Limit, false, false>(key, src); }
template<unsigned Limit> lstring& lstring::isplit(rstring key, rstring src) { return usplit<Limit, true, false>(key, src); }
template<unsigned Limit> lstring& lstring::qsplit(rstring key, rstring src) { return usplit<Limit, false, true>(key, src); }
template<unsigned Limit> lstring& lstring::iqsplit(rstring key, rstring src) { return usplit<Limit, true, true>(key, src); }
template<unsigned L> auto split(string& self, rstring on) -> lstring { return lstring().split<L>(self, on); }
template<unsigned L> auto isplit(string& self, rstring on) -> lstring { return lstring().isplit<L>(self, on); }
template<unsigned L> auto qsplit(string& self, rstring on) -> lstring { return lstring().qsplit<L>(self, on); }
template<unsigned L> auto iqsplit(string& self, rstring on) -> lstring { return lstring().iqsplit<L>(self, on); }
};
template<unsigned L> auto string::split(rstring on) const -> lstring { return lstring().split<L>(*this, on); }
template<unsigned L> auto string::isplit(rstring on) const -> lstring { return lstring().isplit<L>(*this, on); }
template<unsigned L> auto string::qsplit(rstring on) const -> lstring { return lstring().qsplit<L>(*this, on); }
template<unsigned L> auto string::iqsplit(rstring on) const -> lstring { return lstring().iqsplit<L>(*this, on); }
}
#endif

View File

@@ -0,0 +1,93 @@
#ifdef NALL_STRING_INTERNAL_HPP
/* CSS Markup Language (CML) v1.0 parser
* revision 0.01
*/
namespace nall { namespace {
struct CML {
CML(const string& filedata, const string& pathname);
CML(const string& filename);
auto output() -> string;
private:
struct State {
string output;
} state;
struct Variable {
string name;
string value;
};
vector<Variable> variables;
auto parse(const string& filedata, const string& pathname) -> bool;
};
CML::CML(const string& filedata, const string& pathname) {
parse(filedata, pathname);
}
CML::CML(const string& filename) {
parse(string::read(filename), filename.pathname());
}
auto CML::output() -> string {
return state.output;
}
auto CML::parse(const string& filedata, const string& pathname) -> bool {
auto vendorAppend = [&](const string& name, const string& value) {
state.output.append(" -moz-", name, ": ", value, ";\n");
state.output.append(" -webkit-", name, ": ", value, ";\n");
};
for(auto& block : filedata.split("\n\n")) {
lstring lines = block.rstrip().split("\n");
string name = lines.takeFirst();
if(ltrim(name, "include ")) {
string filename{pathname, name};
parse(string::read(filename), filename.pathname());
continue;
}
if(name == "variables") {
for(auto& line : lines) {
auto data = line.split<1>(":").strip();
variables.append({data(0), data(1)});
}
continue;
}
state.output.append(name, " {\n");
for(auto& line : lines) {
auto data = line.split<1>(":").strip();
auto name = data(0), value = data(1);
while(auto offset = value.find("var(")) {
bool found = false;
if(auto length = value.findFrom(*offset, ")")) {
string name = value.slice(*offset + 4, *length - 4);
for(auto& variable : variables) {
if(variable.name == name) {
value = {value.slice(0, *offset), variable.value, value.slice(*offset + *length + 1)};
found = true;
break;
}
}
}
if(!found) break;
}
state.output.append(" ", name, ": ", value, ";\n");
if(name == "box-sizing") vendorAppend(name, value);
}
state.output.append("}\n\n");
}
return true;
}
}}
#endif

View File

@@ -0,0 +1,259 @@
#ifdef NALL_STRING_INTERNAL_HPP
/* Document Markup Language (DML) v1.0 parser
* revision 0.01
*/
namespace nall { namespace {
struct DML {
struct Settings {
bool allowHTML = true;
bool sectioned = true;
} settings;
DML(const string& filedata, const string& pathname);
DML(const string& filename);
auto output() -> string;
private:
struct State {
string output;
unsigned sections = 0;
} state;
auto parse(const string& filedata, const string& pathname) -> bool;
auto parseBlock(string& block, const string& pathname) -> bool;
auto count(const string& text, char value) -> unsigned;
auto escape(const string& text) -> string;
auto markup(const string& text) -> string;
};
DML::DML(const string& filedata, const string& pathname) {
parse(filedata, pathname);
}
DML::DML(const string& filename) {
parse(string::read(filename), filename.pathname());
}
auto DML::output() -> string {
return state.output;
}
auto DML::parse(const string& filedata, const string& pathname) -> bool {
auto blocks = filedata.split("\n\n");
for(auto& block : blocks) parseBlock(block, pathname);
if(settings.sectioned && state.sections) state.output.append("</section>\n");
return true;
}
auto DML::parseBlock(string& block, const string& pathname) -> bool {
if(block.rstrip().empty()) return true;
auto lines = block.split("\n");
//include
if(block.beginsWith("{{include}}")) {
string filename{pathname, block.ltrim("{{include}}").strip()};
parse(string::read(filename), filename.pathname());
}
//html
else if(ltrim(block, "{{html}}") && settings.allowHTML) {
auto data = lines.takeFirst();
if(ltrim(data, "{{html}} ")) state.output.append(data, "\n");
for(auto& line : lines) {
if(ltrim(line, " ")) state.output.append(line, "\n");
}
}
//header
else if(block.beginsWith("# ")) {
if(settings.sectioned) {
if(state.sections++) state.output.append("</section>");
state.output.append("<section>");
}
auto content = lines.takeFirst().ltrim("# ").split<1>(" => ");
auto data = markup(content[0]);
auto name = escape(content(1, data.crc32()));
state.output.append("<header id=\"", name, "\">", data);
for(auto& line : lines) {
if(!line.beginsWith("# ")) continue;
state.output.append("<span>", line.ltrim("# "), "</span>");
}
state.output.append("</header>\n");
}
//subheader
else if(auto depth = count(block, '=')) {
auto content = lines.takeFirst().slice(depth + 1).split<1>(" => ");
auto data = markup(content[0]);
auto name = escape(content(1, data.crc32()));
if(depth <= 6) {
state.output.append("<h", depth, " id=\"", name, "\">", data);
for(auto& line : lines) {
if(count(line, '=') != depth) continue;
state.output.append("<span>", line.slice(depth + 1), "</span>");
}
state.output.append("</h", depth, ">\n");
}
}
//contents
else if(count(block, '-')) {
state.output.append("<nav>\n");
unsigned level = 0;
for(auto& line : lines) {
if(auto depth = count(line, '-')) {
while(level < depth) level++, state.output.append("<ul>\n");
while(level > depth) level--, state.output.append("</ul>\n");
auto content = line.slice(depth + 1).split<1>(" => ");
auto data = markup(content[0]);
auto name = escape(content(1, data.crc32()));
state.output.append("<li><a href=\"#", name, "\">", data, "</a></li>\n");
}
}
while(level--) state.output.append("</ul>\n");
state.output.append("</nav>\n");
}
//list
else if(count(block, '*')) {
unsigned level = 0;
for(auto& line : lines) {
if(auto depth = count(line, '*')) {
while(level < depth) level++, state.output.append("<ul>\n");
while(level > depth) level--, state.output.append("</ul>\n");
auto data = markup(line.slice(depth + 1));
state.output.append("<li>", data, "</li>\n");
}
}
while(level--) state.output.append("</ul>\n");
}
//quote
else if(count(block, '>')) {
unsigned level = 0;
for(auto& line : lines) {
if(auto depth = count(line, '>')) {
while(level < depth) level++, state.output.append("<blockquote>\n");
while(level > depth) level--, state.output.append("</blockquote>\n");
auto data = markup(line.slice(depth + 1));
state.output.append(data, "\n");
}
}
while(level--) state.output.append("</blockquote>\n");
}
//code
else if(block.beginsWith(" ")) {
state.output.append("<pre>");
for(auto& line : lines) {
if(!ltrim(line, " ")) continue;
state.output.append(escape(line), "\n");
}
state.output.rtrim("\n").append("</pre>\n");
}
//divider
else if(block.equals("---")) {
state.output.append("<hr>\n");
}
//paragraph
else {
state.output.append("<p>", markup(block), "</p>\n");
}
return true;
}
auto DML::count(const string& text, char value) -> unsigned {
for(unsigned n = 0; n < text.size(); n++) {
if(text[n] != value) {
if(text[n] == ' ') return n;
break;
}
}
return 0;
}
auto DML::escape(const string& text) -> string {
string output;
for(unsigned n = 0; n < text.size();) {
char x = text[n++];
if(x == '&') { output.append("&amp;"); continue; }
if(x == '<') { output.append("&lt;"); continue; }
if(x == '>') { output.append("&gt;"); continue; }
if(x == '"') { output.append("&quot;"); continue; }
output.append(x);
}
return output;
}
auto DML::markup(const string& text) -> string {
string output;
char flagStrong = 0;
char flagEmphasis = 0;
char flagInsert = 0;
char flagDelete = 0;
char flagCode = 0;
for(unsigned n = 0; n < text.size();) {
char x = text[n], y = text[n + 1];
if(x == '[' && y == '\\') { output.append('['); n += 2; continue; }
if(x == '[' && y == '*' && flagStrong == 0) { flagStrong = 1; output.append("<strong>"); n += 2; continue; }
if(x == '*' && y == ']' && flagStrong == 1) { flagStrong = 0; output.append("</strong>"); n += 2; continue; }
if(x == '[' && y == '/' && flagEmphasis == 0) { flagEmphasis = 1; output.append("<em>"); n += 2; continue; }
if(x == '/' && y == ']' && flagEmphasis == 1) { flagEmphasis = 0; output.append("</em>"); n += 2; continue; }
if(x == '[' && y == '_' && flagInsert == 0) { flagInsert = 1; output.append("<ins>"); n += 2; continue; }
if(x == '_' && y == ']' && flagInsert == 1) { flagInsert = 0; output.append("</ins>"); n += 2; continue; }
if(x == '[' && y == '-' && flagDelete == 0) { flagDelete = 1; output.append("<del>"); n += 2; continue; }
if(x == '-' && y == ']' && flagDelete == 1) { flagDelete = 0; output.append("</del>"); n += 2; continue; }
if(x == '[' && y == '|' && flagCode == 0) { flagCode = 1; output.append("<code>"); n += 2; continue; }
if(x == '|' && y == ']' && flagCode == 1) { flagCode = 0; output.append("</code>"); n += 2; continue; }
if(x == '[' && y == '[') {
if(auto length = text.findFrom(n + 2, "]]")) {
lstring content = text.slice(n + 2, *length).split<1>(" => ");
output.append("<a href=\"", escape(content[0]), "\">", escape(content(1, content[0])), "</a>");
n += *length + 4;
continue;
}
}
if(x == '[' && y == '{') {
if(auto length = text.findFrom(n + 2, "}]")) {
lstring content = text.slice(n + 2, *length).split<1>(" => ");
output.append("<img src=\"", escape(content[0]), "\" alt=\"", escape(content(1, "")), "\">");
n += *length + 4;
continue;
}
}
if(x == '&') { output.append("&amp;"); n++; continue; }
if(x == '<') { output.append("&lt;"); n++; continue; }
if(x == '>') { output.append("&gt;"); n++; continue; }
if(x == '"') { output.append("&quot;"); n++; continue; }
output.append(x);
n++;
}
if(flagStrong) output.append("</strong>");
if(flagEmphasis) output.append("</em>");
if(flagInsert) output.append("</ins>");
if(flagDelete) output.append("</del>");
if(flagCode) output.append("</code>");
return output;
}
}}
#endif

82
nall/string/trim.hpp Normal file
View File

@@ -0,0 +1,82 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
auto trim(string& self, rstring lhs, rstring rhs) -> bool {
if(lhs.size() + rhs.size() > self.size()) return false;
if(memory::compare(self.data(), lhs.data(), lhs.size()) != 0) return false;
if(memory::compare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
self.resize(self.size() - rhs.size());
self.remove(0, lhs.size());
return true;
}
auto ltrim(string& self, rstring lhs) -> bool {
if(lhs.size() > self.size()) return false;
if(memory::compare(self.data(), lhs.data(), lhs.size()) != 0) return false;
self.remove(0, lhs.size());
return true;
}
auto rtrim(string& self, rstring rhs) -> bool {
if(rhs.size() > self.size()) return false;
if(memory::compare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
self.resize(self.size() - rhs.size());
return true;
}
auto itrim(string& self, rstring lhs, rstring rhs) -> bool {
if(lhs.size() + rhs.size() > self.size()) return false;
if(memory::icompare(self.data(), lhs.data(), lhs.size()) != 0) return false;
if(memory::icompare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
self.resize(self.size() - rhs.size());
self.remove(0, lhs.size());
return true;
}
auto iltrim(string& self, rstring lhs) -> bool {
if(lhs.size() > self.size()) return false;
if(memory::icompare(self.data(), lhs.data(), lhs.size()) != 0) return false;
self.remove(0, lhs.size());
return true;
}
auto irtrim(string& self, rstring rhs) -> bool {
if(rhs.size() > self.size()) return false;
if(memory::icompare(self.data() + self.size() - rhs.size(), rhs.data(), rhs.size()) != 0) return false;
self.resize(self.size() - rhs.size());
return true;
}
auto strip(string& self) -> bool {
return rstrip(self) | lstrip(self);
}
auto lstrip(string& self) -> bool {
unsigned size = 0;
while(size < self.size()) {
char input = self[size];
if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
size++;
}
if(size == 0) return false;
self.remove(0, size);
return true;
}
auto rstrip(string& self) -> bool {
unsigned size = 0;
while(size < self.size()) {
bool matched = false;
char input = self[self.size() - size - 1];
if(input != ' ' && input != '\t' && input != '\r' && input != '\n') break;
size++;
}
if(size == 0) return false;
self.resize(self.size() - size);
return true;
}
}
#endif

View File

@@ -2,46 +2,107 @@
namespace nall {
string substr(rstring source, unsigned offset, unsigned length) {
auto string::read(const string& filename) -> string {
#if !defined(_WIN32)
FILE* fp = fopen(filename, "rb");
#else
FILE* fp = _wfopen(utf16_t(filename), L"rb");
#endif
string result;
if(length == ~0u) length = source.size() - offset;
result.resize(length);
memcpy(result.data(), source.data() + offset, length);
if(!fp) return result;
fseek(fp, 0, SEEK_END);
signed filesize = ftell(fp);
if(filesize < 0) return fclose(fp), result;
rewind(fp);
result.resize(filesize);
fread(result.pointer(), 1, filesize, fp);
return fclose(fp), result;
}
template<unsigned L> auto string::repeat(const string& pattern) -> string {
string result;
unsigned times = L;
while(times--) result.append(pattern);
return result;
}
string sha256(const uint8_t* data, unsigned size) {
sha256_ctx sha;
uint8_t hash[32];
sha256_init(&sha);
sha256_chunk(&sha, data, size);
sha256_final(&sha);
sha256_hash(&sha, hash);
string result;
for(auto& byte : hash) result.append(hex<2>(byte));
auto fill(string& self, char fill) -> string& {
memory::fill(self.pointer(), self.size(), fill);
return self;
}
auto hash(const string& self) -> unsigned {
const char* p = self.data();
unsigned size = self.size();
unsigned result = 5381;
while(size--) result = (result << 5) + result + *p++;
return result;
}
bool tokenize(lstring& list, const char* s, const char* p) {
while(*s) {
if(*p == '*') {
const char* b = s;
while(*s) {
if(tokenize(list, s++, p + 1)) {
list.prepend(substr(b, 0, --s - b));
return true;
}
}
list.prepend(b);
return !*++p;
}
if(*s++ != *p++) return false;
auto remove(string& self, unsigned offset, unsigned length) -> string& {
char* p = self.pointer();
length = min(length, self.size());
memory::move(p + offset, p + offset + length, self.size() - length);
return self.resize(self.size() - length);
}
auto reverse(string& self) -> string& {
char* p = self.pointer();
unsigned size = self.size();
unsigned pivot = size >> 1;
for(signed x = 0, y = size - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
return self;
}
//+length => insert/delete from start (right justify)
//-length => insert/delete from end (left justify)
auto size(string& self, signed length, char fill) -> string& {
unsigned size = self.size();
if(size == length) return self;
bool right = length >= 0;
length = abs(length);
if(size < length) { //expand
self.resize(length);
char* p = self.pointer();
unsigned displacement = length - size;
if(right) memory::move(p + displacement, p, size);
else p += size;
while(displacement--) *p++ = fill;
} else { //shrink
char* p = self.pointer();
unsigned displacement = size - length;
if(right) memory::move(p, p + displacement, length);
self.resize(length);
}
while(*p == '*') { list.prepend(s); p++; }
return !*p;
return self;
}
char* integer(char* result, intmax_t value) {
auto slice(const string& self, signed offset, signed length) -> string {
string result;
if(offset < self.size()) {
if(length < 0) length = self.size() - offset;
result.resize(length);
memory::copy(result.pointer(), self.data() + offset, length);
}
return result;
}
//legacy function: required for some library functions, do not use in newly written code
auto substr(rstring source, signed offset, signed length) -> string {
string result;
if(length < 0) length = source.size() - offset;
result.resize(length);
memory::copy(result.pointer(), source.data() + offset, length);
return result;
}
auto integer(char* result, intmax_t value) -> char* {
bool negative = value < 0;
if(negative) value = -value;
@@ -54,14 +115,13 @@ char* integer(char* result, intmax_t value) {
value /= 10;
} while(value);
if(negative) buffer[size++] = '-';
//buffer[size++] = negative ? '-' : '+';
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
result[size] = 0;
return result;
}
char* decimal(char* result, uintmax_t value) {
auto decimal(char* result, uintmax_t value) -> char* {
char buffer[64];
unsigned size = 0;
@@ -79,7 +139,7 @@ char* decimal(char* result, uintmax_t value) {
//using sprintf is certainly not the most ideal method to convert
//a double to a string ... but attempting to parse a double by
//hand, digit-by-digit, results in subtle rounding errors.
unsigned real(char* str, long double value) {
auto real(char* result, long double value) -> unsigned {
char buffer[256];
#ifdef _WIN32
//Windows C-runtime does not support long double via sprintf()
@@ -101,17 +161,10 @@ unsigned real(char* str, long double value) {
}
unsigned length = strlen(buffer);
if(str) strcpy(str, buffer);
if(result) strcpy(result, buffer);
return length + 1;
}
string real(long double value) {
string temp;
temp.resize(real(nullptr, value));
real(temp.data(), value);
return temp;
}
}
#endif

View File

@@ -1,20 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
void sprint(string& output) {
}
template<typename T, typename... Args>
void sprint(string& output, const T& value, Args&&... args) {
output._append(make_string(value));
sprint(output, std::forward<Args>(args)...);
}
template<typename... Args> void print(Args&&... args) {
printf("%s", (const char*)string(std::forward<Args>(args)...));
}
}
#endif

View File

@@ -1,124 +0,0 @@
#ifdef NALL_STRING_INTERNAL_HPP
namespace nall {
template<unsigned limit> lstring string::split(rstring key) const { lstring result; result.split<limit>(key, data()); return result; }
template<unsigned limit> lstring string::isplit(rstring key) const { lstring result; result.isplit<limit>(key, data()); return result; }
template<unsigned limit> lstring string::qsplit(rstring key) const { lstring result; result.qsplit<limit>(key, data()); return result; }
template<unsigned limit> lstring string::iqsplit(rstring key) const { lstring result; result.iqsplit<limit>(key, data()); return result; }
bool string::match(rstring source) const { return nall::strmatch(data(), source); }
bool string::imatch(rstring source) const { return nall::istrmatch(data(), source); }
signed string::compare(rstring source) const {
return strcmp(data(), source.data());
}
signed string::icompare(rstring source) const {
return istrcmp(data(), source.data());
}
bool string::equals(rstring source) const {
if(size() != source.size()) return false;
return compare(source) == 0;
}
bool string::iequals(rstring source) const {
if(size() != source.size()) return false;
return icompare(source) == 0;
}
bool string::beginsWith(rstring source) const {
if(source.size() > size()) return false;
return memcmp(data(), source.data(), source.size()) == 0;
}
bool string::ibeginsWith(rstring source) const {
if(source.size() > size()) return false;
return imemcmp(data(), source.data(), source.size()) == 0;
}
bool string::endsWith(rstring source) const {
if(source.size() > size()) return false;
return memcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
}
bool string::iendsWith(rstring source) const {
if(source.size() > size()) return false;
return imemcmp(data() + size() - source.size(), source.data(), source.size()) == 0;
}
string string::slice(unsigned offset, unsigned length) const {
if(offset >= size()) return "";
if(length == ~0u) length = size() - offset;
return substr(data(), offset, length);
}
string& string::lower() { nall::strlower(data()); return *this; }
string& string::upper() { nall::strupper(data()); return *this; }
string& string::qlower() { nall::qstrlower(data()); return *this; }
string& string::qupper() { nall::qstrupper(data()); return *this; }
string& string::transform(rstring before, rstring after) { nall::strtr(data(), before, after); return *this; }
string& string::reverse() {
unsigned length = size(), pivot = length >> 1;
for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(data()[x], data()[y]);
return *this;
}
template<unsigned Limit> string& string::ltrim(rstring key) {
if(key.size() == 0) return *this;
unsigned limit = Limit ? Limit : ~0u, offset = 0;
while(limit && size() - offset >= key.size()) {
if(memcmp(data() + offset, key.data(), key.size())) break;
offset += key.size();
limit--;
}
if(offset) memmove(data(), data() + offset, size() - offset);
resize(size() - offset);
return *this;
}
template<unsigned Limit> string& string::rtrim(rstring key) {
if(key.size() == 0) return *this;
unsigned limit = Limit ? Limit : ~0u, offset = 0;
while(limit && size() - offset >= key.size()) {
if(memcmp(data() + size() - key.size() - offset, key.data(), key.size())) break;
offset += key.size();
limit--;
}
resize(size() - offset);
return *this;
}
template<unsigned Limit> string& string::trim(rstring key) {
rtrim<Limit>(key);
ltrim<Limit>(key);
return *this;
}
template<unsigned Limit> string& string::trim(rstring lkey, rstring rkey) {
rtrim<Limit>(rkey);
ltrim<Limit>(lkey);
return *this;
}
string& string::strip() {
nall::strip(data());
resize(length());
return *this;
}
maybe<unsigned> string::find(rstring key) const { return strpos(data(), key); }
maybe<unsigned> string::ifind(rstring key) const { return istrpos(data(), key); }
maybe<unsigned> string::qfind(rstring key) const { return qstrpos(data(), key); }
maybe<unsigned> string::iqfind(rstring key) const { return iqstrpos(data(), key); }
}
#endif