diff --git a/arlib/Makefile b/arlib/Makefile index f7adcc3..5dade4b 100644 --- a/arlib/Makefile +++ b/arlib/Makefile @@ -52,7 +52,7 @@ endif #double gcc bug combo: #(1) GCC hates this pattern: -#//define foo(a,b,c) \ +#//#define foo(a,b,c) \ #// bar(a) \ #// bar(b) \ #// bar(c) diff --git a/arlib/array.h b/arlib/array.h index 17a54f9..e177277 100644 --- a/arlib/array.h +++ b/arlib/array.h @@ -19,7 +19,8 @@ protected: static const bool trivial_copy = trivial_cons; #endif //static const bool trivial_comp = std::has_unique_object_representations::value; - static const bool trivial_comp = std::is_integral::value; // comparison operator is memcmp + static const bool trivial_comp = std::is_integral::value; // equality comparison is memcmp + //don't care about destructor being trivial public: const T& operator[](size_t n) const { return items[n]; } @@ -27,7 +28,7 @@ public: const T* ptr() const { return items; } size_t size() const { return count; } - operator bool() { return count; } + operator bool() const { return count; } arrayview() { @@ -53,7 +54,7 @@ public: this->count = N; } - arrayview slice(size_t first, size_t count) const { return arrayview(this->items+first, count); } + arrayview slice(size_t first, size_t count) { return arrayview(this->items+first, count); } T join() const { @@ -76,6 +77,16 @@ public: return out; } + template arrayview cast() const + { + //reject cast() + static_assert(std::is_fundamental::value); + static_assert(std::is_fundamental::value); + + size_t newsize = this->count*sizeof(T)/sizeof(T2); + return arrayview((T2*)this->items, newsize); + } + //arrayview(const arrayview& other) //{ // clone(other); @@ -247,10 +258,7 @@ template class array : public arrayvieww { { this->items[i].~T(); } - size_t bufsize_pre=bitround(this->count); - size_t bufsize_post=bitround(count); - if (bufsize_pre != bufsize_post) this->items=realloc(this->items, sizeof(T)*bufsize_post); - this->count=count; + resize_shrink_noinit(count); } void resize_to(size_t count) @@ -292,7 +300,7 @@ public: { this->items[index].~T(); memmove(this->items+index, this->items+index+1, sizeof(T)*(this->count-1-index)); - this->count--; + resize_shrink_noinit(this->count-1); } array() diff --git a/arlib/bmlwrite.cpp b/arlib/bmlwrite.cpp index c39661e..f2a713f 100644 --- a/arlib/bmlwrite.cpp +++ b/arlib/bmlwrite.cpp @@ -51,6 +51,13 @@ void bmlwriter::node(cstring name, cstring val, mode m, bool enter) if (!enter) m_indent--; } +void bmlwriter::comment(cstring c) +{ + if (m_data) m_data += "\n"+indent(); + m_data += "#"+c; + m_caninline = false; +} + void bmlwriter::enter(cstring name, cstring val, mode m) { node(name, val, m, true); } void bmlwriter::node(cstring name, cstring val, mode m) { node(name, val, m, false); } void bmlwriter::exit() { m_indent--; m_caninline = false; } @@ -163,6 +170,60 @@ test() assert_eq(w.finish(), "a\n b\n :c\n :d"); // ensure this is properly non-inlined } + + //repeat some earlier tests with extra comments + { + bmlwriter w; + w.enter("a", ""); + w.node("b", "1"); + w.comment("x"); + w.node("c", ""); + w.exit(); + + assert_eq(w.finish(), "a b=1\n #x\n c"); + } + + { + bmlwriter w; + w.enter("a", "foo bar"); + w.node("b", "1"); + w.comment("x"); + w.node("c", "foo \"bar\""); + w.node("d", ""); + w.exit(); + + assert_eq(w.finish(), "a=\"foo bar\" b=1\n #x\n c: foo \"bar\"\n d"); + } + + { + bmlwriter w; + w.node("a", ""); + w.comment("x"); + w.enter("b", ""); + w.exit(); + + assert_eq(w.finish(), "a\n#x\nb"); + } + + { + bmlwriter w; + w.node("a", "1"); + w.comment("x"); + w.node("b", "2"); + + assert_eq(w.finish(), "a=1\n#x\nb=2"); + } + + { + bmlwriter w; + w.enter("a", ""); + w.comment("x"); + w.node("b", "c\nd"); + w.comment("x"); + w.exit(); + + assert_eq(w.finish(), "a\n #x\n b\n :c\n :d\n #x"); + } } #endif diff --git a/arlib/file.h b/arlib/file.h index e6b4099..24b84c0 100644 --- a/arlib/file.h +++ b/arlib/file.h @@ -3,12 +3,10 @@ #include "string.h" #include "array.h" -class filewrite; class file : nocopy { public: class impl : nocopy { - friend class file; - protected: + public: virtual size_t size() = 0; virtual bool resize(size_t newsize) = 0; @@ -24,9 +22,8 @@ public: virtual ~impl() {} }; - class implrd : impl { - friend class file; - protected: + class implrd : public impl { + public: virtual size_t size() = 0; bool resize(size_t newsize) { return false; } @@ -36,8 +33,8 @@ public: virtual arrayview mmap(size_t start, size_t len) = 0; virtual void unmap(arrayview data) = 0; - virtual arrayvieww mmapw(size_t start, size_t len) { return NULL; } - virtual void unmapw(arrayvieww data) {} + arrayvieww mmapw(size_t start, size_t len) { return NULL; } + void unmapw(arrayvieww data) {} }; private: impl* core; @@ -68,6 +65,7 @@ public: delete core; core = NULL; } + static file wrap(impl* core) { return file(core); } private: //This one will create the file from the filesystem. @@ -122,6 +120,7 @@ public: { return file(new file::memimpl(data)); } + //the array may not be modified while the file object exists, other than via the file object itself static file mem(array& data) { return file(new file::memimpl(&data)); @@ -184,4 +183,14 @@ private: static bool unlink_fs(cstring filename); }; + +class autommap : public arrayview { + const file& f; +public: + autommap(const file& f, arrayview b) : arrayview(b), f(f) {} + autommap(const file& f, size_t start, size_t end) : arrayview(f.mmap(start, end)), f(f) {} + autommap(const file& f) : arrayview(f.mmap()), f(f) {} + ~autommap() { f.unmap(*this); } +}; + void _window_init_file(); diff --git a/arlib/gui/gtk3-misc.cpp b/arlib/gui/gtk3-misc.cpp index 5f423d3..5f604c6 100644 --- a/arlib/gui/gtk3-misc.cpp +++ b/arlib/gui/gtk3-misc.cpp @@ -1,6 +1,4 @@ #include "window.h" -//#include "../image.h" -//#include "minir.h" #include "../file.h" #include "../os.h" #ifdef ARGUI_GTK3 @@ -104,6 +102,12 @@ bool window_console_attach() return window_console_avail(); } +string window_config_path() +{ +puts(g_get_user_config_dir()); + return g_get_user_config_dir(); +} + //file* file::create(const char * filename) //{ // //TODO diff --git a/arlib/gui/win32-misc.cpp b/arlib/gui/win32-misc.cpp index acad9bd..2d09d1a 100644 --- a/arlib/gui/win32-misc.cpp +++ b/arlib/gui/win32-misc.cpp @@ -19,32 +19,12 @@ //Console handling under Windows is a mess. (But launching a GUI app from a Linux console isn't much better...) //Microsoft dropped Windows XP at April 8, 2014, after an unusually long support period. That is well above two years ago. -//Vista will die on April 11, 2017. But its user count is so low I don't care about dropping that either. -//Therefore, I have no reason to continue caring about it working. -//Incompatibility levels: -//Level 0 - a feature works as intended -//Level 1 - a feature is usable, but behaves weirdly -//Level 2 - attempting to use a feature throws an error box, or reports failure in a way the program can and does handle -//Level 3 - attempting to use a feature reports success internally, but nothing happens -//Level 4 - attempting to use a feature crashes the program -//Level 5 - program won't start -//Maximum allowed incompatibility level: -//XP SP2 and older: 5 -//XP SP3: -// 1 after December 8, 2013 -// 2 after April 8, 2014 -// 3 after August 8, 2014 -// 4 after December 8, 2014 -// 5 after April 8, 2015 -//Vista SP0 and higher: 0 -//List: -//Level 0: SetDllDirectory demands XP SP1 or higher. (But anything below SP3 is, for all intents and purposes, dead.) -//Level 1: LVCFMT_FIXED_WIDTH on the listbox is ignored before Vista -//Danger list (likely to hit): -//Level 4: printf dislikes z (size_t) size specifiers; they must be behind #ifdef DEBUG, or turned into "I" via #define -// NOTE: This is present on Vista too. z requires 7 or higher. -//Level 5: 64-bit programs dislike XP (there are 32bit Vista/7/8, but Vista is practically dead, as is 32bit 7+) -//Level 5: SRWLOCK is Vista+ +//Vista will die on April 11, 2017, but its user count is so low I don't care about dropping that either. +//Therefore, I have no reason to continue caring about anything below Windows 7. +//Known issues: +//- LVCFMT_FIXED_WIDTH on the listbox is ignored before Vista +//- printf dislikes z (size_t) size specifiers prior to 7 +//- SRWLOCK requires Vista //static LARGE_INTEGER timer_freq; diff --git a/arlib/gui/window.h b/arlib/gui/window.h index 20c564d..aad0f80 100644 --- a/arlib/gui/window.h +++ b/arlib/gui/window.h @@ -1,5 +1,6 @@ #pragma once #include "../global.h" +#include "../string.h" #include class window; @@ -29,6 +30,10 @@ bool window_try_init(int * argc, char * * argv[]); bool window_console_avail(); bool window_console_attach(); // Returns whether it worked. +//On Windows, the program is assumed portable, so it returns the program directory. +//On Linux, the program is assumed installed, so it returns the user's config directory. +string window_config_path(); + //window toolkit is not choosable at runtime //It is safe to interact with this window while inside its callbacks, with the exception that you may not free it. //You may also not use window_run_*(). diff --git a/arlib/serialize-test.cpp b/arlib/serialize-test.cpp index 1f606ab..8cb29f0 100644 --- a/arlib/serialize-test.cpp +++ b/arlib/serialize-test.cpp @@ -35,6 +35,49 @@ struct ser4 { template void serialize(T& s) { mem.serialize(s); count++; } }; +struct ser5 { + array data; + SERIALIZE(data); +}; + +struct ser6 { + array data; + SERIALIZE(data); +}; + +struct ser7 { + ser5 par; + SERIALIZE(par); +}; + +struct ser8 { + //signed char a; + //signed short b; + //signed int c; + //signed long d; + //signed long long e; + unsigned char f; + unsigned short g; + unsigned int h; + unsigned long i; + unsigned long long j; + + template + void serialize(T& s) + { + //s.hex("a", a); + //s.hex("b", b); + //s.hex("c", c); + //s.hex("d", d); + //s.hex("e", e); + s.hex("f", f); + s.hex("g", g); + s.hex("h", h); + s.hex("i", i); + s.hex("j", j); + } +}; + test() { { @@ -53,6 +96,43 @@ test() item.d.b = 4; assert_eq(bmlserialize(item), "c a=1 b=2\nd a=3 b=4"); } + + { + ser5 item; + item.data.append(1); + item.data.append(2); + item.data.append(3); + assert_eq(bmlserialize(item), "data=1\ndata=2\ndata=3"); + } + + { + ser6 item; + item.data.append(); + item.data.append(); + item.data[0].a=1; + item.data[0].b=2; + item.data[1].a=3; + item.data[1].b=4; + assert_eq(bmlserialize(item), "data a=1 b=2\ndata a=3 b=4"); + } + + { + ser7 item; + item.par.data.append(1); + item.par.data.append(2); + item.par.data.append(3); + assert_eq(bmlserialize(item), "par data=1 data=2 data=3"); + } + + { + ser8 item; + item.f = 0xAA; + item.g = 0xAAAA; + item.h = 0xAAAAAAAA; + item.i = 0xAAAAAAAA; + item.j = 0xAAAAAAAAAAAAAAAA; + assert_eq(bmlserialize(item), "f=AA\ng=AAAA\nh=AAAAAAAA\ni=AAAAAAAA\nj=AAAAAAAAAAAAAAAA"); + } } test() @@ -94,5 +174,39 @@ test() ser4 item = bmlunserialize("a=1\nb=2\nd=4\ne=5\ne=5\nf=6"); assert_eq(item.count, 1); } + + { + ser5 item = bmlunserialize("data=1\ndata=2\ndata=3"); + assert_eq(item.data.size(), 3); + assert_eq(item.data[0], 1); + assert_eq(item.data[1], 2); + assert_eq(item.data[2], 3); + } + + { + ser6 item = bmlunserialize("data a=1 b=2\ndata a=3 b=4"); + assert_eq(item.data.size(), 2); + assert_eq(item.data[0].a, 1); + assert_eq(item.data[0].b, 2); + assert_eq(item.data[1].a, 3); + assert_eq(item.data[1].b, 4); + } + + { + ser7 item = bmlunserialize("par data=1 data=2 data=3"); + assert_eq(item.par.data.size(), 3); + assert_eq(item.par.data[0], 1); + assert_eq(item.par.data[1], 2); + assert_eq(item.par.data[2], 3); + } + + { + ser8 item = bmlunserialize("f=AA\ng=AAAA\nh=AAAAAAAA\ni=AAAAAAAA\nj=AAAAAAAAAAAAAAAA"); + assert_eq(item.f, 0xAA); + assert_eq(item.g, 0xAAAA); + assert_eq(item.h, 0xAAAAAAAA); + assert_eq(item.i, 0xAAAAAAAA); + assert_eq(item.j, 0xAAAAAAAAAAAAAAAA); + } } #endif diff --git a/arlib/serialize.h b/arlib/serialize.h index 42d94a5..f15f86d 100644 --- a/arlib/serialize.h +++ b/arlib/serialize.h @@ -10,20 +10,40 @@ class bmlserialize_impl { bmlwriter w; template friend string bmlserialize(T& item); -public: - - static const bool serializing = true; - - template void operator()(cstring name, T& item) + template void node(cstring name, T& item) { w.enter(name, ""); item.serialize(*this); w.exit(); } -#define LEAF(T) void operator()(cstring name, T& item) { w.node(name, tostring(item)); } + template void node(cstring name, array& item) + { + for (size_t i=0;i void operator()(cstring name, T& item) { node(name, item); } + + template void hex(cstring name, T& item) + { + w.node(name, tostringhex(item)); + } }; template string bmlserialize(T& item) @@ -81,6 +101,11 @@ class bmlunserialize_impl { } } + template void item(array& out) + { + item(out.append()); + } + void next() { matchagain = false; @@ -110,6 +135,18 @@ public: static const bool serializing = false; + void comment(cstring c) {} + + template void hex(cstring name, T& out) + { + while (thisnode == name) // this should be a loop, in case of documents like 'foo bar=1 bar=2 bar=3' + { + fromstringhex(thisval, out); + thisnode = ""; + next(); + } + } + template void operator()(cstring name, T& out) { while (thisnode == name) // this should be a loop, in case of documents like 'foo bar=1 bar=2 bar=3' diff --git a/arlib/string-test.cpp b/arlib/string-test.cpp deleted file mode 100644 index a16d283..0000000 --- a/arlib/string-test.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "string.h" -#include "test.h" - -test() -{ - { - const char * g = "hi"; - - string a = g; - a[2]='!'; - string b = a; - assert_eq(b, "hi!"); - a[3]='!'; - assert_eq(a, "hi!!"); - assert_eq(b, "hi!"); - a = b; - assert_eq(a, "hi!"); - assert_eq(b, "hi!"); - - - a.replace(1,1, "ello"); - assert_eq(a, "hello!"); - assert_eq(a.substr(1,3), "el"); - a.replace(1,4, "i"); - assert_eq(a, "hi!"); - a.replace(1,2, "ey"); - assert_eq(a, "hey"); - - assert_eq(a.substr(2,2), ""); - } - - { - //ensure it works properly when going across the inline-outline border - string a = "123456789012345"; - a += "678"; - assert_eq(a, "123456789012345678"); - a += (const char*)a; - string b = a; - assert_eq(a, "123456789012345678123456789012345678"); - assert_eq(a.substr(1,3), "23"); - assert_eq(b, "123456789012345678123456789012345678"); - assert_eq(a.substr(1,21), "23456789012345678123"); - assert_eq(a.substr(1,~1), "2345678901234567812345678901234567"); - assert_eq(a.substr(2,2), ""); - assert_eq(a.substr(22,22), ""); - a.replace(1,5, "-"); - assert_eq(a, "1-789012345678123456789012345678"); - a.replace(4,20, "-"); - assert_eq(a, "1-78-12345678"); - } - - { - //ensure outline->outline also works - string a = "123456789012345"; - a += "678"; - assert_eq(a, "123456789012345678"); - a += (const char*)a; - string b = a; - assert_eq(a, "123456789012345678123456789012345678"); - assert_eq(a.substr(1,3), "23"); - assert_eq(b, "123456789012345678123456789012345678"); - assert_eq(a.substr(1,21), "23456789012345678123"); - assert_eq(a.substr(1,~1), "2345678901234567812345678901234567"); - assert_eq(a.substr(2,2), ""); - assert_eq(a.substr(22,22), ""); - a.replace(1,5, "-"); - assert_eq(a, "1-789012345678123456789012345678"); - a.replace(4,20, "-"); - assert_eq(a, "1-78-12345678"); - } - - { - string a = "12345678"; - a += a; - a += a; - cstring b = a; // ensure this takes a proper reference, rather than piggybacking the original string - a = ""; - assert_eq(b, "12345678123456781234567812345678"); - } - - { - string a = "1abc1de1fgh1"; - assert_eq(a.replace("1", ""), "abcdefgh"); - assert_eq(a.replace("1", "@"), "@abc@de@fgh@"); - assert_eq(a.replace("1", "@@"), "@@abc@@de@@fgh@@"); - } - - { - //this has thrown valgrind errors due to derpy allocations - string a = "abcdefghijklmnopqrstuvwxyz"; - string b = a; // needs an extra reference - a += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assert_eq(a, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - } - - { - //this has also crashed, due to unshare() not respecting m_owning=false - cstring a = "aaaaaaaaaaaaaaaa"; - a[0] = 'b'; - assert_eq(a, "baaaaaaaaaaaaaaa"); - } - - { - arrayview a((uint8_t*)"123", 3); - string b = "["+string(a)+"]"; - string c = "["+cstring(a)+"]"; - assert_eq(b, "[123]"); - assert_eq(c, "[123]"); - } - - { - string a = "baaaaaaaaaaaaaaa"; - array b; - - b = a.split("a"); - assert_eq(b[0], "b"); - assert_eq(b[1], ""); - assert_eq(b[15], ""); - assert_eq(b.size(), 16); - - b = a.split("aa"); - assert_eq(b.size(), 8); - assert_eq(b[0], "b"); - assert_eq(b[1], ""); - assert_eq(b[6], ""); - assert_eq(b[7], "a"); - - b = a.split<1>("aa"); - assert_eq(b.size(), 2); - assert_eq(b[0], "b"); - assert_eq(b[1], "aaaaaaaaaaaaa"); - - b = a.split<1>("c"); - assert_eq(b.size(), 1); - assert_eq(b[0], "baaaaaaaaaaaaaaa"); - } -} diff --git a/arlib/string.cpp b/arlib/string.cpp new file mode 100644 index 0000000..73813e5 --- /dev/null +++ b/arlib/string.cpp @@ -0,0 +1,431 @@ +#include "string.h" +#include "test.h" + +uint8_t* string::alloc(uint8_t* prev, uint32_t prevsize, uint32_t newsize) +{ + if (prevsize==0) + { + uint8_t* ptr = malloc(bytes_for(newsize)); + *(int*)ptr = 1; + return ptr+sizeof(int); + } + + if (newsize==0) + { + int* refcount = (int*)(prev-sizeof(int)); + if (--*refcount == 0) free(refcount); + return NULL; + } + + prevsize = bytes_for(prevsize); + newsize = bytes_for(newsize); + if (prevsize==newsize) return prev; + + int* refcount = (int*)(prev-sizeof(int)); + if (*refcount == 1) + { + return (uint8_t*)realloc(refcount, newsize)+sizeof(int); + } + --*refcount; + + uint8_t* ptr = malloc(bytes_for(newsize)); + memcpy(ptr, prev-sizeof(int), min(prevsize, newsize)); + *(int*)ptr = 1; + return ptr+sizeof(int); +} + +void string::unshare() const +{ + if (inlined()) return; + if (m_owning && *(int*)(m_data-sizeof(int))==1) return; + + uint8_t* prevdat = m_data; + m_data = alloc(NULL,0, m_len); + memcpy(m_data, prevdat, m_len); + m_data[m_len] = '\0'; + + if (m_owning) alloc(prevdat,m_len, 0); + + m_owning = true; + m_nul = true; +} + +void string::resize(uint32_t newlen) +{ + unshare(); + + switch (!inlined()<<1 | (newlen>max_inline)) + { + case 0: // small->small + { + m_inline[newlen] = '\0'; + m_inline_len = max_inline-newlen; + } + break; + case 1: // small->big + { + uint8_t* newptr = alloc(NULL,0, newlen); + memcpy(newptr, m_inline, max_inline); + newptr[newlen] = '\0'; + m_data = newptr; + m_len = newlen; + m_owning = true; + m_nul = true; + + m_inline_len = -1; + } + break; + case 2: // big->small + { + uint8_t* oldptr = m_data; + uint32_t oldlen = m_len; + memcpy(m_inline, oldptr, newlen); + alloc(oldptr,oldlen, 0); + m_inline[newlen] = '\0'; + m_inline_len = max_inline-newlen; + } + break; + case 3: // big->big + { + m_data = alloc(m_data,m_len, newlen); + m_data[newlen] = '\0'; + m_len = newlen; + } + break; + } +} + +void string::init_from(arrayview data) +{ + const uint8_t * str = data.ptr(); + uint32_t len = data.size(); + + if (len <= max_inline) + { + memcpy(m_inline, str, len); + m_inline[len] = '\0'; + m_inline_len = max_inline-len; + } + else + { + m_inline_len = -1; + + m_data = alloc(NULL,0, len+1); + memcpy(m_data, str, len); + m_data[len]='\0'; + + m_len = len; + m_owning = true; + m_nul = true; + } +} + +void string::init_from_nocopy(arrayview data) +{ + const uint8_t * str = data.ptr(); + uint32_t len = data.size(); + + if (len <= max_inline) + { + memcpy(m_inline, str, len); + m_inline[len] = '\0'; + m_inline_len = max_inline-len; + } + else + { + m_inline_len = -1; + + m_data = (uint8_t*)str; // if m_owning is false, we know to not modify this + m_len = len; + m_owning = false; + m_nul = false; + } +} + +void string::replace(int32_t pos, int32_t len, const string& newdat) +{ + //if newdat is a cstring backed by this, then modifying this invalidates that string, so it's illegal + //if newdat equals this, then the memmoves will mess things up + if (this == &newdat) + { + string copy = newdat; + replace(pos, len, copy); + return; + } + + uint32_t prevlength = length(); + uint32_t newlength = newdat.length(); + + if (newlength < prevlength) + { + unshare(); + memmove(ptr()+pos+newlength, ptr()+pos+len, prevlength-len-pos); + resize(prevlength - len + newlength); + } + if (newlength == prevlength) + { + unshare(); + } + if (newlength > prevlength) + { + resize(prevlength - len + newlength); + memmove(ptr()+pos+newlength, ptr()+pos+len, prevlength-len-pos); + } + + memcpy(ptr()+pos, newdat.ptr(), newlength); +} + +string string::replace(const string& in, const string& out) +{ + size_t outlen = length(); + + if (in.length() != out.length()) + { + uint8_t* haystack = ptr(); + uint8_t* haystackend = ptr()+length(); + while (true) + { + haystack = (uint8_t*)memmem(haystack, haystackend-haystack, in.ptr(), in.length()); + if (!haystack) break; + + haystack += in.length(); + outlen += out.length(); // outlen-inlen is type uint - bad idea + outlen -= in.length(); + } + } + + string ret; + uint8_t* retptr = ret.construct(outlen).ptr(); + + uint8_t* prev = ptr(); + uint8_t* myend = ptr()+length(); + while (true) + { + uint8_t* match = (uint8_t*)memmem(prev, myend-prev, in.ptr(), in.length()); + if (!match) break; + + memcpy(retptr, prev, match-prev); + retptr += match-prev; + prev = match + in.length(); + + memcpy(retptr, out.ptr(), out.length()); + retptr += out.length(); + } + memcpy(retptr, prev, myend-prev); + + return ret; +} + +array string::split(const string& sep, size_t limit) const +{ + array ret; + const uint8_t * data = ptr(); + const uint8_t * dataend = ptr()+length(); + + while (ret.size() < limit) + { + const uint8_t * next = (uint8_t*)memmem(data, dataend-data, sep.ptr(), sep.length()); + if (!next) break; + ret.append(arrayview(data, next-data)); + data = next+sep.length(); + } + ret.append(arrayview(data, dataend-data)); + return ret; +} + +array string::rsplit(const string& sep, size_t limit) const +{ + array ret; + const uint8_t * datastart = ptr(); + const uint8_t * data = ptr()+length(); + + const uint8_t * sepp = sep.ptr(); + size_t sepl = sep.length(); + + while (ret.size() < limit) + { + if (datastart+sepl > data) break; + const uint8_t * next = data-sepl; + while (memcmp(next, sepp, sepl)!=0) + { + if (datastart==next) goto done; + next--; + } + ret.insert(0, arrayview(next+sepl, data-(next+sepl))); + data = next; + } +done: + ret.insert(0, arrayview(datastart, data-datastart)); + return ret; +} + +string string::codepoint(uint32_t cp) +{ + string ret; + if (cp<=0x7F) + { + ret[0] = cp; + } + else if (cp<=0x07FF) + { + ret[0] = (((cp>> 6) )|0xC0); + ret[1] = (((cp )&0x3F)|0x80); + } + else if (cp>=0xD800 && cp<=0xDFFF) return "\xEF\xBF\xBD"; + else if (cp<=0xFFFF) + { + ret[0] = (((cp>>12)&0x0F)|0xE0); + ret[1] = (((cp>>6 )&0x3F)|0x80); + ret[2] = (((cp )&0x3F)|0x80); + } + else if (cp<=0x10FFFF) + { + ret[0] = (((cp>>18)&0x07)|0xF0); + ret[1] = (((cp>>12)&0x3F)|0x80); + ret[2] = (((cp>>6 )&0x3F)|0x80); + ret[3] = (((cp )&0x3F)|0x80); + } + else return "\xEF\xBF\xBD"; + return ret; +} + +test() +{ + { + const char * g = "hi"; + + string a = g; + a[2]='!'; + string b = a; + assert_eq(b, "hi!"); + a[3]='!'; + assert_eq(a, "hi!!"); + assert_eq(b, "hi!"); + a = b; + assert_eq(a, "hi!"); + assert_eq(b, "hi!"); + + + a.replace(1,1, "ello"); + assert_eq(a, "hello!"); + assert_eq(a.substr(1,3), "el"); + a.replace(1,4, "i"); + assert_eq(a, "hi!"); + a.replace(1,2, "ey"); + assert_eq(a, "hey"); + + assert_eq(a.substr(2,2), ""); + } + + { + //ensure it works properly when going across the inline-outline border + string a = "123456789012345"; + a += "678"; + assert_eq(a, "123456789012345678"); + a += (const char*)a; + string b = a; + assert_eq(a, "123456789012345678123456789012345678"); + assert_eq(a.substr(1,3), "23"); + assert_eq(b, "123456789012345678123456789012345678"); + assert_eq(a.substr(1,21), "23456789012345678123"); + assert_eq(a.substr(1,~1), "2345678901234567812345678901234567"); + assert_eq(a.substr(2,2), ""); + assert_eq(a.substr(22,22), ""); + a.replace(1,5, "-"); + assert_eq(a, "1-789012345678123456789012345678"); + a.replace(4,20, "-"); + assert_eq(a, "1-78-12345678"); + } + + { + //ensure outline->outline also works + string a = "123456789012345"; + a += "678"; + assert_eq(a, "123456789012345678"); + a += (const char*)a; + string b = a; + assert_eq(a, "123456789012345678123456789012345678"); + assert_eq(a.substr(1,3), "23"); + assert_eq(b, "123456789012345678123456789012345678"); + assert_eq(a.substr(1,21), "23456789012345678123"); + assert_eq(a.substr(1,~1), "2345678901234567812345678901234567"); + assert_eq(a.substr(2,2), ""); + assert_eq(a.substr(22,22), ""); + a.replace(1,5, "-"); + assert_eq(a, "1-789012345678123456789012345678"); + a.replace(4,20, "-"); + assert_eq(a, "1-78-12345678"); + } + + { + string a = "12345678"; + a += a; + a += a; + cstring b = a; // ensure this takes a proper reference, rather than piggybacking the original string + a = ""; + assert_eq(b, "12345678123456781234567812345678"); + } + + { + string a = "1abc1de1fgh1"; + assert_eq(a.replace("1", ""), "abcdefgh"); + assert_eq(a.replace("1", "@"), "@abc@de@fgh@"); + assert_eq(a.replace("1", "@@"), "@@abc@@de@@fgh@@"); + } + + { + //this has thrown valgrind errors due to derpy allocations + string a = "abcdefghijklmnopqrstuvwxyz"; + string b = a; // needs an extra reference + a += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + assert_eq(a, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } + + { + //this has also crashed, due to unshare() not respecting m_owning=false + cstring a = "aaaaaaaaaaaaaaaa"; + a[0] = 'b'; + assert_eq(a, "baaaaaaaaaaaaaaa"); + } + + { + arrayview a((uint8_t*)"123", 3); + string b = "["+string(a)+"]"; + string c = "["+cstring(a)+"]"; + assert_eq(b, "[123]"); + assert_eq(c, "[123]"); + } + + { + string a; + a = "192.168.0.1"; + assert_eq(a.split(".").join("/"), "192/168/0/1"); + assert_eq(a.split<1>(".").join("/"), "192/168.0.1"); + assert_eq(a.rsplit(".").join("/"), "192/168/0/1"); + assert_eq(a.rsplit<1>(".").join("/"), "192.168.0/1"); + + a = "baaaaaaaaaaaaaaa"; + assert_eq(a.split("a").join("."), "b..............."); + assert_eq(a.split("aa").join("."), "b.......a"); + assert_eq(a.split<1>("aa").join("."), "b.aaaaaaaaaaaaa"); + assert_eq(a.split<1>("x").join("."), "baaaaaaaaaaaaaaa"); + + a = "aaaaaaaaaaaaaaab"; + assert_eq(a.split("a").join("."), "...............b"); + assert_eq(a.split("aa").join("."), ".......ab"); + assert_eq(a.split<1>("aa").join("."), ".aaaaaaaaaaaaab"); + assert_eq(a.split<1>("x").join("."), "aaaaaaaaaaaaaaab"); + + a = "baaaaaaaaaaaaaaa"; + assert_eq(a.rsplit("a").join("."), "b..............."); + assert_eq(a.rsplit("aa").join("."), "ba......."); + assert_eq(a.rsplit<1>("aa").join("."), "baaaaaaaaaaaaa."); + assert_eq(a.rsplit<1>("x").join("."), "baaaaaaaaaaaaaaa"); + + a = "aaaaaaaaaaaaaaab"; + assert_eq(a.rsplit("a").join("."), "...............b"); + assert_eq(a.rsplit("aa").join("."), "a.......b"); + assert_eq(a.rsplit<1>("aa").join("."), "aaaaaaaaaaaaa.b"); + assert_eq(a.rsplit<1>("x").join("."), "aaaaaaaaaaaaaaab"); + } +} diff --git a/arlib/string.h b/arlib/string.h index f026fb9..f8960bc 100644 --- a/arlib/string.h +++ b/arlib/string.h @@ -64,101 +64,12 @@ class string { //the sizes can be 0 if you want to //sizes are how many characters fit in the string, excluding the NUL //always allocates, doesn't try to inline - static uint8_t* alloc(uint8_t* prev, uint32_t prevsize, uint32_t newsize) - { - if (prevsize==0) - { - uint8_t* ptr = malloc(bytes_for(newsize)); - *(int*)ptr = 1; - return ptr+sizeof(int); - } - - if (newsize==0) - { - int* refcount = (int*)(prev-sizeof(int)); - if (--*refcount == 0) free(refcount); - return NULL; - } - - prevsize = bytes_for(prevsize); - newsize = bytes_for(newsize); - if (prevsize==newsize) return prev; - - int* refcount = (int*)(prev-sizeof(int)); - if (*refcount == 1) - { - return (uint8_t*)realloc(refcount, newsize)+sizeof(int); - } - --*refcount; - - uint8_t* ptr = malloc(bytes_for(newsize)); - memcpy(ptr, prev-sizeof(int), min(prevsize, newsize)); - *(int*)ptr = 1; - return ptr+sizeof(int); - } + static uint8_t* alloc(uint8_t* prev, uint32_t prevsize, uint32_t newsize); - - void unshare() const - { - if (inlined()) return; - if (m_owning && *(int*)(m_data-sizeof(int))==1) return; - - uint8_t* prevdat = m_data; - m_data = alloc(NULL,0, m_len); - memcpy(m_data, prevdat, m_len); - m_data[m_len] = '\0'; - - if (m_owning) alloc(prevdat,m_len, 0); - - m_owning = true; - m_nul = true; - } + void unshare() const; //does not initialize the new data - void resize(uint32_t newlen) - { - unshare(); - - switch (!inlined()<<1 | (newlen>max_inline)) - { - case 0: // small->small - { - m_inline[newlen] = '\0'; - m_inline_len = max_inline-newlen; - } - break; - case 1: // small->big - { - uint8_t* newptr = alloc(NULL,0, newlen); - memcpy(newptr, m_inline, max_inline); - newptr[newlen] = '\0'; - m_data = newptr; - m_len = newlen; - m_owning = true; - m_nul = true; - - m_inline_len = -1; - } - break; - case 2: // big->small - { - uint8_t* oldptr = m_data; - uint32_t oldlen = m_len; - memcpy(m_inline, oldptr, newlen); - alloc(oldptr,oldlen, 0); - m_inline[newlen] = '\0'; - m_inline_len = max_inline-newlen; - } - break; - case 3: // big->big - { - m_data = alloc(m_data,m_len, newlen); - m_data[newlen] = '\0'; - m_len = newlen; - } - break; - } - } + void resize(uint32_t newlen); const char * ptr_withnul() const { @@ -194,30 +105,7 @@ private: { init_from(arrayview((uint8_t*)str, strlen(str))); } - void init_from(arrayview data) - { - const uint8_t * str = data.ptr(); - uint32_t len = data.size(); - - if (len <= max_inline) - { - memcpy(m_inline, str, len); - m_inline[len] = '\0'; - m_inline_len = max_inline-len; - } - else - { - m_inline_len = -1; - - m_data = alloc(NULL,0, len+1); - memcpy(m_data, str, len); - m_data[len]='\0'; - - m_len = len; - m_owning = true; - m_nul = true; - } - } + void init_from(arrayview data); void init_from(const string& other) { memcpy(this, &other, sizeof(*this)); @@ -238,27 +126,7 @@ private: init_from_nocopy(arrayview((uint8_t*)str, strlen(str))); if (!inlined()) m_nul = true; } - void init_from_nocopy(arrayview data) - { - const uint8_t * str = data.ptr(); - uint32_t len = data.size(); - - if (len <= max_inline) - { - memcpy(m_inline, str, len); - m_inline[len] = '\0'; - m_inline_len = max_inline-len; - } - else - { - m_inline_len = -1; - - m_data = (uint8_t*)str; // if m_owning is false, we know to not modify this - m_len = len; - m_owning = false; - m_nul = false; - } - } + void init_from_nocopy(arrayview data); void init_from_nocopy(const string& other) { memcpy(this, &other, sizeof(*this)); @@ -336,79 +204,9 @@ public: return arrayvieww(ptr(), len); } - void replace(int32_t pos, int32_t len, const string& newdat) // const string& is ugly, but cstring isn't declared yet. - { - //if newdat is a cstring backed by this, then modifying this invalidates that string, so it's illegal - //if newdat equals this, then the memmoves will mess things up - if (this == &newdat) - { - string copy = newdat; - replace(pos, len, copy); - return; - } - - uint32_t prevlength = length(); - uint32_t newlength = newdat.length(); - - if (newlength < prevlength) - { - unshare(); - memmove(ptr()+pos+newlength, ptr()+pos+len, prevlength-len-pos); - resize(prevlength - len + newlength); - } - if (newlength == prevlength) - { - unshare(); - } - if (newlength > prevlength) - { - resize(prevlength - len + newlength); - memmove(ptr()+pos+newlength, ptr()+pos+len, prevlength-len-pos); - } - - memcpy(ptr()+pos, newdat.ptr(), newlength); - } + void replace(int32_t pos, int32_t len, const string& newdat); // const string& is ugly, but cstring isn't declared yet - string replace(const string& in, const string& out) - { - size_t outlen = length(); - - if (in.length() != out.length()) - { - uint8_t* haystack = ptr(); - uint8_t* haystackend = ptr()+length(); - while (true) - { - haystack = (uint8_t*)memmem(haystack, haystackend-haystack, in.ptr(), in.length()); - if (!haystack) break; - - haystack += in.length(); - outlen += out.length(); // outlen-inlen is type uint - bad idea - outlen -= in.length(); - } - } - - string ret; - uint8_t* retptr = ret.construct(outlen).ptr(); - - uint8_t* prev = ptr(); - uint8_t* myend = ptr()+length(); - while (true) - { - uint8_t* match = (uint8_t*)memmem(prev, myend-prev, in.ptr(), in.length()); - if (!match) break; - - memcpy(retptr, prev, match-prev); - retptr += match-prev; - prev = match + in.length(); - - memcpy(retptr, out.ptr(), out.length()); - retptr += out.length(); - } - memcpy(retptr, prev, myend-prev); - - return ret; - } + string replace(const string& in, const string& out); string& operator+=(const char * right) { @@ -431,26 +229,15 @@ public: //can't create csplit without things blowing up //limit is maximum number of cuts - array split(const string& sep, size_t limit) const - { - array ret; - const uint8_t * data = ptr(); - const uint8_t * dataend = ptr()+length(); - - while (ret.size() < limit) - { - const uint8_t * next = (uint8_t*)memmem(data, dataend-data, sep.ptr(), sep.length()); - if (!next) break; - ret.append(arrayview(data, next-data)); - data = next+sep.length(); - } - ret.append(arrayview(data, dataend-data)); - return ret; - } + array split(const string& sep, size_t limit) const; - template + template array split(const string& sep) const { return split(sep, limit); } - array split(const string& sep) const { return split(sep, SIZE_MAX); } + + array rsplit(const string& sep, size_t limit) const; + + template + array rsplit(const string& sep) const { return rsplit(sep, limit); } private: class noinit {}; @@ -507,35 +294,7 @@ public: inline bool startswith(cstring other) const; inline bool endswith(cstring other) const; - static string codepoint(uint32_t cp) - { - string ret; - if (cp<=0x7F) - { - ret[0] = cp; - } - else if (cp<=0x07FF) - { - ret[0] = (((cp>> 6) )|0xC0); - ret[1] = (((cp )&0x3F)|0x80); - } - else if (cp>=0xD800 && cp<=0xDFFF) return "\xEF\xBF\xBD"; - else if (cp<=0xFFFF) - { - ret[0] = (((cp>>12)&0x0F)|0xE0); - ret[1] = (((cp>>6 )&0x3F)|0x80); - ret[2] = (((cp )&0x3F)|0x80); - } - else if (cp<=0x10FFFF) - { - ret[0] = (((cp>>18)&0x07)|0xF0); - ret[1] = (((cp>>12)&0x3F)|0x80); - ret[2] = (((cp>>6 )&0x3F)|0x80); - ret[3] = (((cp )&0x3F)|0x80); - } - else return "\xEF\xBF\xBD"; - return ret; - } + static string codepoint(uint32_t cp); }; inline bool operator==(const string& left, const char * right ) { return left.bytes() == arrayview((uint8_t*)right,strlen(right)); } @@ -545,10 +304,10 @@ inline bool operator!=(const string& left, const char * right ) { return !operat inline bool operator!=(const string& left, const string& right) { return !operator==(left, right); } inline bool operator!=(const char * left, const string& right) { return !operator==(left, right); } +inline string operator+(const string& left, const string& right) { string ret=left; ret+=right; return ret; } inline string operator+(string&& left, const char * right) { left+=right; return left; } inline string operator+(const string& left, const char * right) { string ret=left; ret+=right; return ret; } inline string operator+(string&& left, const string& right) { left+=right; return left; } -inline string operator+(const string& left, const string& right) { string ret=left; ret+=right; return ret; } inline string operator+(const char * left, const string& right) { string ret=left; ret+=right; return ret; } inline string operator+(string&& left, char right) { left+=right; return left; } diff --git a/arlib/stringconv.cpp b/arlib/stringconv.cpp index 87a5f85..e12a73f 100644 --- a/arlib/stringconv.cpp +++ b/arlib/stringconv.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "test.h" #define FROMFUNC(t,frt,f) \ bool fromstring(cstring s, t& out) \ @@ -14,16 +15,39 @@ return true; \ } -FROMFUNC(signed char, long, strtol) -FROMFUNC(unsigned char, unsigned long, strtoul) -FROMFUNC(signed short, long, strtol) -FROMFUNC(unsigned short, unsigned long, strtoul) -FROMFUNC(signed int, long, strtol) -FROMFUNC(unsigned int, unsigned long, strtoul) -FROMFUNC(signed long, long, strtol) -FROMFUNC(unsigned long, unsigned long, strtoul) -FROMFUNC(signed long long, long long, strtoll) -FROMFUNC(unsigned long long, unsigned long long, strtoull) +//specification: if the input is a hex number, return something strtoul accepts +//otherwise, return something that strtoul rejects +//this means drop the 0x +static const char * drop0x(const char * in) +{ + if (in[0]=='0' && in[1]!='0') return in+1; + else return in; +} + +#define FROMFUNCHEX(t,frt,f) \ + FROMFUNC(t,frt,f) \ + \ + bool fromstringhex(cstring s, t& out) \ + { \ + const char * in = drop0x(s); \ + out = 0; \ + char * tmp; /* odd that this one isn't overloaded, like strchr */ \ + frt ret = f(in, &tmp, 16); \ + if (*tmp || (t)ret != (frt)ret) return false; \ + out = ret; \ + return true; \ + } + +FROMFUNC( signed char, long, strtol) +FROMFUNCHEX(unsigned char, unsigned long, strtoul) +FROMFUNC( signed short, long, strtol) +FROMFUNCHEX(unsigned short, unsigned long, strtoul) +FROMFUNC( signed int, long, strtol) +FROMFUNCHEX(unsigned int, unsigned long, strtoul) +FROMFUNC( signed long, long, strtol) +FROMFUNCHEX(unsigned long, unsigned long, strtoul) +FROMFUNC( signed long long, long long, strtoll) +FROMFUNCHEX(unsigned long long, unsigned long long, strtoull) bool fromstring(cstring s, double& out) { @@ -63,3 +87,24 @@ bool fromstring(cstring s, bool& out) out=false; return false; } + +template void testunhex(const char * S, unsigned long long V) +{ + T a; + assert_eq(fromstringhex(S, a), true); + assert_eq(a, V); +} + +test() +{ + testcall(testunhex("aa", 0xaa)); + testcall(testunhex("AA", 0xAA)); + testcall(testunhex("aaaa", 0xaaaa)); + testcall(testunhex("AAAA", 0xAAAA)); + testcall(testunhex("aaaaaaaa", 0xaaaaaaaa)); + testcall(testunhex("AAAAAAAA", 0xAAAAAAAA)); + testcall(testunhex("aaaaaaaa", 0xaaaaaaaa)); // this is sometimes 64bit, but good enough + testcall(testunhex("AAAAAAAA", 0xAAAAAAAA)); + testcall(testunhex("aaaaaaaaaaaaaaaa", 0xaaaaaaaaaaaaaaaa)); + testcall(testunhex("AAAAAAAAAAAAAAAA", 0xAAAAAAAAAAAAAAAA)); +} diff --git a/arlib/stringconv.h b/arlib/stringconv.h index 959b302..bb3d5d6 100644 --- a/arlib/stringconv.h +++ b/arlib/stringconv.h @@ -8,31 +8,37 @@ inline string tostring(cstring s) { return s; } //I'd use int123_t, but the set {8, 16, 32, 64} is smaller than {char, short, int, long, long long}, so one disappears //if this one shows up (for example time_t = long on Windows), error //printf has PRIi32, but the native ones are defined in terms of int/long -inline string tostring( signed char val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // the C++ standard says -inline string tostring(unsigned char val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // (un)signed char/short are -//signless char isn't integral, so not here -inline string tostring( signed short val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // promoted to (un)signed int -inline string tostring(unsigned short val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // in ellipsis -inline string tostring( signed int val) { char ret[32]; sprintf(ret, "%i", val); return ret; } -inline string tostring(unsigned int val) { char ret[32]; sprintf(ret, "%u", val); return ret; } -inline string tostring( signed long val) { char ret[32]; sprintf(ret, "%li", val); return ret; } -inline string tostring(unsigned long val) { char ret[32]; sprintf(ret, "%lu", val); return ret; } +inline string tostring( signed char val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // the C++ standard says +inline string tostring(unsigned char val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // (un)signed char/short are +inline string tostringhex(unsigned char val) { char ret[32]; sprintf(ret, "%X", val); return ret; } +//signless char isn't integral, so not here +inline string tostring( signed short val) { char ret[32]; sprintf(ret, "%i", val); return ret; } // promoted to (un)signed int +inline string tostring(unsigned short val) { char ret[32]; sprintf(ret, "%u", val); return ret; } // in ellipsis +inline string tostringhex(unsigned short val) { char ret[32]; sprintf(ret, "%X", val); return ret; } +inline string tostring( signed int val) { char ret[32]; sprintf(ret, "%i", val); return ret; } +inline string tostring(unsigned int val) { char ret[32]; sprintf(ret, "%u", val); return ret; } +inline string tostringhex(unsigned int val) { char ret[32]; sprintf(ret, "%X", val); return ret; } +inline string tostring( signed long val) { char ret[32]; sprintf(ret, "%li", val); return ret; } +inline string tostring(unsigned long val) { char ret[32]; sprintf(ret, "%lu", val); return ret; } +inline string tostringhex(unsigned long val) { char ret[32]; sprintf(ret, "%lX", val); return ret; } #ifdef _WIN32 # ifdef __GNUC__ // my GCC doesn't recognize I64 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wformat" # endif -inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%I64i", val); return ret; } -inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%I64u", val); return ret; } +inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%I64i", val); return ret; } +inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%I64u", val); return ret; } +inline string tostringhex(unsigned long long val) { char ret[32]; sprintf(ret, "%I64X", val); return ret; } # ifdef __GNUC__ # pragma GCC diagnostic pop # endif #else -inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%lli", val); return ret; } -inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%llu", val); return ret; } +inline string tostring( signed long long val) { char ret[32]; sprintf(ret, "%lli", val); return ret; } +inline string tostring(unsigned long long val) { char ret[32]; sprintf(ret, "%llu", val); return ret; } +inline string tostringhex(unsigned long long val) { char ret[32]; sprintf(ret, "%llX", val); return ret; } #endif -inline string tostring(float val) { char ret[64]; sprintf(ret, "%f", val); return ret; } // increase buffer sizes -inline string tostring(double val) { char ret[1024]; sprintf(ret, "%f", val); return ret; } // http://stackoverflow.com/q/7235456 +inline string tostring(float val) { char ret[64]; sprintf(ret, "%f", val); return ret; } // increase buffer sizes +inline string tostring(double val) { char ret[1024]; sprintf(ret, "%f", val); return ret; } // http://stackoverflow.com/q/7235456 inline string tostring(bool val) { return val ? "true" : "false"; } //inline string tostring(char val); // not sure if this one makes sense @@ -43,19 +49,23 @@ inline bool fromstring(cstring s, string& out) { out=s; return true; } inline bool fromstring(cstring s, cstring& out) { out=s; return true; } bool fromstring(cstring s, signed char & out); bool fromstring(cstring s, unsigned char & out); +bool fromstringhex(cstring s, unsigned char & out); bool fromstring(cstring s, signed short & out); bool fromstring(cstring s, unsigned short & out); +bool fromstringhex(cstring s, unsigned short & out); bool fromstring(cstring s, signed int & out); bool fromstring(cstring s, unsigned int & out); +bool fromstringhex(cstring s, unsigned int & out); bool fromstring(cstring s, signed long & out); bool fromstring(cstring s, unsigned long & out); +bool fromstringhex(cstring s, unsigned long & out); bool fromstring(cstring s, signed long long & out); bool fromstring(cstring s, unsigned long long & out); +bool fromstringhex(cstring s, unsigned long long & out); bool fromstring(cstring s, float& out); bool fromstring(cstring s, double& out); bool fromstring(cstring s, bool& out); - #define ALLSTRINGABLE(x) \ x(string) \ x(cstring) \