Update Arlib

This commit is contained in:
Alcaro
2016-12-22 03:39:22 +01:00
parent b39a33f333
commit 81a35ee437
11 changed files with 352 additions and 28 deletions

View File

@@ -113,11 +113,6 @@ endif
OUTNAME = $(PROGRAM)$(EXESUFFIX)
ifneq ($(SELFTEST),)
CONF_CFLAGS += -DARLIB_TEST -Dmain=not_quite_main
OBJNAME += -test
endif
#OBJMANGLE(rule,sources) - takes C/C++ source files and returns the mangled name under the specified rule
OBJMANGLE = $(patsubst %,obj/$1___$(OBJNAME)___%.o,$(subst /,__,$2))
#SOURCENAME(obj) - takes a .o file, returned from OBJMANGLE, and returns the corresponding source file
@@ -135,10 +130,23 @@ obj/%.c.o: $$(call SOURCENAME,$$@) | obj
obj/%.cpp.o: $$(call SOURCENAME,$$@) | obj
$(CXX) $(TRUE_CXXFLAGS) $(CFLAGS_$(call DOMAINNAME,$@)) -c $< -o $@
SOURCES += *.cpp arlib/*.cpp
SOURCES += *.cpp
SOURCES_ARLIB := arlib/*.cpp
ifneq ($(SELFTEST),)
ifeq ($(ARLIB_MAIN),)
CONF_CFLAGS += -DARLIB_TEST -Dmain=not_quite_main
CFLAGS_ARLIB += -UARLIB_TEST -DARLIB_TESTRUNNER
DOMAINS :=
else
SOURCES :=
CFLAGS_ARLIB += -DARLIB_TEST -DARLIB_TESTRUNNER -DARLIB_TEST_ARLIB
endif
OBJNAME += -test
endif
ifeq ($(ARGUI),1)
SOURCES += arlib/gui/*.cpp
SOURCES_ARLIB += arlib/gui/*.cpp
ifeq ($(OS),windows)
CONF_CFLAGS += -DARGUI_WINDOWS
CONF_LFLAGS += -lgdi32 -lcomctl32 -lcomdlg32
@@ -155,7 +163,7 @@ ifeq ($(AROPENGL),1)
ifeq ($(ARGUI),0)
$(error can't use OpenGL without the GUI)
endif
SOURCES += arlib/opengl/*.cpp
SOURCES_ARLIB += arlib/opengl/*.cpp
CONF_CFLAGS += -DARLIB_OPENGL
ifeq ($(OS),linux)
CONF_LFLAGS += -ldl
@@ -166,7 +174,7 @@ ifeq ($(AROPENGL),1)
endif
ifeq ($(ARTHREAD),1)
SOURCES += arlib/thread/*.cpp
SOURCES_ARLIB += arlib/thread/*.cpp
CONF_CFLAGS += -DARLIB_THREAD
ifeq ($(OS),linux)
CONF_CFLAGS += -pthread
@@ -175,7 +183,7 @@ ifeq ($(ARTHREAD),1)
endif
ifeq ($(ARSANDBOX),1)
SOURCES += arlib/sandbox/*.cpp
SOURCES_ARLIB += arlib/sandbox/*.cpp
CONF_CFLAGS += -DARLIB_SANDBOX
#not true since the windows sandbox isn't a real sandbox
#ifeq ($(OS),windows)
@@ -187,12 +195,12 @@ ifeq ($(ARSANDBOX),1)
endif
ifeq ($(ARWUTF),1)
SOURCES += arlib/wutf/*.cpp
SOURCES_ARLIB += arlib/wutf/*.cpp
CONF_CFLAGS += -DARLIB_WUTF
endif
ifeq ($(ARSOCKET),1)
SOURCES += arlib/socket/*.cpp
SOURCES_ARLIB += arlib/socket/*.cpp
CONF_CFLAGS += -DARLIB_SOCKET
ifeq ($(OS),windows)
@@ -219,7 +227,7 @@ ifeq ($(ARSOCKET),1)
endif
endif
SOURCES += arlib/deps/miniz.c
SOURCES_ARLIB += arlib/deps/miniz.c
TRUE_CFLAGS = -std=c99 $(CCXXFLAGS) $(CFLAGS) $(CONF_CFLAGS)
TRUE_CXXFLAGS =-std=c++11 -fno-rtti $(CCXXFLAGS) $(CXXFLAGS) $(CONF_CXXFLAGS)
@@ -240,7 +248,7 @@ obj:
mkdir obj
DOMAINS += DEFAULT NOWARN
DOMAINS += DEFAULT ARLIB NOWARN
OBJNAME := $(subst $(SPACE),,$(OBJNAME))
SOURCES_DEFAULT := $(SOURCES)

View File

@@ -17,6 +17,7 @@
#include "endian.h"
#include "file.h"
#include "function.h"
#include "intsafe.h"
#include "intwrap.h"
#include "os.h"
#include "serialize.h"

View File

@@ -175,6 +175,15 @@ template<typename T> class array : public arrayvieww<T> {
this->count=count;
}
void resize_shrink_noinit(size_t count)
{
if (this->count <= count) return;
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;
}
void resize_grow(size_t count)
{
size_t prevcount = this->count;
@@ -253,6 +262,28 @@ public:
return *this;
}
array<T> operator=(arrayview<T> other)
{
if (other.ptr() >= this->ptr() && other.ptr() < this->ptr()+this->size())
{
size_t start = other.ptr()-this->ptr();
size_t len = other.size();
for (size_t i=0;i<start;i++) this->items[i].~T();
memmove(this->ptr(), this->ptr()+start, sizeof(T)*len);
for (size_t i=start+len;i<this->count;i++) this->items[i].~T();
resize_shrink_noinit(len);
}
else
{
for (size_t i=0;i<this->count;i++) this->items[i].~T();
free(this->items);
clone(other);
}
return *this;
}
~array()
{
for (size_t i=0;i<this->count;i++) this->items[i].~T();

View File

@@ -16,8 +16,3 @@ uint32_t crc32_update(arrayview<uint8_t> data, uint32_t crc)
}
return ~crc;
}
uint32_t crc32(arrayview<uint8_t> data)
{
return crc32_update(data, 0);
}

View File

@@ -1,6 +1,14 @@
#include "global.h"
#include "array.h"
//uses 0xEDB88320 as generator polynomial
uint32_t crc32(arrayview<uint8_t> data);
//uses the standard 0xEDB88320 generator polynomial
uint32_t crc32_update(arrayview<uint8_t> data, uint32_t crc);
static inline uint32_t crc32(arrayview<uint8_t> data) { return crc32_update(data, 0); }
//calculates the 'inverse' crc32 of a buffer
//given
// a = crc32(buf1)
// b = crc32_update(buf2, a) ( == crc32(buf1+buf2) )
//then
// crc32_cancel(buf2, b) == a
uint32_t crc32_cancel(arrayview<uint8_t> data, uint32_t crc);

View File

@@ -64,10 +64,9 @@ typedef void(*funcptr)();
#define STR_(x) #x
#define STR(x) STR_(x)
#define GCC_VER(ma,mi,pa) (ma*10000 + mi*100 + pa)
#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#define LIKELY(expr) __builtin_expect(!!(expr), true)
#define UNLIKELY(expr) __builtin_expect(!!(expr), false)
#else

36
arlib/intsafe.cpp Normal file
View File

@@ -0,0 +1,36 @@
#define INTSAFE_SELFTEST
#include "intsafe.h"
#include "test.h"
#if 0 // apparently running 65536 iterations of anything under Valgrind takes a while
#define TEST(t,op) \
for (int a=0;a<256;a++) \
for (int b=0;b<256;b++) \
{ \
int au = (t)a; \
int bu = (t)b; \
intsafe<t> as = au; \
intsafe<t> bs = bu; \
if (as.valid() && bs.valid()) \
{ \
intsafe<t> cs1 = (1 op 8 == 256/*if op == <<*/ && b>7 ? 999 : au op bu); \
intsafe<t> cs2 = as op bs; \
if (cs1.val() != cs2.val()) \
{ \
assert_msg(false, tostring(au)+#op+tostring(bu)+": expected "+tostring(cs1.val())+", got "+tostring(cs2.val())); \
} \
} \
}
test()
{
TEST(uint8_t, +)
TEST(uint8_t, -)
TEST(uint8_t, *)
TEST(uint8_t, <<)
TEST( int8_t, +)
TEST( int8_t, -)
TEST( int8_t, *)
TEST( int8_t, <<)
}
#endif

239
arlib/intsafe.h Normal file
View File

@@ -0,0 +1,239 @@
#pragma once
#include "global.h"
#define ALLOPER(x) \
x(+, +=) x(-, -=) x(*, *=) x(/, /=) x(%, %=) \
x(|, |=) x(^, ^=) x(<<, <<=) x(>>, >>=)
//Overflow-safe integer class.
//If an operation would overflow, it gets a special sentinel value instead, and all further operations return that.
template<typename T> class intsafe {
T data;
#define HANDLE_BASE(stype, utype) \
static inline bool addov(utype a, utype b, utype* c) \
{ \
*c = a+b; /* rely on unsigned overflow wrapping */ \
return *c < a; \
} \
static inline bool subov(utype a, utype b, utype* c) \
{ \
*c = a-b; \
return (b > a); \
} \
static inline bool mulov(utype a, utype b, utype* c) \
{ \
*c = a*b; \
return a!=0 && *c/a!=b; \
} \
static inline bool lslov(utype a, utype b, utype* c) \
{ \
if (b >= sizeof(utype)*8) return true; \
*c = a<<b; \
return (*c>>b != a); \
} \
static inline bool addov(stype a, stype b, stype* c) \
{ \
*c = (utype)a+(utype)b; \
return (a>0 && b>0 && *c<0) || (a<0 && b<0 && *c>0); \
} \
static inline bool subov(stype a, stype b, stype* c) \
{ \
*c = (utype)a-(utype)b; \
return (a>0 && b<0 && *c<0) || (a<0 && b>0 && *c>0); \
} \
static inline bool mulov(stype a, stype b, stype* c) \
{ \
stype min = ((stype)-1)<<(sizeof(stype)*8-1); \
stype max = -(min+1); \
/* not sure if this can be simplified */ \
if (a<0 && b<0 && max/a > b) return true; \
if (a<0 && b>0 && min/a < b) return true; \
if (a>0 && b<0 && min/a > b) return true; \
if (a>0 && b>0 && max/a < b) return true; \
*c = a*b; \
return false; \
} \
static inline bool lslov(stype a, stype b, stype* c) \
{ \
if (b<0 || b >= sizeof(stype)*8) return true; \
*c = (a << b); \
return (*c>>b != a); \
}
HANDLE_BASE(signed int, unsigned int)
HANDLE_BASE(signed long, unsigned long)
HANDLE_BASE(signed long long, unsigned long long)
#ifndef INTSAFE_SELFTEST
#define HANDLE_EXT(name, op, type, ext) \
static inline bool name(type a, type b, type* c) \
{ \
if (sizeof(type) == sizeof(ext)) return name((ext)a, (ext)b, (ext*)c); \
if ((ext)a op (ext)b != (type)(a op b)) return true; \
*c = (ext)a op (ext)b; \
return false; \
}
HANDLE_EXT(addov, +, signed short, signed int)
HANDLE_EXT(addov, +, signed char, signed int)
HANDLE_EXT(addov, +, unsigned short, unsigned int)
HANDLE_EXT(addov, +, unsigned char, unsigned int)
HANDLE_EXT(subov, -, signed short, signed int)
HANDLE_EXT(subov, -, signed char, signed int)
HANDLE_EXT(subov, -, unsigned short, unsigned int)
HANDLE_EXT(subov, -, unsigned char, unsigned int)
HANDLE_EXT(mulov, *, signed short, signed int)
HANDLE_EXT(mulov, *, signed char, signed int)
HANDLE_EXT(mulov, *, unsigned short, unsigned int)
HANDLE_EXT(mulov, *, unsigned char, unsigned int)
HANDLE_EXT(lslov,<<, signed short, signed int)
HANDLE_EXT(lslov,<<, signed char, signed int)
HANDLE_EXT(lslov,<<, unsigned short, unsigned int)
HANDLE_EXT(lslov,<<, unsigned char, unsigned int)
#undef HANDLE_EXT
#else
//allow forcing the non-casting algorithm, so an exhaustive search can be done over the full range of some types
HANDLE_BASE(signed short, unsigned short)
HANDLE_BASE(signed char, unsigned char)
#endif
#undef HANDLE_BASE
#if GCC_VERSION>=50000 && !defined(INTSAFE_SELFTEST)
#define addov __builtin_add_overflow
#define subov __builtin_sub_overflow
#define mulov __builtin_mul_overflow
#endif
public:
static const T invalid = T(-1)<0 ? T(-1)<<(sizeof(T)*8-1) : T(-1);
//static const T min = T(-1)<0 ? invalid+1 : 0;
//static const T max = T(-1)<0 ? -min : T(-2);
intsafe() : data(0) {}
template<typename U> intsafe(U val) { if (T(val)==val) data=val; else data=invalid; }
bool valid() { return data!=invalid; }
T val() { return data; }
intsafe<T>& operator=(intsafe<T> i) { data=i.val(); return *this; }
intsafe<T> operator++(int) { intsafe<T> r = *this; *this+=1; return r; }
intsafe<T> operator--(int) { intsafe<T> r = *this; *this-=1; return r; }
intsafe<T>& operator++() { *this+=1; return *this; }
intsafe<T>& operator--() { *this+=1; return *this; }
#define OP(op, ope) \
intsafe<T>& operator ope(intsafe<T> i) { *this = *this op i; return *this; }
ALLOPER(OP)
#undef OP
intsafe<T> operator+(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
T ret;
if (addov(val(), b.val(), &ret)) return invalid;
else return ret;
}
intsafe<T> operator-(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
T ret;
if (subov(val(), b.val(), &ret)) return invalid;
else return ret;
}
intsafe<T> operator*(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
T ret;
if (mulov(val(), b.val(), &ret)) return invalid;
else return ret;
}
intsafe<T> operator/(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//overflows in division throw SIGFPE rather than truncating
return val()/b.val();
}
intsafe<T> operator%(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//like division, overflow doesn't truncate
return val()%b.val();
}
intsafe<T> operator&(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//can't overflow
return val()&b.val();
}
intsafe<T> operator|(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//can't overflow (okay, it can become ::invalid, but that's fine)
return val()|b.val();
}
intsafe<T> operator^(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//can't overflow
return val()^b.val();
}
intsafe<T> operator<<(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
T ret;
if (lslov(val(), b.val(), &ret)) return invalid;
else return ret;
}
intsafe<T> operator>>(intsafe<T> b)
{
if (!valid() || !b.valid()) return invalid;
//can't overflow
return val()>>b.val();
}
bool operator==(intsafe<T> b)
{
if (!valid() || !b.valid()) return false;
return val()==b.val();
}
bool operator!=(intsafe<T> b)
{
if (!valid() || !b.valid()) return true; // match NaN
return val()!=b.val();
}
bool operator<(intsafe<T> b)
{
if (!valid() || !b.valid()) return false;
return val()<b.val();
}
bool operator<=(intsafe<T> b)
{
if (!valid() || !b.valid()) return false;
return val()<=b.val();
}
bool operator>(intsafe<T> b)
{
if (!valid() || !b.valid()) return false;
return val()>b.val();
}
bool operator>=(intsafe<T> b)
{
if (!valid() || !b.valid()) return false;
return val()>=b.val();
}
};
#define OP(op, ope) \
template<typename T> intsafe<T> operator op(T a, intsafe<T> b) { return intsafe<T>(a) op b; } \
template<typename T> intsafe<T> operator op(intsafe<T> a, T b) { return b op intsafe<T>(a); }
ALLOPER(OP)
OP(==, _)
OP(!=, _)
OP(<, _)
OP(<=, _)
OP(>, _)
OP(>=, _)
#undef OP
#undef ALLOPER

View File

@@ -4,7 +4,7 @@
//TODO:
//- fetch howsmyssl, ensure the only failure is the session cache
#ifdef ARLIB_TEST
#ifdef ARLIB_TESTggg // disable these because they take way too long
//not in socket.h because this shouldn't really be used for anything, blocking is evil
static array<byte> recvall(socket* sock, unsigned int len)
{

View File

@@ -1,4 +1,7 @@
#ifdef ARLIB_TEST
#ifdef ARLIB_TESTRUNNER
#ifndef ARLIB_TEST
#define ARLIB_TEST
#endif
#include "test.h"
#include "array.h"
#include "gui/window.h"
@@ -104,6 +107,8 @@ int main(int argc, char* argv[])
return 0;
}
#ifdef ARLIB_TEST_ARLIB
test() {}
test() {}
#endif
#endif

View File

@@ -19,8 +19,8 @@ public:
_testdecl(void(*func)(), const char * loc, const char * name);
};
void _testfail(cstring name, int line);
void _testeqfail(cstring name, int line, cstring expected, cstring actual);
void _testfail(cstring why, int line);
void _testeqfail(cstring why, int line, cstring expected, cstring actual);
void _teststack_push(int line);
void _teststack_pop();
@@ -32,6 +32,8 @@ void _teststack_pop();
static void TESTFUNCNAME()
#define assert_ret(x, ret) do { if (!(x)) { _testfail("\nFailed assertion " #x, __LINE__); return ret; } } while(0)
#define assert(x) assert_ret(x,)
#define assert_msg_ret(x, msg, ret) do { if (!(x)) { _testfail((string)"\nFailed assertion " #x ": "+msg, __LINE__); return ret; } } while(0)
#define assert_msg(x, msg) assert_msg_ret(x,msg,)
#define assert_eq(actual,expected) do { \
if ((actual) != (expected)) \
{ \