浏览代码

everything moved from gaming core

Kajetan Johannes Hammerle 1 年之前
父节点
当前提交
61e2ab43c6

+ 1 - 1
data/ArrayList.h

@@ -8,7 +8,7 @@ namespace Core {
     class ArrayList final {
         static_assert(N > 0, "ArrayList size must be positive");
 
-        struct alignas(T) Aligned {
+        struct alignas(T) Aligned final {
             char data[sizeof(T)];
         };
 

+ 2 - 2
data/Components.h

@@ -14,7 +14,7 @@ namespace Core {
 
     public:
         template<typename R>
-        struct Node {
+        struct Node final {
             const Entity& entity;
             R& component;
         };
@@ -45,7 +45,7 @@ namespace Core {
         };
 
         template<typename C, typename R>
-        struct EntityIteratorAdapter {
+        struct EntityIteratorAdapter final {
             C& components;
 
             EntityIterator<C, R> begin() {

+ 1 - 1
data/LinkedList.h

@@ -6,7 +6,7 @@
 namespace Core {
     template<typename T>
     struct LinkedList final {
-        class Node {
+        class Node final {
             friend LinkedList;
 
             Node* next;

+ 1 - 1
data/List.h

@@ -216,7 +216,7 @@ namespace Core {
         }
 
     private:
-        struct alignas(T) Aligned {
+        struct alignas(T) Aligned final {
             char data[sizeof(T)];
         };
 

+ 1 - 1
data/RingBuffer.h

@@ -8,7 +8,7 @@ namespace Core {
     class RingBuffer final {
         static_assert(N > 0, "RingBuffer size must be positive");
 
-        struct alignas(T) Aligned {
+        struct alignas(T) Aligned final {
             char data[sizeof(T)];
         };
 

+ 1 - 1
math/BufferedValue.h

@@ -5,7 +5,7 @@
 
 namespace Core {
     template<typename T>
-    class BufferedValue {
+    class BufferedValue final {
         T last;
         T current;
 

+ 1 - 3
math/Frustum.cpp

@@ -1,9 +1,7 @@
 #include "math/Frustum.h"
 
-#include <math.h>
-
 Core::Frustum::Frustum(float fieldOfView, float nearClip_, float farClip_)
-    : tan(tanf(Core::Math::degreeToRadian(fieldOfView) * 0.5f)),
+    : tan(Core::Math::tan(Core::Math::degreeToRadian(fieldOfView) * 0.5f)),
       nearClip(nearClip_), farClip(farClip_) {
     float diff = 1.0f / (nearClip - farClip);
     projection.set(1, Vector4(0.0f, 1.0f / tan, 0.0f, 0.0f));

+ 12 - 0
math/Math.cpp

@@ -2,6 +2,18 @@
 
 #include <math.h>
 
+float Core::Math::sin(float f) {
+    return sinf(f);
+}
+
+float Core::Math::cos(float f) {
+    return cosf(f);
+}
+
+float Core::Math::tan(float f) {
+    return tanf(f);
+}
+
 void Core::Math::sinCos(float a, float& s, float& c) {
 #ifdef _GNU_SOURCE
     sincosf(a, &s, &c);

+ 3 - 0
math/Math.h

@@ -51,6 +51,9 @@ namespace Core::Math {
 
     constexpr float PI = 3.14159265358979323846f;
 
+    float sin(float f);
+    float cos(float f);
+    float tan(float f);
     void sinCos(float a, float& s, float& c);
 
     constexpr float radianToDegree(float radians) {

+ 7 - 0
meson.build

@@ -4,6 +4,9 @@ src = [
     'utils/Logger.cpp',
     'utils/Utility.cpp',
     'utils/HashCode.cpp',
+    'utils/Buffer.cpp',
+    'utils/Clock.cpp',
+    'utils/Random.cpp',
     'data/BitArray.cpp',
     'math/Math.cpp',
     'math/Vector.cpp',
@@ -40,6 +43,10 @@ src_tests = [
     'tests/FrustumTests.cpp',
     'tests/ViewTests.cpp',
     'tests/MatrixStackTests.cpp',
+    'tests/ColorTests.cpp',
+    'tests/BufferTests.cpp',
+    'tests/ClockTests.cpp',
+    'tests/RandomTests.cpp',
 ]
 
 compiler = meson.get_compiler('cpp')

+ 17 - 9
test/Main.cpp

@@ -1,12 +1,13 @@
-#include <stdio.h>
-
 #include "test/Test.h"
 #include "tests/ArrayListTests.h"
 #include "tests/ArrayStringTests.h"
 #include "tests/ArrayTests.h"
 #include "tests/BitArrayTests.h"
 #include "tests/BoxTests.h"
+#include "tests/BufferTests.h"
 #include "tests/BufferedValueTests.h"
+#include "tests/ClockTests.h"
+#include "tests/ColorTests.h"
 #include "tests/ComponentsTests.h"
 #include "tests/FrustumTests.h"
 #include "tests/HashMapTests.h"
@@ -17,25 +18,30 @@
 #include "tests/MatrixTests.h"
 #include "tests/PlaneTests.h"
 #include "tests/QuaternionTests.h"
+#include "tests/RandomTests.h"
 #include "tests/RingBufferTests.h"
 #include "tests/StackTests.h"
 #include "tests/UniquePointerTests.h"
 #include "tests/UtilityTests.h"
 #include "tests/VectorTests.h"
 #include "tests/ViewTests.h"
+#include "utils/ArrayString.h"
 #include "utils/Utility.h"
 
 static void onExit(int code, void* data) {
     unsigned int i = *static_cast<unsigned int*>(data);
-    printf("Hello from exit %d: 0x%x\n", code, i);
+    Core::ArrayString<1024> s;
+    CORE_TEST_FALSE(s.append("Hello from exit #: #"));
+    CORE_TEST_FALSE(s.format(code, i));
+    CORE_TEST_FALSE(s.printLine());
+    Core::Test::finalize();
 }
 
-static void onError(const char* file, int line, Core::Error e, void* data) {
+static void onError(const char* file, int line, Core::Error e, void*) {
     if(e == Core::Error::CAPACITY_REACHED ||
        e == Core::Error::INVALID_REMOVE_INDEX) {
         return;
     }
-    (void)data;
     CORE_LOG_ERROR("Error in #:# - #", file, line, static_cast<int>(e));
 }
 
@@ -47,7 +53,10 @@ int main() {
     Core::ArrayTests::test();
     Core::BitArrayTests::test();
     Core::BoxTests::test();
+    Core::BufferTests::test();
     Core::BufferedValueTests::test();
+    Core::ClockTests::test();
+    Core::ColorTests::test();
     Core::ComponentsTests::test();
     Core::FrustumTests::test();
     Core::HashMapTests::test();
@@ -58,6 +67,7 @@ int main() {
     Core::MatrixTests::test();
     Core::PlaneTests::test();
     Core::QuaternionTests::test();
+    Core::RandomTests::test();
     Core::RingBufferTests::test();
     Core::StackTests::test();
     Core::UniquePointerTests::test();
@@ -65,11 +75,9 @@ int main() {
     Core::VectorTests::test();
     Core::ViewTests::test();
 
-    Core::Test::finalize();
-
-    unsigned int data = 0x1234;
+    unsigned int data = 123456789;
     Core::setExitHandler(onExit, &data);
-    CORE_EXIT(0);
+    CORE_EXIT(1);
 
     return 0;
 }

+ 6 - 6
test/Test.cpp

@@ -3,19 +3,19 @@
 Core::HashMap<Core::Test::Internal::FileName, Core::Test::Internal::Result>
     Core::Test::Internal::results;
 
-void Core::Test::Internal::checkFloat(const char* file, int line, float wanted,
+bool Core::Test::Internal::checkFloat(const char* file, int line, float wanted,
                                       float actual, float error) {
     FileName fileName;
     if(fileName.append(file)) {
         CORE_LOG_WARNING("cannot append file name: #", file);
-        return;
+        return false;
     }
     Result* result = results.search(fileName);
     if(result == nullptr) {
         result = results.add(fileName, Result());
         if(result == nullptr) {
             CORE_LOG_WARNING("cannot add test result for #", file);
-            return;
+            return false;
         }
     }
     result->tests++;
@@ -23,10 +23,10 @@ void Core::Test::Internal::checkFloat(const char* file, int line, float wanted,
     diff = diff < 0.0f ? -diff : diff;
     if(diff <= error) {
         result->successTests++;
-    } else {
-        CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted,
-                       actual)
+        return true;
     }
+    CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted, actual)
+    return false;
 }
 
 void Core::Test::finalize() {

+ 19 - 15
test/Test.h

@@ -7,7 +7,7 @@
 
 namespace Core::Test {
     namespace Internal {
-        struct Result {
+        struct Result final {
             int tests = 0;
             int successTests = 0;
         };
@@ -15,57 +15,61 @@ namespace Core::Test {
         extern HashMap<FileName, Result> results;
 
         template<typename T>
-        void checkEqual(const char* file, int line, const T& wanted,
+        bool checkEqual(const char* file, int line, const T& wanted,
                         const T& actual) {
             FileName fileName;
             if(fileName.append(file)) {
                 CORE_LOG_WARNING("cannot append file name: #", file);
-                return;
+                return false;
             }
             Result* result = results.search(fileName);
             if(result == nullptr) {
                 result = results.tryEmplace(fileName);
                 if(result == nullptr) {
                     CORE_LOG_WARNING("cannot add test result for #", file);
-                    return;
+                    return false;
                 }
             }
             result->tests++;
             if(wanted == actual) {
                 result->successTests++;
-            } else {
-                CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line,
-                               wanted, actual)
+                return true;
             }
+            CORE_LOG_ERROR("#:# - expected '#' got '#'", fileName, line, wanted,
+                           actual)
+            return false;
         }
 
         template<typename A, typename B>
-        void checkString(const char* file, int line, const A& wanted,
+        bool checkString(const char* file, int line, const A& wanted,
                          const B& actual) {
             ArrayString<2048> a;
             if(a.append(wanted)) {
                 CORE_LOG_WARNING("too small buffer");
-                return;
+                return false;
             }
             ArrayString<2048> b;
             if(b.append(actual)) {
                 CORE_LOG_WARNING("too small buffer");
-                return;
+                return false;
             }
             return checkEqual(file, line, a, b);
         }
 
-        void checkFloat(const char* file, int line, float wanted, float actual,
+        bool checkFloat(const char* file, int line, float wanted, float actual,
                         float error);
 
         template<int N, typename T>
-        void checkVector(const char* file, int line,
+        bool checkVector(const char* file, int line,
                          const Core::Vector<N, T>& wanted,
                          const Core::Vector<N, T>& actual, float error) {
+            bool result = true;
             for(int i = 0; i < N; i++) {
-                checkFloat(file, line, static_cast<float>(wanted[i]),
-                           static_cast<float>(actual[i]), error);
+                result = checkFloat(file, line, static_cast<float>(wanted[i]),
+                                    static_cast<float>(actual[i]), error) &&
+                         result;
             }
+            return result;
         }
     }
     void finalize();
@@ -73,7 +77,7 @@ namespace Core::Test {
 
 #define CORE_TEST_EQUAL(wanted, actual)                                        \
     Core::Test::Internal::checkEqual<Core::RemoveReference<decltype(actual)>>( \
-        __FILE__, __LINE__, wanted, actual);
+        __FILE__, __LINE__, wanted, actual)
 #define CORE_TEST_STRING(wanted, actual)                                       \
     Core::Test::Internal::checkString(__FILE__, __LINE__, wanted, actual);
 #define CORE_TEST_FALSE(actual) CORE_TEST_EQUAL(false, actual);

+ 70 - 0
tests/BufferTests.cpp

@@ -0,0 +1,70 @@
+#include "tests/BufferTests.h"
+
+#include "test/Test.h"
+#include "utils/Buffer.h"
+
+static constexpr int SIZE_TYPES =
+    sizeof(int) + sizeof(long) + sizeof(float) + sizeof(double);
+
+static void testAdd() {
+    Core::Buffer buffer(10);
+    for(int i = 0; i < 100000; i++) {
+        CORE_TEST_FALSE(buffer.add(5));
+        CORE_TEST_FALSE(buffer.add(5L));
+        CORE_TEST_FALSE(buffer.add(5.0f));
+        CORE_TEST_FALSE(buffer.add(5.0));
+    }
+    CORE_TEST_EQUAL(SIZE_TYPES * 100000, buffer.getLength());
+}
+
+static void testCopy() {
+    Core::Buffer buffer(10);
+    for(int i = 0; i < 10; i++) {
+        CORE_TEST_FALSE(buffer.add(5));
+        CORE_TEST_FALSE(buffer.add(5L));
+        CORE_TEST_FALSE(buffer.add(5.0f));
+        CORE_TEST_FALSE(buffer.add(5.0));
+    }
+    Core::Buffer buffer2;
+    CORE_TEST_FALSE(buffer2.copyFrom(buffer));
+    if(CORE_TEST_EQUAL(SIZE_TYPES * 10, buffer.getLength()) &&
+       CORE_TEST_EQUAL(SIZE_TYPES * 10, buffer2.getLength())) {
+        CORE_TEST_TRUE(Core::memoryCompare(buffer, buffer2, SIZE_TYPES * 10));
+    }
+}
+
+static void testMoveConstruct() {
+    Core::Buffer buffer(10);
+    for(int i = 0; i < 10; i++) {
+        CORE_TEST_FALSE(buffer.add(5));
+        CORE_TEST_FALSE(buffer.add(5L));
+        CORE_TEST_FALSE(buffer.add(5.0f));
+        CORE_TEST_FALSE(buffer.add(5.0));
+    }
+    Core::Buffer buffer2(Core::move(buffer));
+
+    CORE_TEST_EQUAL(0, buffer.getLength());
+    CORE_TEST_EQUAL(SIZE_TYPES * 10, buffer2.getLength());
+}
+
+static void testMove() {
+    Core::Buffer buffer(10);
+    for(int i = 0; i < 10; i++) {
+        CORE_TEST_FALSE(buffer.add(5));
+        CORE_TEST_FALSE(buffer.add(5L));
+        CORE_TEST_FALSE(buffer.add(5.0f));
+        CORE_TEST_FALSE(buffer.add(5.0));
+    }
+    Core::Buffer buffer2(3);
+    buffer2 = Core::move(buffer);
+
+    CORE_TEST_EQUAL(0, buffer.getLength());
+    CORE_TEST_EQUAL(SIZE_TYPES * 10, buffer2.getLength());
+}
+
+void Core::BufferTests::test() {
+    testAdd();
+    testCopy();
+    testMoveConstruct();
+    testMove();
+}

+ 8 - 0
tests/BufferTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_BUFFER_TESTS_H
+#define CORE_BUFFER_TESTS_H
+
+namespace Core::BufferTests {
+    void test();
+}
+
+#endif

+ 46 - 0
tests/ClockTests.cpp

@@ -0,0 +1,46 @@
+#include "tests/ClockTests.h"
+
+#include "test/Test.h"
+#include "utils/Clock.h"
+
+static void testUpdate() {
+    Core::Clock c;
+    Core::Clock::Nanos n1 = 0;
+    Core::Clock::Nanos n2 = 0;
+    Core::Clock::Nanos n3 = 0;
+    Core::Clock::Nanos n4 = 0;
+    CORE_TEST_FALSE(c.update(n1));
+    CORE_TEST_FALSE(c.update(n2));
+    CORE_TEST_FALSE(c.update(n3));
+    CORE_TEST_FALSE(c.update(n4));
+    CORE_TEST_TRUE(n1 == 0);
+    CORE_TEST_TRUE(n2 > 0);
+    CORE_TEST_TRUE(n3 > 0);
+    CORE_TEST_TRUE(n4 > 0);
+}
+
+static void testUpdatesPerSecond() {
+    Core::Clock c;
+    for(int i = 0; i < 1000; i++) {
+        Core::Clock::Nanos n = 0;
+        CORE_TEST_FALSE(c.update(n));
+    }
+    CORE_TEST_TRUE(c.getUpdatesPerSecond() > 0.0f);
+}
+
+static void testWait(Core::Clock::Nanos wait) {
+    Core::Clock c;
+    Core::Clock::Nanos n = 0;
+    CORE_TEST_FALSE(c.update(n));
+    CORE_TEST_FALSE(c.wait(wait));
+    Core::Clock::Nanos n2 = 0;
+    CORE_TEST_FALSE(c.update(n2));
+    CORE_TEST_TRUE(n2 >= wait && n2 <= wait * 11 / 10);
+}
+
+void Core::ClockTests::test() {
+    testUpdate();
+    testUpdatesPerSecond();
+    testWait(50'000'000);
+    testWait(1'300'000'000);
+}

+ 8 - 0
tests/ClockTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_CLOCK_TESTS_H
+#define CORE_CLOCK_TESTS_H
+
+namespace Core::ClockTests {
+    void test();
+}
+
+#endif

+ 58 - 0
tests/ColorTests.cpp

@@ -0,0 +1,58 @@
+#include "tests/ColorTests.h"
+
+#include "test/Test.h"
+#include "utils/Color.h"
+
+const float eps = 0.0001f;
+
+static void testColor1() {
+    Core::Color1 c(36);
+    CORE_TEST_EQUAL(36, c[0]);
+    CORE_TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
+}
+
+static void testColor2() {
+    Core::Color2 c(36 + 256, 100.0);
+    CORE_TEST_EQUAL(36, c[0]);
+    CORE_TEST_EQUAL(100, c[1]);
+    CORE_TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
+    CORE_TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
+}
+
+static void testColor3() {
+    Core::Color3 c(36, 100.0f, 200);
+    CORE_TEST_EQUAL(36, c[0]);
+    CORE_TEST_EQUAL(100, c[1]);
+    CORE_TEST_EQUAL(200, c[2]);
+    CORE_TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
+    CORE_TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
+    CORE_TEST_FLOAT(200.0f / 255.0f, c.asFloat(2), eps);
+}
+
+static void testColor4() {
+    Core::Color4 c(36, 100, 200, 142);
+    CORE_TEST_EQUAL(36, c[0]);
+    CORE_TEST_EQUAL(100, c[1]);
+    CORE_TEST_EQUAL(200, c[2]);
+    CORE_TEST_EQUAL(142, c[3]);
+    CORE_TEST_FLOAT(36.0f / 255.0f, c.asFloat(0), eps);
+    CORE_TEST_FLOAT(100.0f / 255.0f, c.asFloat(1), eps);
+    CORE_TEST_FLOAT(200.0f / 255.0f, c.asFloat(2), eps);
+    CORE_TEST_FLOAT(142.0f / 255.0f, c.asFloat(3), eps);
+}
+
+static void testColor4Empty() {
+    Core::Color4 c;
+    CORE_TEST_EQUAL(0, c[0]);
+    CORE_TEST_EQUAL(0, c[1]);
+    CORE_TEST_EQUAL(0, c[2]);
+    CORE_TEST_EQUAL(0, c[3]);
+}
+
+void Core::ColorTests::test() {
+    testColor1();
+    testColor2();
+    testColor3();
+    testColor4();
+    testColor4Empty();
+}

+ 8 - 0
tests/ColorTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_COLOR_TESTS_H
+#define CORE_COLOR_TESTS_H
+
+namespace Core::ColorTests {
+    void test();
+}
+
+#endif

+ 1 - 1
tests/HashMapTests.cpp

@@ -72,7 +72,7 @@ static void testOverflow() {
     }
 }
 
-struct A {
+struct A final {
     int a;
     int b;
 

+ 1 - 1
tests/LinkedListTests.cpp

@@ -3,7 +3,7 @@
 #include "data/LinkedList.h"
 #include "test/Test.h"
 
-struct LinkedListTester {
+struct LinkedListTester final {
     int a;
     LinkedListTester(int a_) : a(a_) {
     }

+ 2 - 4
tests/MathTests.cpp

@@ -1,7 +1,5 @@
 #include "tests/MathTests.h"
 
-#include <math.h>
-
 #include "math/Math.h"
 #include "test/Test.h"
 
@@ -57,8 +55,8 @@ static void testSinCos() {
         float rSin = 0.0f;
         float rCos = 0.0f;
         CMath::sinCos(f, rSin, rCos);
-        CORE_TEST_FLOAT(sinf(f), rSin, eps);
-        CORE_TEST_FLOAT(cosf(f), rCos, eps);
+        CORE_TEST_FLOAT(CMath::sin(f), rSin, eps);
+        CORE_TEST_FLOAT(CMath::cos(f), rCos, eps);
     }
 }
 

+ 78 - 0
tests/RandomTests.cpp

@@ -0,0 +1,78 @@
+#include "tests/RandomTests.h"
+
+#include "data/Array.h"
+#include "test/Test.h"
+#include "utils/Random.h"
+
+static void testAverage() {
+    Core::Random r(553);
+    float sum = 0;
+    for(int i = 0; i < 1000000; i++) {
+        sum += static_cast<float>(r.next(2, 10));
+    }
+    sum /= 1000000.0f;
+    CORE_TEST_FLOAT(6.0f, sum, 0.01f);
+}
+
+static void testCoin() {
+    Core::Random r(553);
+    Core::Array<int, 2> c(0);
+    for(int i = 0; i < 1000000; i++) {
+        c[r.nextBool()]++;
+    }
+    CORE_TEST_FLOAT(0.0f, static_cast<float>(c[0] - c[1]) / 1000000.0f, 0.001f);
+}
+
+static void testDistribution() {
+    Core::Random r(553);
+    Core::Array<int, 102> c(0);
+    for(int i = 0; i < 1000000; i++) {
+        c[r.next(1, c.getLength() - 2)]++;
+    }
+    CORE_TEST_EQUAL(0, c[0]);
+    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
+    for(int i = 1; i < c.getLength() - 1; i++) {
+        CORE_TEST_FLOAT(0.01f, static_cast<float>(c[i]) / 1000000.0f, 0.001f);
+    }
+}
+
+static void testFloatAverage() {
+    Core::Random r(553);
+    float sum = 0;
+    for(int i = 0; i < 1000000; i++) {
+        sum += r.nextFloat();
+    }
+    sum /= 1000000.0f;
+    CORE_TEST_FLOAT(0.5f, sum, 0.001f);
+}
+
+static void testFloatCoin() {
+    Core::Random r(553);
+    Core::Array<int, 2> c(0);
+    for(int i = 0; i < 1000000; i++) {
+        c[r.nextFloat() < 0.5f]++;
+    }
+    CORE_TEST_FLOAT(0.0f, static_cast<float>(c[0] - c[1]) / 1000000.0f, 0.001f);
+}
+
+static void testFloatDistribution() {
+    Core::Random r(553);
+    Core::Array<int, 102> c(0);
+    for(int i = 0; i < 1000000; i++) {
+        c[static_cast<int>(r.nextFloat(1.0f, c.getLength() - 1))]++;
+    }
+    CORE_TEST_EQUAL(0, c[0]);
+    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
+    for(int i = 1; i < c.getLength() - 1; i++) {
+        CORE_TEST_FLOAT(0.01f, static_cast<float>(c[i]) / 1000000.0f, 0.001f);
+    }
+}
+
+void Core::RandomTests::test() {
+    testAverage();
+    testCoin();
+    testDistribution();
+    testFloatAverage();
+    testFloatCoin();
+    testFloatDistribution();
+}

+ 8 - 0
tests/RandomTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_RANDOM_TESTS_H
+#define CORE_RANDOM_TESTS_H
+
+namespace Core::RandomTests {
+    void test();
+}
+
+#endif

+ 1 - 1
tests/UniquePointerTests.cpp

@@ -4,7 +4,7 @@
 #include "utils/UniquePointer.h"
 #include "utils/Utility.h"
 
-struct B {
+struct B final {
     static int instances;
 
     B() {

+ 64 - 0
utils/Buffer.cpp

@@ -0,0 +1,64 @@
+#include "utils/Buffer.h"
+
+#include "math/Math.h"
+#include "utils/Utility.h"
+
+Core::Buffer::Buffer(int initialSize)
+    : length(0), capacity(initialSize <= 0 ? 1 : initialSize), buffer(nullptr) {
+}
+
+Core::Buffer::Buffer(Buffer&& other) : Buffer(1) {
+    swap(other);
+}
+
+Core::Buffer::~Buffer() {
+    free(buffer);
+}
+
+Core::Buffer& Core::Buffer::operator=(Buffer&& other) {
+    swap(other);
+    return *this;
+}
+
+bool Core::Buffer::copyFrom(const Buffer& other) {
+    clear();
+    return add(other.getData(), other.length);
+}
+
+bool Core::Buffer::add(const void* data, int size) {
+    if(length + size > capacity || buffer == nullptr) {
+        while(length + size > capacity) {
+            capacity += Math::max(4, capacity / 4);
+        }
+        char* newBuffer = static_cast<char*>(reallocate(buffer, capacity));
+        if(newBuffer == nullptr) {
+            return true;
+        }
+        buffer = newBuffer;
+    }
+    memoryCopy(buffer + length, data, size);
+    length += size;
+    return false;
+}
+
+int Core::Buffer::getLength() const {
+    return length;
+}
+
+Core::Buffer::operator const char*() const {
+    return buffer;
+}
+
+const char* Core::Buffer::getData() const {
+    return buffer;
+}
+
+void Core::Buffer::swap(Buffer& other) {
+    Core::swap(length, other.length);
+    Core::swap(capacity, other.capacity);
+    Core::swap(buffer, other.buffer);
+}
+
+void Core::Buffer::clear() {
+    length = 0;
+}

+ 43 - 0
utils/Buffer.h

@@ -0,0 +1,43 @@
+#ifndef CORE_BUFFER_H
+#define CORE_BUFFER_H
+
+#include "utils/Check.h"
+
+namespace Core {
+    class Buffer final {
+        int length;
+        int capacity;
+        char* buffer;
+
+    public:
+        Buffer(int initialSize = 32);
+        Buffer(const Buffer& other) = delete;
+        Buffer(Buffer&& other);
+        ~Buffer();
+        Buffer& operator=(const Buffer& other) = delete;
+        Buffer& operator=(Buffer&& other);
+
+        // returns true on error and calls the error callback
+        check_return bool copyFrom(const Buffer& other);
+
+        // returns true on error and calls the error callback
+        check_return bool add(const void* data, int size);
+
+        // returns true on error and calls the error callback
+        template<typename T>
+        check_return bool add(const T& t) {
+            return add(&t, sizeof(T));
+        }
+
+        int getLength() const;
+        operator const char*() const;
+        const char* getData() const;
+
+        void clear();
+
+    private:
+        void swap(Buffer& other);
+    };
+}
+
+#endif

+ 46 - 0
utils/Clock.cpp

@@ -0,0 +1,46 @@
+#include "utils/Clock.h"
+
+#include <threads.h>
+#include <time.h>
+
+Core::Clock::Clock() : index(0), last(0), sum(0), time(0) {
+}
+
+bool Core::Clock::update(Clock::Nanos& n) {
+    Nanos current = 0;
+    if(getNanos(current)) {
+        return true;
+    } else if(last == 0) {
+        last = current;
+        n = 0;
+        return false;
+    }
+    index = (index + 1) & (LENGTH - 1);
+    sum -= time[index];
+    time[index] = current - last;
+    sum += time[index];
+    last = current;
+    n = time[index];
+    return false;
+}
+
+float Core::Clock::getUpdatesPerSecond() const {
+    return (LENGTH * 1000000000.0f) / static_cast<float>(sum);
+}
+
+bool Core::Clock::getNanos(Clock::Nanos& n) {
+    struct timespec ts;
+    if(timespec_get(&ts, TIME_UTC) == 0) {
+        return CORE_ERROR(Error::TIME_NOT_AVAILABLE);
+    }
+    n = static_cast<Clock::Nanos>(ts.tv_sec) * 1'000'000'000L +
+        static_cast<Clock::Nanos>(ts.tv_nsec);
+    return false;
+}
+
+bool Core::Clock::wait(Nanos nanos) const {
+    struct timespec t;
+    t.tv_nsec = nanos % 1'000'000'000;
+    t.tv_sec = nanos / 1'000'000'000;
+    return CORE_ERROR(Error::SLEEP_INTERRUPTED, thrd_sleep(&t, nullptr) != 0);
+}

+ 34 - 0
utils/Clock.h

@@ -0,0 +1,34 @@
+#ifndef CORE_CLOCK_H
+#define CORE_CLOCK_H
+
+#include "data/Array.h"
+#include "utils/Check.h"
+
+namespace Core {
+    struct Clock final {
+        using Nanos = i64;
+
+    private:
+        static constexpr int BITS = 7;
+        static constexpr int LENGTH = 1 << BITS;
+        int index;
+        Nanos last;
+        Nanos sum;
+        Array<Nanos, LENGTH> time;
+
+    public:
+        Clock();
+
+        // returns true on error and calls the error callback
+        // the first invocation will always return 0 nanos
+        check_return bool update(Nanos& n);
+        float getUpdatesPerSecond() const;
+        // returns true on error and calls the error callback
+        check_return bool wait(Nanos nanos) const;
+
+        // returns true on error and calls the error callback
+        check_return static bool getNanos(Nanos& n);
+    };
+}
+
+#endif

+ 54 - 0
utils/Color.h

@@ -0,0 +1,54 @@
+#ifndef CORE_COLOR_H
+#define CORE_COLOR_H
+
+namespace Core {
+    using ColorChannel = unsigned char;
+
+    template<int N>
+    class Color final {
+        static_assert(N >= 1 && N <= 4, "a color has 1 to 4 channels");
+
+        ColorChannel data[static_cast<unsigned int>(N)] = {};
+
+    public:
+        Color() = default;
+
+        template<typename OT, typename... Args>
+        Color(OT a, Args&&... args) {
+            init<0>(a, args...);
+        }
+
+    private:
+        template<int I>
+        void init() {
+            static_assert(I == N,
+                          "color channel parameters do not match its size");
+        }
+
+        template<int I, typename OT, typename... Args>
+        void init(OT a, Args&&... args) {
+            data[I] = static_cast<ColorChannel>(a);
+            init<I + 1>(args...);
+        }
+
+    public:
+        float asFloat(int index) const {
+            return data[index] * (1.0f / 255.0f);
+        }
+
+        ColorChannel& operator[](int index) {
+            return data[index];
+        }
+
+        const ColorChannel& operator[](int index) const {
+            return data[index];
+        }
+    };
+
+    using Color4 = Color<4>;
+    using Color3 = Color<3>;
+    using Color2 = Color<2>;
+    using Color1 = Color<1>;
+}
+
+#endif

+ 48 - 0
utils/Random.cpp

@@ -0,0 +1,48 @@
+#include "utils/Random.h"
+
+Core::Random::Random(Seed seed) : index(0) {
+    for(int i = 0; i < N; i++) {
+        data[i] = seed;
+        seed = seed * 7 + 31;
+    }
+}
+
+void Core::Random::update() {
+    static const Seed map[2] = {0, 0x8EBFD028};
+    for(int i = 0; i < N - M; i++) {
+        data[i] = data[i + M] ^ (data[i] >> 1) ^ map[data[i] & 1];
+    }
+    for(int i = N - M; i < N; i++) {
+        data[i] = data[i + (M - N)] ^ (data[i] >> 1) ^ map[data[i] & 1];
+    }
+    index = 0;
+}
+
+int Core::Random::next() {
+    if(index >= N) {
+        update();
+    }
+    Seed r = data[index++];
+    r ^= (r << 7) & 0x2B5B2500;
+    r ^= (r << 15) & 0xDB8B0000;
+    r ^= (r >> 16);
+    return static_cast<int>(r >> 1);
+}
+
+int Core::Random::next(int min, int inclusiveMax) {
+    return min + next() % (inclusiveMax - min + 1);
+}
+
+bool Core::Random::nextBool() {
+    return next() & 1;
+}
+
+float Core::Random::nextFloat() {
+    static constexpr i32 m = 0x7FFFFFFF;
+    float f = static_cast<float>(next() & m) * (1.0f / static_cast<float>(m));
+    return f >= 1.0f ? nextFloat() : f;
+}
+
+float Core::Random::nextFloat(float min, float exclusiveMax) {
+    return min + nextFloat() * (exclusiveMax - min);
+}

+ 31 - 0
utils/Random.h

@@ -0,0 +1,31 @@
+#ifndef CORE_RANDOM_H
+#define CORE_RANDOM_H
+
+#include "utils/Utility.h"
+
+namespace Core {
+    struct Random final {
+        using Seed = u32;
+
+    private:
+        constexpr static int N = 25;
+        constexpr static int M = 7;
+
+        Seed data[N];
+        int index;
+
+    public:
+        Random(Seed seed);
+
+        int next();
+        int next(int min, int inclusiveMax);
+        bool nextBool();
+        float nextFloat();
+        float nextFloat(float min, float exclusiveMax);
+
+    private:
+        void update();
+    };
+}
+
+#endif

+ 1 - 1
utils/UniquePointer.h

@@ -3,7 +3,7 @@
 
 namespace Core {
     template<typename T>
-    class UniquePointer {
+    class UniquePointer final {
         T* t;
 
     public:

+ 22 - 2
utils/Utility.cpp

@@ -58,7 +58,9 @@ void* operator new(size_t bytes, void* p) noexcept {
 }*/
 
 void Core::exitWithHandler(const char* file, int line, int value) {
-    printf("\33[1;31mExit from %s:%d\33[39;49m\n", file, line);
+    if(value != 0) {
+        printf("\33[1;31mExit from %s:%d\33[39;49m\n", file, line);
+    }
     if(exitHandler != nullptr) {
         exitHandler(value, exitData);
     }
@@ -104,12 +106,30 @@ void Core::memorySet(void* p, int c, int n) {
 }
 
 void Core::memoryCopy(void* dest, const void* src, int n) {
-    if(n < 0) {
+    if(n <= 0) {
         return;
     }
     memcpy(dest, src, static_cast<unsigned int>(n));
 }
 
+bool Core::memoryCompare(const void* a, const void* b, int n) {
+    return n <= 0 ? true : memcmp(a, b, static_cast<unsigned int>(n)) == 0;
+}
+
+void* Core::reallocate(void* p, int n) {
+    if(n <= 0) {
+        free(p);
+        return nullptr;
+    }
+    void* newP = realloc(p, static_cast<unsigned int>(n));
+    CORE_ERROR(Error::OUT_OF_MEMORY, newP == nullptr);
+    return newP;
+}
+
+void Core::free(void* p) {
+    ::free(p);
+}
+
 const char* Core::getFileName(const char* path) {
     int end = 0;
     while(path[end] != '\0') {

+ 13 - 8
utils/Utility.h

@@ -15,7 +15,9 @@ namespace Core {
         INVALID_CHAR,
         NOT_FOUND,
         INVALID_INDEX,
-        INVALID_REMOVE_INDEX
+        INVALID_REMOVE_INDEX,
+        TIME_NOT_AVAILABLE,
+        SLEEP_INTERRUPTED
     };
 
     using ErrorHandler = void (*)(const char*, int, Error, void*);
@@ -26,37 +28,37 @@ namespace Core {
 
     namespace Internal {
         template<typename T>
-        struct BaseRemoveReference {
+        struct BaseRemoveReference final {
             using Type = T;
         };
 
         template<typename T>
-        struct BaseRemoveReference<T&> {
+        struct BaseRemoveReference<T&> final {
             using Type = T;
         };
 
         template<typename T>
-        struct BaseRemoveReference<T&&> {
+        struct BaseRemoveReference<T&&> final {
             using Type = T;
         };
 
         template<typename A, typename B>
-        struct BaseIsSame {
+        struct BaseIsSame final {
             static constexpr bool value = false;
         };
 
         template<typename T>
-        struct BaseIsSame<T, T> {
+        struct BaseIsSame<T, T> final {
             static constexpr bool value = true;
         };
 
         template<bool C, typename A, typename B>
-        struct BaseIf {
+        struct BaseIf final {
             using Type = A;
         };
 
         template<typename A, typename B>
-        struct BaseIf<false, A, B> {
+        struct BaseIf<false, A, B> final {
             using Type = B;
         };
     }
@@ -156,6 +158,9 @@ namespace Core {
 
     void memorySet(void* p, int c, int n);
     void memoryCopy(void* dest, const void* src, int n);
+    bool memoryCompare(const void* a, const void* b, int n);
+    void* reallocate(void* p, int n);
+    void free(void* p);
 
     const char* getFileName(const char* path);
 }