diff --git a/arlib/Makefile b/arlib/Makefile index 02a1b0a..4934b5d 100644 --- a/arlib/Makefile +++ b/arlib/Makefile @@ -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) diff --git a/arlib/arlib.h b/arlib/arlib.h index 692222c..8386c98 100644 --- a/arlib/arlib.h +++ b/arlib/arlib.h @@ -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" diff --git a/arlib/array.h b/arlib/array.h index 66757a0..c1931e1 100644 --- a/arlib/array.h +++ b/arlib/array.h @@ -175,6 +175,15 @@ template class array : public arrayvieww { 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 operator=(arrayview 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;iitems[i].~T(); + memmove(this->ptr(), this->ptr()+start, sizeof(T)*len); + for (size_t i=start+len;icount;i++) this->items[i].~T(); + + resize_shrink_noinit(len); + } + else + { + for (size_t i=0;icount;i++) this->items[i].~T(); + free(this->items); + clone(other); + } + return *this; + } + ~array() { for (size_t i=0;icount;i++) this->items[i].~T(); diff --git a/arlib/crc32.cpp b/arlib/crc32.cpp index 9a8c96a..dcf3c0a 100644 --- a/arlib/crc32.cpp +++ b/arlib/crc32.cpp @@ -16,8 +16,3 @@ uint32_t crc32_update(arrayview data, uint32_t crc) } return ~crc; } - -uint32_t crc32(arrayview data) -{ - return crc32_update(data, 0); -} diff --git a/arlib/crc32.h b/arlib/crc32.h index d624bbf..0bd47e4 100644 --- a/arlib/crc32.h +++ b/arlib/crc32.h @@ -1,6 +1,14 @@ #include "global.h" #include "array.h" -//uses 0xEDB88320 as generator polynomial -uint32_t crc32(arrayview data); +//uses the standard 0xEDB88320 generator polynomial uint32_t crc32_update(arrayview data, uint32_t crc); +static inline uint32_t crc32(arrayview 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 data, uint32_t crc); diff --git a/arlib/global.h b/arlib/global.h index cf8e98f..de6b585 100644 --- a/arlib/global.h +++ b/arlib/global.h @@ -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 diff --git a/arlib/intsafe.cpp b/arlib/intsafe.cpp new file mode 100644 index 0000000..26bc1f0 --- /dev/null +++ b/arlib/intsafe.cpp @@ -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 as = au; \ + intsafe bs = bu; \ + if (as.valid() && bs.valid()) \ + { \ + intsafe cs1 = (1 op 8 == 256/*if op == <<*/ && b>7 ? 999 : au op bu); \ + intsafe 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 diff --git a/arlib/intsafe.h b/arlib/intsafe.h new file mode 100644 index 0000000..8950619 --- /dev/null +++ b/arlib/intsafe.h @@ -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 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 != 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 intsafe(U val) { if (T(val)==val) data=val; else data=invalid; } + bool valid() { return data!=invalid; } + T val() { return data; } + + intsafe& operator=(intsafe i) { data=i.val(); return *this; } + + intsafe operator++(int) { intsafe r = *this; *this+=1; return r; } + intsafe operator--(int) { intsafe r = *this; *this-=1; return r; } + intsafe& operator++() { *this+=1; return *this; } + intsafe& operator--() { *this+=1; return *this; } + +#define OP(op, ope) \ + intsafe& operator ope(intsafe i) { *this = *this op i; return *this; } +ALLOPER(OP) +#undef OP + + + intsafe operator+(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + T ret; + if (addov(val(), b.val(), &ret)) return invalid; + else return ret; + } + intsafe operator-(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + T ret; + if (subov(val(), b.val(), &ret)) return invalid; + else return ret; + } + intsafe operator*(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + T ret; + if (mulov(val(), b.val(), &ret)) return invalid; + else return ret; + } + intsafe operator/(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //overflows in division throw SIGFPE rather than truncating + return val()/b.val(); + } + intsafe operator%(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //like division, overflow doesn't truncate + return val()%b.val(); + } + intsafe operator&(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //can't overflow + return val()&b.val(); + } + intsafe operator|(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //can't overflow (okay, it can become ::invalid, but that's fine) + return val()|b.val(); + } + intsafe operator^(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //can't overflow + return val()^b.val(); + } + intsafe operator<<(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + T ret; + if (lslov(val(), b.val(), &ret)) return invalid; + else return ret; + } + intsafe operator>>(intsafe b) + { + if (!valid() || !b.valid()) return invalid; + //can't overflow + return val()>>b.val(); + } + + bool operator==(intsafe b) + { + if (!valid() || !b.valid()) return false; + return val()==b.val(); + } + bool operator!=(intsafe b) + { + if (!valid() || !b.valid()) return true; // match NaN + return val()!=b.val(); + } + bool operator<(intsafe b) + { + if (!valid() || !b.valid()) return false; + return val() b) + { + if (!valid() || !b.valid()) return false; + return val()<=b.val(); + } + bool operator>(intsafe b) + { + if (!valid() || !b.valid()) return false; + return val()>b.val(); + } + bool operator>=(intsafe b) + { + if (!valid() || !b.valid()) return false; + return val()>=b.val(); + } +}; + +#define OP(op, ope) \ + template intsafe operator op(T a, intsafe b) { return intsafe(a) op b; } \ + template intsafe operator op(intsafe a, T b) { return b op intsafe(a); } +ALLOPER(OP) +OP(==, _) +OP(!=, _) +OP(<, _) +OP(<=, _) +OP(>, _) +OP(>=, _) +#undef OP + +#undef ALLOPER diff --git a/arlib/socket/socket-test.cpp b/arlib/socket/socket-test.cpp index 2962b09..d6c8fcb 100644 --- a/arlib/socket/socket-test.cpp +++ b/arlib/socket/socket-test.cpp @@ -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 recvall(socket* sock, unsigned int len) { diff --git a/arlib/test.cpp b/arlib/test.cpp index d2b4dd4..e96e08c 100644 --- a/arlib/test.cpp +++ b/arlib/test.cpp @@ -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 diff --git a/arlib/test.h b/arlib/test.h index 9c0b4fb..756fb02 100644 --- a/arlib/test.h +++ b/arlib/test.h @@ -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)) \ { \