mirror of
https://github.com/Alcaro/Flips.git
synced 2026-09-06 17:54:33 -05:00
Update Arlib
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
25
arlib/file.h
25
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<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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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_*().
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
431
arlib/string.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
273
arlib/string.h
273
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<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; }
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
Reference in New Issue
Block a user