Update Arlib

This commit is contained in:
Alcaro
2017-01-05 03:51:38 +01:00
parent 3512a31797
commit aae27f20da
14 changed files with 797 additions and 471 deletions

View File

@@ -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)

View File

@@ -19,7 +19,8 @@ protected:
static const bool trivial_copy = trivial_cons;
#endif
//static const bool trivial_comp = std::has_unique_object_representations<T>::value;
static const bool trivial_comp = std::is_integral<T>::value; // comparison operator is memcmp
static const bool trivial_comp = std::is_integral<T>::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<T> slice(size_t first, size_t count) const { return arrayview<T>(this->items+first, count); }
arrayview<T> slice(size_t first, size_t count) { return arrayview<T>(this->items+first, count); }
T join() const
{
@@ -76,6 +77,16 @@ public:
return out;
}
template<typename T2> arrayview<T2> cast() const
{
//reject cast<string>()
static_assert(std::is_fundamental<T>::value);
static_assert(std::is_fundamental<T2>::value);
size_t newsize = this->count*sizeof(T)/sizeof(T2);
return arrayview<T2>((T2*)this->items, newsize);
}
//arrayview(const arrayview<T>& other)
//{
// clone(other);
@@ -247,10 +258,7 @@ template<typename T> class array : public arrayvieww<T> {
{
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()

View File

@@ -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

View File

@@ -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<byte> mmap(size_t start, size_t len) = 0;
virtual void unmap(arrayview<byte> data) = 0;
virtual arrayvieww<byte> mmapw(size_t start, size_t len) { return NULL; }
virtual void unmapw(arrayvieww<byte> data) {}
arrayvieww<byte> mmapw(size_t start, size_t len) { return NULL; }
void unmapw(arrayvieww<byte> 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<byte>& data)
{
return file(new file::memimpl(&data));
@@ -184,4 +183,14 @@ private:
static bool unlink_fs(cstring filename);
};
class autommap : public arrayview<byte> {
const file& f;
public:
autommap(const file& f, arrayview<byte> 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();

View File

@@ -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

View File

@@ -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;

View File

@@ -1,5 +1,6 @@
#pragma once
#include "../global.h"
#include "../string.h"
#include <string.h>
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_*().

View File

@@ -35,6 +35,49 @@ struct ser4 {
template<typename T> void serialize(T& s) { mem.serialize(s); count++; }
};
struct ser5 {
array<int> data;
SERIALIZE(data);
};
struct ser6 {
array<ser1> 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<typename T>
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<ser4>("a=1\nb=2\nd=4\ne=5\ne=5\nf=6");
assert_eq(item.count, 1);
}
{
ser5 item = bmlunserialize<ser5>("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<ser6>("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<ser7>("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<ser8>("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

View File

@@ -10,20 +10,40 @@ class bmlserialize_impl {
bmlwriter w;
template<typename T> friend string bmlserialize(T& item);
public:
static const bool serializing = true;
template<typename T> void operator()(cstring name, T& item)
template<typename T> 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<typename T> void node(cstring name, array<T>& item)
{
for (size_t i=0;i<item.size();i++)
{
node(name, item[i]);
}
}
#define LEAF(T) void node(cstring name, T& item) { w.node(name, tostring(item)); }
ALLSTRINGABLE(LEAF);
#undef LEAF
public:
static const bool serializing = true;
void comment(cstring c)
{
w.comment(c);
}
template<typename T> void operator()(cstring name, T& item) { node(name, item); }
template<typename T> void hex(cstring name, T& item)
{
w.node(name, tostringhex(item));
}
};
template<typename T> string bmlserialize(T& item)
@@ -81,6 +101,11 @@ class bmlunserialize_impl {
}
}
template<typename T> void item(array<T>& out)
{
item(out.append());
}
void next()
{
matchagain = false;
@@ -110,6 +135,18 @@ public:
static const bool serializing = false;
void comment(cstring c) {}
template<typename T> 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<typename T> 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'

View File

@@ -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<byte> 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<string> 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");
}
}

431
arlib/string.cpp Normal file
View File

@@ -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<byte> 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<byte> 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> string::split(const string& sep, size_t limit) const
{
array<string> 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<uint8_t>(data, next-data));
data = next+sep.length();
}
ret.append(arrayview<uint8_t>(data, dataend-data));
return ret;
}
array<string> string::rsplit(const string& sep, size_t limit) const
{
array<string> 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<uint8_t>(next+sepl, data-(next+sepl)));
data = next;
}
done:
ret.insert(0, arrayview<uint8_t>(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<byte> 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");
}
}

View File

@@ -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<byte>((uint8_t*)str, strlen(str)));
}
void init_from(arrayview<byte> 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<byte> data);
void init_from(const string& other)
{
memcpy(this, &other, sizeof(*this));
@@ -238,27 +126,7 @@ private:
init_from_nocopy(arrayview<byte>((uint8_t*)str, strlen(str)));
if (!inlined()) m_nul = true;
}
void init_from_nocopy(arrayview<byte> 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<byte> data);
void init_from_nocopy(const string& other)
{
memcpy(this, &other, sizeof(*this));
@@ -336,79 +204,9 @@ public:
return arrayvieww<byte>(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<string> split(const string& sep, size_t limit) const
{
array<string> 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<uint8_t>(data, next-data));
data = next+sep.length();
}
ret.append(arrayview<uint8_t>(data, dataend-data));
return ret;
}
array<string> split(const string& sep, size_t limit) const;
template<size_t limit>
template<size_t limit = SIZE_MAX>
array<string> split(const string& sep) const { return split(sep, limit); }
array<string> split(const string& sep) const { return split(sep, SIZE_MAX); }
array<string> rsplit(const string& sep, size_t limit) const;
template<size_t limit = SIZE_MAX>
array<string> 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<byte>((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; }

View File

@@ -2,6 +2,7 @@
#include <stdio.h>
#include <math.h>
#include <float.h>
#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<typename T> void testunhex(const char * S, unsigned long long V)
{
T a;
assert_eq(fromstringhex(S, a), true);
assert_eq(a, V);
}
test()
{
testcall(testunhex<unsigned char >("aa", 0xaa));
testcall(testunhex<unsigned char >("AA", 0xAA));
testcall(testunhex<unsigned short >("aaaa", 0xaaaa));
testcall(testunhex<unsigned short >("AAAA", 0xAAAA));
testcall(testunhex<unsigned int >("aaaaaaaa", 0xaaaaaaaa));
testcall(testunhex<unsigned int >("AAAAAAAA", 0xAAAAAAAA));
testcall(testunhex<unsigned long >("aaaaaaaa", 0xaaaaaaaa)); // this is sometimes 64bit, but good enough
testcall(testunhex<unsigned long >("AAAAAAAA", 0xAAAAAAAA));
testcall(testunhex<unsigned long long>("aaaaaaaaaaaaaaaa", 0xaaaaaaaaaaaaaaaa));
testcall(testunhex<unsigned long long>("AAAAAAAAAAAAAAAA", 0xAAAAAAAAAAAAAAAA));
}

View File

@@ -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) \